"Fossies" - the Fresh Open Source Software Archive

Member "cfengine-3.15.4/libpromises/policy.c" (7 Jun 2021, 96705 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. For more information about "policy.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.15.3_vs_3.15.4.

    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 <policy.h>
   26 
   27 #include <syntax.h>
   28 #include <string_lib.h>
   29 #include <conversion.h>
   30 #include <mutex.h>
   31 #include <misc_lib.h>
   32 #include <mod_files.h>
   33 #include <vars.h>
   34 #include <fncall.h>
   35 #include <rlist.h>
   36 #include <set.h>
   37 #include <eval_context.h>
   38 #include <promises.h>
   39 #include <item_lib.h>
   40 #include <hash.h>
   41 #include <files_names.h>
   42 #include <audit.h>
   43 #include <logging.h>
   44 #include <expand.h>
   45 
   46 static const char *const POLICY_ERROR_BUNDLE_NAME_RESERVED =
   47     "Use of a reserved container name as a bundle name \"%s\"";
   48 static const char *const POLICY_ERROR_BUNDLE_REDEFINITION =
   49     "Duplicate definition of bundle %s with type %s";
   50 static const char *const POLICY_ERROR_BUNDLE_UNDEFINED =
   51     "Undefined bundle %s with type %s";
   52 static const char *const POLICY_ERROR_BODY_REDEFINITION =
   53     "Duplicate definition of body %s with type %s";
   54 static const char *const POLICY_ERROR_BODY_UNDEFINED =
   55     "Undefined body %s with type %s";
   56 static const char *const POLICY_ERROR_BODY_CONTROL_ARGS =
   57     "Control bodies cannot take arguments, body %s control";
   58 static const char *const POLICY_ERROR_PROMISE_UNCOMMENTED =
   59     "Promise is missing a comment attribute, and comments are required "
   60     "by policy";
   61 static const char *const POLICY_ERROR_PROMISE_DUPLICATE_HANDLE =
   62     "Duplicate promise handle %s found";
   63 static const char *const POLICY_ERROR_LVAL_INVALID =
   64     "Promise type %s has unknown attribute %s";
   65 
   66 static const char *const POLICY_ERROR_CONSTRAINT_TYPE_MISMATCH =
   67     "Type mismatch in constraint: %s";
   68 
   69 static const char *const POLICY_ERROR_EMPTY_VARREF =
   70     "Empty variable reference";
   71 
   72 //************************************************************************
   73 
   74 static void BundleDestroy(Bundle *bundle);
   75 static void BodyDestroy(Body *body);
   76 static SyntaxTypeMatch ConstraintCheckType(const Constraint *cp);
   77 static bool PromiseCheck(const Promise *pp, Seq *errors);
   78 
   79 /*************************************************************************/
   80 
   81 /**
   82  * @brief Return a default bundle name for this method/service
   83  */
   84 Rval DefaultBundleConstraint(const Promise *pp, char *promisetype)
   85 {
   86     static char name[CF_BUFSIZE];
   87     snprintf(name, CF_BUFSIZE, "%s_%s", promisetype, CanonifyName(pp->promiser));
   88     return (Rval) { name, RVAL_TYPE_SCALAR };
   89 }
   90 
   91 /*************************************************************************/
   92 
   93 const char *NamespaceDefault(void)
   94 {
   95     return "default";
   96 }
   97 
   98 /*************************************************************************/
   99 
  100 Policy *PolicyNew(void)
  101 {
  102     Policy *policy = xcalloc(1, sizeof(Policy));
  103 
  104     policy->release_id = NULL;
  105     policy->bundles = SeqNew(100, BundleDestroy);
  106     policy->bodies = SeqNew(100, BodyDestroy);
  107     policy->policy_files_hashes = NULL;
  108 
  109     return policy;
  110 }
  111 
  112 /*************************************************************************/
  113 
  114 int PolicyCompare(const void *a, const void *b)
  115 {
  116     return a - b;
  117 }
  118 
  119 /*************************************************************************/
  120 
  121 void PolicyDestroy(Policy *policy)
  122 {
  123     if (policy)
  124     {
  125         SeqDestroy(policy->bundles);
  126         SeqDestroy(policy->bodies);
  127         free(policy->release_id);
  128         if (policy->policy_files_hashes != NULL)
  129         {
  130             StringMapDestroy(policy->policy_files_hashes);
  131         }
  132 
  133         free(policy);
  134     }
  135 }
  136 
  137 /*************************************************************************/
  138 
  139 static unsigned ConstraintHash(const Constraint *cp, unsigned seed)
  140 {
  141     unsigned hash = seed;
  142 
  143     hash = StringHash(cp->lval, hash);
  144     hash = StringHash(cp->classes, hash);
  145     hash = RvalHash(cp->rval, hash);
  146 
  147     return hash;
  148 }
  149 
  150 /*************************************************************************/
  151 
  152 static unsigned BodyHash(const Body *body, unsigned seed)
  153 {
  154     unsigned hash = seed;
  155     for (size_t i = 0; i < SeqLength(body->conlist); i++)
  156     {
  157         const Constraint *cp = SeqAt(body->conlist, i);
  158         hash = ConstraintHash(cp, hash);
  159     }
  160 
  161     return hash;
  162 }
  163 /*************************************************************************/
  164 
  165 static unsigned PromiseHash(const Promise *pp, unsigned seed)
  166 {
  167     unsigned hash = seed;
  168 
  169     hash = StringHash(pp->promiser, seed);
  170     hash = RvalHash(pp->promisee, hash);
  171 
  172     for (size_t i = 0; i < SeqLength(pp->conlist); i++)
  173     {
  174         const Constraint *cp = SeqAt(pp->conlist, i);
  175         hash = ConstraintHash(cp, hash);
  176     }
  177 
  178     return hash;
  179 }
  180 
  181 /*************************************************************************/
  182 
  183 static unsigned PromiseTypeHash(const PromiseType *pt, unsigned seed)
  184 {
  185     unsigned hash = seed;
  186 
  187     hash = StringHash(pt->name, hash);
  188     for (size_t i = 0; i < SeqLength(pt->promises); i++)
  189     {
  190         const Promise *pp = SeqAt(pt->promises, i);
  191         hash = PromiseHash(pp, hash);
  192     }
  193 
  194     return hash;
  195 }
  196 
  197 /*************************************************************************/
  198 
  199 static unsigned BundleHash(const Bundle *bundle, unsigned seed)
  200 {
  201     unsigned hash = seed;
  202 
  203     hash = StringHash(bundle->type, hash);
  204     hash = StringHash(bundle->ns, hash);
  205     hash = StringHash(bundle->name, hash);
  206     hash = RlistHash(bundle->args, hash);
  207 
  208     for (size_t i = 0; i < SeqLength(bundle->promise_types); i++)
  209     {
  210         const PromiseType *pt = SeqAt(bundle->promise_types, i);
  211         hash = PromiseTypeHash(pt, hash);
  212     }
  213 
  214     return hash;
  215 }
  216 
  217 /*************************************************************************/
  218 
  219 unsigned PolicyHash(const Policy *policy)
  220 {
  221     unsigned hash = 0;
  222 
  223     for (size_t i = 0; i < SeqLength(policy->bodies); i++)
  224     {
  225         const Body *body = SeqAt(policy->bodies, i);
  226         hash = BodyHash(body, hash);
  227     }
  228 
  229     for (size_t i = 0; i < SeqLength(policy->bundles); i++)
  230     {
  231         const Bundle *bundle = SeqAt(policy->bundles, i);
  232         hash = BundleHash(bundle, hash);
  233     }
  234 
  235     return hash;
  236 }
  237 
  238 /*************************************************************************/
  239 
  240 StringSet *PolicySourceFiles(const Policy *policy)
  241 {
  242     StringSet *files = StringSetNew();
  243 
  244     for (size_t i = 0; i < SeqLength(policy->bundles); i++)
  245     {
  246         const Bundle *bp = SeqAt(policy->bundles, i);
  247         if (bp->source_path)
  248         {
  249             StringSetAdd(files, xstrdup(bp->source_path));
  250         }
  251     }
  252 
  253     for (size_t i = 0; i < SeqLength(policy->bodies); i++)
  254     {
  255         const Bundle *bp = SeqAt(policy->bodies, i);
  256         if (bp->source_path)
  257         {
  258             StringSetAdd(files, xstrdup(bp->source_path));
  259         }
  260     }
  261 
  262     return files;
  263 }
  264 
  265 /*************************************************************************/
  266 
  267 /**
  268  * Get hash digest of the given policy file.
  269  *
  270  * @param policy Policy that is supposed to contain (have loaded) the file
  271  * @param policy_file_path Absolute path of the policy file to get the digest for
  272  * @return Hash digest of the given policy file or %NULL if unknown
  273  * @note   The returned hash digest is owned by the policy, **do not free it**.
  274  */
  275 const char *PolicyGetPolicyFileHash(const Policy *policy, const char *policy_file_path)
  276 {
  277     return StringMapGet(policy->policy_files_hashes, policy_file_path);
  278 }
  279 
  280 /*************************************************************************/
  281 
  282 static const char *StripNamespace(const char *full_symbol)
  283 {
  284     char *sep = strchr(full_symbol, CF_NS);
  285 
  286     if (sep == NULL)
  287     {
  288         return full_symbol;
  289     }
  290     else
  291     {
  292         return sep + 1;
  293     }
  294 }
  295 
  296 /*************************************************************************/
  297 
  298 /**
  299  * @brief Query a policy for a body
  300  * @param policy The policy to query
  301  * @param ns Namespace filter (optionally NULL)
  302  * @param type Body type filter
  303  * @param name Body name filter
  304  * @return Body child object if found, otherwise NULL
  305  */
  306 Body *PolicyGetBody(const Policy *policy, const char *ns, const char *type, const char *name)
  307 {
  308     for (size_t i = 0; i < SeqLength(policy->bodies); i++)
  309     {
  310         Body *bp = SeqAt(policy->bodies, i);
  311         const char *body_symbol = StripNamespace(bp->name);
  312 
  313         if (strcmp(bp->type, type)    == 0 &&
  314             strcmp(body_symbol, name) == 0)
  315         {
  316             // allow namespace to be optionally matched
  317             if (ns && strcmp(bp->ns, ns) != 0)
  318             {
  319                 continue;
  320             }
  321 
  322             return bp;
  323         }
  324     }
  325 
  326     return NULL;
  327 }
  328 
  329 /*************************************************************************/
  330 
  331 /**
  332  * @brief Query a policy for a bundle
  333  * @param policy The policy to query
  334  * @param ns Namespace filter (optionally NULL)
  335  * @param type Bundle type filter
  336  * @param name Bundle name filter
  337  * @return Bundle child object if found, otherwise NULL
  338  */
  339 Bundle *PolicyGetBundle(const Policy *policy, const char *ns, const char *type, const char *name)
  340 {
  341     const char *bundle_symbol = StripNamespace(name);
  342 
  343     for (size_t i = 0; i < SeqLength(policy->bundles); i++)
  344     {
  345         Bundle *bp = SeqAt(policy->bundles, i);
  346 
  347         if ((type == NULL || strcmp(bp->type, type) == 0)
  348             &&
  349             ((strcmp(bp->name, bundle_symbol) == 0) ||
  350              (strcmp(bp->name, name)          == 0)))
  351         {
  352             // allow namespace to be optionally matched
  353             if (ns && strcmp(bp->ns, ns) != 0)
  354             {
  355                 continue;
  356             }
  357 
  358             return bp;
  359         }
  360     }
  361 
  362     return NULL;
  363 }
  364 
  365 /*************************************************************************/
  366 
  367 /**
  368  * @brief Check to see if a policy is runnable (contains body common control)
  369  * @param policy Policy to check
  370  * @return True if policy is runnable
  371  */
  372 bool PolicyIsRunnable(const Policy *policy)
  373 {
  374     return PolicyGetBody(policy, NULL, "common", "control") != NULL;
  375 }
  376 
  377 /*************************************************************************/
  378 
  379 /**
  380  * @brief Merge two partial policy objects. The memory for the child objects of the original policies are transferred to the new parent.
  381  * @param a
  382  * @param b
  383  * @return Merged policy
  384  */
  385 Policy *PolicyMerge(Policy *a, Policy *b)
  386 {
  387     Policy *result = PolicyNew();
  388 
  389     SeqAppendSeq(result->bundles, a->bundles);
  390     SeqSoftDestroy(a->bundles);
  391     SeqAppendSeq(result->bundles, b->bundles);
  392     SeqSoftDestroy(b->bundles);
  393 
  394     for (size_t i = 0; i < SeqLength(result->bundles); i++)
  395     {
  396         Bundle *bp = SeqAt(result->bundles, i);
  397         bp->parent_policy = result;
  398     }
  399 
  400     SeqAppendSeq(result->bodies, a->bodies);
  401     SeqSoftDestroy(a->bodies);
  402     SeqAppendSeq(result->bodies, b->bodies);
  403     SeqSoftDestroy(b->bodies);
  404 
  405     for (size_t i = 0; i < SeqLength(result->bodies); i++)
  406     {
  407         Body *bdp = SeqAt(result->bodies, i);
  408         bdp->parent_policy = result;
  409     }
  410 
  411     StringMap *extra_hashes = NULL;
  412     if (a->policy_files_hashes != NULL)
  413     {
  414         result->policy_files_hashes = a->policy_files_hashes;
  415         a->policy_files_hashes = NULL;
  416         extra_hashes = b->policy_files_hashes;
  417         b->policy_files_hashes = NULL;
  418     }
  419     else if (b->policy_files_hashes != NULL)
  420     {
  421         result->policy_files_hashes = b->policy_files_hashes;
  422         b->policy_files_hashes = NULL;
  423     }
  424     else
  425     {
  426         result->policy_files_hashes = NULL;
  427     }
  428 
  429     if (extra_hashes != NULL)
  430     {
  431         MapIterator it = MapIteratorInit(extra_hashes->impl);
  432         MapKeyValue *item;
  433         while ((item = MapIteratorNext(&it)) != NULL)
  434         {
  435             /* Move data and duplicate just the keys (which are always owned by
  436                the map). */
  437             StringMapInsert(result->policy_files_hashes,
  438                             xstrdup((char*) item->key), (char*) item->value);
  439         }
  440         /* Destroy only the map and the keys, data was moved. */
  441         StringMapSoftDestroy(extra_hashes);
  442     }
  443 
  444     /* Should result take over a release_id ? */
  445     free(a->release_id);
  446     free(b->release_id);
  447     free(a);
  448     free(b);
  449 
  450     return result;
  451 }
  452 
  453 /*************************************************************************/
  454 
  455 const char *ConstraintGetNamespace(const Constraint *cp)
  456 {
  457     switch (cp->type)
  458     {
  459     case POLICY_ELEMENT_TYPE_BODY:
  460         return cp->parent.body->ns;
  461 
  462     case POLICY_ELEMENT_TYPE_PROMISE:
  463         return cp->parent.promise->parent_promise_type->parent_bundle->ns;
  464 
  465     default:
  466         ProgrammingError("Constraint has parent type: %d", cp->type);
  467     }
  468 }
  469 
  470 /*************************************************************************/
  471 
  472 /**
  473  * @brief Convenience function to get the policy object associated with a promise
  474  * @param promise
  475  * @return Policy object
  476  */
  477 const Policy *PolicyFromPromise(const Promise *promise)
  478 {
  479     assert(promise);
  480 
  481     PromiseType *promise_type = promise->parent_promise_type;
  482     assert(promise_type);
  483 
  484     Bundle *bundle = promise_type->parent_bundle;
  485     assert(bundle);
  486 
  487     return bundle->parent_policy;
  488 }
  489 
  490 char *BundleQualifiedName(const Bundle *bundle)
  491 {
  492     assert(bundle);
  493     if (!bundle)
  494     {
  495         return NULL;
  496     }
  497 
  498     if (bundle->name)
  499     {
  500         const char *ns = bundle->ns ? bundle->ns : NamespaceDefault();
  501         return StringConcatenate(3, ns, ":", bundle->name);  // CF_NS == ':'
  502     }
  503 
  504     return NULL;
  505 }
  506 
  507 static bool RvalTypeCheckDataType(RvalType rval_type, DataType expected_datatype)
  508 {
  509     if (rval_type == RVAL_TYPE_FNCALL)
  510     {
  511         return true;
  512     }
  513 
  514     switch (expected_datatype)
  515     {
  516     case CF_DATA_TYPE_BODY:
  517     case CF_DATA_TYPE_BUNDLE:
  518         return rval_type == RVAL_TYPE_SCALAR;
  519 
  520     case CF_DATA_TYPE_CONTEXT:
  521     case CF_DATA_TYPE_COUNTER:
  522     case CF_DATA_TYPE_INT:
  523     case CF_DATA_TYPE_INT_RANGE:
  524     case CF_DATA_TYPE_OPTION:
  525     case CF_DATA_TYPE_REAL:
  526     case CF_DATA_TYPE_REAL_RANGE:
  527     case CF_DATA_TYPE_STRING:
  528         return rval_type == RVAL_TYPE_SCALAR;
  529 
  530     case CF_DATA_TYPE_CONTEXT_LIST:
  531     case CF_DATA_TYPE_INT_LIST:
  532     case CF_DATA_TYPE_OPTION_LIST:
  533     case CF_DATA_TYPE_REAL_LIST:
  534     case CF_DATA_TYPE_STRING_LIST:
  535         return (rval_type == RVAL_TYPE_SCALAR) || (rval_type == RVAL_TYPE_LIST);
  536 
  537     case CF_DATA_TYPE_CONTAINER:
  538         return (rval_type == RVAL_TYPE_CONTAINER);
  539 
  540     default:
  541         ProgrammingError("Unhandled expected datatype in switch: %d", expected_datatype);
  542     }
  543 }
  544 
  545 /*************************************************************************/
  546 
  547 /* Check if a constraint's syntax is correct according to its promise_type and
  548    lvalue.
  549 */
  550 static bool ConstraintCheckSyntax(const Constraint *constraint, Seq *errors)
  551 {
  552     if (constraint->type != POLICY_ELEMENT_TYPE_PROMISE)
  553     {
  554         ProgrammingError("Attempted to check the syntax for a constraint"
  555                          " not belonging to a promise");
  556     }
  557 
  558     const PromiseType *promise_type = constraint->parent.promise->parent_promise_type;
  559     const Bundle *bundle = promise_type->parent_bundle;
  560 
  561     /* Check if lvalue is valid for the bundle's specific promise_type. */
  562     const PromiseTypeSyntax *promise_type_syntax = PromiseTypeSyntaxGet(bundle->type, promise_type->name);
  563     for (size_t i = 0; promise_type_syntax->constraints[i].lval != NULL; i++)
  564     {
  565         const ConstraintSyntax *body_syntax = &promise_type_syntax->constraints[i];
  566         if (strcmp(body_syntax->lval, constraint->lval) == 0)
  567         {
  568             if (!RvalTypeCheckDataType(constraint->rval.type, body_syntax->dtype))
  569             {
  570                 SeqAppend(errors,
  571                           PolicyErrorNew(POLICY_ELEMENT_TYPE_CONSTRAINT, constraint,
  572                                          POLICY_ERROR_CONSTRAINT_TYPE_MISMATCH, constraint->lval));
  573                 return false;
  574             }
  575             return true;
  576         }
  577     }
  578     /* FIX: Call a VerifyConstraint() hook for the specific promise_type, defined
  579        in verify_TYPE.c, that checks for promise_type-specific constraint syntax. */
  580 
  581     /* Check if lvalue is valid for all bodies. */
  582     for (size_t i = 0; CF_COMMON_BODIES[i].lval != NULL; i++)
  583     {
  584         if (strcmp(constraint->lval, CF_COMMON_BODIES[i].lval) == 0)
  585         {
  586             if (!RvalTypeCheckDataType(constraint->rval.type, CF_COMMON_BODIES[i].dtype))
  587             {
  588                 SeqAppend(errors,
  589                           PolicyErrorNew(POLICY_ELEMENT_TYPE_CONSTRAINT, constraint,
  590                                          POLICY_ERROR_CONSTRAINT_TYPE_MISMATCH, constraint->lval));
  591                 return false;
  592             }
  593             return true;
  594         }
  595     }
  596     for (size_t i = 0; CF_COMMON_EDITBODIES[i].lval != NULL; i++)
  597     {
  598         if (strcmp(constraint->lval, CF_COMMON_EDITBODIES[i].lval) == 0)
  599         {
  600             if (!RvalTypeCheckDataType(constraint->rval.type, CF_COMMON_EDITBODIES[i].dtype))
  601             {
  602                 SeqAppend(errors,
  603                           PolicyErrorNew(POLICY_ELEMENT_TYPE_CONSTRAINT, constraint,
  604                                          POLICY_ERROR_CONSTRAINT_TYPE_MISMATCH, constraint->lval));
  605                 return false;
  606             }
  607             return true;
  608         }
  609     }
  610     for (size_t i = 0; CF_COMMON_XMLBODIES[i].lval != NULL; i++)
  611     {
  612         if (strcmp(constraint->lval, CF_COMMON_XMLBODIES[i].lval) == 0)
  613         {
  614             if (!RvalTypeCheckDataType(constraint->rval.type, CF_COMMON_XMLBODIES[i].dtype))
  615             {
  616                 SeqAppend(errors,
  617                           PolicyErrorNew(POLICY_ELEMENT_TYPE_CONSTRAINT, constraint,
  618                                          POLICY_ERROR_CONSTRAINT_TYPE_MISMATCH, constraint->lval));
  619                 return false;
  620             }
  621             return true;
  622         }
  623     }
  624 
  625     /* lval is unknown for this promise type */
  626     SeqAppend(errors,
  627               PolicyErrorNew(POLICY_ELEMENT_TYPE_CONSTRAINT, constraint,
  628                              POLICY_ERROR_LVAL_INVALID,
  629                              constraint->parent.promise->parent_promise_type->name,
  630                              constraint->lval));
  631 
  632     return false;
  633 }
  634 
  635 /*************************************************************************/
  636 
  637 static bool PolicyCheckPromiseType(const PromiseType *promise_type, Seq *errors)
  638 {
  639     assert(promise_type);
  640     assert(promise_type->parent_bundle);
  641     bool success = true;
  642 
  643     for (size_t i = 0; i < SeqLength(promise_type->promises); i++)
  644     {
  645         const Promise *pp = SeqAt(promise_type->promises, i);
  646         success &= PromiseCheck(pp, errors);
  647     }
  648 
  649     return success;
  650 }
  651 
  652 /*************************************************************************/
  653 
  654 static bool PolicyCheckBundle(const Bundle *bundle, Seq *errors)
  655 {
  656     assert(bundle);
  657     bool success = true;
  658 
  659     // ensure no reserved bundle names are used
  660     {
  661         static const char *const reserved_names[] =
  662             { "sys", "const", "mon", "edit", "match", "mon", "this", NULL };
  663         if (IsStrIn(bundle->name, reserved_names))
  664         {
  665             SeqAppend(errors, PolicyErrorNew(POLICY_ELEMENT_TYPE_BUNDLE, bundle,
  666                                              POLICY_ERROR_BUNDLE_NAME_RESERVED, bundle->name));
  667             success = false;
  668         }
  669     }
  670 
  671     for (size_t i = 0; i < SeqLength(bundle->promise_types); i++)
  672     {
  673         const PromiseType *type = SeqAt(bundle->promise_types, i);
  674         success &= PolicyCheckPromiseType(type, errors);
  675     }
  676 
  677     return success;
  678 }
  679 
  680 static bool PolicyCheckBody(const Body *body, Seq *errors)
  681 {
  682     bool success = true;
  683 
  684     if (strcmp("control", body->name) == 0)
  685     {
  686         if (RlistLen(body->args) > 0)
  687         {
  688             SeqAppend(errors, PolicyErrorNew(POLICY_ELEMENT_TYPE_BODY, body,
  689                                              POLICY_ERROR_BODY_CONTROL_ARGS,
  690                                              body->type));
  691             success = false;
  692         }
  693     }
  694 
  695     for (size_t i = 0; i < SeqLength(body->conlist); i++)
  696     {
  697         Constraint *cp = SeqAt(body->conlist, i);
  698         SyntaxTypeMatch err = ConstraintCheckType(cp);
  699         if (err != SYNTAX_TYPE_MATCH_OK && err != SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED)
  700         {
  701             SeqAppend(errors, PolicyErrorNew(POLICY_ELEMENT_TYPE_CONSTRAINT, cp,
  702                                              POLICY_ERROR_CONSTRAINT_TYPE_MISMATCH,
  703                                              cp->lval));
  704             success = false;
  705         }
  706     }
  707 
  708     const BodySyntax *body_syntax = BodySyntaxGet(body->type);
  709     assert(body_syntax && "Should have been checked at parse time");
  710     if (body_syntax->check_body)
  711     {
  712         success &= body_syntax->check_body(body, errors);
  713     }
  714 
  715     return success;
  716 }
  717 
  718 /*************************************************************************/
  719 
  720 /* Get the syntax of a constraint according to its promise_type and lvalue.
  721    Make sure you've already checked the constraint's validity.
  722 */
  723 static const ConstraintSyntax *ConstraintGetSyntax(const Constraint *constraint)
  724 {
  725     if (constraint->type != POLICY_ELEMENT_TYPE_PROMISE)
  726     {
  727         ProgrammingError("Attempted to get the syntax for a constraint not belonging to a promise");
  728     }
  729 
  730     const Promise *promise = constraint->parent.promise;
  731     const PromiseType *promise_type = promise->parent_promise_type;
  732     const Bundle *bundle = promise_type->parent_bundle;
  733 
  734     const PromiseTypeSyntax *promise_type_syntax = PromiseTypeSyntaxGet(bundle->type, promise_type->name);
  735 
  736     /* Check if lvalue is valid for the bundle's specific promise_type. */
  737     for (size_t i = 0; promise_type_syntax->constraints[i].lval != NULL; i++)
  738     {
  739         const ConstraintSyntax *body_syntax = &promise_type_syntax->constraints[i];
  740         if (strcmp(body_syntax->lval, constraint->lval) == 0)
  741         {
  742             return body_syntax;
  743         }
  744     }
  745 
  746     /* Check if lvalue is valid for all bodies. */
  747     for (size_t i = 0; CF_COMMON_BODIES[i].lval != NULL; i++)
  748     {
  749         if (strcmp(constraint->lval, CF_COMMON_BODIES[i].lval) == 0)
  750         {
  751             return &CF_COMMON_BODIES[i];
  752         }
  753     }
  754     for (size_t i = 0; CF_COMMON_EDITBODIES[i].lval != NULL; i++)
  755     {
  756         if (strcmp(constraint->lval, CF_COMMON_EDITBODIES[i].lval) == 0)
  757         {
  758             return &CF_COMMON_EDITBODIES[i];
  759         }
  760     }
  761     for (size_t i = 0; CF_COMMON_XMLBODIES[i].lval != NULL; i++)
  762     {
  763         if (strcmp(constraint->lval, CF_COMMON_XMLBODIES[i].lval) == 0)
  764         {
  765             return &CF_COMMON_XMLBODIES[i];
  766         }
  767     }
  768 
  769     /* Syntax must have been checked first during PolicyCheckPartial(). */
  770     ProgrammingError("ConstraintGetSyntax() was called for constraint with "
  771                      "invalid lvalue: %s", constraint->lval);
  772     return NULL;
  773 }
  774 
  775 /*************************************************************************/
  776 
  777 /**
  778  * @return A reference to the full symbol value of the Rval regardless of type, e.g. "foo:bar""foo:bar"
  779  */
  780 static const char *RvalFullSymbol(const Rval *rval)
  781 {
  782     switch (rval->type)
  783     {
  784     case RVAL_TYPE_SCALAR:
  785         return rval->item;
  786         break;
  787 
  788     case RVAL_TYPE_FNCALL:
  789         return ((FnCall *)rval->item)->name;
  790 
  791     default:
  792         ProgrammingError("Cannot get full symbol value from Rval of type %c", rval->type);
  793         return NULL;
  794     }
  795 }
  796 
  797 /**
  798  * @return A copy of the namespace component of a qualified name, or NULL. e.g. "foo:bar" -> "foo"
  799  */
  800 char *QualifiedNameNamespaceComponent(const char *qualified_name)
  801 {
  802     if (strchr(qualified_name, CF_NS))
  803     {
  804         char ns[256] = { 0 };
  805         sscanf(qualified_name, "%255[^:]", ns);
  806 
  807         return xstrdup(ns);
  808     }
  809     else
  810     {
  811         return NULL;
  812     }
  813 }
  814 
  815 /**
  816  * @return A copy of the symbol compoent of a qualified name, or NULL. e.g. "foo:bar" -> "bar"
  817  */
  818 char *QualifiedNameScopeComponent(const char *qualified_name)
  819 {
  820     char *sep = strchr(qualified_name, CF_NS);
  821     if (sep)
  822     {
  823         return xstrdup(sep + 1);
  824     }
  825     else
  826     {
  827         return xstrdup(qualified_name);
  828     }
  829 }
  830 
  831 static bool PolicyCheckUndefinedBodies(const Policy *policy, Seq *errors)
  832 {
  833     bool success = true;
  834 
  835     for (size_t bpi = 0; bpi < SeqLength(policy->bundles); bpi++)
  836     {
  837         Bundle *bundle = SeqAt(policy->bundles, bpi);
  838 
  839         for (size_t sti = 0; sti < SeqLength(bundle->promise_types); sti++)
  840         {
  841             PromiseType *promise_type = SeqAt(bundle->promise_types, sti);
  842 
  843             for (size_t ppi = 0; ppi < SeqLength(promise_type->promises); ppi++)
  844             {
  845                 Promise *promise = SeqAt(promise_type->promises, ppi);
  846 
  847                 for (size_t cpi = 0; cpi < SeqLength(promise->conlist); cpi++)
  848                 {
  849                     Constraint *constraint = SeqAt(promise->conlist, cpi);
  850 
  851                     const ConstraintSyntax *syntax = ConstraintGetSyntax(constraint);
  852                     if (syntax->dtype == CF_DATA_TYPE_BODY)
  853                     {
  854                         char *ns = QualifiedNameNamespaceComponent(RvalFullSymbol(&constraint->rval));
  855                         char *symbol = QualifiedNameScopeComponent(RvalFullSymbol(&constraint->rval));
  856 
  857                         Body *referenced_body = PolicyGetBody(policy, ns, constraint->lval, symbol);
  858                         if (!referenced_body)
  859                         {
  860                             SeqAppend(errors, PolicyErrorNew(POLICY_ELEMENT_TYPE_CONSTRAINT, constraint,
  861                                                              POLICY_ERROR_BODY_UNDEFINED, symbol, constraint->lval));
  862                             success = false;
  863                         }
  864 
  865                         free(ns);
  866                         free(symbol);
  867                     }
  868                 } // constraints
  869             } // promises
  870         } // promise_types
  871     } // bundles
  872 
  873     return success;
  874 }
  875 
  876 static bool PolicyCheckUndefinedBundles(const Policy *policy, Seq *errors)
  877 {
  878     bool success = true;
  879 
  880     for (size_t bpi = 0; bpi < SeqLength(policy->bundles); bpi++)
  881     {
  882         Bundle *bundle = SeqAt(policy->bundles, bpi);
  883 
  884         for (size_t sti = 0; sti < SeqLength(bundle->promise_types); sti++)
  885         {
  886             PromiseType *promise_type = SeqAt(bundle->promise_types, sti);
  887 
  888             for (size_t ppi = 0; ppi < SeqLength(promise_type->promises); ppi++)
  889             {
  890                 Promise *promise = SeqAt(promise_type->promises, ppi);
  891 
  892                 for (size_t cpi = 0; cpi < SeqLength(promise->conlist); cpi++)
  893                 {
  894                     Constraint *constraint = SeqAt(promise->conlist, cpi);
  895 
  896                     const ConstraintSyntax *syntax = ConstraintGetSyntax(constraint);
  897                     if (syntax->dtype == CF_DATA_TYPE_BUNDLE &&
  898                         !IsCf3VarString(RvalFullSymbol(&constraint->rval)))
  899                     {
  900                         char *ns = QualifiedNameNamespaceComponent(RvalFullSymbol(&constraint->rval));
  901                         char *symbol = QualifiedNameScopeComponent(RvalFullSymbol(&constraint->rval));
  902 
  903                         const Bundle *referenced_bundle = NULL;
  904                         if (strcmp(constraint->lval, "usebundle") == 0 || strcmp(constraint->lval, "home_bundle") == 0)
  905                         {
  906                             referenced_bundle = PolicyGetBundle(policy, ns, "agent", symbol);
  907                             if (!referenced_bundle)
  908                             {
  909                                 referenced_bundle = PolicyGetBundle(policy, ns, "common", symbol);
  910                             }
  911                         }
  912                         else
  913                         {
  914                             referenced_bundle = PolicyGetBundle(policy, ns, constraint->lval, symbol);
  915                         }
  916 
  917                         if (!referenced_bundle)
  918                         {
  919                             SeqAppend(errors, PolicyErrorNew(POLICY_ELEMENT_TYPE_CONSTRAINT, constraint,
  920                                                              POLICY_ERROR_BUNDLE_UNDEFINED, symbol, constraint->lval));
  921                             success = false;
  922                         }
  923 
  924                         free(ns);
  925                         free(symbol);
  926                     }
  927                 } // constraints
  928             } // promises
  929         } // promise_types
  930     } // bundles
  931 
  932     return success;
  933 }
  934 
  935 static bool PolicyCheckRequiredComments(const EvalContext *ctx, const Policy *policy, Seq *errors)
  936 {
  937     const Body *common_control = PolicyGetBody(policy, NULL, "common", "control");
  938     if (common_control)
  939     {
  940         bool require_comments = ConstraintsGetAsBoolean(ctx, "require_comments", common_control->conlist);
  941         if (!require_comments)
  942         {
  943             return true;
  944         }
  945 
  946         bool success = true;
  947 
  948         for (size_t bpi = 0; bpi < SeqLength(policy->bundles); bpi++)
  949         {
  950             Bundle *bundle = SeqAt(policy->bundles, bpi);
  951 
  952             for (size_t sti = 0; sti < SeqLength(bundle->promise_types); sti++)
  953             {
  954                 PromiseType *promise_type = SeqAt(bundle->promise_types, sti);
  955 
  956                 for (size_t ppi = 0; ppi < SeqLength(promise_type->promises); ppi++)
  957                 {
  958                     Promise *promise = SeqAt(promise_type->promises, ppi);
  959 
  960                     bool promise_has_comment = false;
  961                     for (size_t cpi = 0; cpi < SeqLength(promise->conlist); cpi++)
  962                     {
  963                         Constraint *constraint = SeqAt(promise->conlist, cpi);
  964 
  965                         if (strcmp(constraint->lval, "comment") == 0)
  966                         {
  967                             promise_has_comment = true;
  968                             break;
  969                         }
  970                     } // constraints
  971 
  972                     if (!promise_has_comment)
  973                     {
  974                         SeqAppend(errors, PolicyErrorNew(POLICY_ELEMENT_TYPE_CONSTRAINT, promise,
  975                                                          POLICY_ERROR_PROMISE_UNCOMMENTED));
  976                         success = false;
  977                     }
  978                 } // promises
  979             } // promise_types
  980         } // bundles
  981 
  982         return success;
  983     }
  984     else
  985     {
  986         return true;
  987     }
  988 }
  989 
  990 bool PolicyCheckDuplicateHandles(const Policy *policy, Seq *errors)
  991 {
  992     bool success = true;
  993 
  994     Map *recorded = MapNew(StringHash_untyped, StringEqual_untyped, NULL, NULL);
  995 
  996     for (size_t bpi = 0; bpi < SeqLength(policy->bundles); bpi++)
  997     {
  998         Bundle *bundle = SeqAt(policy->bundles, bpi);
  999 
 1000         for (size_t sti = 0; sti < SeqLength(bundle->promise_types); sti++)
 1001         {
 1002             PromiseType *promise_type = SeqAt(bundle->promise_types, sti);
 1003 
 1004             for (size_t ppi = 0; ppi < SeqLength(promise_type->promises); ppi++)
 1005             {
 1006                 Promise *promise = SeqAt(promise_type->promises, ppi);
 1007                 const char *handle = PromiseGetHandle(promise);
 1008 
 1009                 if (handle)
 1010                 {
 1011                     if (IsCf3VarString(handle))
 1012                     {
 1013                         // can't check dynamic handles
 1014                         continue;
 1015                     }
 1016 
 1017                     const Promise *other_promise = MapGet(recorded, handle);
 1018                     if (other_promise)
 1019                     {
 1020                         // Need to make this smarter by comparing parsed expressions for equivalency.
 1021                         if (strcmp(promise->classes, other_promise->classes) == 0)
 1022                         {
 1023                             SeqAppend(errors, PolicyErrorNew(POLICY_ELEMENT_TYPE_PROMISE, promise,
 1024                                                              POLICY_ERROR_PROMISE_DUPLICATE_HANDLE, handle));
 1025                             success = false;
 1026                         }
 1027                     }
 1028                     else
 1029                     {
 1030                         MapInsert(recorded, (void *)handle, (void *)promise);
 1031                     }
 1032                 }
 1033             }
 1034         }
 1035     }
 1036 
 1037     MapDestroy(recorded);
 1038 
 1039     return success;
 1040 }
 1041 
 1042 /**
 1043  * @brief Check a runnable policy DOM for errors
 1044  * @param policy Policy to check
 1045  * @param errors Sequence of PolicyError to append errors to
 1046  * @param ignore_missing_bundles Whether to ignore missing bundle references
 1047  * @return True if no new errors are found
 1048  */
 1049 bool PolicyCheckRunnable(const EvalContext *ctx, const Policy *policy, Seq *errors, bool ignore_missing_bundles)
 1050 {
 1051     bool success = true;
 1052 
 1053     success &= PolicyCheckRequiredComments(ctx, policy, errors);
 1054     success &= PolicyCheckUndefinedBodies(policy, errors);
 1055 
 1056     if (!ignore_missing_bundles)
 1057     {
 1058         success &= PolicyCheckUndefinedBundles(policy, errors);
 1059     }
 1060 
 1061     success &= PolicyCheckDuplicateHandles(policy, errors);
 1062 
 1063     return success;
 1064 }
 1065 
 1066 /**
 1067  * @brief Check a partial policy DOM for errors
 1068  * @param policy Policy to check
 1069  * @param errors Sequence of PolicyError to append errors to
 1070  * @return True if no new errors are found
 1071  */
 1072 bool PolicyCheckPartial(const Policy *policy, Seq *errors)
 1073 {
 1074     bool success = true;
 1075 
 1076     // ensure bundle names are not duplicated
 1077     for (size_t i = 0; i < SeqLength(policy->bundles); i++)
 1078     {
 1079         Bundle *bp = SeqAt(policy->bundles, i);
 1080 
 1081         for (size_t j = 0; j < SeqLength(policy->bundles); j++)
 1082         {
 1083             Bundle *bp2 = SeqAt(policy->bundles, j);
 1084 
 1085             if (bp != bp2
 1086                 && strcmp(bp->type, bp2->type) == 0
 1087                 && strcmp(bp->ns, bp2->ns) == 0
 1088                 && strcmp(bp->name, bp2->name) == 0)
 1089             {
 1090                 SeqAppend(errors, PolicyErrorNew(POLICY_ELEMENT_TYPE_BUNDLE, bp,
 1091                                                  POLICY_ERROR_BUNDLE_REDEFINITION,
 1092                                                  bp->name, bp->type));
 1093                 success = false;
 1094             }
 1095         }
 1096     }
 1097 
 1098     for (size_t i = 0; i < SeqLength(policy->bundles); i++)
 1099     {
 1100         Bundle *bp = SeqAt(policy->bundles, i);
 1101         success &= PolicyCheckBundle(bp, errors);
 1102     }
 1103 
 1104 
 1105     // ensure body names are not duplicated
 1106     for (size_t i = 0; i < SeqLength(policy->bodies); i++)
 1107     {
 1108         const Body *bp = SeqAt(policy->bodies, i);
 1109 
 1110         for (size_t j = 0; j < SeqLength(policy->bodies); j++)
 1111         {
 1112             const Body *bp2 = SeqAt(policy->bodies, j);
 1113 
 1114             if (bp != bp2
 1115                 && strcmp(bp->type, bp2->type) == 0
 1116                 && strcmp(bp->ns, bp2->ns) == 0
 1117                 && strcmp(bp->name, bp2->name) == 0)
 1118             {
 1119                 if (strcmp(bp->type,"file") != 0)
 1120                 {
 1121                     SeqAppend(errors, PolicyErrorNew(POLICY_ELEMENT_TYPE_BODY, bp,
 1122                                                      POLICY_ERROR_BODY_REDEFINITION,
 1123                                                      bp->name, bp->type));
 1124                     success = false;
 1125                 }
 1126             }
 1127         }
 1128     }
 1129 
 1130     for (size_t i = 0; i < SeqLength(policy->bodies); i++)
 1131     {
 1132         const Body *body = SeqAt(policy->bodies, i);
 1133         success &= PolicyCheckBody(body, errors);
 1134 
 1135     }
 1136 
 1137     success &= PolicyCheckDuplicateHandles(policy, errors);
 1138 
 1139     return success;
 1140 }
 1141 
 1142 /*************************************************************************/
 1143 
 1144 PolicyError *PolicyErrorNew(PolicyElementType type, const void *subject, const char *error_msg, ...)
 1145 {
 1146     PolicyError *error = xmalloc(sizeof(PolicyError));
 1147 
 1148     error->type = type;
 1149     error->subject = subject;
 1150 
 1151     va_list args;
 1152     va_start(args, error_msg);
 1153     xvasprintf(&error->message, error_msg, args);
 1154     va_end(args);
 1155 
 1156     return error;
 1157 }
 1158 
 1159 /*************************************************************************/
 1160 
 1161 void PolicyErrorDestroy(PolicyError *error)
 1162 {
 1163     free(error->message);
 1164     free(error);
 1165 }
 1166 
 1167 /*************************************************************************/
 1168 
 1169 static SourceOffset PolicyElementSourceOffset(PolicyElementType type, const void *element)
 1170 {
 1171     assert(element);
 1172 
 1173     switch (type)
 1174     {
 1175     case POLICY_ELEMENT_TYPE_POLICY:
 1176     {
 1177         return (SourceOffset) { 0 };
 1178     }
 1179 
 1180     case POLICY_ELEMENT_TYPE_BUNDLE:
 1181     {
 1182         const Bundle *bundle = (const Bundle *)element;
 1183         return bundle->offset;
 1184     }
 1185 
 1186     case POLICY_ELEMENT_TYPE_BODY:
 1187     {
 1188         const Body *body = (const Body *)element;
 1189         return body->offset;
 1190     }
 1191 
 1192     case POLICY_ELEMENT_TYPE_PROMISE_TYPE:
 1193     {
 1194         const PromiseType *promise_type = (const PromiseType *)element;
 1195         return promise_type->offset;
 1196     }
 1197 
 1198     case POLICY_ELEMENT_TYPE_PROMISE:
 1199     {
 1200         const Promise *promise = (const Promise *)element;
 1201         return promise->offset;
 1202     }
 1203 
 1204     case POLICY_ELEMENT_TYPE_CONSTRAINT:
 1205     {
 1206         const Constraint *constraint = (const Constraint *)element;
 1207         return constraint->offset;
 1208     }
 1209 
 1210     default:
 1211         assert(false && "Invalid policy element");
 1212         return (SourceOffset) { 0 };
 1213     }
 1214 }
 1215 
 1216 /*************************************************************************/
 1217 
 1218 static const char *PolicyElementSourceFile(PolicyElementType type, const void *element)
 1219 {
 1220     assert(element);
 1221 
 1222     switch (type)
 1223     {
 1224     case POLICY_ELEMENT_TYPE_POLICY:
 1225         return "";
 1226 
 1227     case POLICY_ELEMENT_TYPE_BUNDLE:
 1228     {
 1229         const Bundle *bundle = (const Bundle *)element;
 1230         return bundle->source_path;
 1231     }
 1232 
 1233     case POLICY_ELEMENT_TYPE_BODY:
 1234     {
 1235         const Body *body = (const Body *)element;
 1236         return body->source_path;
 1237     }
 1238 
 1239     case POLICY_ELEMENT_TYPE_PROMISE_TYPE:
 1240     {
 1241         const PromiseType *promise_type = (const PromiseType *)element;
 1242         return PolicyElementSourceFile(POLICY_ELEMENT_TYPE_BUNDLE, promise_type->parent_bundle);
 1243     }
 1244 
 1245     case POLICY_ELEMENT_TYPE_PROMISE:
 1246     {
 1247         const Promise *promise = (const Promise *)element;
 1248         return PolicyElementSourceFile(POLICY_ELEMENT_TYPE_PROMISE_TYPE, promise->parent_promise_type);
 1249     }
 1250 
 1251     case POLICY_ELEMENT_TYPE_CONSTRAINT:
 1252     {
 1253         const Constraint *constraint = (const Constraint *)element;
 1254         switch (constraint->type)
 1255         {
 1256         case POLICY_ELEMENT_TYPE_BODY:
 1257             return PolicyElementSourceFile(POLICY_ELEMENT_TYPE_BODY, constraint->parent.body);
 1258 
 1259         case POLICY_ELEMENT_TYPE_PROMISE:
 1260             return PolicyElementSourceFile(POLICY_ELEMENT_TYPE_PROMISE, constraint->parent.promise);
 1261 
 1262         default:
 1263             assert(false && "Constraint has invalid parent element type");
 1264             return NULL;
 1265         }
 1266     }
 1267 
 1268     default:
 1269         assert(false && "Invalid policy element");
 1270         return NULL;
 1271     }
 1272 }
 1273 
 1274 /*************************************************************************/
 1275 
 1276 void PolicyErrorWrite(Writer *writer, const PolicyError *error)
 1277 {
 1278     SourceOffset offset = PolicyElementSourceOffset(error->type, error->subject);
 1279     const char *path = PolicyElementSourceFile(error->type, error->subject);
 1280 
 1281     // FIX: need to track columns in SourceOffset
 1282     WriterWriteF(writer, "%s:%zu:%zu: error: %s\n", path, offset.line, (size_t)0, error->message);
 1283 }
 1284 
 1285 static char *PolicyErrorToString(const PolicyError *error)
 1286 {
 1287     SourceOffset offset = PolicyElementSourceOffset(error->type, error->subject);
 1288     const char *path = PolicyElementSourceFile(error->type, error->subject);
 1289 
 1290     Writer *msg = StringWriter();
 1291     WriterWriteF(msg, "%s:%zu:%zu: %s.",
 1292                  path, offset.line,
 1293                  (size_t)0, error->message);
 1294 
 1295     switch (error->type)
 1296     {
 1297     case POLICY_ELEMENT_TYPE_CONSTRAINT:
 1298     {
 1299         const Constraint *cp = error->subject;
 1300         WriterWrite(msg, " Given attribute value '");
 1301         RvalWrite(msg, cp->rval);
 1302         WriterWriteChar(msg, '\'');
 1303     }
 1304     break;
 1305 
 1306     default:
 1307         break;
 1308     }
 1309 
 1310     return StringWriterClose(msg);
 1311 }
 1312 
 1313 /*************************************************************************/
 1314 
 1315 void PromiseTypeDestroy(PromiseType *promise_type)
 1316 {
 1317     if (promise_type)
 1318     {
 1319         SeqDestroy(promise_type->promises);
 1320 
 1321         free(promise_type->name);
 1322         free(promise_type);
 1323     }
 1324 }
 1325 
 1326 Bundle *PolicyAppendBundle(Policy *policy,
 1327                            const char *ns, const char *name, const char *type,
 1328                            const Rlist *args, const char *source_path)
 1329 {
 1330     Bundle *bundle = xcalloc(1, sizeof(Bundle));
 1331 
 1332     bundle->parent_policy = policy;
 1333 
 1334     SeqAppend(policy->bundles, bundle);
 1335 
 1336     bundle->name = xstrdup(name);
 1337     bundle->type = xstrdup(type);
 1338     bundle->ns = xstrdup(ns);
 1339     bundle->args = RlistCopy(args);
 1340     bundle->source_path = SafeStringDuplicate(source_path);
 1341     bundle->promise_types = SeqNew(10, PromiseTypeDestroy);
 1342 
 1343     return bundle;
 1344 }
 1345 
 1346 /*******************************************************************/
 1347 
 1348 Body *PolicyAppendBody(Policy *policy, const char *ns, const char *name, const char *type, Rlist *args, const char *source_path)
 1349 {
 1350     Body *body = xcalloc(1, sizeof(Body));
 1351     body->parent_policy = policy;
 1352 
 1353     SeqAppend(policy->bodies, body);
 1354 
 1355     body->name = xstrdup(name);
 1356     body->type = xstrdup(type);
 1357     body->ns = xstrdup(ns);
 1358     body->args = RlistCopy(args);
 1359     body->source_path = SafeStringDuplicate(source_path);
 1360     body->conlist = SeqNew(10, ConstraintDestroy);
 1361 
 1362     // TODO: move to standard callback
 1363     if (strcmp("service_method", body->name) == 0)
 1364     {
 1365         Rlist *bundle_args = NULL;
 1366         RlistAppendRval(&bundle_args, RvalNew("$(this.promiser)", RVAL_TYPE_SCALAR));
 1367         RlistAppendRval(&bundle_args, RvalNew("$(this.service_policy)", RVAL_TYPE_SCALAR));
 1368 
 1369         FnCall *service_bundle = FnCallNew("standard_services", bundle_args);
 1370         BodyAppendConstraint(body, "service_bundle", (Rval) { service_bundle, RVAL_TYPE_FNCALL }, "any", false);
 1371     }
 1372 
 1373     return body;
 1374 }
 1375 
 1376 PromiseType *BundleAppendPromiseType(Bundle *bundle, const char *name)
 1377 {
 1378     if (bundle == NULL)
 1379     {
 1380         ProgrammingError("Attempt to add a type without a bundle");
 1381     }
 1382 
 1383     // TODO: review SeqLookup
 1384     for (size_t i = 0; i < SeqLength(bundle->promise_types); i++)
 1385     {
 1386         PromiseType *existing = SeqAt(bundle->promise_types, i);
 1387         if (strcmp(existing->name, name) == 0)
 1388         {
 1389             return existing;
 1390         }
 1391     }
 1392 
 1393     PromiseType *tp = xcalloc(1, sizeof(PromiseType));
 1394 
 1395     tp->parent_bundle = bundle;
 1396     tp->name = xstrdup(name);
 1397     tp->promises = SeqNew(10, PromiseDestroy);
 1398 
 1399     SeqAppend(bundle->promise_types, tp);
 1400 
 1401     return tp;
 1402 }
 1403 
 1404 /*******************************************************************/
 1405 
 1406 Promise *PromiseTypeAppendPromise(PromiseType *type, const char *promiser, Rval promisee, const char *classes, const char *varclasses)
 1407 {
 1408     assert(promiser && "Missing promiser");
 1409     assert(type && "Missing promise type");
 1410 
 1411     Promise *pp = xcalloc(1, sizeof(Promise));
 1412 
 1413     pp->promiser = xstrdup(promiser);
 1414 
 1415     if (classes && strlen(classes) > 0)
 1416     {
 1417         pp->classes = xstrdup(classes);
 1418     }
 1419     else
 1420     {
 1421         pp->classes = xstrdup("any");
 1422     }
 1423 
 1424     SeqAppend(type->promises, pp);
 1425 
 1426     pp->parent_promise_type = type;
 1427 
 1428     pp->promisee = promisee;
 1429     pp->conlist = SeqNew(10, ConstraintDestroy);
 1430     pp->org_pp = pp;
 1431 
 1432     if (varclasses != NULL)
 1433     {
 1434         PromiseAppendConstraint(pp, "ifvarclass", RvalNew(varclasses, RVAL_TYPE_SCALAR), true);
 1435     }
 1436 
 1437     return pp;
 1438 }
 1439 
 1440 static void BundleDestroy(Bundle *bundle)
 1441 {
 1442     if (bundle)
 1443     {
 1444         free(bundle->name);
 1445         free(bundle->type);
 1446         free(bundle->ns);
 1447         free(bundle->source_path);
 1448 
 1449         RlistDestroy(bundle->args);
 1450         SeqDestroy(bundle->promise_types);
 1451         free(bundle);
 1452     }
 1453 }
 1454 
 1455 static void BodyDestroy(Body *body)
 1456 {
 1457     if (body)
 1458     {
 1459         free(body->name);
 1460         free(body->type);
 1461         free(body->ns);
 1462         free(body->source_path);
 1463 
 1464         RlistDestroy(body->args);
 1465         SeqDestroy(body->conlist);
 1466         free(body);
 1467     }
 1468 }
 1469 
 1470 
 1471 void PromiseDestroy(Promise *pp)
 1472 {
 1473     if (pp)
 1474     {
 1475         free(pp->promiser);
 1476 
 1477         if (pp->promisee.item)
 1478         {
 1479             RvalDestroy(pp->promisee);
 1480         }
 1481 
 1482         free(pp->classes);
 1483         free(pp->comment);
 1484 
 1485         SeqDestroy(pp->conlist);
 1486 
 1487         free(pp);
 1488     }
 1489 }
 1490 
 1491 /*******************************************************************/
 1492 
 1493 static Constraint *ConstraintNew(const char *lval, Rval rval, const char *classes, bool references_body)
 1494 {
 1495     Constraint *cp = xcalloc(1, sizeof(Constraint));
 1496 
 1497     cp->lval = SafeStringDuplicate(lval);
 1498     cp->rval = rval;
 1499 
 1500     cp->classes = SafeStringDuplicate(classes);
 1501     cp->references_body = references_body;
 1502 
 1503     return cp;
 1504 }
 1505 
 1506 Constraint *PromiseAppendConstraint(Promise *pp, const char *lval, Rval rval, bool references_body)
 1507 {
 1508     Constraint *cp = ConstraintNew(lval, rval, "any", references_body);
 1509     cp->type = POLICY_ELEMENT_TYPE_PROMISE;
 1510     cp->parent.promise = pp;
 1511 
 1512     for (size_t i = 0; i < SeqLength(pp->conlist); i++)
 1513     {
 1514         Constraint *old_cp = SeqAt(pp->conlist, i);
 1515         if (strcmp(old_cp->lval, lval) == 0)
 1516         {
 1517             if (strcmp(old_cp->lval, "ifvarclass") == 0 ||
 1518                 strcmp(old_cp->lval, "if") == 0)
 1519             {
 1520                 // merge two if/ifvarclass promise attributes this
 1521                 // only happens in a variable context when we have a
 1522                 // scalar already in the attribute (old_cp)
 1523                 switch (rval.type)
 1524                 {
 1525                 case RVAL_TYPE_FNCALL: // case 1: merge FnCall with scalar
 1526                 {
 1527                     char * rval_string = RvalToString(old_cp->rval);
 1528                     Log(LOG_LEVEL_DEBUG, "PromiseAppendConstraint: merging PREVIOUS %s string context rval %s", old_cp->lval, rval_string);
 1529                     Log(LOG_LEVEL_DEBUG, "PromiseAppendConstraint: merging NEW %s rval %s", old_cp->lval, rval_string);
 1530                     free(rval_string);
 1531 
 1532                     Rlist *synthetic_args = NULL;
 1533                     RlistAppendScalar(&synthetic_args, RvalScalarValue(old_cp->rval));
 1534 
 1535                     // append the old Rval (a function call) under the arguments of the new one
 1536                     RlistAppend(&synthetic_args, rval.item, RVAL_TYPE_FNCALL);
 1537 
 1538                     Rval replacement = (Rval) { FnCallNew("and", synthetic_args), RVAL_TYPE_FNCALL };
 1539                     rval_string = RvalToString(replacement);
 1540                     Log(LOG_LEVEL_DEBUG, "PromiseAppendConstraint: MERGED %s rval %s", old_cp->lval, rval_string);
 1541                     free(rval_string);
 1542 
 1543                     // overwrite the old Constraint rval with its replacement
 1544                     RvalDestroy(cp->rval);
 1545                     cp->rval = replacement;
 1546                 }
 1547                 break;
 1548 
 1549                 case RVAL_TYPE_SCALAR:  // case 2: merge scalar with scalar
 1550                 {
 1551                     Buffer *grow = BufferNew();
 1552                     BufferAppendF(grow, "(%s).(%s)",
 1553                                   RvalScalarValue(old_cp->rval),
 1554                                   RvalScalarValue(rval));
 1555                     RvalDestroy(cp->rval);
 1556                     rval = RvalNew(BufferData(grow), RVAL_TYPE_SCALAR);
 1557                     BufferDestroy(grow);
 1558                     cp->rval = rval;
 1559                 }
 1560                 break;
 1561 
 1562                 default:
 1563                     ProgrammingError("PromiseAppendConstraint: unexpected rval type: %c", rval.type);
 1564                     break;
 1565                 }
 1566             }
 1567             SeqSet(pp->conlist, i, cp);
 1568             return cp;
 1569         }
 1570     }
 1571 
 1572     SeqAppend(pp->conlist, cp);
 1573     return cp;
 1574 }
 1575 
 1576 Constraint *BodyAppendConstraint(Body *body, const char *lval, Rval rval, const char *classes,
 1577                                  bool references_body)
 1578 {
 1579     Constraint *cp = ConstraintNew(lval, rval, classes, references_body);
 1580     cp->type = POLICY_ELEMENT_TYPE_BODY;
 1581     cp->parent.body = body;
 1582 
 1583     for (size_t i = 0; i < SeqLength(body->conlist); i++)
 1584     {
 1585         Constraint *old_cp = SeqAt(body->conlist, i);
 1586         if (strcmp(old_cp->lval, lval) == 0 && strcmp(old_cp->classes, classes) == 0)
 1587         {
 1588             SeqSet(body->conlist, i, cp);
 1589             return cp;
 1590         }
 1591     }
 1592 
 1593     SeqAppend(body->conlist, cp);
 1594 
 1595     return cp;
 1596 }
 1597 
 1598 /*******************************************************************/
 1599 
 1600 const PromiseType *BundleGetPromiseType(const Bundle *bp, const char *name)
 1601 {
 1602     // TODO: hiding error, remove and see what will crash
 1603     if (bp == NULL)
 1604     {
 1605         return NULL;
 1606     }
 1607 
 1608     for (size_t i = 0; i < SeqLength(bp->promise_types); i++)
 1609     {
 1610         PromiseType *sp = SeqAt(bp->promise_types, i);
 1611 
 1612         if (strcmp(name, sp->name) == 0)
 1613         {
 1614             return sp;
 1615         }
 1616     }
 1617 
 1618     return NULL;
 1619 }
 1620 
 1621 /****************************************************************************/
 1622 
 1623 static Buffer *EscapeQuotes(const char *raw, Buffer *out)
 1624 {
 1625     const char *spf;
 1626 
 1627     for (spf = raw; *spf != '\0'; spf++)
 1628     {
 1629         switch (*spf)
 1630         {
 1631         case '\'':
 1632         case '\"':
 1633             BufferAppendChar(out, '\\');
 1634             break;
 1635 
 1636         default:
 1637             break;
 1638         }
 1639         BufferAppendChar(out, *spf);
 1640     }
 1641 
 1642     return out;
 1643 }
 1644 
 1645 /**
 1646  * Converts the given attribute rval to a JSON object.
 1647  *
 1648  * @return A JsonElement of type JSON_ELEMENT_TYPE_CONTAINER
 1649  */
 1650 static JsonElement *AttributeValueToJson(Rval rval, bool symbolic_reference)
 1651 {
 1652     switch (rval.type)
 1653     {
 1654     case RVAL_TYPE_CONTAINER:
 1655     {
 1656         return JsonCopy(RvalContainerValue(rval));
 1657     }
 1658 
 1659     case RVAL_TYPE_SCALAR:
 1660     {
 1661         Buffer *buffer = BufferNewWithCapacity(strlen(rval.item));
 1662 
 1663         EscapeQuotes((const char *) rval.item, buffer);
 1664 
 1665         JsonElement *json_attribute = JsonObjectCreate(10);
 1666 
 1667         if (symbolic_reference)
 1668         {
 1669             JsonObjectAppendString(json_attribute, "type", "symbol");
 1670         }
 1671         else
 1672         {
 1673             JsonObjectAppendString(json_attribute, "type", "string");
 1674         }
 1675         JsonObjectAppendString(json_attribute, "value", BufferData(buffer));
 1676 
 1677         BufferDestroy(buffer);
 1678 
 1679         return json_attribute;
 1680     }
 1681 
 1682 
 1683     case RVAL_TYPE_LIST:
 1684     {
 1685         Rlist *rp = NULL;
 1686         JsonElement *list = JsonArrayCreate(10);
 1687 
 1688         JsonElement *json_attribute = JsonObjectCreate(10);
 1689         JsonObjectAppendString(json_attribute, "type", "list");
 1690 
 1691         for (rp = (Rlist *) rval.item; rp != NULL; rp = rp->next)
 1692         {
 1693             JsonArrayAppendObject(list, AttributeValueToJson(rp->val, false));
 1694         }
 1695 
 1696         JsonObjectAppendArray(json_attribute, "value", list);
 1697         return json_attribute;
 1698     }
 1699 
 1700     case RVAL_TYPE_FNCALL:
 1701     {
 1702         Rlist *argp = NULL;
 1703         FnCall *call = (FnCall *) rval.item;
 1704 
 1705         JsonElement *json_attribute = JsonObjectCreate(10);
 1706         JsonObjectAppendString(json_attribute, "type", "functionCall");
 1707         JsonObjectAppendString(json_attribute, "name", call->name);
 1708 
 1709         {
 1710             JsonElement *arguments = JsonArrayCreate(10);
 1711 
 1712             for (argp = call->args; argp != NULL; argp = argp->next)
 1713             {
 1714                 JsonArrayAppendObject(arguments, AttributeValueToJson(argp->val, false));
 1715             }
 1716 
 1717             JsonObjectAppendArray(json_attribute, "arguments", arguments);
 1718         }
 1719 
 1720         return json_attribute;
 1721     }
 1722 
 1723     case RVAL_TYPE_NOPROMISEE:
 1724         ProgrammingError("Attempted to export attribute of type: %c", rval.type);
 1725         return NULL;
 1726     }
 1727 
 1728     assert(false);
 1729     return NULL;
 1730 }
 1731 
 1732 static JsonElement *CreateContextAsJson(const char *name, const char *children_name, JsonElement *children)
 1733 {
 1734     JsonElement *json = JsonObjectCreate(10);
 1735 
 1736     JsonObjectAppendString(json, "name", name);
 1737     JsonObjectAppendArray(json, children_name, children);
 1738 
 1739     return json;
 1740 }
 1741 
 1742 static JsonElement *BodyContextsToJson(const Seq *constraints)
 1743 {
 1744     JsonElement *json_contexts = JsonArrayCreate(10);
 1745     JsonElement *json_attributes = JsonArrayCreate(10);
 1746     char *current_context = "any";
 1747 
 1748     for (size_t i = 0; i < SeqLength(constraints); i++)
 1749     {
 1750         Constraint *cp = SeqAt(constraints, i);
 1751 
 1752         JsonElement *json_attribute = JsonObjectCreate(10);
 1753 
 1754         if (strcmp(current_context, cp->classes) != 0)
 1755         {
 1756             JsonArrayAppendObject(json_contexts,
 1757                                   CreateContextAsJson(current_context,
 1758                                                       "attributes", json_attributes));
 1759             json_attributes = JsonArrayCreate(10);
 1760             current_context = cp->classes;
 1761         }
 1762 
 1763         JsonObjectAppendInteger(json_attribute, "line", cp->offset.line);
 1764 
 1765         JsonObjectAppendString(json_attribute, "lval", cp->lval);
 1766         JsonObjectAppendObject(json_attribute, "rval", AttributeValueToJson(cp->rval, false));
 1767         JsonArrayAppendObject(json_attributes, json_attribute);
 1768     }
 1769 
 1770     JsonArrayAppendObject(json_contexts,
 1771                           CreateContextAsJson(current_context,
 1772                                               "attributes", json_attributes));
 1773 
 1774     return json_contexts;
 1775 }
 1776 
 1777 static JsonElement *BundleContextsToJson(const Seq *promises)
 1778 {
 1779     JsonElement *json_contexts = JsonArrayCreate(10);
 1780     JsonElement *json_promises = JsonArrayCreate(10);
 1781     char *current_context = NULL;
 1782 
 1783     for (size_t ppi = 0; ppi < SeqLength(promises); ppi++)
 1784     {
 1785         Promise *pp = SeqAt(promises, ppi);
 1786 
 1787         if (!current_context)
 1788         {
 1789             current_context = pp->classes;
 1790         }
 1791 
 1792         JsonElement *json_promise = JsonObjectCreate(10);
 1793 
 1794         if (strcmp(current_context, pp->classes) != 0)
 1795         {
 1796             JsonArrayAppendObject(json_contexts,
 1797                                   CreateContextAsJson(current_context,
 1798                                                       "promises", json_promises));
 1799             json_promises = JsonArrayCreate(10);
 1800             current_context = pp->classes;
 1801         }
 1802 
 1803         JsonObjectAppendInteger(json_promise, "line", pp->offset.line);
 1804 
 1805         {
 1806             JsonElement *json_promise_attributes = JsonArrayCreate(10);
 1807 
 1808             for (size_t k = 0; k < SeqLength(pp->conlist); k++)
 1809             {
 1810                 Constraint *cp = SeqAt(pp->conlist, k);
 1811 
 1812                 JsonElement *json_attribute = JsonObjectCreate(10);
 1813 
 1814                 JsonObjectAppendInteger(json_attribute, "line", cp->offset.line);
 1815 
 1816                 JsonObjectAppendString(json_attribute, "lval", cp->lval);
 1817                 JsonElement *json_rval = AttributeValueToJson(cp->rval, cp->references_body);
 1818                 if (JsonGetContainerType(json_rval) == JSON_CONTAINER_TYPE_ARRAY)
 1819                 {
 1820                     JsonObjectAppendArray(json_attribute, "rval", json_rval);
 1821                 }
 1822                 else
 1823                 {
 1824                     JsonObjectAppendObject(json_attribute, "rval", json_rval);
 1825                 }
 1826                 JsonArrayAppendObject(json_promise_attributes, json_attribute);
 1827             }
 1828 
 1829             JsonObjectAppendString(json_promise, "promiser", pp->promiser);
 1830 
 1831             switch (pp->promisee.type)
 1832             {
 1833             case RVAL_TYPE_SCALAR:
 1834                 JsonObjectAppendString(json_promise, "promisee", pp->promisee.item);
 1835                 break;
 1836 
 1837             case RVAL_TYPE_LIST:
 1838             {
 1839                 JsonElement *promisee_list = JsonArrayCreate(10);
 1840                 for (const Rlist *rp = pp->promisee.item; rp; rp = rp->next)
 1841                 {
 1842                     JsonArrayAppendString(promisee_list, RlistScalarValue(rp));
 1843                 }
 1844                 JsonObjectAppendArray(json_promise, "promisee", promisee_list);
 1845             }
 1846             break;
 1847 
 1848             default:
 1849                 break;
 1850             }
 1851 
 1852             JsonObjectAppendArray(json_promise, "attributes", json_promise_attributes);
 1853         }
 1854         JsonArrayAppendObject(json_promises, json_promise);
 1855     }
 1856 
 1857     if (JsonLength(json_promises) > 0)
 1858     {
 1859         JsonArrayAppendObject(json_contexts,
 1860                               CreateContextAsJson(current_context,
 1861                                                   "promises", json_promises));
 1862     }
 1863 
 1864     return json_contexts;
 1865 }
 1866 
 1867 /**
 1868  * @brief Serialize a bundle as JSON
 1869  * @param bundle The bundle to serialize
 1870  * @return A JsonElement representing the input bundle
 1871  */
 1872 JsonElement *BundleToJson(const Bundle *bundle)
 1873 {
 1874     JsonElement *json_bundle = JsonObjectCreate(10);
 1875 
 1876     if (bundle->source_path)
 1877     {
 1878         JsonObjectAppendString(json_bundle, "sourcePath", bundle->source_path);
 1879     }
 1880     JsonObjectAppendInteger(json_bundle, "line", bundle->offset.line);
 1881 
 1882     JsonObjectAppendString(json_bundle, "namespace", bundle->ns);
 1883     JsonObjectAppendString(json_bundle, "name", bundle->name);
 1884     JsonObjectAppendString(json_bundle, "bundleType", bundle->type);
 1885 
 1886     {
 1887         JsonElement *json_args = JsonArrayCreate(10);
 1888         Rlist *argp = NULL;
 1889 
 1890         for (argp = bundle->args; argp != NULL; argp = argp->next)
 1891         {
 1892             JsonArrayAppendString(json_args, RlistScalarValue(argp));
 1893         }
 1894 
 1895         JsonObjectAppendArray(json_bundle, "arguments", json_args);
 1896     }
 1897 
 1898     {
 1899         JsonElement *json_promise_types = JsonArrayCreate(10);
 1900 
 1901         for (size_t i = 0; i < SeqLength(bundle->promise_types); i++)
 1902         {
 1903             const PromiseType *sp = SeqAt(bundle->promise_types, i);
 1904 
 1905             JsonElement *json_promise_type = JsonObjectCreate(10);
 1906 
 1907             JsonObjectAppendInteger(json_promise_type, "line", sp->offset.line);
 1908             JsonObjectAppendString(json_promise_type, "name", sp->name);
 1909             JsonObjectAppendArray(json_promise_type, "contexts", BundleContextsToJson(sp->promises));
 1910 
 1911             JsonArrayAppendObject(json_promise_types, json_promise_type);
 1912         }
 1913 
 1914         JsonObjectAppendArray(json_bundle, "promiseTypes", json_promise_types);
 1915     }
 1916 
 1917     return json_bundle;
 1918 }
 1919 
 1920 /**
 1921  * @brief Serialize a body as JSON
 1922  * @param body The body to serialize
 1923  * @return A JsonElement representing the input body
 1924  */
 1925 JsonElement *BodyToJson(const Body *body)
 1926 {
 1927     JsonElement *json_body = JsonObjectCreate(10);
 1928 
 1929     if (body->source_path)
 1930     {
 1931         JsonObjectAppendString(json_body, "sourcePath", body->source_path);
 1932     }
 1933     JsonObjectAppendInteger(json_body, "line", body->offset.line);
 1934 
 1935     JsonObjectAppendString(json_body, "namespace", body->ns);
 1936     JsonObjectAppendString(json_body, "name", body->name);
 1937     JsonObjectAppendString(json_body, "bodyType", body->type);
 1938 
 1939     {
 1940         JsonElement *json_args = JsonArrayCreate(10);
 1941         Rlist *argp = NULL;
 1942 
 1943         for (argp = body->args; argp != NULL; argp = argp->next)
 1944         {
 1945             JsonArrayAppendString(json_args, RlistScalarValue(argp));
 1946         }
 1947 
 1948         JsonObjectAppendArray(json_body, "arguments", json_args);
 1949     }
 1950 
 1951     JsonObjectAppendArray(json_body, "contexts", BodyContextsToJson(body->conlist));
 1952 
 1953     return json_body;
 1954 }
 1955 
 1956 /**
 1957  * @brief Serialize a policy as JSON
 1958  * @param policy The policy to serialize
 1959  * @return A JsonElement representing the input policy
 1960  */
 1961 JsonElement *PolicyToJson(const Policy *policy)
 1962 {
 1963     JsonElement *json_policy = JsonObjectCreate(10);
 1964 
 1965     {
 1966         JsonElement *json_bundles = JsonArrayCreate(10);
 1967 
 1968         for (size_t i = 0; i < SeqLength(policy->bundles); i++)
 1969         {
 1970             const Bundle *bp = SeqAt(policy->bundles, i);
 1971             JsonArrayAppendObject(json_bundles, BundleToJson(bp));
 1972         }
 1973 
 1974         JsonObjectAppendArray(json_policy, "bundles", json_bundles);
 1975     }
 1976 
 1977     {
 1978         JsonElement *json_bodies = JsonArrayCreate(10);
 1979 
 1980         for (size_t i = 0; i < SeqLength(policy->bodies); i++)
 1981         {
 1982             const Body *bdp = SeqAt(policy->bodies, i);
 1983 
 1984             JsonArrayAppendObject(json_bodies, BodyToJson(bdp));
 1985         }
 1986 
 1987         JsonObjectAppendArray(json_policy, "bodies", json_bodies);
 1988     }
 1989 
 1990     return json_policy;
 1991 }
 1992 
 1993 /****************************************************************************/
 1994 
 1995 
 1996 static void IndentPrint(Writer *writer, int indent_level)
 1997 {
 1998     static const int PRETTY_PRINT_SPACES_PER_INDENT = 2;
 1999 
 2000     int i = 0;
 2001 
 2002     for (i = 0; i < PRETTY_PRINT_SPACES_PER_INDENT * indent_level; i++)
 2003     {
 2004         WriterWriteChar(writer, ' ');
 2005     }
 2006 }
 2007 
 2008 static void AttributeToString(Writer *writer, Constraint *attribute, bool symbolic_reference)
 2009 {
 2010     WriterWriteF(writer, "%s => ", attribute->lval);
 2011     if (symbolic_reference)
 2012     {
 2013         RvalWrite(writer, attribute->rval);
 2014     }
 2015     else
 2016     {
 2017         RvalWriteQuoted(writer, attribute->rval);
 2018     }
 2019 }
 2020 
 2021 
 2022 static void ArgumentsToString(Writer *writer, Rlist *args)
 2023 {
 2024     Rlist *argp = NULL;
 2025 
 2026     WriterWriteChar(writer, '(');
 2027     for (argp = args; argp != NULL; argp = argp->next)
 2028     {
 2029         WriterWriteF(writer, "%s", RlistScalarValue(argp));
 2030 
 2031         if (argp->next != NULL)
 2032         {
 2033             WriterWrite(writer, ", ");
 2034         }
 2035     }
 2036     WriterWriteChar(writer, ')');
 2037 }
 2038 
 2039 
 2040 void BodyToString(Writer *writer, Body *body)
 2041 {
 2042     char *current_class = NULL;
 2043 
 2044     WriterWriteF(writer, "body %s %s", body->type, body->name);
 2045     ArgumentsToString(writer, body->args);
 2046     WriterWrite(writer, "\n{");
 2047 
 2048     for (size_t i = 0; i < SeqLength(body->conlist); i++)
 2049     {
 2050         Constraint *cp = SeqAt(body->conlist, i);
 2051 
 2052         if (current_class == NULL || strcmp(cp->classes, current_class) != 0)
 2053         {
 2054             current_class = cp->classes;
 2055 
 2056             if (strcmp(current_class, "any") == 0)
 2057             {
 2058                 WriterWrite(writer, "\n");
 2059             }
 2060             else
 2061             {
 2062                 WriterWriteF(writer, "\n\n%s::", current_class);
 2063             }
 2064         }
 2065 
 2066         IndentPrint(writer, 1);
 2067         AttributeToString(writer, cp, false);
 2068         WriterWriteChar(writer, ';');
 2069         WriterWriteChar(writer, '\n');
 2070     }
 2071 
 2072     WriterWrite(writer, "\n}\n");
 2073 }
 2074 
 2075 
 2076 void BundleToString(Writer *writer, Bundle *bundle)
 2077 {
 2078     WriterWriteF(writer, "bundle %s %s", bundle->type, bundle->name);
 2079     ArgumentsToString(writer, bundle->args);
 2080     WriterWrite(writer, "\n{");
 2081 
 2082     for (size_t i = 0; i < SeqLength(bundle->promise_types); i++)
 2083     {
 2084         PromiseType *promise_type = SeqAt(bundle->promise_types, i);
 2085 
 2086         WriterWriteF(writer, "\n%s:\n", promise_type->name);
 2087 
 2088         char *current_class = NULL;
 2089         for (size_t ppi = 0; ppi < SeqLength(promise_type->promises); ppi++)
 2090         {
 2091             Promise *pp = SeqAt(promise_type->promises, ppi);
 2092 
 2093             if (current_class == NULL || strcmp(pp->classes, current_class) != 0)
 2094             {
 2095                 current_class = pp->classes;
 2096                 IndentPrint(writer, 1);
 2097                 WriterWriteF(writer, "%s::\n", current_class);
 2098             }
 2099 
 2100             IndentPrint(writer, 2);
 2101             ScalarWrite(writer, pp->promiser, true);
 2102 
 2103             /* FIX: add support
 2104              *
 2105              if (pp->promisee != NULL)
 2106              {
 2107              fprintf(out, "%s", pp->promisee);
 2108              }
 2109             */
 2110 
 2111             for (size_t k = 0; k < SeqLength(pp->conlist); k++)
 2112             {
 2113                 Constraint *cp = SeqAt(pp->conlist, k);
 2114 
 2115                 IndentPrint(writer, 4);
 2116                 AttributeToString(writer, cp, cp->references_body);
 2117                 if (k < SeqLength(pp->conlist)-1)
 2118                 {
 2119                     WriterWriteChar(writer, ',');
 2120                     WriterWriteChar(writer, '\n');
 2121                 }
 2122             }
 2123             WriterWriteChar(writer, ';');
 2124             WriterWriteChar(writer, '\n');
 2125         }
 2126 
 2127         if (i == (SeqLength(bundle->promise_types) - 1))
 2128         {
 2129             WriterWriteChar(writer, '\n');
 2130         }
 2131     }
 2132 
 2133     WriterWrite(writer, "\n}\n");
 2134 }
 2135 
 2136 /**
 2137  * @brief Pretty-print a policy
 2138  * @param policy The policy to print
 2139  * @param writer Writer to write into
 2140  */
 2141 void PolicyToString(const Policy *policy, Writer *writer)
 2142 {
 2143     for (size_t i = 0; i < SeqLength(policy->bundles); i++)
 2144     {
 2145         Bundle *bundle = SeqAt(policy->bundles, i);
 2146         BundleToString(writer, bundle);
 2147         WriterWriteChar(writer, '\n');
 2148     }
 2149 
 2150     for (size_t i = 0; i < SeqLength(policy->bodies); i++)
 2151     {
 2152         Body *body = SeqAt(policy->bodies, i);
 2153         BodyToString(writer, body);
 2154         WriterWriteChar(writer, '\n');
 2155     }
 2156 
 2157 }
 2158 
 2159 //*****************************************************************************
 2160 
 2161 static Rval RvalFromJson(JsonElement *json_rval)
 2162 {
 2163     const char *type = JsonObjectGetAsString(json_rval, "type");
 2164 
 2165     if (strcmp("string", type) == 0 || strcmp("symbol", type) == 0)
 2166     {
 2167         const char *value = JsonObjectGetAsString(json_rval, "value");
 2168         return ((Rval) { xstrdup(value), RVAL_TYPE_SCALAR });
 2169     }
 2170     else if (strcmp("list", type) == 0)
 2171     {
 2172         JsonElement *json_list = JsonObjectGetAsArray(json_rval, "value");
 2173         Rlist *rlist = NULL;
 2174 
 2175         for (size_t i = 0; i < JsonLength(json_list); i++)
 2176         {
 2177             Rval list_value = RvalFromJson(JsonArrayGetAsObject(json_list, i));
 2178             RlistAppend(&rlist, list_value.item, list_value.type);
 2179             RvalDestroy(list_value);
 2180         }
 2181 
 2182         return ((Rval) { rlist, RVAL_TYPE_LIST });
 2183     }
 2184     else if (strcmp("functionCall", type) == 0)
 2185     {
 2186         const char *name = JsonObjectGetAsString(json_rval, "name");
 2187         JsonElement *json_args = JsonObjectGetAsArray(json_rval, "arguments");
 2188         Rlist *args = NULL;
 2189 
 2190         for (size_t i = 0; i < JsonLength(json_args); i++)
 2191         {
 2192             JsonElement *json_arg = JsonArrayGetAsObject(json_args, i);
 2193             Rval arg = RvalFromJson(json_arg);
 2194 
 2195             RlistAppend(&args, arg.item, arg.type);
 2196             RvalDestroy(arg);
 2197         }
 2198 
 2199         FnCall *fn = FnCallNew(name, args);
 2200 
 2201         return ((Rval) { fn, RVAL_TYPE_FNCALL });
 2202     }
 2203     else
 2204     {
 2205         ProgrammingError("Unexpected rval type: %s", type);
 2206     }
 2207 }
 2208 
 2209 static Constraint *PromiseAppendConstraintJson(Promise *promise, JsonElement *json_constraint)
 2210 {
 2211     const char *lval = JsonObjectGetAsString(json_constraint, "lval");
 2212 
 2213     JsonElement *json_rval = JsonObjectGetAsObject(json_constraint, "rval");
 2214     const char *type = JsonObjectGetAsString(json_rval, "type");
 2215 
 2216     Rval rval = RvalFromJson(json_rval);
 2217 
 2218     Constraint *cp = PromiseAppendConstraint(promise, lval, rval, (strcmp("symbol", type) == 0));
 2219 
 2220     return cp;
 2221 }
 2222 
 2223 static Promise *PromiseTypeAppendPromiseJson(PromiseType *promise_type, JsonElement *json_promise, const char *context)
 2224 {
 2225     const char *promiser = JsonObjectGetAsString(json_promise, "promiser");
 2226 
 2227     Promise *promise = PromiseTypeAppendPromise(promise_type, promiser, (Rval) { NULL, RVAL_TYPE_NOPROMISEE }, context, NULL);
 2228 
 2229     JsonElement *json_attributes = JsonObjectGetAsArray(json_promise, "attributes");
 2230     for (size_t i = 0; i < JsonLength(json_attributes); i++)
 2231     {
 2232         JsonElement *json_attribute = JsonArrayGetAsObject(json_attributes, i);
 2233         PromiseAppendConstraintJson(promise, json_attribute);
 2234     }
 2235 
 2236     return promise;
 2237 }
 2238 
 2239 static PromiseType *BundleAppendPromiseTypeJson(Bundle *bundle, JsonElement *json_promise_type)
 2240 {
 2241     const char *name = JsonObjectGetAsString(json_promise_type, "name");
 2242 
 2243     PromiseType *promise_type = BundleAppendPromiseType(bundle, name);
 2244 
 2245     JsonElement *json_contexts = JsonObjectGetAsArray(json_promise_type, "contexts");
 2246     for (size_t i = 0; i < JsonLength(json_contexts); i++)
 2247     {
 2248         JsonElement *json_context = JsonArrayGetAsObject(json_contexts, i);
 2249 
 2250         const char *context = JsonObjectGetAsString(json_context, "name");
 2251 
 2252         JsonElement *json_context_promises = JsonObjectGetAsArray(json_context, "promises");
 2253         for (size_t j = 0; j < JsonLength(json_context_promises); j++)
 2254         {
 2255             JsonElement *json_promise = JsonArrayGetAsObject(json_context_promises, j);
 2256             PromiseTypeAppendPromiseJson(promise_type, json_promise, context);
 2257         }
 2258     }
 2259 
 2260     return promise_type;
 2261 }
 2262 
 2263 static Bundle *PolicyAppendBundleJson(Policy *policy, JsonElement *json_bundle)
 2264 {
 2265     const char *ns = JsonObjectGetAsString(json_bundle, "namespace");
 2266     const char *name = JsonObjectGetAsString(json_bundle, "name");
 2267     const char *type = JsonObjectGetAsString(json_bundle, "bundleType");
 2268     const char *source_path = JsonObjectGetAsString(json_bundle, "sourcePath");
 2269 
 2270     Rlist *args = NULL;
 2271     {
 2272         JsonElement *json_args = JsonObjectGetAsArray(json_bundle, "arguments");
 2273         for (size_t i = 0; i < JsonLength(json_args); i++)
 2274         {
 2275             RlistAppendScalar(&args, JsonArrayGetAsString(json_args, i));
 2276         }
 2277     }
 2278 
 2279     Bundle *bundle = PolicyAppendBundle(policy, ns, name, type, args, source_path);
 2280 
 2281     {
 2282         JsonElement *json_promise_types = JsonObjectGetAsArray(json_bundle, "promiseTypes");
 2283         for (size_t i = 0; i < JsonLength(json_promise_types); i++)
 2284         {
 2285             JsonElement *json_promise_type = JsonArrayGetAsObject(json_promise_types, i);
 2286             BundleAppendPromiseTypeJson(bundle, json_promise_type);
 2287         }
 2288     }
 2289 
 2290     return bundle;
 2291 }
 2292 
 2293 static Constraint *BodyAppendConstraintJson(Body *body, JsonElement *json_constraint, const char *context)
 2294 {
 2295     const char *lval = JsonObjectGetAsString(json_constraint, "lval");
 2296 
 2297     JsonElement *json_rval = JsonObjectGetAsObject(json_constraint, "rval");
 2298     const char *type = JsonObjectGetAsString(json_rval, "type");
 2299 
 2300     Rval rval = RvalFromJson(json_rval);
 2301 
 2302     Constraint *cp = BodyAppendConstraint(body, lval, rval, context, (strcmp("symbol", type) == 0));
 2303 
 2304     return cp;
 2305 }
 2306 
 2307 static Body *PolicyAppendBodyJson(Policy *policy, JsonElement *json_body)
 2308 {
 2309     const char *ns = JsonObjectGetAsString(json_body, "namespace");
 2310     const char *name = JsonObjectGetAsString(json_body, "name");
 2311     const char *type = JsonObjectGetAsString(json_body, "bodyType");
 2312     const char *source_path = JsonObjectGetAsString(json_body, "sourcePath");
 2313 
 2314     Rlist *args = NULL;
 2315     {
 2316         JsonElement *json_args = JsonObjectGetAsArray(json_body, "arguments");
 2317         for (size_t i = 0; i < JsonLength(json_args); i++)
 2318         {
 2319             RlistAppendScalar(&args, JsonArrayGetAsString(json_args, i));
 2320         }
 2321     }
 2322 
 2323     Body *body = PolicyAppendBody(policy, ns, name, type, args, source_path);
 2324 
 2325     {
 2326         JsonElement *json_contexts = JsonObjectGetAsArray(json_body, "contexts");
 2327         for (size_t i = 0; i < JsonLength(json_contexts); i++)
 2328         {
 2329             JsonElement *json_context = JsonArrayGetAsObject(json_contexts, i);
 2330             const char *context = JsonObjectGetAsString(json_context, "name");
 2331 
 2332             {
 2333                 JsonElement *json_attributes = JsonObjectGetAsArray(json_context, "attributes");
 2334                 for (size_t j = 0; j < JsonLength(json_attributes); j++)
 2335                 {
 2336                     JsonElement *json_attribute = JsonArrayGetAsObject(json_attributes, j);
 2337                     BodyAppendConstraintJson(body, json_attribute, context);
 2338                 }
 2339             }
 2340         }
 2341     }
 2342 
 2343     return body;
 2344 }
 2345 
 2346 /**
 2347  * @brief Deserialize a policy from JSON
 2348  * @param json_policy JSON to deserialize
 2349  * @return A policy DOM
 2350  */
 2351 Policy *PolicyFromJson(JsonElement *json_policy)
 2352 {
 2353     Policy *policy = PolicyNew();
 2354 
 2355     JsonElement *json_bundles = JsonObjectGetAsArray(json_policy, "bundles");
 2356     JsonElement *json_bodies = JsonObjectGetAsArray(json_policy, "bodies");
 2357 
 2358     if ((json_bundles == NULL) && (json_bodies == NULL))
 2359     {
 2360         return NULL;
 2361     }
 2362 
 2363     if (json_bundles != NULL)
 2364     {
 2365         for (size_t i = 0; i < JsonLength(json_bundles); i++)
 2366         {
 2367             JsonElement *json_bundle = JsonArrayGetAsObject(json_bundles, i);
 2368             PolicyAppendBundleJson(policy, json_bundle);
 2369         }
 2370     }
 2371     if (json_bodies != NULL)
 2372     {
 2373         for (size_t i = 0; i < JsonLength(json_bodies); i++)
 2374         {
 2375             JsonElement *json_body = JsonArrayGetAsObject(json_bodies, i);
 2376             PolicyAppendBodyJson(policy, json_body);
 2377         }
 2378     }
 2379 
 2380     return policy;
 2381 }
 2382 
 2383 /**
 2384  * @brief A sequence of constraints matching the l-value.
 2385  * @param body Body to query
 2386  * @param lval l-value to match
 2387  * @return Sequence of pointers to the constraints. Destroying it does not alter the DOM.
 2388  */
 2389 Seq *BodyGetConstraint(Body *body, const char *lval)
 2390 {
 2391     Seq *matches = SeqNew(5, NULL);
 2392 
 2393     for (size_t i = 0; i < SeqLength(body->conlist); i++)
 2394     {
 2395         Constraint *cp = SeqAt(body->conlist, i);
 2396         if (strcmp(cp->lval, lval) == 0)
 2397         {
 2398             SeqAppend(matches, cp);
 2399         }
 2400     }
 2401 
 2402     return matches;
 2403 }
 2404 
 2405 bool BodyHasConstraint(const Body *body, const char *lval)
 2406 {
 2407     for (int i = 0; i < SeqLength(body->conlist); i++)
 2408     {
 2409         Constraint *cp = SeqAt(body->conlist, i);
 2410         if (strcmp(lval, cp->lval) == 0)
 2411         {
 2412             return true;
 2413         }
 2414     }
 2415 
 2416     return false;
 2417 }
 2418 
 2419 /**
 2420  * @brief Get the context of the given constraint
 2421  * @param cp
 2422  * @return context. never returns NULL.
 2423  */
 2424 const char *ConstraintContext(const Constraint *cp)
 2425 {
 2426     switch (cp->type)
 2427     {
 2428     case POLICY_ELEMENT_TYPE_BODY:
 2429         return cp->classes;
 2430 
 2431     case POLICY_ELEMENT_TYPE_BUNDLE:
 2432         return cp->parent.promise->classes;
 2433 
 2434     default:
 2435         ProgrammingError("Constraint had parent element type: %d", cp->type);
 2436         return NULL;
 2437     }
 2438 }
 2439 
 2440 /**
 2441  * @brief Returns the first effective constraint from a list of candidates, depending on evaluation state.
 2442  * @param constraints The list of potential candidates
 2443  * @return The effective constraint, or NULL if none are found.
 2444  */
 2445 Constraint *EffectiveConstraint(const EvalContext *ctx, Seq *constraints)
 2446 {
 2447     for (size_t i = 0; i < SeqLength(constraints); i++)
 2448     {
 2449         Constraint *cp = SeqAt(constraints, i);
 2450 
 2451         const char *context = ConstraintContext(cp);
 2452         if (IsDefinedClass(ctx, context))
 2453         {
 2454             return cp;
 2455         }
 2456     }
 2457 
 2458     return NULL;
 2459 }
 2460 
 2461 void ConstraintDestroy(Constraint *cp)
 2462 {
 2463     if (cp)
 2464     {
 2465         RvalDestroy(cp->rval);
 2466         free(cp->lval);
 2467         free(cp->classes);
 2468 
 2469         free(cp);
 2470     }
 2471 }
 2472 
 2473 /*****************************************************************************/
 2474 
 2475 /**
 2476  * @brief Get the trinary boolean value of the first effective constraint found matching, from a promise
 2477  * @param lval
 2478  * @param list
 2479  * @return True/false, or CF_UNDEFINED if not found
 2480  */
 2481 int PromiseGetConstraintAsBoolean(const EvalContext *ctx, const char *lval, const Promise *pp)
 2482 {
 2483     int retval = CF_UNDEFINED;
 2484 
 2485     for (size_t i = 0; i < SeqLength(pp->conlist); i++)
 2486     {
 2487         Constraint *cp = SeqAt(pp->conlist, i);
 2488 
 2489         if (strcmp(cp->lval, lval) == 0)
 2490         {
 2491             if (IsDefinedClass(ctx, cp->classes))
 2492             {
 2493                 if (retval != CF_UNDEFINED)
 2494                 {
 2495                     Log(LOG_LEVEL_ERR, "Multiple '%s' (boolean) constraints break this promise", lval);
 2496                     PromiseRef(LOG_LEVEL_ERR, pp);
 2497                 }
 2498             }
 2499             else
 2500             {
 2501                 continue;
 2502             }
 2503 
 2504             if (cp->rval.type != RVAL_TYPE_SCALAR)
 2505             {
 2506                 Log(LOG_LEVEL_ERR, "Type mismatch on rhs - expected type %c for boolean constraint '%s'",
 2507                     cp->rval.type, lval);
 2508                 PromiseRef(LOG_LEVEL_ERR, pp);
 2509                 FatalError(ctx, "Aborted");
 2510             }
 2511 
 2512             if (strcmp(cp->rval.item, "true") == 0 || strcmp(cp->rval.item, "yes") == 0)
 2513             {
 2514                 retval = true;
 2515                 continue;
 2516             }
 2517 
 2518             if (strcmp(cp->rval.item, "false") == 0 || strcmp(cp->rval.item, "no") == 0)
 2519             {
 2520                 retval = false;
 2521             }
 2522         }
 2523     }
 2524 
 2525     if (retval == CF_UNDEFINED)
 2526     {
 2527         retval = false;
 2528     }
 2529 
 2530     return retval;
 2531 }
 2532 
 2533 /*****************************************************************************/
 2534 
 2535 /**
 2536  * @brief Get the trinary boolean value of the first effective constraint found matching
 2537  * @param lval
 2538  * @param constraints
 2539  * @return True/false, or CF_UNDEFINED if not found
 2540  */
 2541 int ConstraintsGetAsBoolean(const EvalContext *ctx, const char *lval, const Seq *constraints)
 2542 {
 2543     int retval = CF_UNDEFINED;
 2544 
 2545     for (size_t i = 0; i < SeqLength(constraints); i++)
 2546     {
 2547         Constraint *cp = SeqAt(constraints, i);
 2548 
 2549         if (strcmp(cp->lval, lval) == 0)
 2550         {
 2551             if (IsDefinedClass(ctx, cp->classes))
 2552             {
 2553                 if (retval != CF_UNDEFINED)
 2554                 {
 2555                     Log(LOG_LEVEL_ERR, "Multiple '%s' (boolean) body constraints break this promise", lval);
 2556                 }
 2557             }
 2558             else
 2559             {
 2560                 continue;
 2561             }
 2562 
 2563             if (cp->rval.type != RVAL_TYPE_SCALAR)
 2564             {
 2565                 Log(LOG_LEVEL_ERR, "Type mismatch - expected type %c for boolean constraint '%s'",
 2566                     cp->rval.type, lval);
 2567                 FatalError(ctx, "Aborted");
 2568             }
 2569 
 2570             if (strcmp(cp->rval.item, "true") == 0 || strcmp(cp->rval.item, "yes") == 0)
 2571             {
 2572                 retval = true;
 2573                 continue;
 2574             }
 2575 
 2576             if (strcmp(cp->rval.item, "false") == 0 || strcmp(cp->rval.item, "no") == 0)
 2577             {
 2578                 retval = false;
 2579             }
 2580         }
 2581     }
 2582 
 2583     if (retval == CF_UNDEFINED)
 2584     {
 2585         retval = false;
 2586     }
 2587 
 2588     return retval;
 2589 }
 2590 
 2591 /*****************************************************************************/
 2592 
 2593 bool PromiseBundleOrBodyConstraintExists(const EvalContext *ctx, const char *lval, const Promise *pp)
 2594 {
 2595     int retval = CF_UNDEFINED;
 2596 
 2597     for (size_t i = 0; i < SeqLength(pp->conlist); i++)
 2598     {
 2599         const Constraint *cp = SeqAt(pp->conlist, i);
 2600 
 2601         if (strcmp(cp->lval, lval) == 0)
 2602         {
 2603             if (IsDefinedClass(ctx, cp->classes))
 2604             {
 2605                 if (retval != CF_UNDEFINED)
 2606                 {
 2607                     Log(LOG_LEVEL_ERR, "Multiple '%s' constraints break this promise", lval);
 2608                     PromiseRef(LOG_LEVEL_ERR, pp);
 2609                 }
 2610             }
 2611             else
 2612             {
 2613                 continue;
 2614             }
 2615 
 2616             if (!(cp->rval.type == RVAL_TYPE_FNCALL || cp->rval.type == RVAL_TYPE_SCALAR))
 2617             {
 2618                 Log(LOG_LEVEL_ERR,
 2619                     "Anomalous type mismatch - type %c for bundle constraint '%s' did not match internals",
 2620                     cp->rval.type, lval);
 2621                 PromiseRef(LOG_LEVEL_ERR, pp);
 2622                 FatalError(ctx, "Aborted");
 2623             }
 2624 
 2625             return true;
 2626         }
 2627     }
 2628 
 2629     return false;
 2630 }
 2631 
 2632 static bool CheckScalarNotEmptyVarRef(const char *scalar)
 2633 {
 2634     return (strcmp("$()", scalar) != 0) && (strcmp("${}", scalar) != 0);
 2635 }
 2636 
 2637 static bool PromiseCheck(const Promise *pp, Seq *errors)
 2638 {
 2639     bool success = true;
 2640 
 2641     if (!CheckScalarNotEmptyVarRef(pp->promiser))
 2642     {
 2643         SeqAppend(errors, PolicyErrorNew(POLICY_ELEMENT_TYPE_PROMISE, pp,
 2644                                          POLICY_ERROR_EMPTY_VARREF));
 2645         success = false;
 2646     }
 2647 
 2648     // check if promise's constraints are valid
 2649     for (size_t i = 0; i < SeqLength(pp->conlist); i++)
 2650     {
 2651         Constraint *constraint = SeqAt(pp->conlist, i);
 2652         success &= ConstraintCheckSyntax(constraint, errors);
 2653     }
 2654 
 2655     const PromiseTypeSyntax *pts = PromiseTypeSyntaxGet(pp->parent_promise_type->parent_bundle->type,
 2656                                                         pp->parent_promise_type->name);
 2657 
 2658     if (pts->check_promise)
 2659     {
 2660         success &= pts->check_promise(pp, errors);
 2661     }
 2662 
 2663     return success;
 2664 }
 2665 
 2666 const char *PromiseGetNamespace(const Promise *pp)
 2667 {
 2668     return pp->parent_promise_type->parent_bundle->ns;
 2669 }
 2670 
 2671 const Bundle *PromiseGetBundle(const Promise *pp)
 2672 {
 2673     return pp->parent_promise_type->parent_bundle;
 2674 }
 2675 
 2676 const Policy *PromiseGetPolicy(const Promise *pp)
 2677 {
 2678     return PromiseGetBundle(pp)->parent_policy;
 2679 }
 2680 
 2681 static void BundlePath(Writer *w, const Bundle *bp)
 2682 {
 2683     WriterWriteChar(w, '/');
 2684     WriterWrite(w, bp->ns);
 2685     WriterWriteChar(w, '/');
 2686     WriterWrite(w, bp->name);
 2687 }
 2688 
 2689 static void PromiseTypePath(Writer *w, const PromiseType *pt)
 2690 {
 2691     BundlePath(w, pt->parent_bundle);
 2692     WriterWriteChar(w, '/');
 2693     WriterWrite(w, pt->name);
 2694 }
 2695 
 2696 /**
 2697  * @brief Write a string describing the promise location in policy, e.g. /default/foo/packages/'emacs'
 2698  */
 2699 void PromisePath(Writer *w, const Promise *pp)
 2700 {
 2701     PromiseTypePath(w, pp->parent_promise_type);
 2702     WriterWriteChar(w, '/');
 2703     WriterWriteChar(w, '\'');
 2704     WriterWrite(w, pp->promiser);
 2705     WriterWriteChar(w, '\'');
 2706 }
 2707 
 2708 /**
 2709  * @brief Return handle of the promise.
 2710  * @param pp
 2711  * @return Promise handle or NULL if no handle is provided
 2712  */
 2713 const char *PromiseGetHandle(const Promise *pp)
 2714 {
 2715     return (const char *)PromiseGetImmediateRvalValue("handle", pp, RVAL_TYPE_SCALAR);
 2716 }
 2717 
 2718 /**
 2719  * @brief Get the int value of the first effective constraint found matching, from a promise
 2720  * @param lval
 2721  * @param pp
 2722  * @return Int value, or CF_NOINT
 2723  */
 2724 int PromiseGetConstraintAsInt(const EvalContext *ctx, const char *lval, const Promise *pp)
 2725 {
 2726     int retval = CF_NOINT;
 2727     const Constraint *cp = PromiseGetConstraint(pp, lval);
 2728     if (cp)
 2729     {
 2730         if (cp->rval.type != RVAL_TYPE_SCALAR)
 2731         {
 2732             Log(LOG_LEVEL_ERR,
 2733                 "Anomalous type mismatch - expected type for int constraint %s did not match internals", lval);
 2734             PromiseRef(LOG_LEVEL_ERR, pp);
 2735             FatalError(ctx, "Aborted");
 2736         }
 2737 
 2738         retval = (int) IntFromString((char *) cp->rval.item);
 2739     }
 2740 
 2741     return retval;
 2742 }
 2743 
 2744 /*****************************************************************************/
 2745 
 2746 /**
 2747  * @brief Get the real value of the first effective constraint found matching, from a promise
 2748  * @return true if value could be extracted
 2749  */
 2750 bool PromiseGetConstraintAsReal(const EvalContext *ctx, const char *lval, const Promise *pp, double *value_out)
 2751 {
 2752     const Constraint *cp = PromiseGetConstraint(pp, lval);
 2753     if (cp)
 2754     {
 2755         if (cp->rval.type != RVAL_TYPE_SCALAR)
 2756         {
 2757             Log(LOG_LEVEL_ERR,
 2758                 "Anomalous type mismatch - expected type for int constraint '%s' did not match internals", lval);
 2759             FatalError(ctx, "Aborted");
 2760         }
 2761 
 2762         *value_out = DoubleFromString((char *) cp->rval.item, value_out);
 2763         return true;
 2764     }
 2765 
 2766     return false;
 2767 }
 2768 
 2769 /*****************************************************************************/
 2770 
 2771 /**
 2772  * @return true if successful
 2773  */
 2774 static bool Str2Mode(const char *s, mode_t *mode_out)
 2775 {
 2776     int a = CF_UNDEFINED;
 2777 
 2778     if (s == NULL)
 2779     {
 2780         *mode_out = (mode_t)0;
 2781         return true;
 2782     }
 2783 
 2784     sscanf(s, "%o", &a);
 2785 
 2786     if (a == CF_UNDEFINED)
 2787     {
 2788         return false;
 2789     }
 2790 
 2791     *mode_out = (mode_t)a;
 2792     return true;
 2793 }
 2794 
 2795 /**
 2796  * @brief Get the octal value of the first effective constraint found matching, from a promise
 2797  * @param lval
 2798  * @param list
 2799  * @return Double value, or 077 if not found
 2800  */
 2801 mode_t PromiseGetConstraintAsOctal(const EvalContext *ctx, const char *lval, const Promise *pp)
 2802 {
 2803     mode_t retval = 077;
 2804 
 2805 // We could handle units here, like kb,b,mb
 2806 
 2807     const Constraint *cp = PromiseGetConstraint(pp, lval);
 2808     if (cp)
 2809     {
 2810         if (cp->rval.type != RVAL_TYPE_SCALAR)
 2811         {
 2812             Log(LOG_LEVEL_ERR,
 2813                 "Anomalous type mismatch - expected type for int constraint %s did not match internals", lval);
 2814             PromiseRef(LOG_LEVEL_ERR, pp);
 2815             FatalError(ctx, "Aborted");
 2816         }
 2817 
 2818         if (!Str2Mode(cp->rval.item, &retval))
 2819         {
 2820             Log(LOG_LEVEL_ERR, "Error reading assumed octal value '%s'", (const char *)cp->rval.item);
 2821             PromiseRef(LOG_LEVEL_ERR, pp);
 2822         }
 2823     }
 2824 
 2825     return retval;
 2826 }
 2827 
 2828 /*****************************************************************************/
 2829 
 2830 #ifdef __MINGW32__
 2831 
 2832 uid_t PromiseGetConstraintAsUid(const EvalContext *ctx, const char *lval, const Promise *pp)
 2833 {                               // we use sids on windows instead
 2834     return CF_SAME_OWNER;
 2835 }
 2836 
 2837 #else /* !__MINGW32__ */
 2838 
 2839 /**
 2840  * @brief Get the uid value of the first effective constraint found matching, from a promise
 2841  * @param lval
 2842  * @param pp
 2843  * @return Uid value, or CF_SAME_OWNER if not found
 2844  */
 2845 uid_t PromiseGetConstraintAsUid(const EvalContext *ctx, const char *lval, const Promise *pp)
 2846 {
 2847     int retval = CF_SAME_OWNER;
 2848     char buffer[CF_MAXVARSIZE];
 2849 
 2850     const Constraint *cp = PromiseGetConstraint(pp, lval);
 2851     if (cp)
 2852     {
 2853         if (cp->rval.type != RVAL_TYPE_SCALAR)
 2854         {
 2855             Log(LOG_LEVEL_ERR,
 2856                 "Anomalous type mismatch - expected type for owner constraint %s did not match internals",
 2857                 lval);
 2858             PromiseRef(LOG_LEVEL_ERR, pp);
 2859             FatalError(ctx, "Aborted");
 2860         }
 2861 
 2862         retval = Str2Uid((char *) cp->rval.item, buffer, pp);
 2863     }
 2864 
 2865     return retval;
 2866 }
 2867 
 2868 #endif /* !__MINGW32__ */
 2869 
 2870 /*****************************************************************************/
 2871 
 2872 #ifdef __MINGW32__
 2873 
 2874 gid_t PromiseGetConstraintAsGid(const EvalContext *ctx, char *lval, const Promise *pp)
 2875 {                               // not applicable on windows: processes have no group
 2876     return CF_SAME_GROUP;
 2877 }
 2878 
 2879 #else /* !__MINGW32__ */
 2880 
 2881 /**
 2882  * @brief Get the uid value of the first effective constraint found matching, from a promise
 2883  * @param lval
 2884  * @param pp
 2885  * @return Gid value, or CF_SAME_GROUP if not found
 2886  */
 2887 gid_t PromiseGetConstraintAsGid(const EvalContext *ctx, char *lval, const Promise *pp)
 2888 {
 2889     int retval = CF_SAME_GROUP;
 2890     char buffer[CF_MAXVARSIZE];
 2891 
 2892     const Constraint *cp = PromiseGetConstraint(pp, lval);
 2893     if (cp)
 2894     {
 2895         if (cp->rval.type != RVAL_TYPE_SCALAR)
 2896         {
 2897             Log(LOG_LEVEL_ERR,
 2898                 "Anomalous type mismatch - expected type for group constraint '%s' did not match internals",
 2899                 lval);
 2900             PromiseRef(LOG_LEVEL_ERR, pp);
 2901             FatalError(ctx, "Aborted");
 2902         }
 2903 
 2904         retval = Str2Gid((char *) cp->rval.item, buffer, pp);
 2905     }
 2906 
 2907     return retval;
 2908 }
 2909 #endif /* !__MINGW32__ */
 2910 
 2911 /*****************************************************************************/
 2912 
 2913 /**
 2914  * @brief Get the Rlist value of the first effective constraint found matching, from a promise
 2915  * @param lval
 2916  * @param list
 2917  * @return Rlist or NULL if not found (note: same as empty list)
 2918  */
 2919 // FIX: promise constrained classed?
 2920 Rlist *PromiseGetConstraintAsList(const EvalContext *ctx, const char *lval, const Promise *pp)
 2921 {
 2922     const Constraint *cp = PromiseGetConstraint(pp, lval);
 2923     if (cp)
 2924     {
 2925         if (cp->rval.type != RVAL_TYPE_LIST)
 2926         {
 2927             Log(LOG_LEVEL_ERR, "Type mismatch on rhs - expected type for list constraint '%s'", lval);
 2928             PromiseRef(LOG_LEVEL_ERR, pp);
 2929             FatalError(ctx, "Aborted");
 2930         }
 2931 
 2932         return RvalRlistValue(cp->rval);
 2933     }
 2934 
 2935     return NULL;
 2936 }
 2937 
 2938 /**
 2939  * @brief Get the first effective constraint from the promise, also does some checking
 2940  * @param promise
 2941  * @param lval
 2942  * @return Effective constraint if found, otherwise NULL
 2943  */
 2944 Constraint *PromiseGetConstraint(const Promise *pp, const char *lval)
 2945 {
 2946     if (!pp)
 2947     {
 2948         return NULL;
 2949     }
 2950 
 2951     for (size_t i = 0; i < SeqLength(pp->conlist); i++)
 2952     {
 2953         Constraint *cp = SeqAt(pp->conlist, i);
 2954 
 2955         if (strcmp(cp->lval, lval) == 0)
 2956         {
 2957             return cp;
 2958         }
 2959     }
 2960 
 2961     return NULL;
 2962 }
 2963 
 2964 Constraint *PromiseGetConstraintWithType(const Promise *pp, const char *lval, RvalType type)
 2965 {
 2966     assert(pp);
 2967     for (size_t i = 0; i < SeqLength(pp->conlist); i++)
 2968     {
 2969         Constraint *cp = SeqAt(pp->conlist, i);
 2970         if (cp->rval.type != type) {
 2971             continue;
 2972         }
 2973 
 2974         if (strcmp(cp->lval, lval) == 0)
 2975         {
 2976             return cp;
 2977         }
 2978     }
 2979 
 2980     return NULL;
 2981 }
 2982 
 2983 /**
 2984  * @brief Get the first constraint from the promise
 2985  *
 2986  * Kill this function with fire once we have separated promise constraints and body constraints.
 2987  *
 2988  * @param promise
 2989  * @param lval
 2990  * @return Constraint if found, otherwise NULL
 2991  */
 2992 Constraint *PromiseGetImmediateConstraint(const Promise *pp, const char *lval)
 2993 {
 2994     if (pp == NULL)
 2995     {
 2996         return NULL;
 2997     }
 2998 
 2999     for (size_t i = 0; i < SeqLength(pp->conlist); ++i)
 3000     {
 3001         Constraint *cp = SeqAt(pp->conlist, i);
 3002 
 3003         if (strcmp(cp->lval, lval) == 0)
 3004         {
 3005             /* It would be nice to check whether the constraint we have asked
 3006                for is defined in promise (not in referenced body), but there
 3007                seem to be no way to do it easily.
 3008 
 3009                Checking for absence of classes does not work, as constrains
 3010                obtain classes defined on promise itself.
 3011             */
 3012 
 3013             return cp;
 3014         }
 3015     }
 3016 
 3017     return NULL;
 3018 }
 3019 
 3020 /**
 3021  * @brief Get the Rval value of the first constraint that matches the given
 3022  * type. Checks that this constraint does not have any contexts attached.
 3023  *
 3024  * Kill this function with fire once we have separated body constraints and bundle constraints.
 3025  *
 3026  * @param lval
 3027  * @param promise
 3028  * @param type
 3029  * @return Rval value if found, NULL otherwise
 3030  */
 3031 void *PromiseGetImmediateRvalValue(const char *lval, const Promise *pp, RvalType rtype)
 3032 {
 3033     const Constraint *constraint = PromiseGetImmediateConstraint(pp, lval);
 3034 
 3035     if (constraint && constraint->rval.type == rtype)
 3036     {
 3037         return constraint->rval.item;
 3038     }
 3039     else
 3040     {
 3041         return NULL;
 3042     }
 3043 }
 3044 
 3045 /*****************************************************************************/
 3046 
 3047 /**
 3048  * @brief Get the Rval value of the first effective constraint that matches the given type
 3049  * @param lval
 3050  * @param promise
 3051  * @param type
 3052  * @return Rval value if found, NULL otherwise
 3053  */
 3054 void *PromiseGetConstraintAsRval(const Promise *pp, const char *lval, RvalType rtype)
 3055 {
 3056     const Constraint *constraint = PromiseGetConstraint(pp, lval);
 3057 
 3058     if (constraint && constraint->rval.type == rtype)
 3059     {
 3060         return constraint->rval.item;
 3061     }
 3062     else
 3063     {
 3064         return NULL;
 3065     }
 3066 }
 3067 
 3068 /*****************************************************************************/
 3069 
 3070 /*
 3071  * Check promise constraints while iterating through all slist/containers
 3072  * combinations, called from ExpandPromiseAndDo() during PRE-EVAL.  This is
 3073  * currently running also in cf-promises, but it's enough if such errors are
 3074  * detected in the agent run.
 3075  *
 3076  * TODO remove CommonEvalPromise() actuator, that all it does is call this
 3077  * function, and call this one at the beginning of the main actuators, like
 3078  * KeepAgentPromise().
 3079  */
 3080 void PromiseRecheckAllConstraints(const EvalContext *ctx, const Promise *pp)
 3081 {
 3082     for (size_t i = 0; i < SeqLength(pp->conlist); i++)
 3083     {
 3084         Constraint *cp = SeqAt(pp->conlist, i);
 3085         SyntaxTypeMatch err = ConstraintCheckType(cp);
 3086         if (err != SYNTAX_TYPE_MATCH_OK && err != SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED)
 3087         {
 3088             PolicyError *error =
 3089                 PolicyErrorNew(POLICY_ELEMENT_TYPE_CONSTRAINT, cp,
 3090                                "In attribute '%s', %s",
 3091                                cp->lval, SyntaxTypeMatchToString(err));
 3092             char *error_str = PolicyErrorToString(error);
 3093 
 3094             Log(LOG_LEVEL_ERR, "%s", error_str);
 3095             PolicyErrorDestroy(error);
 3096             free(error_str);
 3097 
 3098             FatalError(ctx, "Cannot continue");
 3099         }
 3100     }
 3101 
 3102     /* Check and warn for non-convergence, see commits
 3103        4f8c19b84327b8f3c2e269173196282ccedfdad9 and
 3104        30c109d22e170a781a647b04b4b0a4a2f7244871. */
 3105     if (strcmp(pp->parent_promise_type->name, "insert_lines") == 0)
 3106     {
 3107         /* TODO without static var, actually remove this check from here
 3108          * completely, do it at the end of PRE-EVAL promise iterations
 3109          * (ExpandPromiseAndDo() after the main loop). */
 3110         static Item *EDIT_ANCHORS = NULL; /* GLOBAL_X */
 3111 
 3112         const char *sp = PromiseGetConstraintAsRval(pp, "select_line_matching",
 3113                                                     RVAL_TYPE_SCALAR);
 3114         if (sp != NULL && !IsExpandable(sp))
 3115         {
 3116             /* Avoid adding twice the same select_line_matching anchor. */
 3117             const char *bun = PromiseGetBundle(pp)->name;
 3118             const Item *ptr = ReturnItemInClass(EDIT_ANCHORS, sp, bun);
 3119             if (ptr != NULL)
 3120             {
 3121                 Log(LOG_LEVEL_INFO,
 3122                     "insert_lines promise uses the same select_line_matching anchor '%s' as another promise."
 3123                     " This will lead to non-convergent behaviour unless 'empty_file_before_editing' is set",
 3124                     sp);
 3125                 PromiseRef(LOG_LEVEL_INFO, pp);
 3126                 return;
 3127             }
 3128 
 3129             PrependItem(&EDIT_ANCHORS, sp, bun);
 3130         }
 3131     }
 3132 }
 3133 
 3134 /*****************************************************************************/
 3135 
 3136 static SyntaxTypeMatch ConstraintCheckType(const Constraint *cp)
 3137 {
 3138     // Check class
 3139     for (size_t i = 0; CF_CLASSBODY[i].lval != NULL; i++)
 3140     {
 3141         if (strcmp(cp->lval, CF_CLASSBODY[i].lval) == 0)
 3142         {
 3143             SyntaxTypeMatch err = CheckConstraintTypeMatch(cp->lval, cp->rval, CF_CLASSBODY[i].dtype, CF_CLASSBODY[i].range.validation_string, 0);
 3144             if (err != SYNTAX_TYPE_MATCH_OK && err != SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED)
 3145             {
 3146                 return err;
 3147             }
 3148         }
 3149     }
 3150 
 3151     if (cp->type == POLICY_ELEMENT_TYPE_PROMISE)
 3152     {
 3153         PromiseType *promise_type = cp->parent.promise->parent_promise_type;
 3154 
 3155         for (size_t i = 0; i < CF3_MODULES; i++)
 3156         {
 3157             const PromiseTypeSyntax *ssp = CF_ALL_PROMISE_TYPES[i];
 3158             if (!ssp)
 3159             {
 3160                 continue;
 3161             }
 3162 
 3163             for (size_t j = 0; ssp[j].bundle_type != NULL; j++)
 3164             {
 3165                 PromiseTypeSyntax ss = ssp[j];
 3166 
 3167                 if (ss.promise_type != NULL)
 3168                 {
 3169                     if (strcmp(ss.promise_type, promise_type->name) == 0)
 3170                     {
 3171                         const ConstraintSyntax *bs = ss.constraints;
 3172 
 3173                         for (size_t l = 0; bs[l].lval != NULL; l++)
 3174                         {
 3175                             // No validation for CF_DATA_TYPE_BUNDLE here
 3176                             // see: PolicyCheckUndefinedBundles() etc.
 3177                             if (bs[l].dtype == CF_DATA_TYPE_BODY)
 3178                             {
 3179                                 const ConstraintSyntax *bs2 = bs[l].range.body_type_syntax->constraints;
 3180 
 3181                                 for (size_t m = 0; bs2[m].lval != NULL; m++)
 3182                                 {
 3183                                     if (strcmp(cp->lval, bs2[m].lval) == 0)
 3184                                     {
 3185                                         return CheckConstraintTypeMatch(cp->lval, cp->rval, bs2[m].dtype, bs2[m].range.validation_string, 0);
 3186                                     }
 3187                                 }
 3188                             }
 3189 
 3190                             if (strcmp(cp->lval, bs[l].lval) == 0)
 3191                             {
 3192                                 return CheckConstraintTypeMatch(cp->lval, cp->rval, bs[l].dtype, bs[l].range.validation_string, 0);
 3193                             }
 3194                         }
 3195                     }
 3196                 }
 3197             }
 3198         }
 3199     }
 3200 
 3201 /* Now check the functional modules - extra level of indirection */
 3202 
 3203     for (size_t i = 0; CF_COMMON_BODIES[i].lval != NULL; i++)
 3204     {
 3205         if (CF_COMMON_BODIES[i].dtype == CF_DATA_TYPE_BODY)
 3206         {
 3207             continue;
 3208         }
 3209 
 3210         if (strcmp(cp->lval, CF_COMMON_BODIES[i].lval) == 0)
 3211         {
 3212             return CheckConstraintTypeMatch(cp->lval, cp->rval, CF_COMMON_BODIES[i].dtype, CF_COMMON_BODIES[i].range.validation_string, 0);
 3213         }
 3214     }
 3215 
 3216     return SYNTAX_TYPE_MATCH_OK;
 3217 }
 3218 
 3219 /**
 3220  * @brief Check whether the promise type is allowed one
 3221  */
 3222 /* FIXME: need to be done automatically */
 3223 bool BundleTypeCheck(const char *name)
 3224 {
 3225     /* FIXME: export size of CF_AGENTTYPES somewhere */
 3226     for (int i = 0; strcmp(CF_AGENTTYPES[i], "<notype>") != 0; ++i)
 3227     {
 3228         if (!strcmp(CF_AGENTTYPES[i], name))
 3229         {
 3230             return true;
 3231         }
 3232     }
 3233 
 3234     if (!strcmp("knowledge", name))
 3235     {
 3236         return true;
 3237     }
 3238 
 3239     if (!strcmp("edit_line", name))
 3240     {
 3241         return true;
 3242     }
 3243 
 3244     if (!strcmp("edit_xml", name))
 3245     {
 3246         return true;
 3247     }
 3248 
 3249     return false;
 3250 }