cfengine  3.15.4
About: CFEngine is a configuration management system for configuring and maintaining Unix-like computers (using an own high level policy language). Community version.
  Fossies Dox: cfengine-3.15.4.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

eval_context.c
Go to the documentation of this file.
1 /*
2  Copyright 2019 Northern.tech AS
3 
4  This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5 
6  This program is free software; you can redistribute it and/or modify it
7  under the terms of the GNU General Public License as published by the
8  Free Software Foundation; version 3.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program; if not, write to the Free Software
17  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18 
19  To the extent this program is licensed as part of the Enterprise
20  versions of CFEngine, the applicable Commercial Open Source License
21  (COSL) may apply to this file if you as a licensee so wish it. See
22  included file COSL.txt.
23 */
24 
25 #include <eval_context.h>
26 
27 #include <files_names.h>
28 #include <logic_expressions.h>
29 #include <syntax.h>
30 #include <item_lib.h>
31 #include <ornaments.h>
32 #include <expand.h> /* ExpandPrivateRval */
33 #include <matching.h>
34 #include <string_lib.h>
35 #include <misc_lib.h>
36 #include <file_lib.h>
37 #include <assoc.h>
38 #include <scope.h>
39 #include <vars.h>
40 #include <syslog_client.h>
41 #include <audit.h>
42 #include <rlist.h>
43 #include <buffer.h>
44 #include <promises.h>
45 #include <fncall.h>
46 #include <ring_buffer.h>
47 #include <logging_priv.h>
48 #include <known_dirs.h>
49 #include <printsize.h>
50 #include <regex.h>
51 #include <map.h>
52 #include <conversion.h> /* DataTypeIsIterable */
53 #include <cleanup.h>
54 
55 /* If we need to put a scoped variable into a special scope, use the string
56  * below to replace the original scope separator.
57  * (e.g. "config.var" -> "this.config___var" ) */
58 #define NESTED_SCOPE_SEP "___"
59 
61  "BUNDLE",
62  "BODY",
63  "PROMISE_TYPE",
64  "PROMISE",
65  "PROMISE_ITERATION"
66 };
67 
68 
69 /**
70  Define FuncCacheMap.
71  Key: an Rlist (which is linked list of Rvals)
72  listing all the argument of the function
73  Value: an Rval, the result of the function
74  */
75 
76 static void RvalDestroy2(void *p)
77 {
78  Rval *rv = p;
79  RvalDestroy(*rv);
80  free(rv);
81 }
82 
83 TYPED_MAP_DECLARE(FuncCache, Rlist *, Rval *)
84 
85 TYPED_MAP_DEFINE(FuncCache, Rlist *, Rval *,
90 
91 /**
92  Define RemoteVarsPromisesMap.
93  Key: bundle name (char *)
94  Value: a sequence of promises (const *Promise), only the container
95  (sequence) should be deallocated)
96  */
97 
98 static void SeqDestroy_untyped(void *p)
99 {
100  Seq *s = p;
101  SeqDestroy(s);
102 }
103 
104 TYPED_MAP_DECLARE(RemoteVarPromises, char *, Seq *)
105 
106 TYPED_MAP_DEFINE(RemoteVarPromises, char *, Seq *,
109  free,
111 
112 
114 
115 #include <policy.h>
116 
117 static bool BundleAborted(const EvalContext *ctx);
118 static void SetBundleAborted(EvalContext *ctx);
119 static void SetEvalAborted(EvalContext *ctx);
120 
121 static bool EvalContextStackFrameContainsSoft(const EvalContext *ctx, const char *context);
122 static bool EvalContextHeapContainsSoft(const EvalContext *ctx, const char *ns, const char *name);
123 static bool EvalContextHeapContainsHard(const EvalContext *ctx, const char *name);
124 static bool EvalContextClassPut(EvalContext *ctx, const char *ns, const char *name, bool is_soft, ContextScope scope, const char *tags);
125 static const char *EvalContextCurrentNamespace(const EvalContext *ctx);
126 static ClassRef IDRefQualify(const EvalContext *ctx, const char *id);
127 
128 /**
129  * Every agent has only one EvalContext from process start to finish.
130  */
132 {
133  /* TODO: a pointer to read-only version of config is often needed. */
134  /* const GenericAgentConfig *config; */
135 
142 
143  int pass;
145 
148 
150 
153 
155 
159 
160  uid_t uid;
161  uid_t gid;
162  pid_t pid;
163  pid_t ppid;
164 
165  // Full path to directory that the binary was launched from.
167 
168  char *entry_point;
169 
170  /* new package promise evaluation context */
172 
173  /* select_end_match_eof context*/
175 
176  /* List if all classes set during policy evaluation */
178 
179  /* These following two fields are needed for remote variable injection
180  * detection (CFE-1915) */
181  /* Names of all bundles */
183 
184  /* Promises possibly remotely-injecting variables */
185  /* ONLY INITIALIZED WHEN NON-EMPTY, OTHERWISE NULL */
187 };
188 
190 {
191  return ctx->select_end_match_eof;
192 }
193 
195 {
196  ctx->select_end_match_eof = value;
197 }
198 
199 
200 void AddDefaultPackageModuleToContext(const EvalContext *ctx, char *name)
201 {
202  assert(ctx);
203  assert(ctx->package_promise_context);
204 
207  SafeStringDuplicate(name);
208 }
209 
210 void AddDefaultInventoryToContext(const EvalContext *ctx, Rlist *inventory)
211 {
212  assert(ctx);
213  assert(ctx->package_promise_context);
214 
217  RlistCopy(inventory);
218 }
219 
220 static
221 int PackageManagerSeqCompare(const void *a, const void *b, ARG_UNUSED void *data)
222 {
223  return StringSafeCompare((char*)a, ((PackageModuleBody*)b)->name);
224 }
225 
227 {
228  assert(ctx != NULL);
229  assert(pm != NULL);
230 
231  /* First check if the body is there added from previous pre-evaluation
232  * iteration. If it is there update it as we can have new expanded variables. */
233  Seq *const bodies = ctx->package_promise_context->package_modules_bodies;
234  ssize_t index = SeqIndexOf(bodies, pm->name, PackageManagerSeqCompare);
235  if (index != -1)
236  {
237  SeqRemove(bodies, index);
238  }
239  SeqAppend(bodies, pm);
240 }
241 
243  const char *name)
244 {
245  if (name == NULL)
246  {
247  return NULL;
248  }
249 
250  for (int i = 0;
252  i++)
253  {
254  PackageModuleBody *pm =
256  if (strcmp(name, pm->name) == 0)
257  {
258  return pm;
259  }
260  }
261  return NULL;
262 }
263 
265 {
266  char *def_pm_name = ctx->package_promise_context->control_package_module;
267  return GetPackageModuleFromContext(ctx, def_pm_name);
268 }
269 
271 {
273 }
274 
276 {
277  return ctx->package_promise_context;
278 }
279 
280 
281 static StackFrame *LastStackFrame(const EvalContext *ctx, size_t offset)
282 {
283  if (SeqLength(ctx->stack) <= offset)
284  {
285  return NULL;
286  }
287  return SeqAt(ctx->stack, SeqLength(ctx->stack) - 1 - offset);
288 }
289 
291 {
292  for (size_t i = 0; i < SeqLength(ctx->stack); i++)
293  {
294  StackFrame *frame = LastStackFrame(ctx, i);
295  if (frame->type == type)
296  {
297  return frame;
298  }
299  }
300 
301  return NULL;
302 }
303 
305 {
306  if (adjust == -1)
307  {
308  return base;
309  }
310  else
311  {
312  return MAX(base, adjust);
313  }
314 }
315 
316 static LogLevel StringToLogLevel(const char *value)
317 {
318  if (value)
319  {
320  if (!strcmp(value, "verbose"))
321  {
322  return LOG_LEVEL_VERBOSE;
323  }
324  if (!strcmp(value, "inform"))
325  {
326  return LOG_LEVEL_INFO;
327  }
328  if (!strcmp(value, "error"))
329  {
330  return LOG_LEVEL_NOTICE; /* Error level includes warnings and notices */
331  }
332  }
333  return -1;
334 }
335 
336 static LogLevel GetLevelForPromise(const Promise *pp, const char *attr_name)
337 {
339 }
340 
342 {
343  LogLevel log_level = LogGetGlobalLevel();
344 
345  if (pp)
346  {
347  log_level = AdjustLogLevel(log_level, GetLevelForPromise(pp, "log_level"));
348  }
349 
350  /* Disable system log for dry-runs */
351  if (DONTDO)
352  {
353  log_level = LOG_LEVEL_NOTHING;
354  }
355 
356  return log_level;
357 }
358 
360 {
361  LogLevel report_level = LogGetGlobalLevel();
362 
363  if (pp)
364  {
365  report_level = AdjustLogLevel(report_level, GetLevelForPromise(pp, "report_level"));
366  }
367 
368  return report_level;
369 }
370 
372 {
373  StackFrame *last_frame = LastStackFrame(ctx, 0);
374  if (last_frame)
375  {
376  return last_frame->path;
377  }
378  return "";
379 }
380 
381 static const char *GetAgentAbortingContext(const EvalContext *ctx)
382 {
383  for (const Item *ip = ctx->heap_abort; ip != NULL; ip = ip->next)
384  {
385  if (IsDefinedClass(ctx, ip->classes))
386  {
387  const char *regex = ip->name;
388  Class *cls = EvalContextClassMatch(ctx, regex);
389 
390  if (cls)
391  {
392  return cls->name;
393  }
394  }
395  }
396  return NULL;
397 }
398 
399 static void EvalContextStackFrameAddSoft(EvalContext *ctx, const char *context, const char *tags)
400 {
401  assert(SeqLength(ctx->stack) > 0);
402 
403  StackFrameBundle frame;
404  {
406  if (!last_frame)
407  {
408  ProgrammingError("Attempted to add a soft class on the stack, but stack had no bundle frame");
409  }
410  frame = last_frame->data.bundle;
411  }
412 
413  char copy[CF_BUFSIZE];
414  if (strcmp(frame.owner->ns, "default") != 0)
415  {
416  snprintf(copy, CF_MAXVARSIZE, "%s:%s", frame.owner->ns, context);
417  }
418  else
419  {
420  strlcpy(copy, context, CF_MAXVARSIZE);
421  }
422 
423  if (Chop(copy, CF_EXPANDSIZE) == -1)
424  {
425  Log(LOG_LEVEL_ERR, "Chop was called on a string that seemed to have no terminator");
426  }
427 
428  if (strlen(copy) == 0)
429  {
430  return;
431  }
432 
433  if (EvalContextHeapContainsSoft(ctx, frame.owner->ns, context))
434  {
435  Log(LOG_LEVEL_WARNING, "Private class '%s' in bundle '%s' shadows a global class - you should choose a different name to avoid conflicts",
436  copy, frame.owner->name);
437  }
438 
439  if (IsRegexItemIn(ctx, ctx->heap_abort_current_bundle, copy))
440  {
441  Log(LOG_LEVEL_ERR, "Bundle '%s' aborted on defined class '%s'", frame.owner->name, copy);
442  SetBundleAborted(ctx);
443  }
444 
445  if (IsRegexItemIn(ctx, ctx->heap_abort, copy))
446  {
447  Log(LOG_LEVEL_NOTICE, "cf-agent aborted on defined class '%s'", copy);
448  SetEvalAborted(ctx);
449  }
450 
451  if (EvalContextStackFrameContainsSoft(ctx, copy))
452  {
453  return;
454  }
455 
456  ClassTablePut(frame.classes, frame.owner->ns, context, true, CONTEXT_SCOPE_BUNDLE, tags);
457 
458  if (!BundleAborted(ctx))
459  {
460  for (const Item *ip = ctx->heap_abort_current_bundle; ip != NULL; ip = ip->next)
461  {
462  if (IsDefinedClass(ctx, ip->name))
463  {
464  Log(LOG_LEVEL_ERR, "Setting abort for '%s' when setting '%s'", ip->name, context);
465  SetBundleAborted(ctx);
466  break;
467  }
468  }
469  }
470 }
471 
472 static ExpressionValue EvalTokenAsClass(const char *classname, void *param)
473 {
474  const EvalContext *ctx = param;
475  ClassRef ref = ClassRefParse(classname);
476  if (ClassRefIsQualified(ref))
477  {
478  if (strcmp(ref.ns, NamespaceDefault()) == 0)
479  {
480  if (EvalContextHeapContainsHard(ctx, ref.name))
481  {
482  ClassRefDestroy(ref);
483  return EXPRESSION_VALUE_TRUE;
484  }
485  }
486  }
487  else
488  {
489  if (EvalContextHeapContainsHard(ctx, ref.name))
490  {
491  ClassRefDestroy(ref);
492  return EXPRESSION_VALUE_TRUE;
493  }
494 
495  const char *ns = EvalContextCurrentNamespace(ctx);
496  if (ns)
497  {
498  ClassRefQualify(&ref, ns);
499  }
500  else
501  {
503  }
504  }
505 
506  assert(ClassRefIsQualified(ref));
507  bool classy = (strcmp("any", ref.name) == 0 ||
508  EvalContextHeapContainsSoft(ctx, ref.ns, ref.name) ||
510 
511  ClassRefDestroy(ref);
512  return (ExpressionValue) classy; // ExpressionValue extends bool
513 }
514 
515 /**********************************************************************/
516 
517 static char *EvalVarRef(ARG_UNUSED const char *varname, ARG_UNUSED VarRefType type, ARG_UNUSED void *param)
518 {
519 /*
520  * There should be no unexpanded variables when we evaluate any kind of
521  * logic expressions, until parsing of logic expression changes and they are
522  * not pre-expanded before evaluation.
523  */
524  return NULL;
525 }
526 
527 /**********************************************************************/
528 
530 {
531  return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
532 }
533 
534 ExpressionValue CheckClassExpression(const EvalContext *ctx, const char *context)
535 {
536  assert(context != NULL);
537  ParseResult res;
538 
539  if (!context)
540  {
541  // TODO: Remove this, seems like a hack
542  return EXPRESSION_VALUE_TRUE;
543  }
544 
546  {
548  }
549 
551  {
552  Log(LOG_LEVEL_ERR, "The context expression whitespace regular expression could not be compiled, aborting.");
553  return EXPRESSION_VALUE_ERROR;
554  }
555 
557  {
558  Log(LOG_LEVEL_ERR, "class expressions can't be separated by whitespace without an intervening operator in expression '%s'", context);
559  return EXPRESSION_VALUE_ERROR;
560  }
561 
562  Buffer *condensed = BufferNewFrom(context, strlen(context));
563  BufferRewrite(condensed, &ClassCharIsWhitespace, true);
564  res = ParseExpression(BufferData(condensed), 0, BufferSize(condensed));
565  BufferDestroy(condensed);
566 
567  if (!res.result)
568  {
569  Log(LOG_LEVEL_ERR, "Unable to parse class expression '%s'", context);
570  return EXPRESSION_VALUE_ERROR;
571  }
572  else
573  {
576  (void *)ctx); // controlled cast. None of these should modify EvalContext
577 
578  FreeExpression(res.result);
579  return r;
580  }
581 }
582 
583 /**********************************************************************/
584 
585 static ExpressionValue EvalTokenFromList(const char *token, void *param)
586 {
587  StringSet *set = param;
588  return (ExpressionValue) StringSetContains(set, token); // EV extends bool
589 }
590 
591 /**********************************************************************/
592 
593 static bool EvalWithTokenFromList(const char *expr, StringSet *token_set)
594 {
595  ParseResult res = ParseExpression(expr, 0, strlen(expr));
596 
597  if (!res.result)
598  {
599  Log(LOG_LEVEL_ERR, "Syntax error in expression '%s'", expr);
600  return false; /* FIXME: return error */
601  }
602  else
603  {
606  &EvalVarRef,
607  token_set);
608 
609  FreeExpression(res.result);
610 
611  /* r is EvalResult which could be ERROR */
612  return r == EXPRESSION_VALUE_TRUE;
613  }
614 }
615 
616 /**********************************************************************/
617 
618 /* Process result expression */
619 
620 bool EvalProcessResult(const char *process_result, StringSet *proc_attr)
621 {
622  assert(process_result != NULL);
623  if (StringEqual(process_result, ""))
624  {
625  /* nothing to evaluate */
626  return false;
627  }
628  return EvalWithTokenFromList(process_result, proc_attr);
629 }
630 
631 /**********************************************************************/
632 
633 /* File result expressions */
634 
635 bool EvalFileResult(const char *file_result, StringSet *leaf_attr)
636 {
637  return EvalWithTokenFromList(file_result, leaf_attr);
638 }
639 
640 /*****************************************************************************/
641 
642 
643 void EvalContextHeapPersistentSave(EvalContext *ctx, const char *name, unsigned int ttl_minutes,
644  PersistentClassPolicy policy, const char *tags)
645 {
646  assert(tags);
647 
648  time_t now = time(NULL);
649 
650  CF_DB *dbp;
651  if (!OpenDB(&dbp, dbid_state))
652  {
653  char *db_path = DBIdToPath(dbid_state);
654  Log(LOG_LEVEL_ERR, "While persisting class, unable to open database at '%s' (OpenDB: %s)",
655  db_path, GetErrorStr());
656  free(db_path);
657  return;
658  }
659 
660  ClassRef ref = IDRefQualify(ctx, name);
661  char *key = ClassRefToString(ref.ns, ref.name);
662  ClassRefDestroy(ref);
663 
664  size_t tags_length = strlen(tags) + 1;
665  size_t new_info_size = sizeof(PersistentClassInfo) + tags_length;
666 
667  PersistentClassInfo *new_info = xcalloc(1, new_info_size);
668 
669  new_info->expires = now + ttl_minutes * 60;
670  new_info->policy = policy;
671  strlcpy(new_info->tags, tags, tags_length);
672 
673  // first see if we have an existing record, and if we should bother to update
674  {
675  int existing_info_size = ValueSizeDB(dbp, key, strlen(key));
676  if (existing_info_size > 0)
677  {
678  PersistentClassInfo *existing_info = xcalloc(existing_info_size, 1);
679  if (ReadDB(dbp, key, existing_info, existing_info_size))
680  {
681  if (existing_info->policy == CONTEXT_STATE_POLICY_PRESERVE &&
682  now < existing_info->expires &&
683  strcmp(existing_info->tags, new_info->tags) == 0)
684  {
685  Log(LOG_LEVEL_VERBOSE, "Persistent class '%s' is already in a preserved state -- %jd minutes to go",
686  key, (intmax_t)((existing_info->expires - now) / 60));
687  CloseDB(dbp);
688  free(key);
689  free(new_info);
690  return;
691  }
692  }
693  else
694  {
695  Log(LOG_LEVEL_ERR, "While persisting class '%s', error reading existing value", key);
696  CloseDB(dbp);
697  free(key);
698  free(new_info);
699  return;
700  }
701  }
702  }
703 
704  Log(LOG_LEVEL_VERBOSE, "Updating persistent class '%s'", key);
705 
706  WriteDB(dbp, key, new_info, new_info_size);
707 
708  CloseDB(dbp);
709  free(key);
710  free(new_info);
711 }
712 
713 /*****************************************************************************/
714 
715 void EvalContextHeapPersistentRemove(const char *context)
716 {
717  CF_DB *dbp;
718 
719  if (!OpenDB(&dbp, dbid_state))
720  {
721  return;
722  }
723 
724  DeleteDB(dbp, context);
725  Log(LOG_LEVEL_DEBUG, "Deleted persistent class '%s'", context);
726  CloseDB(dbp);
727 }
728 
729 /*****************************************************************************/
730 
732 {
733  time_t now = time(NULL);
734 
735  Log(LOG_LEVEL_VERBOSE, "Loading persistent classes");
736 
737  CF_DB *dbp;
738  if (!OpenDB(&dbp, dbid_state))
739  {
740  return;
741  }
742 
743  CF_DBC *dbcp;
744  if (!NewDBCursor(dbp, &dbcp))
745  {
746  Log(LOG_LEVEL_INFO, "Unable to scan persistence cache");
747  return;
748  }
749 
750  const char *key;
751  int key_size = 0;
752  void *info_p;
753  int info_size = 0;
754 
755  while (NextDB(dbcp, (char **)&key, &key_size, &info_p, &info_size))
756  {
757  Log(LOG_LEVEL_DEBUG, "Found key persistent class key '%s'", key);
758 
759  /* Info points to db-owned data, which is not aligned properly and
760  * dereferencing might be slow or even cause SIGBUS! */
761  PersistentClassInfo info = { 0 };
762  memcpy(&info, info_p,
763  info_size < sizeof(info) ? info_size : sizeof(info));
764 
765  const char *tags = NULL;
766  if (info_size > sizeof(PersistentClassInfo))
767  {
768  /* This is char pointer, it can point to unaligned data. */
769  tags = ((PersistentClassInfo *) info_p)->tags;
770  }
771  else
772  {
773  tags = ""; /* no tags */
774  }
775 
776  if (now > info.expires)
777  {
778  Log(LOG_LEVEL_VERBOSE, "Persistent class '%s' expired", key);
779  DBCursorDeleteEntry(dbcp);
780  }
781  else
782  {
783  Log(LOG_LEVEL_VERBOSE, "Persistent class '%s' for %jd more minutes",
784  key, (intmax_t) ((info.expires - now) / 60));
785  Log(LOG_LEVEL_DEBUG, "Adding persistent class '%s'", key);
786 
787  ClassRef ref = ClassRefParse(key);
788  EvalContextClassPut(ctx, ref.ns, ref.name, true, CONTEXT_SCOPE_NAMESPACE, tags);
789 
790  StringSet *tag_set = EvalContextClassTags(ctx, ref.ns, ref.name);
791  assert(tag_set);
792 
793  StringSetAdd(tag_set, xstrdup("source=persistent"));
794 
795  ClassRefDestroy(ref);
796  }
797  }
798 
799  DeleteDBCursor(dbcp);
800  CloseDB(dbp);
801 }
802 
804 {
805  assert(ctx != NULL);
806  if (ctx->bundle_aborted)
807  {
808  ctx->bundle_aborted = false;
809  return true;
810  }
811 
812  return false;
813 }
814 
815 static bool BundleAborted(const EvalContext* ctx)
816 {
817  assert(ctx != NULL);
818  return ctx->bundle_aborted;
819 }
820 
821 static void SetBundleAborted(EvalContext *ctx)
822 {
823  assert(ctx != NULL);
824  ctx->bundle_aborted = true;
825 }
826 
827 static void SetEvalAborted(EvalContext *ctx)
828 {
829  assert(ctx != NULL);
830  ctx->eval_aborted = true;
831 }
832 
833 bool EvalAborted(const EvalContext *ctx)
834 {
835  assert(ctx != NULL);
836  return ctx->eval_aborted;
837 }
838 
839 void EvalContextHeapAddAbort(EvalContext *ctx, const char *context, const char *activated_on_context)
840 {
841  assert(ctx != NULL);
842  if (!IsItemIn(ctx->heap_abort, context))
843  {
844  AppendItem(&ctx->heap_abort, context, activated_on_context);
845  }
846 
847  const char *aborting_context = GetAgentAbortingContext(ctx);
848 
849  if (aborting_context)
850  {
851  Log(LOG_LEVEL_NOTICE, "cf-agent aborted on defined class '%s'", aborting_context);
852  SetEvalAborted(ctx);
853  }
854 }
855 
856 void EvalContextHeapAddAbortCurrentBundle(EvalContext *ctx, const char *context, const char *activated_on_context)
857 {
858  assert(ctx != NULL);
859  if (!IsItemIn(ctx->heap_abort_current_bundle, context))
860  {
861  AppendItem(&ctx->heap_abort_current_bundle, context, activated_on_context);
862  }
863 }
864 
865 /*****************************************************************************/
866 
868 {
869  assert(ctx != NULL);
870  const Rlist *dependenies = PromiseGetConstraintAsList(ctx, "depends_on", pp);
871  if (RlistIsNullList(dependenies))
872  {
873  return false;
874  }
875 
876  for (const Rlist *rp = PromiseGetConstraintAsList(ctx, "depends_on", pp); rp; rp = rp->next)
877  {
878  if (rp->val.type != RVAL_TYPE_SCALAR)
879  {
880  return true;
881  }
882 
884  {
885  Log(LOG_LEVEL_VERBOSE, "Skipping promise '%s', as promise dependency '%s' has not yet been kept",
886  pp->promiser, RlistScalarValue(rp));
887  return true;
888  }
889  }
890 
891  return false;
892 }
893 
895 {
896  ClassTableDestroy(frame.classes);
897  VariableTableDestroy(frame.vars);
898 }
899 
901 {
902  VariableTableDestroy(frame.vars);
903 }
904 
906 {
907  VariableTableDestroy(frame.vars);
908 }
909 
911 {
912  PromiseDestroy(frame.owner);
914 }
915 
916 static void StackFrameDestroy(StackFrame *frame)
917 {
918  if (frame)
919  {
920  switch (frame->type)
921  {
924  break;
925 
928  break;
929 
931  break;
932 
935  break;
936 
939  break;
940 
941  default:
942  ProgrammingError("Unhandled stack frame type");
943  }
944 
945  free(frame->path);
946  free(frame);
947  }
948 }
949 
950 static
952 {
953  assert(manager != NULL);
954 
955  free(manager->name);
956  free(manager->interpreter);
957  free(manager->module_path);
958  RlistDestroy(manager->options);
959  free(manager);
960 }
961 
962 static
964 {
965  PackagePromiseContext *package_promise_defaults =
967  package_promise_defaults->control_package_module = NULL;
968  package_promise_defaults->control_package_inventory = NULL;
969  package_promise_defaults->package_modules_bodies =
971 
972  return package_promise_defaults;
973 }
974 
975 static
977 {
980  free(pp_ctx->control_package_module);
981  free(pp_ctx);
982 }
983 
984 /* Keeps the last 5 messages of each promise in a ring buffer in the
985  * EvalContext, which are written to a JSON file from the Enterprise function
986  * EvalContextLogPromiseIterationOutcome() at the end of each promise. */
987 char *MissionPortalLogHook(LoggingPrivContext *pctx, LogLevel level, const char *message)
988 {
989  const EvalContext *ctx = pctx->param;
990 
991  StackFrame *last_frame = LastStackFrame(ctx, 0);
992  if (last_frame
993  && last_frame->type == STACK_FRAME_TYPE_PROMISE_ITERATION
994  && level <= LOG_LEVEL_INFO)
995  {
997  }
998  return xstrdup(message);
999 }
1000 
1003 {
1004 }
1005 
1007 {
1008  EvalContext *ctx = xcalloc(1, sizeof(EvalContext));
1009 
1011  ctx->stack = SeqNew(10, StackFrameDestroy);
1012  ctx->global_classes = ClassTableNew();
1016 
1017  ctx->uid = getuid();
1018  ctx->gid = getgid();
1019  ctx->pid = getpid();
1020 
1021 #ifndef __MINGW32__
1022  ctx->ppid = getppid();
1023 #endif
1024 
1027 
1029 
1031 
1032  ctx->all_classes = NULL;
1033  ctx->bundle_names = StringSetNew();
1034  ctx->remote_var_promises = NULL;
1035 
1036  ctx->select_end_match_eof = false;
1037 
1038  return ctx;
1039 }
1040 
1042 {
1043  if (ctx)
1044  {
1045  free(ctx->launch_directory);
1046  free(ctx->entry_point);
1047 
1048  // Freeing logging context doesn't belong here...
1049  {
1051  free(pctx);
1053  }
1055 
1057 
1058  DeleteItemList(ctx->heap_abort);
1060 
1061  RlistDestroy(ctx->args);
1062 
1063  SeqDestroy(ctx->stack);
1064 
1068 
1071 
1073 
1075 
1078  if (ctx->remote_var_promises != NULL)
1079  {
1081  ctx->remote_var_promises = NULL;
1082  }
1083 
1084  free(ctx);
1085  }
1086 }
1087 
1088 static bool EvalContextHeapContainsSoft(const EvalContext *ctx, const char *ns, const char *name)
1089 {
1090  const Class *cls = ClassTableGet(ctx->global_classes, ns, name);
1091  return cls && cls->is_soft;
1092 }
1093 
1094 static bool EvalContextHeapContainsHard(const EvalContext *ctx, const char *name)
1095 {
1096  const Class *cls = ClassTableGet(ctx->global_classes, NULL, name);
1097  return cls && !cls->is_soft;
1098 }
1099 
1100 bool StackFrameContainsSoftRecursive(const EvalContext *ctx, const char *context, size_t stack_index)
1101 {
1102  StackFrame *frame = SeqAt(ctx->stack, stack_index);
1103  if (frame->type == STACK_FRAME_TYPE_BUNDLE && ClassTableGet(frame->data.bundle.classes, frame->data.bundle.owner->ns, context) != NULL)
1104  {
1105  return true;
1106  }
1107  else if (stack_index > 0 && frame->inherits_previous)
1108  {
1109  return StackFrameContainsSoftRecursive(ctx, context, stack_index - 1);
1110  }
1111  else
1112  {
1113  return false;
1114  }
1115 }
1116 
1117 static bool EvalContextStackFrameContainsSoft(const EvalContext *ctx, const char *context)
1118 {
1119  if (SeqLength(ctx->stack) == 0)
1120  {
1121  return false;
1122  }
1123 
1124  size_t stack_index = SeqLength(ctx->stack) - 1;
1125  return StackFrameContainsSoftRecursive(ctx, context, stack_index);
1126 }
1127 
1128 bool EvalContextHeapRemoveSoft(EvalContext *ctx, const char *ns, const char *name)
1129 {
1130  return ClassTableRemove(ctx->global_classes, ns, name);
1131 }
1132 
1133 bool EvalContextHeapRemoveHard(EvalContext *ctx, const char *name)
1134 {
1135  return ClassTableRemove(ctx->global_classes, NULL, name);
1136 }
1137 
1139 {
1145  SeqClear(ctx->stack);
1147 }
1148 
1150  Rlist *callers_promisers = NULL;
1151 
1152  for (size_t i = 0; i < SeqLength(ctx->stack); i++)
1153  {
1154  StackFrame *frame = SeqAt(ctx->stack, i);
1155  switch (frame->type)
1156  {
1157  case STACK_FRAME_TYPE_BODY:
1158  break;
1159 
1161  break;
1162 
1164  break;
1165 
1167  if (strcmp(frame->data.promise.owner->parent_promise_type->name, "methods") == 0) {
1168  RlistAppendScalar(&callers_promisers, frame->data.promise.owner->promiser);
1169  }
1170  break;
1171 
1173  break;
1174 
1175  default:
1176  ProgrammingError("Unhandled stack frame type");
1177  }
1178  }
1179  return callers_promisers;
1180 }
1181 
1183  JsonElement *callers = JsonArrayCreate(4);
1184  size_t depth = SeqLength(ctx->stack);
1185 
1186  for (size_t i = 0; i < depth; i++)
1187  {
1188  StackFrame *frame = SeqAt(ctx->stack, i);
1189  JsonElement *f = JsonObjectCreate(10);
1190  JsonObjectAppendInteger(f, "frame", depth-i);
1191  JsonObjectAppendInteger(f, "depth", i);
1192 
1193  switch (frame->type)
1194  {
1195  case STACK_FRAME_TYPE_BODY:
1196  JsonObjectAppendString(f, "type", "body");
1197  JsonObjectAppendObject(f, "body", BodyToJson(frame->data.body.owner));
1198  break;
1199 
1201  JsonObjectAppendString(f, "type", "bundle");
1202  JsonObjectAppendObject(f, "bundle", BundleToJson(frame->data.bundle.owner));
1203  break;
1204 
1206  JsonObjectAppendString(f, "type", "iteration");
1207  JsonObjectAppendInteger(f, "iteration_index", frame->data.promise_iteration.index);
1208 
1209  break;
1210 
1212  JsonObjectAppendString(f, "type", "promise");
1213  JsonObjectAppendString(f, "promise_type", frame->data.promise.owner->parent_promise_type->name);
1214  JsonObjectAppendString(f, "promiser", frame->data.promise.owner->promiser);
1215  JsonObjectAppendString(f, "promise_classes", frame->data.promise.owner->classes);
1216  JsonObjectAppendString(f, "promise_comment",
1217  (frame->data.promise.owner->comment == NULL) ? "" : frame->data.promise.owner->comment);
1218  break;
1219 
1221  JsonObjectAppendString(f, "type", "promise_type");
1222  JsonObjectAppendString(f, "promise_type", frame->data.promise_type.owner->name);
1223  break;
1224 
1225  default:
1226  ProgrammingError("Unhandled stack frame type");
1227  }
1228 
1229  JsonArrayAppendObject(callers, f);
1230  }
1231 
1232  return callers;
1233 }
1234 
1236 {
1237  if (ctx->args)
1238  {
1239  RlistDestroy(ctx->args);
1240  }
1241 
1242  ctx->args = RlistCopy(args);
1243 }
1244 
1246 {
1247  return (Rlist *) ctx->args;
1248 }
1249 
1250 void EvalContextSetPass(EvalContext *ctx, int pass)
1251 {
1252  ctx->pass = pass;
1253 }
1254 
1256 {
1257  return ctx->pass;
1258 }
1259 
1260 static StackFrame *StackFrameNew(StackFrameType type, bool inherit_previous)
1261 {
1262  StackFrame *frame = xmalloc(sizeof(StackFrame));
1263 
1264  frame->type = type;
1265  frame->inherits_previous = inherit_previous;
1266  frame->path = NULL;
1267 
1268  return frame;
1269 }
1270 
1271 static StackFrame *StackFrameNewBundle(const Bundle *owner, bool inherit_previous)
1272 {
1273  StackFrame *frame = StackFrameNew(STACK_FRAME_TYPE_BUNDLE, inherit_previous);
1274 
1275  frame->data.bundle.owner = owner;
1276  frame->data.bundle.classes = ClassTableNew();
1277  frame->data.bundle.vars = VariableTableNew();
1278 
1279  return frame;
1280 }
1281 
1282 static StackFrame *StackFrameNewBody(const Body *owner)
1283 {
1285 
1286  frame->data.body.owner = owner;
1287  frame->data.body.vars = VariableTableNew();
1288 
1289  return frame;
1290 }
1291 
1293 {
1295 
1296  frame->data.promise_type.owner = owner;
1297 
1298  return frame;
1299 }
1300 
1302 {
1304 
1305  frame->data.promise.owner = owner;
1306 
1307  return frame;
1308 }
1309 
1311 {
1313 
1314  frame->data.promise_iteration.owner = owner;
1315  frame->data.promise_iteration.iter_ctx = iter_ctx;
1317 
1318  return frame;
1319 }
1320 
1321 void EvalContextStackFrameRemoveSoft(EvalContext *ctx, const char *context)
1322 {
1324  assert(frame);
1325 
1326  ClassTableRemove(frame->data.bundle.classes, frame->data.bundle.owner->ns, context);
1327 }
1328 
1330 {
1331  StackFrame *last_frame = LastStackFrame(ctx, 0);
1332  if (last_frame)
1333  {
1334  if (last_frame->type == STACK_FRAME_TYPE_PROMISE_ITERATION)
1335  {
1337  }
1338  }
1339 
1340  SeqAppend(ctx->stack, frame);
1341 
1342  assert(!frame->path);
1343  frame->path = EvalContextStackPath(ctx);
1344 
1345  LogDebug(LOG_MOD_EVALCTX, "PUSHED FRAME (type %s)",
1346  STACK_FRAME_TYPE_STR[frame->type]);
1347 }
1348 
1349 void EvalContextStackPushBundleFrame(EvalContext *ctx, const Bundle *owner, const Rlist *args, bool inherits_previous)
1350 {
1351  assert(!LastStackFrame(ctx, 0) || LastStackFrame(ctx, 0)->type == STACK_FRAME_TYPE_PROMISE_ITERATION);
1352 
1353  EvalContextStackPushFrame(ctx, StackFrameNewBundle(owner, inherits_previous));
1354 
1355  if (RlistLen(args) > 0)
1356  {
1357  const Promise *caller = EvalContextStackCurrentPromise(ctx);
1358  if (caller)
1359  {
1361  VariableTableClear(table, NULL, NULL, NULL);
1362  }
1363 
1364  ScopeAugment(ctx, owner, caller, args);
1365  }
1366 
1367  {
1369  Variable *var = NULL;
1370  while ((var = VariableTableIteratorNext(iter)))
1371  {
1372  Rval retval = ExpandPrivateRval(ctx, owner->ns, owner->name, var->rval.item, var->rval.type);
1373  RvalDestroy(var->rval);
1374  var->rval = retval;
1375  }
1377  }
1378 }
1379 
1380 void EvalContextStackPushBodyFrame(EvalContext *ctx, const Promise *caller, const Body *body, const Rlist *args)
1381 {
1382 #ifndef NDEBUG
1383  StackFrame *last_frame = LastStackFrame(ctx, 0);
1384  if (last_frame)
1385  {
1386  assert(last_frame->type == STACK_FRAME_TYPE_PROMISE_TYPE);
1387  }
1388  else
1389  {
1390  assert(strcmp("control", body->name) == 0);
1391  }
1392 #endif
1393 
1394 
1396 
1397  if (RlistLen(body->args) != RlistLen(args))
1398  {
1399  if (caller)
1400  {
1401  Log(LOG_LEVEL_ERR, "Argument arity mismatch in body '%s' at line %zu in file '%s', expected %d, got %d",
1402  body->name, caller->offset.line, PromiseGetBundle(caller)->source_path, RlistLen(body->args), RlistLen(args));
1403  }
1404  else
1405  {
1406  assert(strcmp("control", body->name) == 0);
1407  ProgrammingError("Control body stack frame was pushed with arguments. This should have been caught before");
1408  }
1409  return;
1410  }
1411  else
1412  {
1413  ScopeMapBodyArgs(ctx, body, args);
1414  }
1415 }
1416 
1418 {
1419  assert(LastStackFrame(ctx, 0) && LastStackFrame(ctx, 0)->type == STACK_FRAME_TYPE_BUNDLE);
1420 
1421  StackFrame *frame = StackFrameNewPromiseType(owner);
1422  EvalContextStackPushFrame(ctx, frame);
1423 }
1424 
1426 {
1427  assert(LastStackFrame(ctx, 0));
1428  assert(LastStackFrame(ctx, 0)->type == STACK_FRAME_TYPE_PROMISE_TYPE);
1429 
1431 
1432  StackFrame *frame = StackFrameNewPromise(owner);
1433 
1434  EvalContextStackPushFrame(ctx, frame);
1435 
1436  // create an empty table
1437  frame->data.promise.vars = VariableTableNew();
1438 
1439  if (PromiseGetBundle(owner)->source_path)
1440  {
1441  char path[CF_BUFSIZE];
1442  if (!IsAbsoluteFileName(PromiseGetBundle(owner)->source_path) && ctx->launch_directory)
1443  {
1444  snprintf(path, CF_BUFSIZE, "%s%c%s", ctx->launch_directory, FILE_SEPARATOR, PromiseGetBundle(owner)->source_path);
1445  }
1446  else
1447  {
1448  strlcpy(path, PromiseGetBundle(owner)->source_path, CF_BUFSIZE);
1449  }
1450 
1451  EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promise_filename", path, CF_DATA_TYPE_STRING, "source=promise");
1452 
1453  // We now make path just the directory name!
1454  DeleteSlash(path);
1455  ChopLastNode(path);
1456 
1457  EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promise_dirname", path, CF_DATA_TYPE_STRING, "source=promise");
1458  char number[PRINTSIZE(uintmax_t)];
1459  xsnprintf(number, CF_SMALLBUF, "%ju", (uintmax_t) owner->offset.line);
1460  EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promise_linenumber", number, CF_DATA_TYPE_STRING, "source=promise");
1461  }
1462 
1463  char v[PRINTSIZE(int)];
1464  xsnprintf(v, sizeof(v), "%d", (int) ctx->uid);
1465  EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser_uid", v, CF_DATA_TYPE_INT, "source=agent");
1466  xsnprintf(v, sizeof(v), "%d", (int) ctx->gid);
1467  EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser_gid", v, CF_DATA_TYPE_INT, "source=agent");
1468  xsnprintf(v, sizeof(v), "%d", (int) ctx->pid);
1469  EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser_pid", v, CF_DATA_TYPE_INT, "source=agent");
1470  xsnprintf(v, sizeof(v), "%d", (int) ctx->ppid);
1471  EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser_ppid", v, CF_DATA_TYPE_INT, "source=agent");
1472 
1473  EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "bundle", PromiseGetBundle(owner)->name, CF_DATA_TYPE_STRING, "source=promise");
1474  EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "namespace", PromiseGetNamespace(owner), CF_DATA_TYPE_STRING, "source=promise");
1475 
1476  // Recompute `with`
1477  for (size_t i = 0; i < SeqLength(owner->conlist); i++)
1478  {
1479  Constraint *cp = SeqAt(owner->conlist, i);
1480  if (StringEqual(cp->lval, "with"))
1481  {
1482  Rval final = EvaluateFinalRval(ctx, PromiseGetPolicy(owner), NULL,
1483  "this", cp->rval, false, owner);
1484  if (final.type == RVAL_TYPE_SCALAR && !IsCf3VarString(RvalScalarValue(final)))
1485  {
1486  EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "with", RvalScalarValue(final), CF_DATA_TYPE_STRING, "source=promise_iteration/with");
1487  }
1488  RvalDestroy(final);
1489  }
1490  }
1491 }
1492 
1494 {
1495  const StackFrame *last_frame = LastStackFrame(ctx, 0);
1496 
1497  assert(last_frame != NULL);
1498  assert(last_frame->type == STACK_FRAME_TYPE_PROMISE);
1499 
1500  /* Evaluate all constraints by calling functions etc. */
1501  bool excluded;
1502  Promise *pexp = ExpandDeRefPromise(ctx, last_frame->data.promise.owner,
1503  &excluded);
1504  if (excluded || !pexp)
1505  {
1506  PromiseDestroy(pexp);
1507  return NULL;
1508  }
1509 
1511 
1513 
1514  return pexp;
1515 }
1516 
1518 {
1519  assert(SeqLength(ctx->stack) > 0);
1520 
1521  StackFrame *last_frame = LastStackFrame(ctx, 0);
1522  StackFrameType last_frame_type = last_frame->type;
1523 
1524  switch (last_frame_type)
1525  {
1527  {
1528  const Bundle *bp = last_frame->data.bundle.owner;
1529  if (strcmp(bp->type, "edit_line") == 0 || strcmp(bp->type, "edit_xml") == 0)
1530  {
1531  VariableTableClear(last_frame->data.bundle.vars, "default", "edit", NULL);
1532  }
1533  }
1534  break;
1535 
1538  break;
1539 
1540  default:
1541  break;
1542  }
1543 
1544  SeqRemove(ctx->stack, SeqLength(ctx->stack) - 1);
1545 
1546  last_frame = LastStackFrame(ctx, 0);
1547  if (last_frame)
1548  {
1549  if (last_frame->type == STACK_FRAME_TYPE_PROMISE_ITERATION)
1550  {
1551  const Promise *pp = EvalContextStackCurrentPromise(ctx);
1553  }
1554  }
1555 
1556  LogDebug(LOG_MOD_EVALCTX, "POPPED FRAME (type %s)",
1557  STACK_FRAME_TYPE_STR[last_frame_type]);
1558 }
1559 
1560 bool EvalContextClassRemove(EvalContext *ctx, const char *ns, const char *name)
1561 {
1562  for (size_t i = 0; i < SeqLength(ctx->stack); i++)
1563  {
1564  StackFrame *frame = SeqAt(ctx->stack, i);
1565  if (frame->type != STACK_FRAME_TYPE_BUNDLE)
1566  {
1567  continue;
1568  }
1569 
1570  ClassTableRemove(frame->data.bundle.classes, ns, name);
1571  }
1572 
1573  return ClassTableRemove(ctx->global_classes, ns, name);
1574 }
1575 
1576 Class *EvalContextClassGet(const EvalContext *ctx, const char *ns, const char *name)
1577 {
1579  if (frame)
1580  {
1581  Class *cls = ClassTableGet(frame->data.bundle.classes, ns, name);
1582  if (cls)
1583  {
1584  return cls;
1585  }
1586  }
1587 
1588  return ClassTableGet(ctx->global_classes, ns, name);
1589 }
1590 
1591 Class *EvalContextClassMatch(const EvalContext *ctx, const char *regex)
1592 {
1594  if (frame)
1595  {
1596  Class *cls = ClassTableMatch(frame->data.bundle.classes, regex);
1597  if (cls)
1598  {
1599  return cls;
1600  }
1601  }
1602 
1603  return ClassTableMatch(ctx->global_classes, regex);
1604 }
1605 
1606 static bool EvalContextClassPut(EvalContext *ctx, const char *ns, const char *name, bool is_soft, ContextScope scope, const char *tags)
1607 {
1608  {
1609  char context_copy[CF_MAXVARSIZE];
1610  char canonified_context[CF_MAXVARSIZE];
1611 
1612 
1613  /* Redmine #7013
1614  * Fix for classes names longer than CF_MAXVARSIZE. */
1615  if (strlen(name) >= sizeof(canonified_context))
1616  {
1617  Log(LOG_LEVEL_WARNING, "Skipping adding class [%s] as its name "
1618  "is equal or longer than %zu", name, sizeof(canonified_context));
1619  return false;
1620  }
1621 
1622  strlcpy(canonified_context, name, sizeof(canonified_context));
1623 
1624  if (Chop(canonified_context, CF_EXPANDSIZE) == -1)
1625  {
1626  Log(LOG_LEVEL_ERR, "Chop was called on a string that seemed to have no terminator");
1627  }
1628  CanonifyNameInPlace(canonified_context);
1629 
1630  if (ns && strcmp(ns, "default") != 0)
1631  {
1632  snprintf(context_copy, CF_MAXVARSIZE, "%s:%s", ns, canonified_context);
1633  }
1634  else
1635  {
1636  strlcpy(context_copy, canonified_context, CF_MAXVARSIZE);
1637  }
1638 
1639  if (strlen(context_copy) == 0)
1640  {
1641  return false;
1642  }
1643 
1644  if (IsRegexItemIn(ctx, ctx->heap_abort_current_bundle, context_copy))
1645  {
1646  const Bundle *bundle = EvalContextStackCurrentBundle(ctx);
1647  if (bundle != NULL)
1648  {
1649  Log(LOG_LEVEL_ERR, "Bundle '%s' aborted on defined class '%s'", bundle->name, context_copy);
1650  }
1651  else
1652  {
1653  Log(LOG_LEVEL_ERR, "Bundle (unknown) aborted on defined class '%s'", context_copy);
1654  }
1655  SetBundleAborted(ctx);
1656  }
1657 
1658  if (IsRegexItemIn(ctx, ctx->heap_abort, context_copy))
1659  {
1660  Log(LOG_LEVEL_NOTICE, "cf-agent aborted on defined class '%s'", context_copy);
1661  SetEvalAborted(ctx);
1662  }
1663  }
1664 
1665  Class *existing_class = EvalContextClassGet(ctx, ns, name);
1666  if (existing_class && existing_class->scope == scope)
1667  {
1668  return false;
1669  }
1670 
1672 
1673  switch (scope)
1674  {
1675  case CONTEXT_SCOPE_BUNDLE:
1676  {
1678  if (!frame)
1679  {
1680  ProgrammingError("Attempted to add bundle class '%s' while not evaluating a bundle", name);
1681  }
1682  ClassTablePut(frame->data.bundle.classes, ns, name, is_soft, scope, tags);
1683  }
1684  break;
1685 
1687  ClassTablePut(ctx->global_classes, ns, name, is_soft, scope, tags);
1688  break;
1689 
1690  case CONTEXT_SCOPE_NONE:
1691  ProgrammingError("Attempted to add a class without a set scope");
1692  }
1693 
1694  if (!BundleAborted(ctx))
1695  {
1696  for (const Item *ip = ctx->heap_abort_current_bundle; ip != NULL; ip = ip->next)
1697  {
1698  const char *class_expr = ip->name;
1699 
1700  if (IsDefinedClass(ctx, class_expr))
1701  {
1702  Log(LOG_LEVEL_ERR, "Setting abort for '%s' when setting class '%s'", ip->name, name);
1703  SetBundleAborted(ctx);
1704  break;
1705  }
1706  }
1707  }
1708 
1709  return true;
1710 }
1711 
1712 static const char *EvalContextCurrentNamespace(const EvalContext *ctx)
1713 {
1714  size_t i = SeqLength(ctx->stack);
1715  while (i > 0)
1716  {
1717  i--;
1718  StackFrame *frame = SeqAt(ctx->stack, i);
1719  switch (frame->type)
1720  {
1722  return frame->data.bundle.owner->ns;
1723  case STACK_FRAME_TYPE_BODY:
1724  return frame->data.body.owner->ns;
1725  default:
1726  break; /* out of the switch but not the loop ! */
1727  }
1728  }
1729 
1730  return NULL;
1731 }
1732 
1733 bool EvalContextClassPutHard(EvalContext *ctx, const char *name, const char *tags)
1734 {
1735  return EvalContextClassPut(ctx, NULL, name, false, CONTEXT_SCOPE_NAMESPACE, tags);
1736 }
1737 
1738 bool EvalContextClassPutSoft(EvalContext *ctx, const char *name, ContextScope scope, const char *tags)
1739 {
1740  bool ret;
1741  char *ns = NULL;
1742  char *delim = strchr(name, ':');
1743 
1744  if (delim)
1745  {
1746  ns = xstrndup(name, delim - name);
1747  }
1748 
1749  ret = EvalContextClassPut(ctx, ns ? ns : EvalContextCurrentNamespace(ctx),
1750  ns ? delim + 1 : name, true, scope, tags);
1751  free(ns);
1752  return ret;
1753 }
1754 
1755 
1756 ClassTableIterator *EvalContextClassTableIteratorNewGlobal(const EvalContext *ctx, const char *ns, bool is_hard, bool is_soft)
1757 {
1758  return ClassTableIteratorNew(ctx->global_classes, ns, is_hard, is_soft);
1759 }
1760 
1762 {
1764  if (!frame)
1765  {
1766  return NULL;
1767  }
1768 
1769  return ClassTableIteratorNew(frame->data.bundle.classes, frame->data.bundle.owner->ns, false, true);
1770 }
1771 
1773 {
1775  return frame ? frame->data.promise_iteration.owner : NULL;
1776 }
1777 
1779 {
1781  return frame ? frame->data.bundle.owner : NULL;
1782 }
1783 
1785 {
1787  return frame ? frame->data.promise_iteration.log_messages : NULL;
1788 }
1789 
1790 
1791 
1792 /**
1793  * @brief Concatenate string #str to #buf, replacing mangling
1794  * characters '*' and '#' with their visible counterparts.
1795  */
1796 static void BufferAppendPromiseStr(Buffer *buf, const char *str)
1797 {
1798  for (const char *ch = str; *ch != '\0'; ch++)
1799  {
1800  switch (*ch)
1801  {
1802  case CF_MANGLED_NS:
1803  BufferAppendChar(buf, ':');
1804  break;
1805 
1806  case CF_MANGLED_SCOPE:
1807  BufferAppendChar(buf, '.');
1808  break;
1809 
1810  default:
1811  BufferAppendChar(buf, *ch);
1812  break;
1813  }
1814  }
1815 }
1816 
1817 /**
1818  * @brief Like @c BufferAppendPromiseStr, but if @c str contains newlines
1819  * and is longer than 2*N+3, then only copy an abbreviated version
1820  * consisting of the first and last N characters, separated by @c `...`
1821  *
1822  * @param buffer Buffer to be used.
1823  * @param promiser Constant string to append
1824  * @param N Max. length of initial/final segment of @c promiser to keep
1825  * @note 2*N+3 is the maximum length of the appended string (excl. terminating NULL)
1826  *
1827  */
1829  const char *promiser, const int N)
1830 {
1831  /* check if `promiser` contains a new line (may happen for "insert_lines") */
1832  const char *const nl = strchr(promiser, '\n');
1833  if (NULL == nl)
1834  {
1835  BufferAppendPromiseStr(buf, promiser);
1836  }
1837  else
1838  {
1839  /* `promiser` contains a newline: abbreviate it by taking the first and last few characters */
1840  static const char sep[] = "...";
1841  char abbr[sizeof(sep) + 2 * N];
1842  const int head = (nl > promiser + N) ? N : (nl - promiser);
1843  const char * last_line = strrchr(promiser, '\n') + 1;
1844  assert(last_line); /* not NULL, we know we have at least one '\n' */
1845  const int tail = strlen(last_line);
1846  if (tail > N)
1847  {
1848  last_line += tail - N;
1849  }
1850  memcpy(abbr, promiser, head);
1851  strcpy(abbr + head, sep);
1852  strcat(abbr, last_line);
1853  BufferAppendPromiseStr(buf, abbr);
1854  }
1855 }
1856 
1858 {
1859  Buffer *path = BufferNew();
1860 
1861  for (size_t i = 0; i < SeqLength(ctx->stack); i++)
1862  {
1863  StackFrame *frame = SeqAt(ctx->stack, i);
1864  switch (frame->type)
1865  {
1866  case STACK_FRAME_TYPE_BODY:
1867  BufferAppendChar(path, '/');
1868  BufferAppend(path, frame->data.body.owner->name, CF_BUFSIZE);
1869  break;
1870 
1872  BufferAppendChar(path, '/');
1873  BufferAppend(path, frame->data.bundle.owner->ns, CF_BUFSIZE);
1874  BufferAppendChar(path, '/');
1875  BufferAppend(path, frame->data.bundle.owner->name, CF_BUFSIZE);
1876  break;
1877 
1879  BufferAppendChar(path, '/');
1881 
1883  break;
1884 
1886  BufferAppendChar(path, '/');
1887  BufferAppendChar(path, '\'');
1889  BufferAppendChar(path, '\'');
1890  if (i == SeqLength(ctx->stack) - 1 &&
1891  /* For some reason verify_packages.c is adding NULL iteration
1892  * frames all over the place; TODO fix. */
1893  frame->data.promise_iteration.iter_ctx != NULL)
1894  {
1895  BufferAppendF(path, "[%zu]",
1897  }
1898  break;
1899 
1900  default:
1901  ProgrammingError("Unhandled stack frame type");
1902  }
1903  }
1904 
1905  return BufferClose(path);
1906 }
1907 
1909 {
1910  StringSet *promisees = StringSetNew();
1911 
1912  for (size_t i = 0; i < SeqLength(ctx->stack); i++)
1913  {
1914  StackFrame *frame = SeqAt(ctx->stack, i);
1916  {
1917  continue;
1918  }
1919 
1920  Rval promisee = frame->data.promise_iteration.owner->promisee;
1921 
1922  switch (promisee.type)
1923  {
1924  case RVAL_TYPE_SCALAR:
1925  StringSetAdd(promisees, xstrdup(RvalScalarValue(promisee)));
1926  break;
1927 
1928  case RVAL_TYPE_LIST:
1929  {
1930  for (const Rlist *rp = RvalRlistValue(promisee); rp; rp = rp->next)
1931  {
1932  if (rp->val.type == RVAL_TYPE_SCALAR)
1933  {
1934  StringSetAdd(promisees, xstrdup(RvalScalarValue(rp->val)));
1935  }
1936  else
1937  {
1938  assert(false && "Canary: promisee list contained non-scalar value");
1939  }
1940  }
1941  }
1942  break;
1943 
1944  case RVAL_TYPE_NOPROMISEE:
1945  break;
1946 
1947  default:
1948  assert(false && "Canary: promisee not scalar or list");
1949  }
1950  }
1951 
1952  return promisees;
1953 }
1954 
1955 /**
1956  * We cannot have double-scoped variables (e.g. "this.config.var1"), so if we
1957  * want to put a scoped variable into a special scope, we need to mangle the
1958  * name like this:
1959  * "config.var1" -> "config___var1"
1960  */
1961 static inline char *MangleScopedVarNameIntoSpecialScopeName(const char *scope, const char *var_name)
1962 {
1963  const size_t var_name_len = strlen(var_name);
1964 
1965  /* Replace '.' with NESTED_SCOPE_SEP */
1966  char *new_var_name = xmalloc(var_name_len + sizeof(NESTED_SCOPE_SEP));
1967  memcpy(new_var_name, var_name, var_name_len + 1 /* including '\0' */);
1968 
1969  /* Make sure we only replace the "scope." string, not all dots. */
1970  char *scope_with_dot = StringConcatenate(2, scope, ".");
1971  char *scope_with_underscores = StringConcatenate(2, scope, NESTED_SCOPE_SEP);
1972 
1973  ssize_t ret = StringReplace(new_var_name, var_name_len + sizeof(NESTED_SCOPE_SEP),
1974  scope_with_dot, scope_with_underscores);
1975  assert(ret == (var_name_len + sizeof(NESTED_SCOPE_SEP) - 2));
1976 
1977  free(scope_with_dot);
1978  free(scope_with_underscores);
1979 
1980  return new_var_name;
1981 }
1982 
1983 /*
1984  * Copies value, so you need to free your own copy afterwards.
1985  */
1986 bool EvalContextVariablePutSpecial(EvalContext *ctx, SpecialScope scope, const char *lval, const void *value, DataType type, const char *tags)
1987 {
1988  char *new_lval = NULL;
1989  if (strchr(lval, '.') != NULL)
1990  {
1991  VarRef *ref = VarRefParse(lval);
1992  if (ref->scope != NULL)
1993  {
1994  new_lval = MangleScopedVarNameIntoSpecialScopeName(ref->scope, lval);
1995  }
1996  VarRefDestroy(ref);
1997  }
1998  if (strchr(lval, '['))
1999  {
2000  // dealing with (legacy) array reference in lval, must parse
2001  VarRef *ref = VarRefParseFromScope(new_lval ? new_lval : lval, SpecialScopeToString(scope));
2002  bool ret = EvalContextVariablePut(ctx, ref, value, type, tags);
2003  free(new_lval);
2004  VarRefDestroy(ref);
2005  return ret;
2006  }
2007  else
2008  {
2009  // plain lval, skip parsing
2010  const VarRef ref = VarRefConst(NULL, SpecialScopeToString(scope), new_lval ? new_lval : lval);
2011  bool ret = EvalContextVariablePut(ctx, &ref, value, type, tags);
2012  free(new_lval);
2013  return ret;
2014  }
2015 }
2016 
2018  const EvalContext *const ctx,
2019  const SpecialScope scope,
2020  const char *const varname,
2021  DataType *const type_out)
2022 {
2023  VarRef *const ref = VarRefParseFromScope(
2024  varname, SpecialScopeToString(scope));
2025  const void *const result = EvalContextVariableGet(ctx, ref, type_out);
2026  VarRefDestroy(ref);
2027 
2028  return result;
2029 }
2030 
2031 /**
2032  * @note Only use this when you know the variable is a string
2033  * @see EvalContextVariableGetSpecial()
2034  */
2036  const EvalContext *const ctx,
2037  const SpecialScope scope,
2038  const char *const varname)
2039 {
2040  DataType type_out;
2041  const void *const result = EvalContextVariableGetSpecial(
2042  ctx, scope, varname, &type_out);
2043  assert(type_out == CF_DATA_TYPE_STRING); // Programming error if not string
2044  return (type_out == CF_DATA_TYPE_STRING) ? result : NULL;
2045 }
2046 
2047 bool EvalContextVariableRemoveSpecial(const EvalContext *ctx, SpecialScope scope, const char *lval)
2048 {
2049  switch (scope)
2050  {
2051  case SPECIAL_SCOPE_SYS:
2052  case SPECIAL_SCOPE_MON:
2053  case SPECIAL_SCOPE_CONST:
2054  case SPECIAL_SCOPE_EDIT:
2055  case SPECIAL_SCOPE_BODY:
2056  case SPECIAL_SCOPE_THIS:
2057  {
2058  VarRef *ref = VarRefParseFromScope(lval, SpecialScopeToString(scope));
2059  bool ret = EvalContextVariableRemove(ctx, ref);
2060  VarRefDestroy(ref);
2061  return ret;
2062  }
2063 
2064  case SPECIAL_SCOPE_NONE:
2065  assert(false && "Attempted to remove none-special variable");
2066  return false;
2067 
2068  default:
2069  assert(false && "Unhandled case in switch");
2070  return false;
2071  }
2072 }
2073 
2075 #ifdef NDEBUG
2076  ARG_UNUSED
2077 #endif /* ns is only used in assertions ... */
2078  const char *ns,
2079  const char *scope)
2080 {
2081  assert(ctx != NULL);
2082 
2083  switch (SpecialScopeFromString(scope))
2084  {
2085  case SPECIAL_SCOPE_DEF:
2086  /* 'def.' is not as special as the other scopes below. (CFE-3668) */
2087  return ctx->global_variables;
2088 
2089  case SPECIAL_SCOPE_SYS:
2090  case SPECIAL_SCOPE_MON:
2091  case SPECIAL_SCOPE_CONST:
2092  assert(!ns || strcmp("default", ns) == 0);
2093  return ctx->global_variables;
2094 
2095  case SPECIAL_SCOPE_MATCH:
2096  assert(!ns || strcmp("default", ns) == 0);
2097  return ctx->match_variables;
2098 
2099  case SPECIAL_SCOPE_EDIT:
2100  assert(!ns || strcmp("default", ns) == 0);
2101  {
2103  assert(frame);
2104  return frame->data.bundle.vars;
2105  }
2106 
2107  case SPECIAL_SCOPE_BODY:
2108  assert(!ns || strcmp("default", ns) == 0);
2109  {
2111  return frame ? frame->data.body.vars : NULL;
2112  }
2113 
2114  // "this" variables can be in local or global variable table (when this is used for non-special
2115  // varables), so return local as VariableResolve will try global table anyway.
2116  case SPECIAL_SCOPE_THIS:
2117  {
2119  return frame ? frame->data.promise.vars : NULL;
2120  }
2121 
2122  case SPECIAL_SCOPE_NONE:
2123  return ctx->global_variables;
2124 
2125  default:
2126  assert(false && "Unhandled case in switch");
2127  return NULL;
2128  }
2129 }
2130 
2131 bool EvalContextVariableRemove(const EvalContext *ctx, const VarRef *ref)
2132 {
2133  VariableTable *table = GetVariableTableForScope(ctx, ref->ns, ref->scope);
2134  return VariableTableRemove(table, ref);
2135 }
2136 
2137 static bool IsVariableSelfReferential(const VarRef *ref, const void *value, RvalType rval_type)
2138 {
2139  switch (rval_type)
2140  {
2141  case RVAL_TYPE_SCALAR:
2142  if (StringContainsVar(value, ref->lval))
2143  {
2144  char *ref_str = VarRefToString(ref, true);
2145  Log(LOG_LEVEL_ERR, "The value of variable '%s' contains a reference to itself, '%s'", ref_str, (char *)value);
2146  free(ref_str);
2147  return true;
2148  }
2149  break;
2150 
2151  case RVAL_TYPE_LIST:
2152  for (const Rlist *rp = value; rp != NULL; rp = rp->next)
2153  {
2154  if (rp->val.type != RVAL_TYPE_SCALAR)
2155  {
2156  continue;
2157  }
2158 
2159  if (StringContainsVar(RlistScalarValue(rp), ref->lval))
2160  {
2161  char *ref_str = VarRefToString(ref, true);
2162  Log(LOG_LEVEL_ERR, "An item in list variable '%s' contains a reference to itself", ref_str);
2163  free(ref_str);
2164  return true;
2165  }
2166  }
2167  break;
2168 
2169  case RVAL_TYPE_FNCALL:
2170  case RVAL_TYPE_CONTAINER:
2171  case RVAL_TYPE_NOPROMISEE:
2172  break;
2173  }
2174 
2175  return false;
2176 }
2177 
2178 static void VarRefStackQualify(const EvalContext *ctx, VarRef *ref)
2179 {
2180  StackFrame *last_frame = LastStackFrame(ctx, 0);
2181  assert(last_frame);
2182 
2183  switch (last_frame->type)
2184  {
2185  case STACK_FRAME_TYPE_BODY:
2187  break;
2188 
2190  {
2191  StackFrame *last_last_frame = LastStackFrame(ctx, 1);
2192  assert(last_last_frame);
2193  assert(last_last_frame->type == STACK_FRAME_TYPE_BUNDLE);
2194  VarRefQualify(ref,
2195  last_last_frame->data.bundle.owner->ns,
2196  last_last_frame->data.bundle.owner->name);
2197  }
2198  break;
2199 
2201  VarRefQualify(ref,
2202  last_frame->data.bundle.owner->ns,
2203  last_frame->data.bundle.owner->name);
2204  break;
2205 
2208  // Allow special "this" variables to work when used without "this"
2210  break;
2211 
2212  default:
2213  ProgrammingError("Unhandled stack frame type");
2214  }
2215 }
2216 
2217 /*
2218  * Copies value, so you need to free your own copy afterwards.
2219  */
2221  const VarRef *ref, const void *value,
2222  DataType type, const char *tags)
2223 {
2224  assert(type != CF_DATA_TYPE_NONE);
2225  assert(ref);
2226  assert(ref->lval);
2227 
2228  /* The only possible way to get a NULL value is if it's an empty linked
2229  * list (Rlist usually). */
2230  assert(value != NULL || DataTypeIsIterable(type));
2231 
2232  if (strlen(ref->lval) > CF_MAXVARSIZE)
2233  {
2234  char *lval_str = VarRefToString(ref, true);
2235  Log(LOG_LEVEL_ERR, "Variable '%s'' cannot be added because "
2236  "its length exceeds the maximum length allowed ('%d' characters)",
2237  lval_str, CF_MAXVARSIZE);
2238  free(lval_str);
2239  return false;
2240  }
2241 
2242  if (strcmp(ref->scope, "body") != 0 &&
2243  IsVariableSelfReferential(ref, value, DataTypeToRvalType(type)))
2244  {
2245  return false;
2246  }
2247 
2248  Rval rval = (Rval) { (void *)value, DataTypeToRvalType(type) };
2249  VariableTable *table = GetVariableTableForScope(ctx, ref->ns, ref->scope);
2250  const Promise *pp = EvalContextStackCurrentPromise(ctx);
2251  VariableTablePut(table, ref, &rval, type, tags, pp ? pp->org_pp : pp);
2252  return true;
2253 }
2254 
2255 /**
2256  * Change ref for e.g. 'config.var1' to 'this.config___var1'
2257  *
2258  * @see MangleScopedVarNameIntoSpecialScopeName()
2259  */
2260 static inline VarRef *MangledThisScopedRef(const VarRef *ref)
2261 {
2262  VarRef *mangled_this_ref = VarRefCopy(ref);
2263  char *scope_underscores_lval = StringConcatenate(3, mangled_this_ref->scope,
2265  mangled_this_ref->lval);
2266  free(mangled_this_ref->lval);
2267  mangled_this_ref->lval = scope_underscores_lval;
2268  free(mangled_this_ref->scope);
2269  mangled_this_ref->scope = xstrdup("this");
2270 
2271  return mangled_this_ref;
2272 }
2273 
2274 static Variable *VariableResolve2(const EvalContext *ctx, const VarRef *ref)
2275 {
2276  assert(ref != NULL);
2277 
2278  // Get the variable table associated to the scope
2279  VariableTable *table = GetVariableTableForScope(ctx, ref->ns, ref->scope);
2280 
2281  Variable *var;
2282  if (table)
2283  {
2284  var = VariableTableGet(table, ref);
2285  if (var)
2286  {
2287  return var;
2288  }
2289  else if (ref->num_indices > 0)
2290  {
2291  /* Iteration over slists creates special variables in the 'this.'
2292  * scope with the slist variable replaced by the individual
2293  * values. However, if a scoped variable is part of the variable
2294  * reference, e.g. 'config.data[$(list)]', the special iteration
2295  * variables use mangled names to avoid having two scopes
2296  * (e.g. 'this.config___data[list_item1]' instead of
2297  * 'this.config.data[list_item1]').
2298  *
2299  * If the ref we are looking for has indices and it has a scope, it
2300  * might be the case described above. Let's give it a try before
2301  * falling back to the indexless container lookup described below
2302  * (which will not have the list-iteration variables expanded). */
2303  if (ref->scope != NULL)
2304  {
2305  VariableTable *this_table = GetVariableTableForScope(ctx, ref->ns,
2307  if (this_table != NULL)
2308  {
2309  VarRef *mangled_this_ref = MangledThisScopedRef(ref);
2310  var = VariableTableGet(this_table, mangled_this_ref);
2311  VarRefDestroy(mangled_this_ref);
2312  if (var != NULL)
2313  {
2314  return var;
2315  }
2316  }
2317  }
2318 
2319  /* If the lookup with indices (the [idx1][idx2]... part of the
2320  * variable reference) fails, there might still be a container
2321  * variable where the indices actually refer to child objects inside
2322  * the container structure. */
2323  VarRef *base_ref = VarRefCopyIndexless(ref);
2324  var = VariableTableGet(table, base_ref);
2325  VarRefDestroy(base_ref);
2326 
2327  if (var && var->type == CF_DATA_TYPE_CONTAINER)
2328  {
2329  return var;
2330  }
2331  }
2332  }
2333 
2334  return NULL;
2335 }
2336 
2337 /*
2338  * Looks up a variable in the the context of the 'current scope'. This
2339  * basically means that an unqualified reference will be looked up in the
2340  * context of the top stack frame.
2341  *
2342  * Note that when evaluating a promise, this
2343  * will qualify a reference to 'this' scope and when evaluating a body, it
2344  * will qualify a reference to 'body' scope.
2345  */
2346 static Variable *VariableResolve(const EvalContext *ctx, const VarRef *ref)
2347 {
2348  assert(ref->lval);
2349 
2350  /* We will make a first lookup that works in almost all cases: will look
2351  * for local or global variables, depending of the current scope. */
2352 
2353  Variable *ret_var = VariableResolve2(ctx, ref);
2354  if (ret_var != NULL)
2355  {
2356  return ret_var;
2357  }
2358 
2359  /* Try to qualify non-scoped vars to the scope:
2360  "this" for promises, "body" for bodies, current bundle for bundles. */
2361  VarRef *scoped_ref = NULL;
2362  if (!VarRefIsQualified(ref))
2363  {
2364  scoped_ref = VarRefCopy(ref);
2365  VarRefStackQualify(ctx, scoped_ref);
2366  ret_var = VariableResolve2(ctx, scoped_ref);
2367  if (ret_var != NULL)
2368  {
2369  VarRefDestroy(scoped_ref);
2370  return ret_var;
2371  }
2372  ref = scoped_ref; /* continue with the scoped variable */
2373  }
2374 
2375  const Bundle *last_bundle = EvalContextStackCurrentBundle(ctx);
2376 
2377  /* If we are in a promise or a body, the variable might be coming from the
2378  * last bundle. So try a last lookup with "this" or "body" special scopes
2379  * replaced with the last bundle. */
2380 
2383  && last_bundle != NULL)
2384  {
2385  VarRef *ref2 = VarRefCopy(ref);
2386  VarRefQualify(ref2, last_bundle->ns, last_bundle->name);
2387  ret_var = VariableResolve2(ctx, ref2);
2388 
2389  VarRefDestroy(scoped_ref);
2390  VarRefDestroy(ref2);
2391  return ret_var;
2392  }
2393  VarRefDestroy(scoped_ref);
2394 
2395  return NULL;
2396 }
2397 
2398 /**
2399  *
2400  * @NOTE NULL is a valid return value if #type_out is of list type and the
2401  * list is empty. To check if the variable didn't resolve, check if
2402  * #type_out was set to CF_DATA_TYPE_NONE.
2403  */
2404 const void *EvalContextVariableGet(const EvalContext *ctx, const VarRef *ref, DataType *type_out)
2405 {
2406  Variable *var = VariableResolve(ctx, ref);
2407  if (var)
2408  {
2409  if (var->ref->num_indices == 0 &&
2410  ref->num_indices > 0 &&
2411  var->type == CF_DATA_TYPE_CONTAINER)
2412  {
2414  ref->num_indices, ref->indices);
2415  if (child)
2416  {
2417  if (type_out)
2418  {
2419  *type_out = CF_DATA_TYPE_CONTAINER;
2420  }
2421  return child;
2422  }
2423  }
2424  else
2425  {
2426  if (type_out)
2427  {
2428  *type_out = var->type;
2429  }
2430  return var->rval.item;
2431  }
2432  }
2433 
2434  if (type_out)
2435  {
2436  *type_out = CF_DATA_TYPE_NONE;
2437  }
2438  return NULL;
2439 }
2440 
2442 {
2443  Variable *var = VariableResolve(ctx, ref);
2444  return var ? var->promise : NULL;
2445 }
2446 
2447 StringSet *EvalContextClassTags(const EvalContext *ctx, const char *ns, const char *name)
2448 {
2449  Class *cls = EvalContextClassGet(ctx, ns, name);
2450  if (!cls)
2451  {
2452  return NULL;
2453  }
2454 
2455  assert(cls->tags != NULL);
2456  return cls->tags;
2457 }
2458 
2460 {
2461  Variable *var = VariableResolve(ctx, ref);
2462  if (!var)
2463  {
2464  return NULL;
2465  }
2466 
2467  assert(var->tags != NULL);
2468  return var->tags;
2469 }
2470 
2472 {
2474 }
2475 
2476 VariableTableIterator *EvalContextVariableTableIteratorNew(const EvalContext *ctx, const char *ns, const char *scope, const char *lval)
2477 {
2478  VariableTable *table = scope ? GetVariableTableForScope(ctx, ns, scope) : ctx->global_variables;
2479  return table ? VariableTableIteratorNew(table, ns, scope, lval) : NULL;
2480 }
2481 
2482 
2484 {
2485  assert(ref);
2486  VariableTable *table = ref->scope ? GetVariableTableForScope(ctx, ref->ns, ref->scope) : ctx->global_variables;
2487  return table ? VariableTableIteratorNewFromVarRef(table, ref) : NULL;
2488 }
2489 
2491 {
2492  assert(lval >= 0 && lval < COMMON_CONTROL_MAX);
2493 
2494  VarRef *ref = VarRefParseFromScope(CFG_CONTROLBODY[lval].lval, "control_common");
2495  const void *ret = EvalContextVariableGet(ctx, ref, NULL);
2496  VarRefDestroy(ref);
2497  return ret;
2498 }
2499 
2500 static ClassRef IDRefQualify(const EvalContext *ctx, const char *id)
2501 {
2502  // HACK: Because call reference names are equivalent to class names, we abuse ClassRef here
2503  ClassRef ref = ClassRefParse(id);
2504  if (!ClassRefIsQualified(ref))
2505  {
2506  const char *ns = EvalContextCurrentNamespace(ctx);
2507  if (ns)
2508  {
2509  ClassRefQualify(&ref, ns);
2510  }
2511  else
2512  {
2514  }
2515  }
2516 
2517  return ref;
2518 }
2519 
2521  const char *callee_reference, const char *callee_type)
2522 {
2523  ClassRef ref = IDRefQualify(ctx, callee_reference);
2524 
2525  const Bundle *bp = NULL;
2526  for (size_t i = 0; i < SeqLength(policy->bundles); i++)
2527  {
2528  const Bundle *curr_bp = SeqAt(policy->bundles, i);
2529  if ((strcmp(curr_bp->type, callee_type) != 0) ||
2530  (strcmp(curr_bp->name, ref.name) != 0) ||
2531  !StringEqual(curr_bp->ns, ref.ns))
2532  {
2533  continue;
2534  }
2535 
2536  bp = curr_bp;
2537  break;
2538  }
2539 
2540  ClassRefDestroy(ref);
2541  return bp;
2542 }
2543 
2544 const Body *EvalContextFindFirstMatchingBody(const Policy *policy, const char *type,
2545  const char *namespace, const char *name)
2546 {
2547  for (size_t i = 0; i < SeqLength(policy->bodies); i++)
2548  {
2549  const Body *curr_bp = SeqAt(policy->bodies, i);
2550  if ((strcmp(curr_bp->type, type) == 0) &&
2551  (strcmp(curr_bp->name, name) == 0) &&
2552  StringEqual(curr_bp->ns, namespace))
2553  {
2554  return curr_bp;
2555  }
2556  }
2557 
2558  return NULL;
2559 }
2560 
2562  Seq* chain, const Body *bp, const char *callee_type,
2563  int depth)
2564 {
2565  if (depth > 30) // sanity check
2566  {
2567  Log(LOG_LEVEL_ERR, "EvalContextAppendBodyParentsAndArgs: body inheritance chain depth %d in body %s is too much, aborting", depth, bp->name);
2568  DoCleanupAndExit(EXIT_FAILURE);
2569  }
2570 
2571  for (size_t k = 0; bp->conlist && k < SeqLength(bp->conlist); k++)
2572  {
2573  Constraint *scp = SeqAt(bp->conlist, k);
2574  if (strcmp("inherit_from", scp->lval) == 0)
2575  {
2576  char* call = NULL;
2577 
2578  if (RVAL_TYPE_SCALAR == scp->rval.type)
2579  {
2580  call = RvalScalarValue(scp->rval);
2581  }
2582  else if (RVAL_TYPE_FNCALL == scp->rval.type)
2583  {
2584  call = RvalFnCallValue(scp->rval)->name;
2585  }
2586 
2587  ClassRef parent_ref = IDRefQualify(ctx, call);
2588 
2589  // We don't do a more detailed check for circular
2590  // inheritance because the depth check above will catch it
2591  if (strcmp(parent_ref.name, bp->name) == 0)
2592  {
2593  Log(LOG_LEVEL_ERR, "EvalContextAppendBodyParentsAndArgs: self body inheritance in %s->%s, aborting", bp->name, parent_ref.name);
2594  DoCleanupAndExit(EXIT_FAILURE);
2595  }
2596 
2597  const Body *parent = EvalContextFindFirstMatchingBody(policy, callee_type, parent_ref.ns, parent_ref.name);
2598  if (parent)
2599  {
2600  SeqAppend(chain, (void *)parent);
2601  SeqAppend(chain, &(scp->rval));
2602  EvalContextAppendBodyParentsAndArgs(ctx, policy, chain, parent, callee_type, depth+1);
2603  }
2604  ClassRefDestroy(parent_ref);
2605  }
2606  }
2607 }
2608 
2610  const char *callee_reference, const char *callee_type)
2611 {
2612  ClassRef ref = IDRefQualify(ctx, callee_reference);
2613  Seq *bodies = NULL;
2614 
2615  const Body *bp = EvalContextFindFirstMatchingBody(policy, callee_type, ref.ns, ref.name);
2616  if (bp)
2617  {
2618  bodies = SeqNew(2, NULL);
2619  SeqAppend(bodies, (void *)bp);
2620  SeqAppend(bodies, (void *)NULL);
2621  EvalContextAppendBodyParentsAndArgs(ctx, policy, bodies, bp, callee_type, 1);
2622  }
2623 
2624  ClassRefDestroy(ref);
2625  return bodies;
2626 }
2627 
2628 bool EvalContextPromiseLockCacheContains(const EvalContext *ctx, const char *key)
2629 {
2630  return StringSetContains(ctx->promise_lock_cache, key);
2631 }
2632 
2633 void EvalContextPromiseLockCachePut(EvalContext *ctx, const char *key)
2634 {
2636 }
2637 
2639 {
2641 }
2642 
2644  const FnCall *fp ARG_UNUSED,
2645  const Rlist *args, Rval *rval_out)
2646 {
2648  {
2649  return false;
2650  }
2651 
2652  Rval *rval = FuncCacheMapGet(ctx->function_cache, args);
2653  if (rval)
2654  {
2655  if (rval_out)
2656  {
2657  *rval_out = *rval;
2658  }
2659  return true;
2660  }
2661  else
2662  {
2663  return false;
2664  }
2665 }
2666 
2668  const FnCall *fp ARG_UNUSED,
2669  const Rlist *args, const Rval *rval)
2670 {
2672  {
2673  return;
2674  }
2675 
2676  Rval *rval_copy = xmalloc(sizeof(Rval));
2677  *rval_copy = RvalCopy(*rval);
2678  FuncCacheMapInsert(ctx->function_cache, RlistCopy(args), rval_copy);
2679 }
2680 
2681 /* cfPS and associated machinery */
2682 
2683 
2684 
2685 /*
2686  * Internal functions temporarily used from logging implementation
2687  */
2688 
2689 static const char *const NO_STATUS_TYPES[] =
2690  { "vars", "classes", "insert_lines", "delete_lines", "replace_patterns", "field_edits", NULL };
2691 static const char *const NO_LOG_TYPES[] =
2692  { "vars", "classes", "insert_lines", "delete_lines", "replace_patterns", "field_edits", NULL };
2693 
2694 /*
2695  * Vars, classes and similar promises which do not affect the system itself (but
2696  * just support evalution) do not need to be counted as repaired/failed, as they
2697  * may change every iteration and introduce lot of churn in reports without
2698  * giving any value.
2699  */
2700 static bool IsPromiseValuableForStatus(const Promise *pp)
2701 {
2702  return pp && (pp->parent_promise_type->name != NULL) && (!IsStrIn(pp->parent_promise_type->name, NO_STATUS_TYPES));
2703 }
2704 
2705 /*
2706  * Vars, classes and subordinate promises (like edit_line) do not need to be
2707  * logged, as they exist to support other promises.
2708  */
2709 
2710 static bool IsPromiseValuableForLogging(const Promise *pp)
2711 {
2712  return pp && (pp->parent_promise_type->name != NULL) && (!IsStrIn(pp->parent_promise_type->name, NO_LOG_TYPES));
2713 }
2714 
2715 static void AddAllClasses(EvalContext *ctx, const Rlist *list, unsigned int persistence_ttl,
2716  PersistentClassPolicy policy, ContextScope context_scope)
2717 {
2718  for (const Rlist *rp = list; rp != NULL; rp = rp->next)
2719  {
2720  char *classname = xstrdup(RlistScalarValue(rp));
2721  if (strcmp(classname, "a_class_global_from_command") == 0 || strcmp(classname, "xxx:a_class_global_from_command") == 0)
2722  {
2723  Log(LOG_LEVEL_ERR, "Hit '%s'", classname);
2724  }
2725 
2726  CanonifyNameInPlace(classname);
2727 
2728  if (EvalContextHeapContainsHard(ctx, classname))
2729  {
2730  Log(LOG_LEVEL_ERR, "You cannot use reserved hard class '%s' as post-condition class", classname);
2731  // TODO: ok.. but should we take any action? continue; maybe?
2732  }
2733 
2734  if (persistence_ttl > 0)
2735  {
2736  if (context_scope != CONTEXT_SCOPE_NAMESPACE)
2737  {
2738  Log(LOG_LEVEL_INFO, "Automatically promoting context scope for '%s' to namespace visibility, due to persistence", classname);
2739  }
2740 
2741  Log(LOG_LEVEL_VERBOSE, "C: + persistent outcome class '%s'", classname);
2742  EvalContextHeapPersistentSave(ctx, classname, persistence_ttl, policy, "");
2743  EvalContextClassPutSoft(ctx, classname, CONTEXT_SCOPE_NAMESPACE, "");
2744  }
2745  else
2746  {
2747  Log(LOG_LEVEL_VERBOSE, "C: + promise outcome class '%s'", classname);
2748 
2749  switch (context_scope)
2750  {
2751  case CONTEXT_SCOPE_BUNDLE:
2752  EvalContextStackFrameAddSoft(ctx, classname, "");
2753  break;
2754 
2755  case CONTEXT_SCOPE_NONE:
2757  EvalContextClassPutSoft(ctx, classname, CONTEXT_SCOPE_NAMESPACE, "");
2758  break;
2759 
2760  default:
2761  ProgrammingError("AddAllClasses: Unexpected context_scope %d!",
2762  context_scope);
2763  }
2764  }
2765  free(classname);
2766  }
2767 }
2768 
2769 static void DeleteAllClasses(EvalContext *ctx, const Rlist *list)
2770 {
2771  for (const Rlist *rp = list; rp != NULL; rp = rp->next)
2772  {
2774  {
2775  return; // TODO: interesting course of action, but why is the check there in the first place?
2776  }
2777 
2779  {
2780  Log(LOG_LEVEL_ERR, "You cannot cancel a reserved hard class '%s' in post-condition classes",
2781  RlistScalarValue(rp));
2782  }
2783 
2784  const char *string = RlistScalarValue(rp);
2785 
2786  Log(LOG_LEVEL_VERBOSE, "Cancelling class '%s'", string);
2787 
2789 
2790  {
2791  ClassRef ref = ClassRefParse(CanonifyName(string));
2792  EvalContextClassRemove(ctx, ref.ns, ref.name);
2793  ClassRefDestroy(ref);
2794  }
2796  }
2797 }
2798 
2800 {
2801 }
2802 
2804 {
2805  Rlist *add_classes = NULL;
2806  Rlist *del_classes = NULL;
2807 
2808  switch (status)
2809  {
2810  case PROMISE_RESULT_CHANGE:
2811  add_classes = dc->change;
2812  del_classes = dc->del_change;
2813  break;
2814 
2816  add_classes = dc->timeout;
2817  del_classes = dc->del_notkept;
2818  break;
2819 
2820  case PROMISE_RESULT_WARN:
2821  case PROMISE_RESULT_FAIL:
2823  add_classes = dc->failure;
2824  del_classes = dc->del_notkept;
2825  break;
2826 
2827  case PROMISE_RESULT_DENIED:
2828  add_classes = dc->denied;
2829  del_classes = dc->del_notkept;
2830  break;
2831 
2832  case PROMISE_RESULT_NOOP:
2833  add_classes = dc->kept;
2834  del_classes = dc->del_kept;
2835  break;
2836 
2837  default:
2838  ProgrammingError("Unexpected status '%c' has been passed to SetPromiseOutcomeClasses", status);
2839  }
2840 
2841  AddAllClasses(ctx, add_classes, dc->persist, dc->timer, dc->scope);
2842  DeleteAllClasses(ctx, del_classes);
2843 }
2844 
2845 static void SummarizeTransaction(EvalContext *ctx, const TransactionContext *tc, const char *logname)
2846 {
2847  if (logname && (tc->log_string))
2848  {
2849  Buffer *buffer = BufferNew();
2850  ExpandScalar(ctx, NULL, NULL, tc->log_string, buffer);
2851 
2852  if (strcmp(logname, "udp_syslog") == 0)
2853  {
2854  RemoteSysLog(tc->log_priority, BufferData(buffer));
2855  }
2856  else if (strcmp(logname, "stdout") == 0)
2857  {
2858  Log(LOG_LEVEL_INFO, "L: %s", BufferData(buffer));
2859  }
2860  else
2861  {
2862  struct stat dsb;
2863 
2864  // Does the file exist already?
2865  if (lstat(logname, &dsb) == -1)
2866  {
2867  mode_t filemode = 0600; /* Mode for log file creation */
2868  int fd = creat(logname, filemode);
2869  if (fd >= 0)
2870  {
2872  "Created log file '%s' with requested permissions %jo",
2873  logname, (intmax_t) filemode);
2874  close(fd);
2875  }
2876  }
2877 
2878  FILE *fout = safe_fopen(logname, "a");
2879 
2880  if (fout == NULL)
2881  {
2882  Log(LOG_LEVEL_ERR, "Unable to open private log '%s'", logname);
2883  return;
2884  }
2885 
2886  Log(LOG_LEVEL_VERBOSE, "Logging string '%s' to '%s'", BufferData(buffer), logname);
2887  fprintf(fout, "%s\n", BufferData(buffer));
2888 
2889  fclose(fout);
2890  }
2891 
2892  BufferDestroy(buffer);
2893  // FIXME: This was overwriting a local copy, with no side effects.
2894  // The intention was clearly to skip this function if called
2895  // repeatedly. Try to introduce this change:
2896  // tc.log_string = NULL; /* To avoid repetition */
2897  }
2898 }
2899 
2900 static void DoSummarizeTransaction(EvalContext *ctx, PromiseResult status, const Promise *pp, const TransactionContext *tc)
2901 {
2902  if (!IsPromiseValuableForLogging(pp))
2903  {
2904  return;
2905  }
2906 
2907  char *log_name = NULL;
2908 
2909  switch (status)
2910  {
2911  case PROMISE_RESULT_CHANGE:
2912  log_name = tc->log_repaired;
2913  break;
2914 
2915  case PROMISE_RESULT_WARN:
2916  /* FIXME: nothing? */
2917  return;
2918 
2920  case PROMISE_RESULT_FAIL:
2921  case PROMISE_RESULT_DENIED:
2923  log_name = tc->log_failed;
2924  break;
2925 
2926  case PROMISE_RESULT_NOOP:
2927  log_name = tc->log_kept;
2928  break;
2929 
2930  default:
2931  ProgrammingError("Unexpected promise result status: %d", status);
2932  }
2933 
2934  SummarizeTransaction(ctx, tc, log_name);
2935 }
2936 
2938 {
2939  switch (result)
2940  {
2941  case PROMISE_RESULT_CHANGE:
2942  case PROMISE_RESULT_NOOP:
2943  {
2944  const char *handle = PromiseGetHandle(pp);
2945  if (handle)
2946  {
2947  StringSetAdd(ctx->dependency_handles, xstrdup(handle));
2948  }
2949  }
2950  break;
2951 
2952  default:
2953  /* This promise is not yet done, don't mark it is as such */
2954  break;
2955  }
2956 }
2957 
2958 void ClassAuditLog(EvalContext *ctx, const Promise *pp, const Attributes *attr, PromiseResult status)
2959 {
2960  assert(attr != NULL);
2962  {
2963  TrackTotalCompliance(status, pp);
2964  UpdatePromiseCounters(status);
2965  }
2966 
2967  SetPromiseOutcomeClasses(ctx, status, &(attr->classes));
2968  DoSummarizeTransaction(ctx, status, pp, &(attr->transaction));
2969 }
2970 
2971 static void LogPromiseContext(const EvalContext *ctx, const Promise *pp)
2972 {
2973  Writer *w = StringWriter();
2974  WriterWrite(w, "Additional promise info:");
2975  if (PromiseGetHandle(pp))
2976  {
2977  WriterWriteF(w, " handle '%s'", PromiseGetHandle(pp));
2978  }
2979 
2980  {
2981  const char *version = EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_VERSION);
2982  if (version)
2983  {
2984  WriterWriteF(w, " version '%s'", version);
2985  }
2986  }
2987 
2988  if (PromiseGetBundle(pp)->source_path)
2989  {
2990  WriterWriteF(w, " source path '%s' at line %zu", PromiseGetBundle(pp)->source_path, pp->offset.line);
2991  }
2992 
2993  switch (pp->promisee.type)
2994  {
2995  case RVAL_TYPE_SCALAR:
2996  WriterWriteF(w, " promisee '%s'", RvalScalarValue(pp->promisee));
2997  break;
2998 
2999  case RVAL_TYPE_LIST:
3000  WriterWrite(w, " promisee ");
3001  RlistWrite(w, pp->promisee.item);
3002  break;
3003  default:
3004  break;
3005  }
3006 
3007  if (pp->comment)
3008  {
3009  WriterWriteF(w, " comment '%s'", pp->comment);
3010  }
3011 
3013  WriterClose(w);
3014 }
3015 
3016 void cfPS(EvalContext *ctx, LogLevel level, PromiseResult status, const Promise *pp, const Attributes *attr, const char *fmt, ...)
3017 {
3018  /*
3019  * This stub implementation of cfPS delegates to the new logging backend.
3020  *
3021  * Due to the fact very little of the code has been converted, this code
3022  * does a full initialization and shutdown of logging subsystem for each
3023  * cfPS.
3024  *
3025  * Instead, LoggingInit should be called at the moment EvalContext is
3026  * created, LoggingPromiseEnter/LoggingPromiseFinish should be called around
3027  * ExpandPromise and LoggingFinish should be called when EvalContext is
3028  * going to be destroyed.
3029  *
3030  * But it requires all calls to cfPS to be eliminated.
3031  */
3032 
3033  /* FIXME: Ensure that NULL pp is never passed into cfPS */
3034 
3035  assert(pp);
3036  assert(attr != NULL);
3037 
3038  if (level >= LOG_LEVEL_VERBOSE)
3039  {
3040  LogPromiseContext(ctx, pp);
3041  }
3042 
3043  va_list ap;
3044  va_start(ap, fmt);
3045  char *msg = NULL;
3046  xvasprintf(&msg, fmt, ap);
3047  Log(level, "%s", msg);
3048  va_end(ap);
3049 
3050  /* Now complete the exits status classes and auditing */
3051 
3052  ClassAuditLog(ctx, pp, attr, status);
3053  free(msg);
3054 }
3055 
3056 void SetChecksumUpdatesDefault(EvalContext *ctx, bool enabled)
3057 {
3058  ctx->checksum_updates_default = enabled;
3059 }
3060 
3062 {
3063  return ctx->checksum_updates_default;
3064 }
3065 
3066 void EvalContextAddIpAddress(EvalContext *ctx, const char *ip_address, const char *iface)
3067 {
3068  AppendItem(&ctx->ip_addresses, ip_address,
3069  (iface == NULL) ? "" : iface);
3070 }
3071 
3073 {
3075  ctx->ip_addresses = NULL;
3076 }
3077 
3079 {
3080  return ctx->ip_addresses;
3081 }
3082 
3084 {
3085  if (value)
3086  {
3087  ctx->eval_options |= option;
3088  }
3089  else
3090  {
3091  ctx->eval_options &= ~~option;
3092  }
3093 }
3094 
3096 {
3097  return ((ctx->eval_options & option) != 0);
3098 }
3099 
3100 void EvalContextSetLaunchDirectory(EvalContext *ctx, const char *path)
3101 {
3102  free(ctx->launch_directory);
3103  ctx->launch_directory = xstrdup(path);
3104 }
3105 
3107  EvalContext *const ctx, const char *const entry_point)
3108 {
3109  assert(ctx != NULL);
3110  free(ctx->entry_point);
3111  ctx->entry_point = SafeStringDuplicate(entry_point);
3112 }
3113 
3114 const char *EvalContextGetEntryPoint(EvalContext *const ctx)
3115 {
3116  assert(ctx != NULL);
3117  return ctx->entry_point;
3118 }
3119 
3121 {
3122  ctx->ignore_locks = ignore;
3123 }
3124 
3126 {
3127  return ctx->ignore_locks;
3128 }
3129 
3130 StringSet *ClassesMatching(const EvalContext *ctx, ClassTableIterator *iter, const char* regex, const Rlist *tags, bool first_only)
3131 {
3132  StringSet *matching = StringSetNew();
3133 
3134  pcre *rx = CompileRegex(regex);
3135 
3136  Class *cls;
3137  while ((cls = ClassTableIteratorNext(iter)))
3138  {
3139  char *expr = ClassRefToString(cls->ns, cls->name);
3140 
3141  /* FIXME: review this strcmp. Moved out from StringMatch */
3142  if (!strcmp(regex, expr) ||
3143  (rx && StringMatchFullWithPrecompiledRegex(rx, expr)))
3144  {
3145  bool pass = false;
3146  StringSet *tagset = EvalContextClassTags(ctx, cls->ns, cls->name);
3147 
3148  if (tags)
3149  {
3150  for (const Rlist *arg = tags; arg; arg = arg->next)
3151  {
3152  const char *tag_regex = RlistScalarValue(arg);
3153  const char *element;
3155  while ((element = StringSetIteratorNext(&it)))
3156  {
3157  /* FIXME: review this strcmp. Moved out from StringMatch */
3158  if (strcmp(tag_regex, element) == 0 ||
3159  StringMatchFull(tag_regex, element))
3160  {
3161  pass = true;
3162  break;
3163  }
3164  }
3165  }
3166  }
3167  else // without any tags queried, accept class
3168  {
3169  pass = true;
3170  }
3171 
3172  if (pass)
3173  {
3174  StringSetAdd(matching, expr);
3175  }
3176  else
3177  {
3178  free(expr);
3179  }
3180  }
3181  else
3182  {
3183  free(expr);
3184  }
3185 
3186  if (first_only && StringSetSize(matching) > 0)
3187  {
3188  break;
3189  }
3190  }
3191 
3192  if (rx)
3193  {
3194  pcre_free(rx);
3195  }
3196 
3197  return matching;
3198 }
3199 
3201 {
3203  {
3204  Buffer *expbuf;
3205  JsonElement *expanded_json;
3206 
3207  switch (JsonGetPrimitiveType(source))
3208  {
3210  expbuf = BufferNew();
3211  ExpandScalar(ctx, NULL, "this", JsonPrimitiveGetAsString(source), expbuf);
3212  expanded_json = JsonStringCreate(BufferData(expbuf));
3213  BufferDestroy(expbuf);
3214  return expanded_json;
3215  break;
3216 
3217  default:
3218  return JsonCopy(source);
3219  break;
3220  }
3221  }
3222  else if (JsonGetElementType(source) == JSON_ELEMENT_TYPE_CONTAINER)
3223  {
3225  {
3226  JsonElement *dest = JsonObjectCreate(JsonLength(source));
3227  JsonIterator iter = JsonIteratorInit(source);
3228  const char *key;
3229  while ((key = JsonIteratorNextKey(&iter)))
3230  {
3231  Buffer *expbuf = BufferNew();
3232  ExpandScalar(ctx, NULL, "this", key, expbuf);
3233  JsonObjectAppendElement(dest, BufferData(expbuf), JsonExpandElement(ctx, JsonObjectGet(source, key)));
3234  BufferDestroy(expbuf);
3235  }
3236 
3237  return dest;
3238  }
3239  else
3240  {
3241  JsonElement *dest = JsonArrayCreate(JsonLength(source));
3242  for (size_t i = 0; i < JsonLength(source); i++)
3243  {
3244  JsonArrayAppendElement(dest, JsonExpandElement(ctx, JsonArrayGet(source, i)));
3245  }
3246  return dest;
3247  }
3248  }
3249 
3250  ProgrammingError("JsonExpandElement: unexpected container type");
3251  return NULL;
3252 }
3253 
3255 {
3256  assert (ctx);
3257  return ctx->all_classes;
3258 }
3259 
3261 {
3262  assert (ctx);
3263  Nova_ClassHistoryEnable(&(ctx->all_classes), enable);
3264 }
3265 
3266 void EvalContextPushBundleName(const EvalContext *ctx, const char *bundle_name)
3267 {
3268  assert (ctx);
3269  StringSetAdd(ctx->bundle_names, xstrdup(bundle_name));
3270 }
3271 
3273 {
3274  assert (ctx);
3275  return ctx->bundle_names;
3276 }
3277 
3278 void EvalContextPushRemoteVarPromise(EvalContext *ctx, const char *bundle_name, const Promise *pp)
3279 {
3280  assert (ctx);
3281 
3282  /* initiliaze the map if needed */
3283  if (ctx->remote_var_promises == NULL)
3284  {
3286  }
3287 
3288  Seq *promises = RemoteVarPromisesMapGet(ctx->remote_var_promises, bundle_name);
3289  if (promises == NULL)
3290  {
3291  /* initialize the sequence if needed */
3292  /* ItemDestroy == NULL because we need to store the exact pointers not
3293  * copies */
3294  promises = SeqNew(10, NULL);
3295  RemoteVarPromisesMapInsert(ctx->remote_var_promises, xstrdup(bundle_name), promises);
3296  }
3297  /* intentionally not making a copy here, we need the exact pointer */
3298  SeqAppend(promises, (void *) pp);
3299 }
3300 
3301 const Seq *EvalContextGetRemoteVarPromises(const EvalContext *ctx, const char *bundle_name)
3302 {
3303  assert (ctx);
3304  if (ctx->remote_var_promises == NULL)
3305  {
3306  return NULL;
3307  }
3308  return RemoteVarPromisesMapGet(ctx->remote_var_promises, bundle_name);
3309 }
void * xmalloc(size_t size)
Definition: alloc-mini.c:46
void * xcalloc(size_t nmemb, size_t size)
Definition: alloc-mini.c:51
char * xstrdup(const char *str)
Definition: alloc-mini.c:56
int xvasprintf(char **strp, const char *fmt, va_list ap)
Definition: alloc.c:82
char * xstrndup(const char *str, size_t n)
Definition: alloc.c:61
void UpdatePromiseCounters(PromiseResult status)
Definition: audit.c:43
void BufferDestroy(Buffer *buffer)
Destroys a buffer and frees the memory associated with it.
Definition: buffer.c:72
Buffer * BufferNew(void)
Buffer initialization routine.
Definition: buffer.c:48
Buffer * BufferNewFrom(const char *data, unsigned int length)
Initializes a buffer based on a const char pointer.
Definition: buffer.c:64
const char * BufferData(const Buffer *buffer)
Provides a pointer to the internal data.
Definition: buffer.c:470
unsigned int BufferSize(const Buffer *buffer)
Returns the size of the buffer.
Definition: buffer.c:464
void BufferAppendF(Buffer *buffer, const char *format,...)
Definition: buffer.c:318
void BufferAppendChar(Buffer *buffer, char byte)
Appends a char to an existing buffer.
Definition: buffer.c:299
void BufferAppend(Buffer *buffer, const char *bytes, unsigned int length)
Definition: buffer.c:269
char * BufferClose(Buffer *buffer)
Destroys a buffer structure returning the its contents.
Definition: buffer.c:81
void BufferRewrite(Buffer *buffer, BufferFilterFn filter, const bool invert)
Filters a Buffer in place.
Definition: buffer.c:536
ENTERPRISE_VOID_FUNC_1ARG_DEFINE_STUB(void, Nova_TrackExecution, ARG_UNUSED const char *, input_file)
#define ARG_UNUSED
Definition: cf-net.c:47
#define CF_MAXFRAGMENT
Definition: cf3.defs.h:54
#define CF_MANGLED_SCOPE
Definition: cf3.defs.h:115
#define CF_MANGLED_NS
Definition: cf3.defs.h:114
const ConstraintSyntax CFG_CONTROLBODY[]
Definition: mod_common.c:245
CommonControl
Definition: cf3.defs.h:415
@ COMMON_CONTROL_VERSION
Definition: cf3.defs.h:421
@ COMMON_CONTROL_MAX
Definition: cf3.defs.h:438
RvalType
Definition: cf3.defs.h:605
@ RVAL_TYPE_CONTAINER
Definition: cf3.defs.h:609
@ RVAL_TYPE_LIST
Definition: cf3.defs.h:607
@ RVAL_TYPE_SCALAR
Definition: cf3.defs.h:606
@ RVAL_TYPE_FNCALL
Definition: cf3.defs.h:608
@ RVAL_TYPE_NOPROMISEE
Definition: cf3.defs.h:610
#define CF_IDRANGE
Definition: cf3.defs.h:572
PromiseResult
Definition: cf3.defs.h:122
@ PROMISE_RESULT_CHANGE
Definition: cf3.defs.h:125
@ PROMISE_RESULT_INTERRUPTED
Definition: cf3.defs.h:130
@ PROMISE_RESULT_NOOP
Definition: cf3.defs.h:124
@ PROMISE_RESULT_WARN
Definition: cf3.defs.h:126
@ PROMISE_RESULT_DENIED
Definition: cf3.defs.h:128
@ PROMISE_RESULT_FAIL
Definition: cf3.defs.h:127
@ PROMISE_RESULT_TIMEOUT
Definition: cf3.defs.h:129
DataType
Definition: cf3.defs.h:368
@ CF_DATA_TYPE_NONE
Definition: cf3.defs.h:385
@ CF_DATA_TYPE_STRING
Definition: cf3.defs.h:369
@ CF_DATA_TYPE_INT
Definition: cf3.defs.h:370
@ CF_DATA_TYPE_CONTAINER
Definition: cf3.defs.h:384
ContextScope
Definition: cf3.defs.h:945
@ CONTEXT_SCOPE_NAMESPACE
Definition: cf3.defs.h:946
@ CONTEXT_SCOPE_NONE
Definition: cf3.defs.h:948
@ CONTEXT_SCOPE_BUNDLE
Definition: cf3.defs.h:947
bool DONTDO
Definition: cf3globals.c:55
void free(void *)
void ClassTableDestroy(ClassTable *table)
Definition: class.c:127
Class * ClassTableGet(const ClassTable *table, const char *ns, const char *name)
Definition: class.c:162
void ClassRefQualify(ClassRef *ref, const char *ns)
Definition: class.c:339
ClassTableIterator * ClassTableIteratorNew(const ClassTable *table, const char *ns, bool is_hard, bool is_soft)
Definition: class.c:234
ClassTable * ClassTableNew(void)
Definition: class.c:118
char * ClassRefToString(const char *ns, const char *name)
Definition: class.c:319
void ClassRefDestroy(ClassRef ref)
Definition: class.c:345
ClassRef ClassRefParse(const char *expr)
Definition: class.c:295
bool ClassTableRemove(ClassTable *table, const char *ns, const char *name)
Definition: class.c:214
bool ClassRefIsQualified(ClassRef ref)
Definition: class.c:334
bool ClassTablePut(ClassTable *table, const char *ns, const char *name, bool is_soft, ContextScope scope, const char *tags)
Definition: class.c:136
Class * ClassTableMatch(const ClassTable *table, const char *regex)
Definition: class.c:175
bool ClassTableClear(ClassTable *table)
Definition: class.c:227
Class * ClassTableIteratorNext(ClassTableIterator *iter)
Definition: class.c:248
void DoCleanupAndExit(int ret)
Definition: cleanup.c:57
bool DataTypeIsIterable(DataType t)
Definition: conversion.c:757
PersistentClassPolicy
Definition: db_structs.h:136
@ CONTEXT_STATE_POLICY_PRESERVE
Definition: db_structs.h:138
int ValueSizeDB(DBHandle *handle, const char *key, int key_size)
Definition: dbm_api.c:578
bool NextDB(DBCursor *cursor, char **key, int *ksize, void **value, int *vsize)
Definition: dbm_api.c:601
bool DeleteDB(DBHandle *handle, const char *key)
Definition: dbm_api.c:583
bool DBCursorDeleteEntry(DBCursor *cursor)
Definition: dbm_api.c:607
bool OpenDB(DBHandle **dbp, dbid id)
Definition: dbm_api.c:441
char * DBIdToPath(dbid id)
Definition: dbm_api.c:171
bool ReadDB(DBHandle *handle, const char *key, void *dest, int destSz)
Definition: dbm_api.c:556
bool DeleteDBCursor(DBCursor *cursor)
Definition: dbm_api.c:617
bool WriteDB(DBHandle *handle, const char *key, const void *src, int srcSz)
Definition: dbm_api.c:561
void CloseDB(DBHandle *handle)
Definition: dbm_api.c:472
bool NewDBCursor(DBHandle *handle, DBCursor **cursor)
Definition: dbm_api.c:588
@ dbid_state
Definition: dbm_api.h:44
#define CF_BUFSIZE
Definition: definitions.h:50
#define CF_EXPANDSIZE
Definition: definitions.h:51
#define CF_MAXVARSIZE
Definition: definitions.h:36
#define CF_SMALLBUF
Definition: definitions.h:49
#define ENTERPRISE_VOID_FUNC_2ARG_DEFINE_STUB(__ret, __func, __t1, __p1, __t2, __p2)
void Nova_ClassHistoryEnable(StringSet **list, bool enable)
void Nova_ClassHistoryAddContextName(const StringSet *list, const char *context_name)
static StackFrame * StackFrameNew(StackFrameType type, bool inherit_previous)
static StackFrame * StackFrameNewPromiseType(const PromiseType *owner)
void EvalContextHeapPersistentSave(EvalContext *ctx, const char *name, unsigned int ttl_minutes, PersistentClassPolicy policy, const char *tags)
Definition: eval_context.c:643
void RemoteVarPromisesMapDestroy(RemoteVarPromisesMap *map)
Definition: eval_context.c:110
void EvalContextSetEntryPoint(EvalContext *const ctx, const char *const entry_point)
void EvalContextFunctionCachePut(EvalContext *ctx, const FnCall *fp, const Rlist *args, const Rval *rval)
void EvalContextSetupMissionPortalLogHook(EvalContext *ctx)
static StackFrame * LastStackFrame(const EvalContext *ctx, size_t offset)
Definition: eval_context.c:281
bool RemoteVarPromisesMapInsert(const RemoteVarPromisesMap *map, char *key, Seq *value)
Definition: eval_context.c:110
static bool IsVariableSelfReferential(const VarRef *ref, const void *value, RvalType rval_type)
void AddPackageModuleToContext(const EvalContext *ctx, PackageModuleBody *pm)
Definition: eval_context.c:226
bool EvalContextVariableRemoveSpecial(const EvalContext *ctx, SpecialScope scope, const char *lval)
bool EvalContextPromiseLockCacheContains(const EvalContext *ctx, const char *key)
void EvalContextClear(EvalContext *ctx)
static void StackFrameBodyDestroy(StackFrameBody frame)
Definition: eval_context.c:900
bool BundleAbort(EvalContext *ctx)
Definition: eval_context.c:803
static ClassRef IDRefQualify(const EvalContext *ctx, const char *id)
StringSet * EvalContextStackPromisees(const EvalContext *ctx)
static StackFrame * StackFrameNewBody(const Body *owner)
StringSet * ClassesMatching(const EvalContext *ctx, ClassTableIterator *iter, const char *regex, const Rlist *tags, bool first_only)
static char * EvalVarRef(const char *varname, VarRefType type, void *param)
Definition: eval_context.c:517
PackageModuleBody * GetDefaultPackageModuleFromContext(const EvalContext *ctx)
Definition: eval_context.c:264
void ClassAuditLog(EvalContext *ctx, const Promise *pp, const Attributes *attr, PromiseResult status)
void TrackTotalCompliance(PromiseResult status, const Promise *pp)
static void AddAllClasses(EvalContext *ctx, const Rlist *list, unsigned int persistence_ttl, PersistentClassPolicy policy, ContextScope context_scope)
static VariableTable * GetVariableTableForScope(const EvalContext *ctx, const char *ns, const char *scope)
bool EvalContextGetSelectEndMatchEof(const EvalContext *ctx)
Definition: eval_context.c:189
Seq * RemoteVarPromisesMapGet(const RemoteVarPromisesMap *map, const char *key)
Definition: eval_context.c:110
static void BufferAppendAbbreviatedStr(Buffer *buf, const char *promiser, const int N)
Like BufferAppendPromiseStr, but if str contains newlines and is longer than 2*N+3,...
const RingBuffer * EvalContextStackCurrentMessages(const EvalContext *ctx)
static bool EvalWithTokenFromList(const char *expr, StringSet *token_set)
Definition: eval_context.c:593
static const char * STACK_FRAME_TYPE_STR[STACK_FRAME_TYPE_MAX]
Definition: eval_context.c:60
static LogLevel CalculateLogLevel(const Promise *pp)
Definition: eval_context.c:341
bool EvalContextFunctionCacheGet(const EvalContext *ctx, const FnCall *fp, const Rlist *args, Rval *rval_out)
static StackFrame * StackFrameNewBundle(const Bundle *owner, bool inherit_previous)
bool StackFrameContainsSoftRecursive(const EvalContext *ctx, const char *context, size_t stack_index)
static VarRef * MangledThisScopedRef(const VarRef *ref)
void AddDefaultInventoryToContext(const EvalContext *ctx, Rlist *inventory)
Definition: eval_context.c:210
void EvalContextAllClassesLoggingEnable(EvalContext *ctx, bool enable)
FuncCacheMap * FuncCacheMapNew(void)
Definition: eval_context.c:89
JsonElement * EvalContextGetPromiseCallers(EvalContext *ctx)
bool EvalFileResult(const char *file_result, StringSet *leaf_attr)
Definition: eval_context.c:635
static ExpressionValue EvalTokenAsClass(const char *classname, void *param)
Definition: eval_context.c:472
Seq * EvalContextResolveBodyExpression(const EvalContext *ctx, const Policy *policy, const char *callee_reference, const char *callee_type)
Returns a Sequence of const Body* elements, first the body and then its parents.
void EvalContextSetSelectEndMatchEof(EvalContext *ctx, bool value)
Definition: eval_context.c:194
bool EvalContextHeapRemoveHard(EvalContext *ctx, const char *name)
JsonElement * JsonExpandElement(EvalContext *ctx, const JsonElement *source)
bool ClassCharIsWhitespace(char ch)
Definition: eval_context.c:529
const char * EvalContextVariableGetSpecialString(const EvalContext *const ctx, const SpecialScope scope, const char *const varname)
void AddDefaultPackageModuleToContext(const EvalContext *ctx, char *name)
Definition: eval_context.c:200
void EvalContextHeapAddAbort(EvalContext *ctx, const char *context, const char *activated_on_context)
Definition: eval_context.c:839
void EvalContextStackPushPromiseFrame(EvalContext *ctx, const Promise *owner)
const StringSet * EvalContextGetBundleNames(const EvalContext *ctx)
static void LogPromiseContext(const EvalContext *ctx, const Promise *pp)
static const char * GetAgentAbortingContext(const EvalContext *ctx)
Definition: eval_context.c:381
static Variable * VariableResolve(const EvalContext *ctx, const VarRef *ref)
static void EvalContextStackFrameAddSoft(EvalContext *ctx, const char *context, const char *tags)
Definition: eval_context.c:399
bool EvalContextClassRemove(EvalContext *ctx, const char *ns, const char *name)
bool EvalProcessResult(const char *process_result, StringSet *proc_attr)
Definition: eval_context.c:620
const void * EvalContextVariableGetSpecial(const EvalContext *const ctx, const SpecialScope scope, const char *const varname, DataType *const type_out)
bool EvalContextGetEvalOption(EvalContext *ctx, EvalContextOption option)
static void StackFrameDestroy(StackFrame *frame)
Definition: eval_context.c:916
const char * EvalContextGetEntryPoint(EvalContext *const ctx)
static void RvalDestroy2(void *p)
Definition: eval_context.c:76
void cfPS(EvalContext *ctx, LogLevel level, PromiseResult status, const Promise *pp, const Attributes *attr, const char *fmt,...)
static void StackFrameBundleDestroy(StackFrameBundle frame)
Definition: eval_context.c:894
EvalContext * EvalContextNew(void)
void EvalContextStackPushPromiseTypeFrame(EvalContext *ctx, const PromiseType *owner)
bool EvalAborted(const EvalContext *ctx)
Definition: eval_context.c:833
Class * EvalContextClassMatch(const EvalContext *ctx, const char *regex)
RemoteVarPromisesMap * RemoteVarPromisesMapNew(void)
Definition: eval_context.c:110
void NotifyDependantPromises(EvalContext *ctx, const Promise *pp, PromiseResult result)
static pcre * context_expression_whitespace_rx
Definition: eval_context.c:113
void EvalContextHeapAddAbortCurrentBundle(EvalContext *ctx, const char *context, const char *activated_on_context)
Definition: eval_context.c:856
StringSet * EvalContextVariableTags(const EvalContext *ctx, const VarRef *ref)
static void SeqDestroy_untyped(void *p)
Definition: eval_context.c:98
static void SetBundleAborted(EvalContext *ctx)
Definition: eval_context.c:821
VariableTableIterator * EvalContextVariableTableFromRefIteratorNew(const EvalContext *ctx, const VarRef *ref)
static ExpressionValue EvalTokenFromList(const char *token, void *param)
Definition: eval_context.c:585
static bool IsPromiseValuableForStatus(const Promise *pp)
static bool IsPromiseValuableForLogging(const Promise *pp)
static void VarRefStackQualify(const EvalContext *ctx, VarRef *ref)
static LogLevel CalculateReportLevel(const Promise *pp)
Definition: eval_context.c:359
const void * EvalContextVariableControlCommonGet(const EvalContext *ctx, CommonControl lval)
static bool BundleAborted(const EvalContext *ctx)
Definition: eval_context.c:815
const StringSet * EvalContextAllClassesGet(const EvalContext *ctx)
const Bundle * EvalContextResolveBundleExpression(const EvalContext *ctx, const Policy *policy, const char *callee_reference, const char *callee_type)
Find a bundle for a bundle call, given a callee reference (in the form of ns:bundle),...
Promise * EvalContextStackPushPromiseIterationFrame(EvalContext *ctx, const PromiseIterator *iter_ctx)
void EvalContextSetBundleArgs(EvalContext *ctx, const Rlist *args)
bool MissingDependencies(EvalContext *ctx, const Promise *pp)
Definition: eval_context.c:867
PackagePromiseContext * GetPackagePromiseContext(const EvalContext *ctx)
Definition: eval_context.c:275
ExpressionValue CheckClassExpression(const EvalContext *ctx, const char *context)
Definition: eval_context.c:534
ClassTableIterator * EvalContextClassTableIteratorNewGlobal(const EvalContext *ctx, const char *ns, bool is_hard, bool is_soft)
void EvalContextDeleteIpAddresses(EvalContext *ctx)
void EvalContextStackPushBundleFrame(EvalContext *ctx, const Bundle *owner, const Rlist *args, bool inherits_previous)
const char * EvalContextStackToString(EvalContext *ctx)
Definition: eval_context.c:371
void EvalContextSetLaunchDirectory(EvalContext *ctx, const char *path)
static StackFrame * LastStackFrameByType(const EvalContext *ctx, StackFrameType type)
Definition: eval_context.c:290
bool EvalContextClassPutHard(EvalContext *ctx, const char *name, const char *tags)
static bool EvalContextHeapContainsHard(const EvalContext *ctx, const char *name)
void EvalContextPromiseLockCacheRemove(EvalContext *ctx, const char *key)
Rlist * EvalContextGetBundleArgs(EvalContext *ctx)
Rlist * EvalContextGetPromiseCallerMethods(EvalContext *ctx)
const Promise * EvalContextVariablePromiseGet(const EvalContext *ctx, const VarRef *ref)
void EvalContextDestroy(EvalContext *ctx)
Rval * FuncCacheMapGet(const FuncCacheMap *map, const Rlist *key)
Definition: eval_context.c:89
static void DeleteAllClasses(EvalContext *ctx, const Rlist *list)
void EvalContextSetPass(EvalContext *ctx, int pass)
bool EvalContextIsIgnoringLocks(const EvalContext *ctx)
#define NESTED_SCOPE_SEP
Definition: eval_context.c:58
static StackFrame * StackFrameNewPromise(const Promise *owner)
static int PackageManagerSeqCompare(const void *a, const void *b, void *data)
Definition: eval_context.c:221
const void * EvalContextVariableGet(const EvalContext *ctx, const VarRef *ref, DataType *type_out)
void EvalContextAddIpAddress(EvalContext *ctx, const char *ip_address, const char *iface)
static Variable * VariableResolve2(const EvalContext *ctx, const VarRef *ref)
char * MissionPortalLogHook(LoggingPrivContext *pctx, LogLevel level, const char *message)
Definition: eval_context.c:987
static StackFrame * StackFrameNewPromiseIteration(Promise *owner, const PromiseIterator *iter_ctx)
static const char * EvalContextCurrentNamespace(const EvalContext *ctx)
void EvalContextPushRemoteVarPromise(EvalContext *ctx, const char *bundle_name, const Promise *pp)
bool FuncCacheMapInsert(const FuncCacheMap *map, Rlist *key, Rval *value)
Definition: eval_context.c:89
static void StackFramePromiseIterationDestroy(StackFramePromiseIteration frame)
Definition: eval_context.c:910
bool EvalContextVariableRemove(const EvalContext *ctx, const VarRef *ref)
static void BufferAppendPromiseStr(Buffer *buf, const char *str)
Concatenate string #str to #buf, replacing mangling characters '*' and '#' with their visible counter...
static const char *const NO_LOG_TYPES[]
bool EvalContextVariableClearMatch(EvalContext *ctx)
Rlist * GetDefaultInventoryFromContext(const EvalContext *ctx)
Definition: eval_context.c:270
void EvalContextSetIgnoreLocks(EvalContext *ctx, bool ignore)
const Body * EvalContextFindFirstMatchingBody(const Policy *policy, const char *type, const char *namespace, const char *name)
static void SetEvalAborted(EvalContext *ctx)
Definition: eval_context.c:827
static char * MangleScopedVarNameIntoSpecialScopeName(const char *scope, const char *var_name)
static void EvalContextStackPushFrame(EvalContext *ctx, StackFrame *frame)
void EvalContextAppendBodyParentsAndArgs(const EvalContext *ctx, const Policy *policy, Seq *chain, const Body *bp, const char *callee_type, int depth)
bool GetChecksumUpdatesDefault(const EvalContext *ctx)
static bool EvalContextClassPut(EvalContext *ctx, const char *ns, const char *name, bool is_soft, ContextScope scope, const char *tags)
ClassTableIterator * EvalContextClassTableIteratorNewLocal(const EvalContext *ctx)
const Promise * EvalContextStackCurrentPromise(const EvalContext *ctx)
static void FreePackageManager(PackageModuleBody *manager)
Definition: eval_context.c:951
void EvalContextHeapPersistentLoadAll(EvalContext *ctx)
Definition: eval_context.c:731
void EvalContextStackPushBodyFrame(EvalContext *ctx, const Promise *caller, const Body *body, const Rlist *args)
void EvalContextStackFrameRemoveSoft(EvalContext *ctx, const char *context)
static bool EvalContextHeapContainsSoft(const EvalContext *ctx, const char *ns, const char *name)
Item * EvalContextGetIpAddresses(const EvalContext *ctx)
PackageModuleBody * GetPackageModuleFromContext(const EvalContext *ctx, const char *name)
Definition: eval_context.c:242
char * EvalContextStackPath(const EvalContext *ctx)
StringSet * EvalContextClassTags(const EvalContext *ctx, const char *ns, const char *name)
static PackagePromiseContext * PackagePromiseConfigNew()
Definition: eval_context.c:963
void EvalContextStackPopFrame(EvalContext *ctx)
static void FreePackagePromiseContext(PackagePromiseContext *pp_ctx)
Definition: eval_context.c:976
void SetChecksumUpdatesDefault(EvalContext *ctx, bool enabled)
void EvalContextSetEvalOption(EvalContext *ctx, EvalContextOption option, bool value)
bool EvalContextVariablePutSpecial(EvalContext *ctx, SpecialScope scope, const char *lval, const void *value, DataType type, const char *tags)
static void SetPromiseOutcomeClasses(EvalContext *ctx, PromiseResult status, const DefineClasses *dc)
static LogLevel StringToLogLevel(const char *value)
Definition: eval_context.c:316
void FuncCacheMapDestroy(FuncCacheMap *map)
Definition: eval_context.c:89
static LogLevel GetLevelForPromise(const Promise *pp, const char *attr_name)
Definition: eval_context.c:336
void EvalContextHeapPersistentRemove(const char *context)
Definition: eval_context.c:715
void FuncCacheMapClear(FuncCacheMap *map)
Definition: eval_context.c:89
static void StackFramePromiseDestroy(StackFramePromise frame)
Definition: eval_context.c:905
const Bundle * EvalContextStackCurrentBundle(const EvalContext *ctx)
static LogLevel AdjustLogLevel(LogLevel base, LogLevel adjust)
Definition: eval_context.c:304
int EvalContextGetPass(EvalContext *ctx)
static const char *const NO_STATUS_TYPES[]
void EvalContextPushBundleName(const EvalContext *ctx, const char *bundle_name)
void EvalContextPromiseLockCachePut(EvalContext *ctx, const char *key)
const Seq * EvalContextGetRemoteVarPromises(const EvalContext *ctx, const char *bundle_name)
static void DoSummarizeTransaction(EvalContext *ctx, PromiseResult status, const Promise *pp, const TransactionContext *tc)
static void SummarizeTransaction(EvalContext *ctx, const TransactionContext *tc, const char *logname)
bool EvalContextHeapRemoveSoft(EvalContext *ctx, const char *ns, const char *name)
Class * EvalContextClassGet(const EvalContext *ctx, const char *ns, const char *name)
bool EvalContextVariablePut(EvalContext *ctx, const VarRef *ref, const void *value, DataType type, const char *tags)
static bool EvalContextStackFrameContainsSoft(const EvalContext *ctx, const char *context)
bool EvalContextClassPutSoft(EvalContext *ctx, const char *name, ContextScope scope, const char *tags)
VariableTableIterator * EvalContextVariableTableIteratorNew(const EvalContext *ctx, const char *ns, const char *scope, const char *lval)
StackFrameType
Definition: eval_context.h:43
@ STACK_FRAME_TYPE_PROMISE_ITERATION
Definition: eval_context.h:48
@ STACK_FRAME_TYPE_PROMISE
Definition: eval_context.h:47
@ STACK_FRAME_TYPE_PROMISE_TYPE
Definition: eval_context.h:46
@ STACK_FRAME_TYPE_BODY
Definition: eval_context.h:45
@ STACK_FRAME_TYPE_MAX
Definition: eval_context.h:49
@ STACK_FRAME_TYPE_BUNDLE
Definition: eval_context.h:44
static bool IsDefinedClass(const EvalContext *ctx, const char *context)
Definition: eval_context.h:213
EvalContextOption
Definition: eval_context.h:105
@ EVAL_OPTION_FULL
Definition: eval_context.h:111
@ EVAL_OPTION_CACHE_SYSTEM_FUNCTIONS
Definition: eval_context.h:109
char * ExpandScalar(const EvalContext *ctx, const char *ns, const char *scope, const char *string, Buffer *out)
Definition: expand.c:516
Rval EvaluateFinalRval(EvalContext *ctx, const Policy *policy, const char *ns, const char *scope, Rval rval, bool forcelist, const Promise *pp)
Definition: expand.c:610
Rval ExpandPrivateRval(EvalContext *ctx, const char *ns, const char *scope, const void *rval_item, RvalType rval_type)
Definition: expand.c:313
bool IsAbsoluteFileName(const char *f)
Definition: file_lib.c:333
FILE * safe_fopen(const char *const path, const char *const mode)
Definition: file_lib.c:812
#define FILE_SEPARATOR
Definition: file_lib.h:102
bool ChopLastNode(char *str)
Definition: files_names.c:422
char * CanonifyName(const char *str)
Definition: files_names.c:483
void DeleteSlash(char *str)
Definition: files_names.c:320
#define NULL
Definition: getopt1.c:56
void AppendItem(Item **liststart, const char *itemstring, const char *classes)
Definition: item_lib.c:415
void DeleteItemList(Item *item)
Definition: item_lib.c:808
bool IsItemIn(const Item *list, const char *item)
Definition: item_lib.c:226
size_t PromiseIteratorIndex(const PromiseIterator *iter_ctx)
Definition: iteration.c:217
void JsonArrayAppendObject(JsonElement *const array, JsonElement *const object)
Append an object to an array.
Definition: json.c:1327
const char * JsonIteratorNextKey(JsonIterator *const iter)
Definition: json.c:557
JsonElement * JsonObjectCreate(const size_t initialCapacity)
Create a new JSON object.
Definition: json.c:880
JsonElementType JsonGetElementType(const JsonElement *const element)
Definition: json.c:667
void JsonObjectAppendInteger(JsonElement *const object, const char *const key, const int value)
Append an integer field to an object.
Definition: json.c:1062
JsonIterator JsonIteratorInit(const JsonElement *const container)
Definition: json.c:549
JsonElement * JsonCopy(const JsonElement *const element)
Definition: json.c:235
JsonElement * JsonObjectGet(const JsonElement *const object, const char *const key)
Definition: json.c:1266
void JsonObjectAppendElement(JsonElement *const object, const char *const key, JsonElement *const element)
Append any JSON element to an object.
Definition: json.c:1111
JsonElement * JsonArrayCreate(const size_t initialCapacity)
Create a new JSON array.
Definition: json.c:1281
JsonContainerType JsonGetContainerType(const JsonElement *const container)
Definition: json.c:685
JsonElement * JsonArrayGet(const JsonElement *const array, const size_t index)
Definition: json.c:1397
JsonPrimitiveType JsonGetPrimitiveType(const JsonElement *const primitive)
Definition: json.c:693
JsonElement * JsonStringCreate(const char *const value)
Definition: json.c:1439
void JsonObjectAppendString(JsonElement *const object, const char *const key, const char *const value)
Append a string field to an object.
Definition: json.c:1055
const char * JsonPrimitiveGetAsString(const JsonElement *const primitive)
Definition: json.c:701
size_t JsonLength(const JsonElement *const element)
Get the length of a JsonElement. This is the number of elements or fields in an array or object respe...
Definition: json.c:531
JsonElement * JsonSelect(JsonElement *const element, const size_t num_indices, char **const indices)
Definition: json.c:824
void JsonArrayAppendElement(JsonElement *const array, JsonElement *const element)
Append any JSON element to an array.
Definition: json.c:1336
void JsonObjectAppendObject(JsonElement *const object, const char *const key, JsonElement *const childObject)
Append an object field to an object.
Definition: json.c:1099
@ JSON_PRIMITIVE_TYPE_STRING
Definition: json.h:64
@ JSON_ELEMENT_TYPE_PRIMITIVE
Definition: json.h:53
@ JSON_ELEMENT_TYPE_CONTAINER
Definition: json.h:52
@ JSON_CONTAINER_TYPE_OBJECT
Definition: json.h:58
LogLevel
Definition: log.h:30
@ LogDebug
Definition: log.h:34
void LoggingPrivSetContext(LoggingPrivContext *pctx)
Attaches context to logging for current thread.
Definition: logging.c:93
LoggingPrivContext * LoggingPrivGetContext(void)
Retrieves logging context for current thread.
Definition: logging.c:99
LogLevel LogGetGlobalLevel(void)
Definition: logging.c:581
void LoggingFreeCurrentThreadContext(void)
Definition: logging.c:70
void LoggingPrivSetLevels(LogLevel log_level, LogLevel report_level)
Set logging (syslog) and reporting (stdout) level for current thread.
Definition: logging.c:105
const char * GetErrorStr(void)
Definition: logging.c:275
void Log(LogLevel level, const char *fmt,...)
Definition: logging.c:409
@ LOG_MOD_EVALCTX
Definition: logging.h:58
@ LOG_LEVEL_ERR
Definition: logging.h:42
@ LOG_LEVEL_NOTICE
Definition: logging.h:44
@ LOG_LEVEL_DEBUG
Definition: logging.h:47
@ LOG_LEVEL_WARNING
Definition: logging.h:43
@ LOG_LEVEL_VERBOSE
Definition: logging.h:46
@ LOG_LEVEL_NOTHING
Definition: logging.h:40
@ LOG_LEVEL_INFO
Definition: logging.h:45
ParseResult ParseExpression(const char *expr, int start, int end)
void FreeExpression(Expression *e)
ExpressionValue EvalExpression(const Expression *expr, NameEvaluator nameevalfn, VarRefEvaluator varrefevalfn, void *param)
ExpressionValue
@ EXPRESSION_VALUE_ERROR
@ EXPRESSION_VALUE_TRUE
#define TYPED_MAP_DECLARE(Prefix, KeyType, ValueType)
Definition: map.h:116
#define TYPED_MAP_DEFINE(Prefix, KeyType, ValueType, hash_fn, equal_fn, destroy_key_fn, destroy_value_fn)
Definition: map.h:134
bool IsRegexItemIn(const EvalContext *ctx, const Item *list, const char *regex)
Definition: matching.c:235
void xsnprintf(char *str, size_t str_size, const char *format,...)
Definition: misc_lib.c:114
#define ProgrammingError(...)
Definition: misc_lib.h:33
uid_t getuid(void)
gid_t getgid(void)
int lstat(const char *file_name, struct stat *buf)
const char * PromiseGetNamespace(const Promise *pp)
Definition: policy.c:2666
Rlist * PromiseGetConstraintAsList(const EvalContext *ctx, const char *lval, const Promise *pp)
Get the Rlist value of the first effective constraint found matching, from a promise.
Definition: policy.c:2920
const char * PromiseGetHandle(const Promise *pp)
Return handle of the promise.
Definition: policy.c:2713
const Policy * PromiseGetPolicy(const Promise *pp)
Definition: policy.c:2676
const Bundle * PromiseGetBundle(const Promise *pp)
Definition: policy.c:2671
JsonElement * BundleToJson(const Bundle *bundle)
Serialize a bundle as JSON.
Definition: policy.c:1872
void * PromiseGetConstraintAsRval(const Promise *pp, const char *lval, RvalType rtype)
Get the Rval value of the first effective constraint that matches the given type.
Definition: policy.c:3054
JsonElement * BodyToJson(const Body *body)
Serialize a body as JSON.
Definition: policy.c:1925
const char * NamespaceDefault(void)
Definition: policy.c:93
void PromiseDestroy(Promise *pp)
Definition: policy.c:1471
#define PRINTSIZE(what)
Definition: printsize.h:66
Promise * ExpandDeRefPromise(EvalContext *ctx, const Promise *pp, bool *excluded)
Definition: promises.c:566
pcre * CompileRegex(const char *regex)
Definition: regex.c:37
bool StringMatchFull(const char *regex, const char *str)
Definition: regex.c:106
bool StringMatchFullWithPrecompiledRegex(pcre *pattern, const char *str)
Definition: regex.c:121
#define CFENGINE_REGEX_WHITESPACE_IN_CONTEXTS
Definition: regex.h:34
void RingBufferDestroy(RingBuffer *buf)
Definition: ring_buffer.c:134
void RingBufferAppend(RingBuffer *buf, void *item)
Definition: ring_buffer.c:63
RingBuffer * RingBufferNew(size_t capacity, void *(*copy)(const void *), void(*destroy)(void *))
Definition: ring_buffer.c:46
JsonElement * RvalContainerValue(Rval rval)
Definition: rlist.c:165
char * RlistScalarValue(const Rlist *rlist)
Definition: rlist.c:83
unsigned int RlistHash_untyped(const void *list, unsigned seed)
Definition: rlist.c:1438
Rlist * RvalRlistValue(Rval rval)
Definition: rlist.c:153
FnCall * RvalFnCallValue(Rval rval)
Definition: rlist.c:141
char * RvalScalarValue(Rval rval)
Definition: rlist.c:129
void RlistWrite(Writer *writer, const Rlist *list)
Definition: rlist.c:1318
void RlistDestroy_untyped(void *rl)
Definition: rlist.c:518
bool RlistEqual_untyped(const void *list1, const void *list2)
Definition: rlist.c:1652
void RlistDestroy(Rlist *rl)
Definition: rlist.c:501
RvalType DataTypeToRvalType(DataType datatype)
Definition: rlist.c:44
void RvalDestroy(Rval rval)
Definition: rlist.c:940
Rlist * RlistAppendScalar(Rlist **start, const char *scalar)
Definition: rlist.c:545
int RlistLen(const Rlist *start)
Definition: rlist.c:672
bool RlistIsNullList(const Rlist *list)
Definition: rlist.c:262
Rlist * RlistCopy(const Rlist *rp)
Definition: rlist.c:494
Rval RvalCopy(Rval rval)
Definition: rlist.c:474
const char * SpecialScopeToString(SpecialScope scope)
Definition: scope.c:42
SpecialScope SpecialScopeFromString(const char *scope)
Definition: scope.c:67
void ScopeMapBodyArgs(EvalContext *ctx, const Body *body, const Rlist *args)
Definition: scope.c:238
void ScopeAugment(EvalContext *ctx, const Bundle *bp, const Promise *pp, const Rlist *arguments)
augments a scope, expecting corresponding lists of lvals and rvals (implying same length)....
Definition: scope.c:111
SpecialScope
Definition: scope.h:33
@ SPECIAL_SCOPE_THIS
Definition: scope.h:39
@ SPECIAL_SCOPE_EDIT
Definition: scope.h:35
@ SPECIAL_SCOPE_MON
Definition: scope.h:37
@ SPECIAL_SCOPE_SYS
Definition: scope.h:38
@ SPECIAL_SCOPE_CONST
Definition: scope.h:34
@ SPECIAL_SCOPE_MATCH
Definition: scope.h:36
@ SPECIAL_SCOPE_BODY
Definition: scope.h:40
@ SPECIAL_SCOPE_DEF
Definition: scope.h:41
@ SPECIAL_SCOPE_NONE
Definition: scope.h:43
void SeqRemove(Seq *seq, size_t index)
Remove a single item in the sequence.
Definition: sequence.c:156
size_t SeqLength(const Seq *seq)
Length of the sequence.
Definition: sequence.c:354
Seq * SeqNew(size_t initialCapacity, void(ItemDestroy)(void *item))
Definition: sequence.c:31
ssize_t SeqIndexOf(Seq *seq, const void *key, SeqItemComparator Compare)
Linearly searches through the sequence and returns the index of the first matching object,...
Definition: sequence.c:189
void SeqClear(Seq *seq)
Remove all elements in sequence.
Definition: sequence.c:314
void SeqDestroy(Seq *seq)
Destroy an existing Sequence.
Definition: sequence.c:60
void SeqAppend(Seq *seq, void *item)
Append a new item to the Sequence.
Definition: sequence.c:104
static void * SeqAt(const Seq *seq, int i)
Definition: sequence.h:57
StringSetIterator StringSetIteratorInit(StringSet *set)
Definition: set.c:34
bool StringSetContains(const StringSet *set, const char *element)
Definition: set.c:34
size_t StringSetSize(const StringSet *set)
Definition: set.c:34
StringSet * StringSetNew(void)
Definition: set.c:34
bool StringSetRemove(const StringSet *set, const char *element)
Definition: set.c:34
void StringSetDestroy(StringSet *set)
Definition: set.c:34
void StringSetAdd(const StringSet *set, char *element)
Definition: set.c:34
void StringSetClear(StringSet *set)
Definition: set.c:34
char * StringSetIteratorNext(StringSetIterator *iter)
Definition: set.c:34
#define MAX(x, y)
Definition: snprintf.c:500
VarRefType
char * SafeStringDuplicate(const char *str)
Definition: string_lib.c:172
bool IsStrIn(const char *str, const char *const strs[])
Definition: string_lib.c:782
bool StringEqual_untyped(const void *a, const void *b)
Definition: string_lib.c:306
bool StringEqual(const char *const a, const char *const b)
Definition: string_lib.c:256
int Chop(char *str, size_t max_length)
Remove trailing spaces.
Definition: string_lib.c:1174
unsigned int StringHash_untyped(const void *str, unsigned int seed)
Definition: string_lib.c:114
int StringSafeCompare(const char *const a, const char *const b)
Definition: string_lib.c:226
ssize_t StringReplace(char *buf, size_t buf_size, const char *find, const char *replace)
Definition: string_lib.c:868
char * StringConcatenate(size_t count, const char *first,...)
Definition: string_lib.c:348
void CanonifyNameInPlace(char *s)
Definition: string_lib.c:1574
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:34
DefineClasses classes
Definition: cf3.defs.h:1568
TransactionContext transaction
Definition: cf3.defs.h:1567
Definition: policy.h:85
char * type
Definition: policy.h:88
char * ns
Definition: policy.h:90
Seq * conlist
Definition: policy.h:93
Rlist * args
Definition: policy.h:91
char * name
Definition: policy.h:89
Definition: buffer.h:50
Definition: policy.h:70
char * name
Definition: policy.h:74
char * type
Definition: policy.h:73
char * ns
Definition: policy.h:75
Definition: class.h:59
char * name
Definition: class.h:61
char * ns
Definition: class.h:60
Definition: class.h:31
char * ns
Definition: class.h:32
char * name
Definition: class.h:33
bool is_soft
Definition: class.h:36
StringSet * tags
Definition: class.h:37
ContextScope scope
Definition: class.h:35
Rval rval
Definition: policy.h:133
char * lval
Definition: policy.h:132
Rlist * change
Definition: cf3.defs.h:954
Rlist * kept
Definition: cf3.defs.h:958
Rlist * del_kept
Definition: cf3.defs.h:962
Rlist * del_notkept
Definition: cf3.defs.h:963
ContextScope scope
Definition: cf3.defs.h:953
Rlist * failure
Definition: cf3.defs.h:955
Rlist * denied
Definition: cf3.defs.h:956
PersistentClassPolicy timer
Definition: cf3.defs.h:960
Rlist * timeout
Definition: cf3.defs.h:957
Rlist * del_change
Definition: cf3.defs.h:961
ClassTable * global_classes
Definition: eval_context.c:151
FuncCacheMap * function_cache
Definition: eval_context.c:158
RemoteVarPromisesMap * remote_var_promises
Definition: eval_context.c:186
Rlist * args
Definition: eval_context.c:144
StringSet * bundle_names
Definition: eval_context.c:182
Item * heap_abort
Definition: eval_context.c:146
StringSet * all_classes
Definition: eval_context.c:177
VariableTable * global_variables
Definition: eval_context.c:152
bool bundle_aborted
Definition: eval_context.c:137
bool select_end_match_eof
Definition: eval_context.c:174
Item * heap_abort_current_bundle
Definition: eval_context.c:147
PackagePromiseContext * package_promise_context
Definition: eval_context.c:171
char * launch_directory
Definition: eval_context.c:166
StringSet * dependency_handles
Definition: eval_context.c:157
bool checksum_updates_default
Definition: eval_context.c:139
VariableTable * match_variables
Definition: eval_context.c:154
Item * ip_addresses
Definition: eval_context.c:140
StringSet * promise_lock_cache
Definition: eval_context.c:156
char * entry_point
Definition: eval_context.c:168
Definition: fncall.h:31
char * name
Definition: fncall.h:32
Definition: item_lib.h:33
char * module_path
Definition: cf3.defs.h:1366
char * interpreter
Definition: cf3.defs.h:1365
Seq * package_modules_bodies
Definition: cf3.defs.h:1375
Rlist * control_package_inventory
Definition: cf3.defs.h:1372
char * control_package_module
Definition: cf3.defs.h:1374
Expression * result
PersistentClassPolicy policy
Definition: db_structs.h:144
unsigned int expires
Definition: db_structs.h:143
Definition: policy.h:53
Seq * bundles
Definition: policy.h:56
Seq * bodies
Definition: policy.h:57
char * name
Definition: policy.h:103
PromiseType * parent_promise_type
Definition: policy.h:111
const Promise * org_pp
Definition: policy.h:119
Seq * conlist
Definition: policy.h:117
char * comment
Definition: policy.h:114
char * promiser
Definition: policy.h:115
char * classes
Definition: policy.h:113
Rval promisee
Definition: policy.h:116
SourceOffset offset
Definition: policy.h:121
Definition: rlist.h:35
Rlist * next
Definition: rlist.h:37
Definition: cf3.defs.h:614
RvalType type
Definition: cf3.defs.h:616
void * item
Definition: cf3.defs.h:615
Sequence data-structure.
Definition: sequence.h:50
size_t line
Definition: policy.h:65
const Body * owner
Definition: eval_context.h:62
VariableTable * vars
Definition: eval_context.h:64
VariableTable * vars
Definition: eval_context.h:57
const Bundle * owner
Definition: eval_context.h:54
ClassTable * classes
Definition: eval_context.h:56
const PromiseIterator * iter_ctx
Definition: eval_context.h:82
const PromiseType * owner
Definition: eval_context.h:76
VariableTable * vars
Definition: eval_context.h:71
const Promise * owner
Definition: eval_context.h:69
StackFramePromiseType promise_type
Definition: eval_context.h:96
StackFrameBody body
Definition: eval_context.h:95
StackFramePromiseIteration promise_iteration
Definition: eval_context.h:98
bool inherits_previous
Definition: eval_context.h:90
StackFramePromise promise
Definition: eval_context.h:97
char * path
Definition: eval_context.h:101
union StackFrame::@13 data
StackFrameType type
Definition: eval_context.h:89
StackFrameBundle bundle
Definition: eval_context.h:94
Definition: set.h:138
char * log_repaired
Definition: cf3.defs.h:933
char ** indices
char * ns
char * scope
size_t num_indices
char * lval
VarRef * ref
Definition: variable.h:33
StringSet * tags
Definition: variable.h:36
const Promise * promise
Definition: variable.h:37
DataType type
Definition: variable.h:35
Rval rval
Definition: variable.h:34
Definition: writer.c:45
Definition: getopt.h:83
SyntaxTypeMatch CheckParseContext(const char *context, const char *range)
Definition: syntax.c:548
@ SYNTAX_TYPE_MATCH_OK
Definition: syntax.h:42
void RemoteSysLog(int log_priority, const char *log_string)
Definition: syslog_client.c:67
VarRef * VarRefParse(const char *var_ref_string)
VarRef * VarRefParseFromScope(const char *var_ref_string, const char *scope)
void VarRefDestroy(VarRef *ref)
bool VarRefIsQualified(const VarRef *ref)
VarRef VarRefConst(const char *ns, const char *scope, const char *lval)
void VarRefQualify(VarRef *ref, const char *ns, const char *scope)
VarRef * VarRefCopyIndexless(const VarRef *ref)
VarRef * VarRefCopy(const VarRef *ref)
char * VarRefToString(const VarRef *ref, bool qualified)
bool VariableTableRemove(VariableTable *table, const VarRef *ref)
Definition: variable.c:144
bool VariableTablePut(VariableTable *table, const VarRef *ref, const Rval *rval, DataType type, const char *tags, const Promise *promise)
Definition: variable.c:170
Variable * VariableTableIteratorNext(VariableTableIterator *iter)
Definition: variable.c:283
VariableTableIterator * VariableTableIteratorNew(const VariableTable *table, const char *ns, const char *scope, const char *lval)
Definition: variable.c:273
VariableTableIterator * VariableTableIteratorNewFromVarRef(const VariableTable *table, const VarRef *ref)
Definition: variable.c:263
VariableTable * VariableTableNew(void)
Definition: variable.c:79
void VariableTableIteratorDestroy(VariableTableIterator *iter)
Definition: variable.c:336
bool VariableTableClear(VariableTable *table, const char *ns, const char *scope, const char *lval)
Definition: variable.c:196
void VariableTableDestroy(VariableTable *table)
Definition: variable.c:88
Variable * VariableTableGet(const VariableTable *table, const VarRef *ref)
Definition: variable.c:98
bool StringContainsVar(const char *s, const char *v)
Definition: vars.c:79
bool IsCf3VarString(const char *str)
Definition: vars.c:123
size_t WriterWrite(Writer *writer, const char *str)
Definition: writer.c:193
const char * StringWriterData(const Writer *writer)
Definition: writer.c:229
void WriterClose(Writer *writer)
Definition: writer.c:242
size_t WriterWriteF(Writer *writer, const char *fmt,...)
Definition: writer.c:144
Writer * StringWriter(void)
Definition: writer.c:67