"Fossies" - the Fresh Open Source Software Archive

Member "cfengine-3.15.4/libpromises/eval_context.c" (7 Jun 2021, 98426 Bytes) of package /linux/misc/cfengine-3.15.4.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code 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 
   60 static const char *STACK_FRAME_TYPE_STR[STACK_FRAME_TYPE_MAX] = {
   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 *,
   86                  RlistHash_untyped,
   87                  RlistEqual_untyped,
   88                  RlistDestroy_untyped,
   89                  RvalDestroy2)
   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 *,
  107                  StringHash_untyped,
  108                  StringEqual_untyped,
  109                  free,
  110                  SeqDestroy_untyped)
  111 
  112 
  113 static pcre *context_expression_whitespace_rx = NULL;
  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  */
  131 struct EvalContext_
  132 {
  133     /* TODO: a pointer to read-only version of config is often needed. */
  134     /* const GenericAgentConfig *config; */
  135 
  136     int eval_options;
  137     bool bundle_aborted;
  138     bool eval_aborted;
  139     bool checksum_updates_default;
  140     Item *ip_addresses;
  141     bool ignore_locks;
  142 
  143     int pass;
  144     Rlist *args;
  145 
  146     Item *heap_abort;
  147     Item *heap_abort_current_bundle;
  148 
  149     Seq *stack;
  150 
  151     ClassTable *global_classes;
  152     VariableTable *global_variables;
  153 
  154     VariableTable *match_variables;
  155 
  156     StringSet *promise_lock_cache;
  157     StringSet *dependency_handles;
  158     FuncCacheMap *function_cache;
  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.
  166     char *launch_directory;
  167 
  168     char *entry_point;
  169 
  170     /* new package promise evaluation context */
  171     PackagePromiseContext *package_promise_context;
  172 
  173     /* select_end_match_eof context*/
  174     bool select_end_match_eof;
  175 
  176     /* List if all classes set during policy evaluation */
  177     StringSet *all_classes;
  178 
  179     /* These following two fields are needed for remote variable injection
  180      * detection (CFE-1915) */
  181     /* Names of all bundles */
  182     StringSet *bundle_names;
  183 
  184     /* Promises possibly remotely-injecting variables */
  185     /* ONLY INITIALIZED WHEN NON-EMPTY, OTHERWISE NULL */
  186     RemoteVarPromisesMap *remote_var_promises;
  187 };
  188 
  189 bool EvalContextGetSelectEndMatchEof(const EvalContext *ctx)
  190 {
  191     return ctx->select_end_match_eof;
  192 }
  193 
  194 void EvalContextSetSelectEndMatchEof(EvalContext *ctx, bool value)
  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 
  205     free(ctx->package_promise_context->control_package_module);
  206     ctx->package_promise_context->control_package_module =
  207             SafeStringDuplicate(name);
  208 }
  209 
  210 void AddDefaultInventoryToContext(const EvalContext *ctx, Rlist *inventory)
  211 {
  212     assert(ctx);
  213     assert(ctx->package_promise_context);
  214 
  215     RlistDestroy(ctx->package_promise_context->control_package_inventory);
  216     ctx->package_promise_context->control_package_inventory =
  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 
  226 void AddPackageModuleToContext(const EvalContext *ctx, PackageModuleBody *pm)
  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 
  242 PackageModuleBody *GetPackageModuleFromContext(const EvalContext *ctx,
  243         const char *name)
  244 {
  245     if (name == NULL)
  246     {
  247         return NULL;
  248     }
  249 
  250     for (int i = 0;
  251          i < SeqLength(ctx->package_promise_context->package_modules_bodies);
  252          i++)
  253     {
  254         PackageModuleBody *pm =
  255                 SeqAt(ctx->package_promise_context->package_modules_bodies, i);
  256         if (strcmp(name, pm->name) == 0)
  257         {
  258             return pm;
  259         }
  260     }
  261     return NULL;
  262 }
  263 
  264 PackageModuleBody *GetDefaultPackageModuleFromContext(const EvalContext *ctx)
  265 {
  266     char *def_pm_name = ctx->package_promise_context->control_package_module;
  267     return GetPackageModuleFromContext(ctx, def_pm_name);
  268 }
  269 
  270 Rlist *GetDefaultInventoryFromContext(const EvalContext *ctx)
  271 {
  272     return ctx->package_promise_context->control_package_inventory;
  273 }
  274 
  275 PackagePromiseContext *GetPackagePromiseContext(const EvalContext *ctx)
  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 
  290 static StackFrame *LastStackFrameByType(const EvalContext *ctx, StackFrameType type)
  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 
  304 static LogLevel AdjustLogLevel(LogLevel base, LogLevel adjust)
  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 {
  338     return StringToLogLevel(PromiseGetConstraintAsRval(pp, attr_name, RVAL_TYPE_SCALAR));
  339 }
  340 
  341 static LogLevel CalculateLogLevel(const Promise *pp)
  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 
  359 static LogLevel CalculateReportLevel(const Promise *pp)
  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 
  371 const char *EvalContextStackToString(EvalContext *ctx)
  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     {
  405         StackFrame *last_frame = LastStackFrameByType(ctx, STACK_FRAME_TYPE_BUNDLE);
  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         {
  502             ClassRefQualify(&ref, NamespaceDefault());
  503         }
  504     }
  505 
  506     assert(ClassRefIsQualified(ref));
  507     bool classy = (strcmp("any", ref.name) == 0 ||
  508                    EvalContextHeapContainsSoft(ctx, ref.ns, ref.name) ||
  509                    EvalContextStackFrameContainsSoft(ctx, 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 
  529 bool ClassCharIsWhitespace(char ch)
  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 
  545     if (context_expression_whitespace_rx == NULL)
  546     {
  547         context_expression_whitespace_rx = CompileRegex(CFENGINE_REGEX_WHITESPACE_IN_CONTEXTS);
  548     }
  549 
  550     if (context_expression_whitespace_rx == NULL)
  551     {
  552         Log(LOG_LEVEL_ERR, "The context expression whitespace regular expression could not be compiled, aborting.");
  553         return EXPRESSION_VALUE_ERROR;
  554     }
  555 
  556     if (StringMatchFullWithPrecompiledRegex(context_expression_whitespace_rx, context))
  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     {
  574         ExpressionValue r = EvalExpression(res.result,
  575                                            &EvalTokenAsClass, &EvalVarRef,
  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     {
  604         ExpressionValue r = EvalExpression(res.result,
  605                                            &EvalTokenFromList,
  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 
  731 void EvalContextHeapPersistentLoadAll(EvalContext *ctx)
  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 
  803 bool BundleAbort(EvalContext *ctx)
  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 
  867 bool MissingDependencies(EvalContext *ctx, const Promise *pp)
  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 
  883         if (!StringSetContains(ctx->dependency_handles, RlistScalarValue(rp)))
  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 
  894 static void StackFrameBundleDestroy(StackFrameBundle frame)
  895 {
  896     ClassTableDestroy(frame.classes);
  897     VariableTableDestroy(frame.vars);
  898 }
  899 
  900 static void StackFrameBodyDestroy(ARG_UNUSED StackFrameBody frame)
  901 {
  902     VariableTableDestroy(frame.vars);
  903 }
  904 
  905 static void StackFramePromiseDestroy(StackFramePromise frame)
  906 {
  907     VariableTableDestroy(frame.vars);
  908 }
  909 
  910 static void StackFramePromiseIterationDestroy(StackFramePromiseIteration frame)
  911 {
  912     PromiseDestroy(frame.owner);
  913     RingBufferDestroy(frame.log_messages);
  914 }
  915 
  916 static void StackFrameDestroy(StackFrame *frame)
  917 {
  918     if (frame)
  919     {
  920         switch (frame->type)
  921         {
  922         case STACK_FRAME_TYPE_BUNDLE:
  923             StackFrameBundleDestroy(frame->data.bundle);
  924             break;
  925 
  926         case STACK_FRAME_TYPE_BODY:
  927             StackFrameBodyDestroy(frame->data.body);
  928             break;
  929 
  930         case STACK_FRAME_TYPE_PROMISE_TYPE:
  931             break;
  932 
  933         case STACK_FRAME_TYPE_PROMISE:
  934             StackFramePromiseDestroy(frame->data.promise);
  935             break;
  936 
  937         case STACK_FRAME_TYPE_PROMISE_ITERATION:
  938             StackFramePromiseIterationDestroy(frame->data.promise_iteration);
  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
  951 void FreePackageManager(PackageModuleBody *manager)
  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
  963 PackagePromiseContext *PackagePromiseConfigNew()
  964 {
  965     PackagePromiseContext *package_promise_defaults =
  966             xmalloc(sizeof(PackagePromiseContext));
  967     package_promise_defaults->control_package_module = NULL;
  968     package_promise_defaults->control_package_inventory = NULL;
  969     package_promise_defaults->package_modules_bodies =
  970             SeqNew(5, FreePackageManager);
  971 
  972     return package_promise_defaults;
  973 }
  974 
  975 static
  976 void FreePackagePromiseContext(PackagePromiseContext *pp_ctx)
  977 {
  978     SeqDestroy(pp_ctx->package_modules_bodies);
  979     RlistDestroy(pp_ctx->control_package_inventory);
  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     {
  996         RingBufferAppend(last_frame->data.promise_iteration.log_messages, xstrdup(message));
  997     }
  998     return xstrdup(message);
  999 }
 1000 
 1001 ENTERPRISE_VOID_FUNC_1ARG_DEFINE_STUB(void, EvalContextSetupMissionPortalLogHook,
 1002                                       ARG_UNUSED EvalContext *, ctx)
 1003 {
 1004 }
 1005 
 1006 EvalContext *EvalContextNew(void)
 1007 {
 1008     EvalContext *ctx = xcalloc(1, sizeof(EvalContext));
 1009 
 1010     ctx->eval_options = EVAL_OPTION_FULL;
 1011     ctx->stack = SeqNew(10, StackFrameDestroy);
 1012     ctx->global_classes = ClassTableNew();
 1013     ctx->global_variables = VariableTableNew();
 1014     ctx->match_variables = VariableTableNew();
 1015     ctx->dependency_handles = StringSetNew();
 1016 
 1017     ctx->uid = getuid();
 1018     ctx->gid = getgid();
 1019     ctx->pid = getpid();
 1020 
 1021 #ifndef __MINGW32__
 1022     ctx->ppid = getppid();
 1023 #endif
 1024 
 1025     ctx->promise_lock_cache = StringSetNew();
 1026     ctx->function_cache = FuncCacheMapNew();
 1027 
 1028     EvalContextSetupMissionPortalLogHook(ctx);
 1029 
 1030     ctx->package_promise_context = PackagePromiseConfigNew();
 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 
 1041 void EvalContextDestroy(EvalContext *ctx)
 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         {
 1050             LoggingPrivContext *pctx = LoggingPrivGetContext();
 1051             free(pctx);
 1052             LoggingPrivSetContext(NULL);
 1053         }
 1054         LoggingFreeCurrentThreadContext();
 1055 
 1056         EvalContextDeleteIpAddresses(ctx);
 1057 
 1058         DeleteItemList(ctx->heap_abort);
 1059         DeleteItemList(ctx->heap_abort_current_bundle);
 1060 
 1061         RlistDestroy(ctx->args);
 1062 
 1063         SeqDestroy(ctx->stack);
 1064 
 1065         ClassTableDestroy(ctx->global_classes);
 1066         VariableTableDestroy(ctx->global_variables);
 1067         VariableTableDestroy(ctx->match_variables);
 1068 
 1069         StringSetDestroy(ctx->dependency_handles);
 1070         StringSetDestroy(ctx->promise_lock_cache);
 1071 
 1072         FuncCacheMapDestroy(ctx->function_cache);
 1073 
 1074         FreePackagePromiseContext(ctx->package_promise_context);
 1075 
 1076         StringSetDestroy(ctx->all_classes);
 1077         StringSetDestroy(ctx->bundle_names);
 1078         if (ctx->remote_var_promises != NULL)
 1079         {
 1080             RemoteVarPromisesMapDestroy(ctx->remote_var_promises);
 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 
 1138 void EvalContextClear(EvalContext *ctx)
 1139 {
 1140     ClassTableClear(ctx->global_classes);
 1141     EvalContextDeleteIpAddresses(ctx);
 1142     VariableTableClear(ctx->global_variables, NULL, NULL, NULL);
 1143     VariableTableClear(ctx->match_variables, NULL, NULL, NULL);
 1144     StringSetClear(ctx->promise_lock_cache);
 1145     SeqClear(ctx->stack);
 1146     FuncCacheMapClear(ctx->function_cache);
 1147 }
 1148 
 1149 Rlist *EvalContextGetPromiseCallerMethods(EvalContext *ctx) {
 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 
 1160         case STACK_FRAME_TYPE_BUNDLE:
 1161             break;
 1162 
 1163         case STACK_FRAME_TYPE_PROMISE_ITERATION:
 1164             break;
 1165 
 1166         case STACK_FRAME_TYPE_PROMISE:
 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 
 1172         case STACK_FRAME_TYPE_PROMISE_TYPE:
 1173             break;
 1174 
 1175         default:
 1176             ProgrammingError("Unhandled stack frame type");
 1177         }
 1178     }
 1179     return callers_promisers;
 1180 }
 1181 
 1182 JsonElement *EvalContextGetPromiseCallers(EvalContext *ctx) {
 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 
 1200         case STACK_FRAME_TYPE_BUNDLE:
 1201             JsonObjectAppendString(f, "type", "bundle");
 1202             JsonObjectAppendObject(f, "bundle", BundleToJson(frame->data.bundle.owner));
 1203             break;
 1204 
 1205         case STACK_FRAME_TYPE_PROMISE_ITERATION:
 1206             JsonObjectAppendString(f, "type", "iteration");
 1207             JsonObjectAppendInteger(f, "iteration_index", frame->data.promise_iteration.index);
 1208 
 1209             break;
 1210 
 1211         case STACK_FRAME_TYPE_PROMISE:
 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 
 1220         case STACK_FRAME_TYPE_PROMISE_TYPE:
 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 
 1235 void EvalContextSetBundleArgs(EvalContext *ctx, const Rlist *args)
 1236 {
 1237     if (ctx->args)
 1238     {
 1239         RlistDestroy(ctx->args);
 1240     }
 1241 
 1242     ctx->args = RlistCopy(args);
 1243 }
 1244 
 1245 Rlist *EvalContextGetBundleArgs(EvalContext *ctx)
 1246 {
 1247     return (Rlist *) ctx->args;
 1248 }
 1249 
 1250 void EvalContextSetPass(EvalContext *ctx, int pass)
 1251 {
 1252     ctx->pass = pass;
 1253 }
 1254 
 1255 int EvalContextGetPass(EvalContext *ctx)
 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 {
 1284     StackFrame *frame = StackFrameNew(STACK_FRAME_TYPE_BODY, false);
 1285 
 1286     frame->data.body.owner = owner;
 1287     frame->data.body.vars = VariableTableNew();
 1288 
 1289     return frame;
 1290 }
 1291 
 1292 static StackFrame *StackFrameNewPromiseType(const PromiseType *owner)
 1293 {
 1294     StackFrame *frame = StackFrameNew(STACK_FRAME_TYPE_PROMISE_TYPE, true);
 1295 
 1296     frame->data.promise_type.owner = owner;
 1297 
 1298     return frame;
 1299 }
 1300 
 1301 static StackFrame *StackFrameNewPromise(const Promise *owner)
 1302 {
 1303     StackFrame *frame = StackFrameNew(STACK_FRAME_TYPE_PROMISE, true);
 1304 
 1305     frame->data.promise.owner = owner;
 1306 
 1307     return frame;
 1308 }
 1309 
 1310 static StackFrame *StackFrameNewPromiseIteration(Promise *owner, const PromiseIterator *iter_ctx)
 1311 {
 1312     StackFrame *frame = StackFrameNew(STACK_FRAME_TYPE_PROMISE_ITERATION, true);
 1313 
 1314     frame->data.promise_iteration.owner = owner;
 1315     frame->data.promise_iteration.iter_ctx = iter_ctx;
 1316     frame->data.promise_iteration.log_messages = RingBufferNew(5, NULL, free);
 1317 
 1318     return frame;
 1319 }
 1320 
 1321 void EvalContextStackFrameRemoveSoft(EvalContext *ctx, const char *context)
 1322 {
 1323     StackFrame *frame = LastStackFrameByType(ctx, STACK_FRAME_TYPE_BUNDLE);
 1324     assert(frame);
 1325 
 1326     ClassTableRemove(frame->data.bundle.classes, frame->data.bundle.owner->ns, context);
 1327 }
 1328 
 1329 static void EvalContextStackPushFrame(EvalContext *ctx, StackFrame *frame)
 1330 {
 1331     StackFrame *last_frame = LastStackFrame(ctx, 0);
 1332     if (last_frame)
 1333     {
 1334         if (last_frame->type == STACK_FRAME_TYPE_PROMISE_ITERATION)
 1335         {
 1336             LoggingPrivSetLevels(LogGetGlobalLevel(), LogGetGlobalLevel());
 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         {
 1360             VariableTable *table = LastStackFrameByType(ctx, STACK_FRAME_TYPE_BUNDLE)->data.bundle.vars;
 1361             VariableTableClear(table, NULL, NULL, NULL);
 1362         }
 1363 
 1364         ScopeAugment(ctx, owner, caller, args);
 1365     }
 1366 
 1367     {
 1368         VariableTableIterator *iter = VariableTableIteratorNew(ctx->global_variables, owner->ns, owner->name, NULL);
 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         }
 1376         VariableTableIteratorDestroy(iter);
 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 
 1395     EvalContextStackPushFrame(ctx, StackFrameNewBody(body));
 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 
 1417 void EvalContextStackPushPromiseTypeFrame(EvalContext *ctx, const PromiseType *owner)
 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 
 1425 void EvalContextStackPushPromiseFrame(EvalContext *ctx, const Promise *owner)
 1426 {
 1427     assert(LastStackFrame(ctx, 0));
 1428     assert(LastStackFrame(ctx, 0)->type == STACK_FRAME_TYPE_PROMISE_TYPE);
 1429 
 1430     EvalContextVariableClearMatch(ctx);
 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 
 1493 Promise *EvalContextStackPushPromiseIterationFrame(EvalContext *ctx, const PromiseIterator *iter_ctx)
 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 
 1510     EvalContextStackPushFrame(ctx, StackFrameNewPromiseIteration(pexp, iter_ctx));
 1511 
 1512     LoggingPrivSetLevels(CalculateLogLevel(pexp), CalculateReportLevel(pexp));
 1513 
 1514     return pexp;
 1515 }
 1516 
 1517 void EvalContextStackPopFrame(EvalContext *ctx)
 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     {
 1526     case STACK_FRAME_TYPE_BUNDLE:
 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 
 1536     case STACK_FRAME_TYPE_PROMISE_ITERATION:
 1537         LoggingPrivSetLevels(LogGetGlobalLevel(), LogGetGlobalLevel());
 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);
 1552             LoggingPrivSetLevels(CalculateLogLevel(pp), CalculateReportLevel(pp));
 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 {
 1578     StackFrame *frame = LastStackFrameByType(ctx, STACK_FRAME_TYPE_BUNDLE);
 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 {
 1593     StackFrame *frame = LastStackFrameByType(ctx, STACK_FRAME_TYPE_BUNDLE);
 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 
 1671     Nova_ClassHistoryAddContextName(ctx->all_classes, name);
 1672 
 1673     switch (scope)
 1674     {
 1675     case CONTEXT_SCOPE_BUNDLE:
 1676         {
 1677             StackFrame *frame = LastStackFrameByType(ctx, STACK_FRAME_TYPE_BUNDLE);
 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 
 1686     case CONTEXT_SCOPE_NAMESPACE:
 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         {
 1721         case STACK_FRAME_TYPE_BUNDLE:
 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 
 1761 ClassTableIterator *EvalContextClassTableIteratorNewLocal(const EvalContext *ctx)
 1762 {
 1763     StackFrame *frame = LastStackFrameByType(ctx, STACK_FRAME_TYPE_BUNDLE);
 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 
 1772 const Promise *EvalContextStackCurrentPromise(const EvalContext *ctx)
 1773 {
 1774     StackFrame *frame = LastStackFrameByType(ctx, STACK_FRAME_TYPE_PROMISE_ITERATION);
 1775     return frame ? frame->data.promise_iteration.owner : NULL;
 1776 }
 1777 
 1778 const Bundle *EvalContextStackCurrentBundle(const EvalContext *ctx)
 1779 {
 1780     StackFrame *frame = LastStackFrameByType(ctx, STACK_FRAME_TYPE_BUNDLE);
 1781     return frame ? frame->data.bundle.owner : NULL;
 1782 }
 1783 
 1784 const RingBuffer *EvalContextStackCurrentMessages(const EvalContext *ctx)
 1785 {
 1786     StackFrame *frame = LastStackFrameByType(ctx, STACK_FRAME_TYPE_PROMISE_ITERATION);
 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  */
 1828 static void BufferAppendAbbreviatedStr(Buffer *buf,
 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 
 1857 char *EvalContextStackPath(const EvalContext *ctx)
 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 
 1871         case STACK_FRAME_TYPE_BUNDLE:
 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 
 1878         case STACK_FRAME_TYPE_PROMISE_TYPE:
 1879             BufferAppendChar(path, '/');
 1880             BufferAppend(path, frame->data.promise_type.owner->name, CF_BUFSIZE);
 1881 
 1882         case STACK_FRAME_TYPE_PROMISE:
 1883             break;
 1884 
 1885         case STACK_FRAME_TYPE_PROMISE_ITERATION:
 1886             BufferAppendChar(path, '/');
 1887             BufferAppendChar(path, '\'');
 1888             BufferAppendAbbreviatedStr(path, frame->data.promise_iteration.owner->promiser, CF_MAXFRAGMENT);
 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]",
 1896                               PromiseIteratorIndex(frame->data.promise_iteration.iter_ctx));
 1897             }
 1898             break;
 1899 
 1900             default:
 1901                 ProgrammingError("Unhandled stack frame type");
 1902         }
 1903     }
 1904 
 1905     return BufferClose(path);
 1906 }
 1907 
 1908 StringSet *EvalContextStackPromisees(const EvalContext *ctx)
 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);
 1915         if (frame->type != STACK_FRAME_TYPE_PROMISE_ITERATION)
 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 
 2017 const void *EvalContextVariableGetSpecial(
 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  */
 2035 const char *EvalContextVariableGetSpecialString(
 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 
 2074 static VariableTable *GetVariableTableForScope(const EvalContext *ctx,
 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         {
 2102             StackFrame *frame = LastStackFrameByType(ctx, STACK_FRAME_TYPE_BUNDLE);
 2103             assert(frame);
 2104             return frame->data.bundle.vars;
 2105         }
 2106 
 2107     case SPECIAL_SCOPE_BODY:
 2108         assert(!ns || strcmp("default", ns) == 0);
 2109         {
 2110             StackFrame *frame = LastStackFrameByType(ctx, STACK_FRAME_TYPE_BODY);
 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         {
 2118             StackFrame *frame = LastStackFrameByType(ctx, STACK_FRAME_TYPE_PROMISE);
 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:
 2186         VarRefQualify(ref, NULL, SpecialScopeToString(SPECIAL_SCOPE_BODY));
 2187         break;
 2188 
 2189     case STACK_FRAME_TYPE_PROMISE_TYPE:
 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 
 2200     case STACK_FRAME_TYPE_BUNDLE:
 2201         VarRefQualify(ref,
 2202                       last_frame->data.bundle.owner->ns,
 2203                       last_frame->data.bundle.owner->name);
 2204         break;
 2205 
 2206     case STACK_FRAME_TYPE_PROMISE:
 2207     case STACK_FRAME_TYPE_PROMISE_ITERATION:
 2208         // Allow special "this" variables to work when used without "this"
 2209         VarRefQualify(ref, NULL, SpecialScopeToString(SPECIAL_SCOPE_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  */
 2220 bool EvalContextVariablePut(EvalContext *ctx,
 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,
 2264                                                      NESTED_SCOPE_SEP,
 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,
 2306                                                                      SpecialScopeToString(SPECIAL_SCOPE_THIS));
 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 
 2381     if ((SpecialScopeFromString(ref->scope) == SPECIAL_SCOPE_THIS  ||
 2382          SpecialScopeFromString(ref->scope) == SPECIAL_SCOPE_BODY)
 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         {
 2413             JsonElement *child = JsonSelect(RvalContainerValue(var->rval),
 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 
 2441 const Promise *EvalContextVariablePromiseGet(const EvalContext *ctx, const VarRef *ref)
 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 
 2459 StringSet *EvalContextVariableTags(const EvalContext *ctx, const VarRef *ref)
 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 
 2471 bool EvalContextVariableClearMatch(EvalContext *ctx)
 2472 {
 2473     return VariableTableClear(ctx->match_variables, NULL, NULL, NULL);
 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 
 2483 VariableTableIterator *EvalContextVariableTableFromRefIteratorNew(const EvalContext *ctx, const VarRef *ref)
 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 
 2490 const void *EvalContextVariableControlCommonGet(const EvalContext *ctx, CommonControl lval)
 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         {
 2513             ClassRefQualify(&ref, NamespaceDefault());
 2514         }
 2515     }
 2516 
 2517     return ref;
 2518 }
 2519 
 2520 const Bundle *EvalContextResolveBundleExpression(const EvalContext *ctx, const Policy *policy,
 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 
 2561 void EvalContextAppendBodyParentsAndArgs(const EvalContext *ctx, const Policy *policy,
 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 
 2609 Seq *EvalContextResolveBodyExpression(const EvalContext *ctx, const Policy *policy,
 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 {
 2635     StringSetAdd(ctx->promise_lock_cache, xstrdup(key));
 2636 }
 2637 
 2638 void EvalContextPromiseLockCacheRemove(EvalContext *ctx, const char *key)
 2639 {
 2640     StringSetRemove(ctx->promise_lock_cache, key);
 2641 }
 2642 
 2643 bool EvalContextFunctionCacheGet(const EvalContext *ctx,
 2644                                  const FnCall *fp ARG_UNUSED,
 2645                                  const Rlist *args, Rval *rval_out)
 2646 {
 2647     if (!(ctx->eval_options & EVAL_OPTION_CACHE_SYSTEM_FUNCTIONS))
 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 
 2667 void EvalContextFunctionCachePut(EvalContext *ctx,
 2668                                  const FnCall *fp ARG_UNUSED,
 2669                                  const Rlist *args, const Rval *rval)
 2670 {
 2671     if (!(ctx->eval_options & EVAL_OPTION_CACHE_SYSTEM_FUNCTIONS))
 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:
 2756             case CONTEXT_SCOPE_NAMESPACE:
 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     {
 2773         if (CheckParseContext(RlistScalarValue(rp), CF_IDRANGE) != SYNTAX_TYPE_MATCH_OK)
 2774         {
 2775             return; // TODO: interesting course of action, but why is the check there in the first place?
 2776         }
 2777 
 2778         if (EvalContextHeapContainsHard(ctx, RlistScalarValue(rp)))
 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 
 2788         EvalContextHeapPersistentRemove(string);
 2789 
 2790         {
 2791             ClassRef ref = ClassRefParse(CanonifyName(string));
 2792             EvalContextClassRemove(ctx, ref.ns, ref.name);
 2793             ClassRefDestroy(ref);
 2794         }
 2795         EvalContextStackFrameRemoveSoft(ctx, CanonifyName(string));
 2796     }
 2797 }
 2798 
 2799 ENTERPRISE_VOID_FUNC_2ARG_DEFINE_STUB(void, TrackTotalCompliance, ARG_UNUSED PromiseResult, status, ARG_UNUSED const Promise *, pp)
 2800 {
 2801 }
 2802 
 2803 static void SetPromiseOutcomeClasses(EvalContext *ctx, PromiseResult status, const DefineClasses *dc)
 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 
 2815     case PROMISE_RESULT_TIMEOUT:
 2816         add_classes = dc->timeout;
 2817         del_classes = dc->del_notkept;
 2818         break;
 2819 
 2820     case PROMISE_RESULT_WARN:
 2821     case PROMISE_RESULT_FAIL:
 2822     case PROMISE_RESULT_INTERRUPTED:
 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                 {
 2871                     Log(LOG_LEVEL_VERBOSE,
 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 
 2919     case PROMISE_RESULT_TIMEOUT:
 2920     case PROMISE_RESULT_FAIL:
 2921     case PROMISE_RESULT_DENIED:
 2922     case PROMISE_RESULT_INTERRUPTED:
 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 
 2937 void NotifyDependantPromises(EvalContext *ctx, const Promise *pp, PromiseResult result)
 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);
 2961     if (IsPromiseValuableForStatus(pp))
 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 
 3012     Log(LOG_LEVEL_VERBOSE, "%s", StringWriterData(w));
 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 
 3061 bool GetChecksumUpdatesDefault(const EvalContext *ctx)
 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 
 3072 void EvalContextDeleteIpAddresses(EvalContext *ctx)
 3073 {
 3074     DeleteItemList(ctx->ip_addresses);
 3075     ctx->ip_addresses = NULL;
 3076 }
 3077 
 3078 Item *EvalContextGetIpAddresses(const EvalContext *ctx)
 3079 {
 3080     return ctx->ip_addresses;
 3081 }
 3082 
 3083 void EvalContextSetEvalOption(EvalContext *ctx, EvalContextOption option, bool value)
 3084 {
 3085     if (value)
 3086     {
 3087         ctx->eval_options |= option;
 3088     }
 3089     else
 3090     {
 3091         ctx->eval_options &= ~option;
 3092     }
 3093 }
 3094 
 3095 bool EvalContextGetEvalOption(EvalContext *ctx, EvalContextOption option)
 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 
 3106 void EvalContextSetEntryPoint(
 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 
 3120 void EvalContextSetIgnoreLocks(EvalContext *ctx, bool ignore)
 3121 {
 3122     ctx->ignore_locks = ignore;
 3123 }
 3124 
 3125 bool EvalContextIsIgnoringLocks(const EvalContext *ctx)
 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;
 3154                     StringSetIterator it = StringSetIteratorInit(tagset);
 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 
 3200 JsonElement* JsonExpandElement(EvalContext *ctx, const JsonElement *source)
 3201 {
 3202     if (JsonGetElementType(source) == JSON_ELEMENT_TYPE_PRIMITIVE)
 3203     {
 3204         Buffer *expbuf;
 3205         JsonElement *expanded_json;
 3206 
 3207         switch (JsonGetPrimitiveType(source))
 3208         {
 3209         case JSON_PRIMITIVE_TYPE_STRING:
 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     {
 3224         if (JsonGetContainerType(source) == JSON_CONTAINER_TYPE_OBJECT)
 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 
 3254 const StringSet *EvalContextAllClassesGet(const EvalContext *ctx)
 3255 {
 3256     assert (ctx);
 3257     return ctx->all_classes;
 3258 }
 3259 
 3260 void EvalContextAllClassesLoggingEnable(EvalContext *ctx, bool enable)
 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 
 3272 const StringSet *EvalContextGetBundleNames(const EvalContext *ctx)
 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     {
 3285         ctx->remote_var_promises = RemoteVarPromisesMapNew();
 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 }