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

promises.c
Go to the documentation of this file.
1 /*
2  Copyright 2019 Northern.tech AS
3 
4  This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5 
6  This program is free software; you can redistribute it and/or modify it
7  under the terms of the GNU General Public License as published by the
8  Free Software Foundation; version 3.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program; if not, write to the Free Software
17  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18 
19  To the extent this program is licensed as part of the Enterprise
20  versions of CFEngine, the applicable Commercial Open Source License
21  (COSL) may apply to this file if you as a licensee so wish it. See
22  included file COSL.txt.
23 */
24 
25 #include <promises.h>
26 
27 #include <policy.h>
28 #include <syntax.h>
29 #include <expand.h>
30 #include <files_names.h>
31 #include <scope.h>
32 #include <vars.h>
33 #include <locks.h>
34 #include <misc_lib.h>
35 #include <fncall.h>
36 #include <eval_context.h>
37 #include <string_lib.h>
38 #include <audit.h>
39 
40 static void AddDefaultBodiesToPromise(EvalContext *ctx, Promise *promise, const PromiseTypeSyntax *syntax);
41 
43  const Body *bp)
44 {
45  for (size_t k = 0; k < SeqLength(bp->conlist); k++)
46  {
47  Constraint *scp = SeqAt(bp->conlist, k);
48 
49  if (IsDefinedClass(ctx, scp->classes))
50  {
51  Rval returnval = ExpandPrivateRval(ctx, NULL, "body",
52  scp->rval.item, scp->rval.type);
53  PromiseAppendConstraint(pp, scp->lval, returnval, false);
54  }
55  }
56 }
57 
58 /**
59  * Get a map that rewrites body according to parameters.
60  *
61  * @NOTE make sure you free the returned map with JsonDestroy().
62  */
64  const Body *current_body,
65  const Rval *called_rval,
66  bool in_inheritance_chain)
67 {
68  size_t given_args = 0;
69  JsonElement *arg_rewriter = JsonObjectCreate(2);
70 
71  if (called_rval == NULL)
72  {
73  // nothing needed, this is not an inherit_from rval
74  }
75  else if (called_rval->type == RVAL_TYPE_SCALAR)
76  {
77  // We leave the parameters as they were.
78 
79  // Unless the current body matches the
80  // parameters of the inherited body, there
81  // will be unexpanded variables. But the
82  // alternative is to match up body and fncall
83  // arguments, which is not trivial.
84  }
85  else if (called_rval->type == RVAL_TYPE_FNCALL)
86  {
87  const Rlist *call_args = RvalFnCallValue(*called_rval)->args;
88  const Rlist *body_args = current_body->args;
89 
90  given_args = RlistLen(call_args);
91 
92  while (call_args != NULL &&
93  body_args != NULL)
94  {
95  JsonObjectAppendString(arg_rewriter,
96  RlistScalarValue(body_args),
97  RlistScalarValue(call_args));
98  call_args = call_args->next;
99  body_args = body_args->next;
100  }
101  }
102 
103  size_t required_args = RlistLen(current_body->args);
104  // only check arguments for inherited bodies
105  if (in_inheritance_chain && required_args != given_args)
106  {
107  FatalError(ctx,
108  "Argument count mismatch for body "
109  "(gave %zu arguments) vs. inherited body '%s:%s' "
110  "(requires %zu arguments)",
111  given_args,
112  current_body->ns, current_body->name, required_args);
113  }
114 
115  return arg_rewriter;
116 }
117 
118 /**
119  * Appends expanded bodies to the promise #pcopy. It expands the bodies based
120  * on arguments, inheritance, and it can optionally flatten the '@' slists and
121  * expand the variables in the body according to the EvalContext.
122  */
123 static void AppendExpandedBodies(EvalContext *ctx, Promise *pcopy,
124  const Seq *bodies_and_args,
125  bool flatten_slists, bool expand_body_vars)
126 {
127  size_t ba_len = SeqLength(bodies_and_args);
128 
129  /* Iterate over all parent bodies, and finally over the body of the
130  * promise itself, expanding arguments. We have already reversed the Seq
131  * so we start with the most distant parent in the inheritance tree. */
132  for (size_t i = 0; i < ba_len; i += 2)
133  {
134  const Rval *called_rval = SeqAt(bodies_and_args, i);
135  const Body *current_body = SeqAt(bodies_and_args, i + 1);
136  bool in_inheritance_chain= (ba_len - i > 2);
137 
138  JsonElement *arg_rewriter =
139  GetBodyRewriter(ctx, current_body, called_rval,
140  in_inheritance_chain);
141 
142  size_t constraints_num = SeqLength(current_body->conlist);
143  for (size_t k = 0; k < constraints_num; k++)
144  {
145  const Constraint *scp = SeqAt(current_body->conlist, k);
146 
147  // we don't copy the inherit_from attribute or associated call
148  if (strcmp("inherit_from", scp->lval) == 0)
149  {
150  continue;
151  }
152 
153  if (IsDefinedClass(ctx, scp->classes))
154  {
155  /* We copy the Rval expanding all, including inherited,
156  * body arguments. */
157  Rval newrv = RvalCopyRewriter(scp->rval, arg_rewriter);
158 
159  /* Expand '@' slists. */
160  if (flatten_slists && newrv.type == RVAL_TYPE_LIST)
161  {
162  RlistFlatten(ctx, (Rlist **) &newrv.item);
163  }
164 
165  /* Expand body vars; note it has to happen ONLY ONCE. */
166  if (expand_body_vars)
167  {
168  Rval newrv2 = ExpandPrivateRval(ctx, NULL, "body",
169  newrv.item, newrv.type);
170  RvalDestroy(newrv);
171  newrv = newrv2;
172  }
173 
174  /* PromiseAppendConstraint() overwrites existing constraints,
175  thus inheritance just works, as it correctly overwrites
176  parents' constraints. */
177  Constraint *scp_copy =
178  PromiseAppendConstraint(pcopy, scp->lval,
179  newrv, false);
180  scp_copy->offset = scp->offset;
181 
182  char *rval_s = RvalToString(scp->rval);
183  char *rval_exp_s = RvalToString(scp_copy->rval);
184  Log(LOG_LEVEL_DEBUG, "DeRefCopyPromise(): "
185  "expanding constraint '%s': '%s' -> '%s'",
186  scp->lval, rval_s, rval_exp_s);
187  free(rval_exp_s);
188  free(rval_s);
189  }
190  } /* for all body constraints */
191 
192  JsonDestroy(arg_rewriter);
193  }
194 }
195 
196 /**
197  * Copies the promise, expanding the constraints.
198  *
199  * 1. copy the promise itself
200  * 2. copy constraints: copy the bodies expanding arguments passed
201  * (arg_rewrite), copy the bundles, copy the rest of the constraints
202  * 3. flatten '@' slists everywhere
203  * 4. handle body inheritance
204  */
206 {
207  Log(LOG_LEVEL_DEBUG, "DeRefCopyPromise(): "
208  "promiser:'%s'",
209  SAFENULL(pp->promiser));
210 
211  Promise *pcopy = xcalloc(1, sizeof(Promise));
212 
213  if (pp->promiser)
214  {
215  pcopy->promiser = xstrdup(pp->promiser);
216  }
217 
218  /* Copy promisee (if not NULL) while expanding '@' slists. */
219  pcopy->promisee = RvalCopy(pp->promisee);
220  if (pcopy->promisee.type == RVAL_TYPE_LIST)
221  {
222  RlistFlatten(ctx, (Rlist **) &pcopy->promisee.item);
223  }
224 
225  if (pp->promisee.item != NULL)
226  {
227  char *promisee_string = RvalToString(pp->promisee);
228 
229  CF_ASSERT(pcopy->promisee.item != NULL,
230  "DeRefCopyPromise: Failed to copy promisee: %s",
231  promisee_string);
232  Log(LOG_LEVEL_DEBUG, "DeRefCopyPromise(): "
233  "expanded promisee: '%s'",
234  promisee_string);
235  free(promisee_string);
236  }
237 
238  assert(pp->classes);
239  pcopy->classes = xstrdup(pp->classes);
241  pcopy->offset.line = pp->offset.line;
242  pcopy->comment = pp->comment ? xstrdup(pp->comment) : NULL;
243  pcopy->conlist = SeqNew(10, ConstraintDestroy);
244  pcopy->org_pp = pp->org_pp;
245  pcopy->offset = pp->offset;
246 
247 /* No further type checking should be necessary here, already done by CheckConstraintTypeMatch */
248 
249  for (size_t i = 0; i < SeqLength(pp->conlist); i++)
250  {
251  Constraint *cp = SeqAt(pp->conlist, i);
252  const Policy *policy = PolicyFromPromise(pp);
253 
254  /* bodies_and_args: Do we have body to expand, possibly with arguments?
255  * At position 0 we'll have the body, then its rval, then the same for
256  * each of its inherit_from parents. */
257  Seq *bodies_and_args = NULL;
258  const Rlist *args = NULL;
259  const char *body_reference = NULL;
260 
261  /* A body template reference could look like a scalar or fn to the parser w/w () */
262  switch (cp->rval.type)
263  {
264  case RVAL_TYPE_SCALAR:
265  if (cp->references_body)
266  {
267  body_reference = RvalScalarValue(cp->rval);
268  bodies_and_args = EvalContextResolveBodyExpression(ctx, policy, body_reference, cp->lval);
269  }
270  args = NULL;
271  break;
272  case RVAL_TYPE_FNCALL:
273  body_reference = RvalFnCallValue(cp->rval)->name;
274  bodies_and_args = EvalContextResolveBodyExpression(ctx, policy, body_reference, cp->lval);
275  args = RvalFnCallValue(cp->rval)->args;
276  break;
277  default:
278  break;
279  }
280 
281  /* First case is: we have a body to expand lval = body(args). */
282 
283  if (bodies_and_args != NULL &&
284  SeqLength(bodies_and_args) > 0)
285  {
286  const Body *bp = SeqAt(bodies_and_args, 0);
287  assert(bp != NULL);
288 
289  SeqReverse(bodies_and_args); // when we iterate, start with the furthest parent
290 
291  EvalContextStackPushBodyFrame(ctx, pcopy, bp, args);
292 
293  if (strcmp(bp->type, cp->lval) != 0)
294  {
296  "Body type mismatch for body reference '%s' in promise "
297  "at line %zu of file '%s', '%s' does not equal '%s'",
298  body_reference, pp->offset.line,
299  PromiseGetBundle(pp)->source_path, bp->type, cp->lval);
300  }
301 
302  Log(LOG_LEVEL_DEBUG, "DeRefCopyPromise(): "
303  "copying body %s: '%s'",
304  cp->lval, body_reference);
305 
306  if (IsDefinedClass(ctx, cp->classes))
307  {
308  /* For new package promises we need to have name of the
309  * package_manager body. */
310  char body_name[strlen(cp->lval) + 6];
311  xsnprintf(body_name, sizeof(body_name), "%s_name", cp->lval);
312  PromiseAppendConstraint(pcopy, body_name,
313  (Rval) {xstrdup(bp->name), RVAL_TYPE_SCALAR }, false);
314 
315  /* Keep the referent body type as a boolean for convenience
316  * when checking later. */
317  PromiseAppendConstraint(pcopy, cp->lval,
318  (Rval) {xstrdup("true"), RVAL_TYPE_SCALAR }, false);
319  }
320 
321  if (bp->args) /* There are arguments to insert */
322  {
323  if (!args)
324  {
326  "Argument mismatch for body reference '%s' in promise "
327  "at line %zu of file '%s'",
328  body_reference, pp->offset.line,
329  PromiseGetBundle(pp)->source_path);
330  }
331 
332  AppendExpandedBodies(ctx, pcopy, bodies_and_args,
333  false, true);
334  }
335  else /* No body arguments or body undeclared */
336  {
337  if (args) /* body undeclared */
338  {
340  "Apparent body '%s' was undeclared or could "
341  "have incorrect args, but used in a promise near "
342  "line %zu of %s (possible unquoted literal value)",
343  RvalScalarValue(cp->rval), pp->offset.line,
344  PromiseGetBundle(pp)->source_path);
345  }
346  else /* no body arguments, but maybe the inherited bodies have */
347  {
348  AppendExpandedBodies(ctx, pcopy, bodies_and_args,
349  true, false);
350  }
351  }
352 
354  SeqDestroy(bodies_and_args);
355  }
356  else /* constraint is not a body */
357  {
358  if (cp->references_body)
359  {
360  // assume this is a typed bundle (e.g. edit_line)
361  const Bundle *callee =
363  body_reference,
364  cp->lval);
365  if (!callee)
366  {
367  // otherwise, assume this is a method-type call
368  callee = EvalContextResolveBundleExpression(ctx, policy,
369  body_reference,
370  "agent");
371  if (!callee)
372  {
373  callee = EvalContextResolveBundleExpression(ctx, policy,
374  body_reference,
375  "common");
376  }
377  }
378 
379  if (callee == NULL &&
380  cp->rval.type != RVAL_TYPE_FNCALL &&
381  strcmp("ifvarclass", cp->lval) != 0 &&
382  strcmp("if", cp->lval) != 0)
383  {
384  char *rval_string = RvalToString(cp->rval);
386  "Apparent bundle '%s' was undeclared, but "
387  "used in a promise near line %zu of %s "
388  "(possible unquoted literal value)",
389  rval_string, pp->offset.line,
390  PromiseGetBundle(pp)->source_path);
391  free(rval_string);
392  }
393 
395  "DeRefCopyPromise(): copying bundle: '%s'",
396  body_reference);
397  }
398  else
399  {
401  "DeRefCopyPromise(): copying constraint: '%s'",
402  cp->lval);
403  }
404 
405  /* For all non-body constraints: copy the Rval expanding the
406  * '@' list variables. */
407 
408  if (IsDefinedClass(ctx, cp->classes))
409  {
410  Rval newrv = RvalCopy(cp->rval);
411  if (newrv.type == RVAL_TYPE_LIST)
412  {
413  RlistFlatten(ctx, (Rlist **) &newrv.item);
414  }
415 
416  PromiseAppendConstraint(pcopy, cp->lval, newrv, false);
417  }
418  }
419 
420  } /* for all constraints */
421 
422  // Add default body for promise body types that are not present
423  char *bundle_type = pcopy->parent_promise_type->parent_bundle->type;
424  char *promise_type = pcopy->parent_promise_type->name;
425  const PromiseTypeSyntax *syntax = PromiseTypeSyntaxGet(bundle_type, promise_type);
426  AddDefaultBodiesToPromise(ctx, pcopy, syntax);
427 
428  // Add default body for global body types that are not present
429  const PromiseTypeSyntax *global_syntax = PromiseTypeSyntaxGet("*", "*");
430  AddDefaultBodiesToPromise(ctx, pcopy, global_syntax);
431 
432  return pcopy;
433 }
434 
435 // Try to add default bodies to promise for every body type found in syntax
436 static void AddDefaultBodiesToPromise(EvalContext *ctx, Promise *promise, const PromiseTypeSyntax *syntax)
437 {
438  // do nothing if syntax is not defined
439  if (syntax == NULL) {
440  return;
441  }
442 
443  // iterate over possible constraints
444  for (int i = 0; syntax->constraints[i].lval; i++)
445  {
446  // of type body
447  if(syntax->constraints[i].dtype == CF_DATA_TYPE_BODY) {
448  const char *constraint_type = syntax->constraints[i].lval;
449  // if there is no matching body in this promise
450  if(!PromiseBundleOrBodyConstraintExists(ctx, constraint_type, promise)) {
451  const Policy *policy = PolicyFromPromise(promise);
452  // default format is <promise_type>_<body_type>
453  char* default_body_name = StringConcatenate(3, promise->parent_promise_type->name, "_", constraint_type);
454  const Body *bp = EvalContextFindFirstMatchingBody(policy, constraint_type, "bodydefault", default_body_name);
455  if(bp) {
456  Log(LOG_LEVEL_VERBOSE, "Using the default body: %60s", default_body_name);
457  CopyBodyConstraintsToPromise(ctx, promise, bp);
458  }
459  free(default_body_name);
460  }
461  }
462  }
463 }
464 
465 /*****************************************************************************/
466 
467 static bool EvaluateConstraintIteration(EvalContext *ctx, const Constraint *cp, Rval *rval_out)
468 {
469  assert(cp->type == POLICY_ELEMENT_TYPE_PROMISE);
470  const Promise *pp = cp->parent.promise;
471 
472  if (!IsDefinedClass(ctx, cp->classes))
473  {
474  return false;
475  }
476 
478  {
479  *rval_out = ExpandBundleReference(ctx, NULL, "this", cp->rval);
480  }
481  else
482  {
483  *rval_out = EvaluateFinalRval(ctx, PromiseGetPolicy(pp), NULL,
484  "this", cp->rval, false, pp);
485  }
486 
487  return true;
488 }
489 
490 /**
491  @brief Helper function to determine whether the Rval of ifvarclass/if/unless is defined.
492  If the Rval is a function, call that function.
493 */
495 {
496  assert(ctx);
497  assert(cp);
498  assert(pcopy);
499 
500  /*
501  This might fail to expand if there are unexpanded variables in function arguments
502  (in which case the function won't be called at all), but the function still returns true.
503 
504  If expansion fails for other reasons, assume that we don't know this class.
505  */
506  Rval final;
507  if (!EvaluateConstraintIteration((EvalContext*)ctx, cp, &final))
508  {
509  return EXPRESSION_VALUE_ERROR;
510  }
511 
512  char *classes = NULL;
513  PromiseAppendConstraint(pcopy, cp->lval, final, false);
514  switch (final.type)
515  {
516  case RVAL_TYPE_SCALAR:
517  classes = RvalScalarValue(final);
518  break;
519 
520  case RVAL_TYPE_FNCALL:
521  Log(LOG_LEVEL_DEBUG, "Function call in class expression did not succeed");
522  break;
523 
524  default:
525  break;
526  }
527 
528  if (classes == NULL)
529  {
530  return EXPRESSION_VALUE_ERROR;
531  }
532  // sanity check for unexpanded variables
533  if (strchr(classes, '$') || strchr(classes, '@'))
534  {
535  Log(LOG_LEVEL_DEBUG, "Class expression did not evaluate");
536  return EXPRESSION_VALUE_ERROR;
537  }
538 
539  return CheckClassExpression(ctx, classes);
540 }
541 
542 /* Expands "$(this.promiser)" comment if present. Writes the result to pp. */
543 static void DereferenceAndPutComment(Promise* pp, const char *comment)
544 {
545  free(pp->comment);
546 
547  char *sp;
548  if ((sp = strstr(comment, "$(this.promiser)")) != NULL ||
549  (sp = strstr(comment, "${this.promiser}")) != NULL)
550  {
551  char *s;
552  int this_len = strlen("$(this.promiser)");
553  int this_offset = sp - comment;
554  xasprintf(&s, "%.*s%s%s",
555  this_offset, comment, pp->promiser,
556  &comment[this_offset + this_len]);
557 
558  pp->comment = s;
559  }
560  else
561  {
562  pp->comment = xstrdup(comment);
563  }
564 }
565 
566 Promise *ExpandDeRefPromise(EvalContext *ctx, const Promise *pp, bool *excluded)
567 {
568  assert(pp != NULL);
569  assert(pp->parent_promise_type != NULL);
570  assert(pp->promiser != NULL);
571  assert(pp->classes != NULL);
572  assert(excluded != NULL);
573 
574  *excluded = false;
575 
576  Rval returnval = ExpandPrivateRval(ctx, NULL, "this", pp->promiser, RVAL_TYPE_SCALAR);
577  if (returnval.item == NULL)
578  {
579  assert(returnval.type == RVAL_TYPE_LIST ||
580  returnval.type == RVAL_TYPE_NOPROMISEE);
581  /* TODO Log() empty slist, promise skipped? */
582  *excluded = true;
583  return NULL;
584  }
585  Promise *pcopy = xcalloc(1, sizeof(Promise));
586  pcopy->promiser = RvalScalarValue(returnval);
587 
588  /* TODO remove the conditions here for fixing redmine#7880. */
589  if ((strcmp("files", pp->parent_promise_type->name) != 0) &&
590  (strcmp("storage", pp->parent_promise_type->name) != 0))
591  {
593  CF_DATA_TYPE_STRING, "source=promise");
594  }
595 
596  if (pp->promisee.item)
597  {
598  pcopy->promisee = EvaluateFinalRval(ctx, PromiseGetPolicy(pp), NULL, "this", pp->promisee, true, pp);
599  }
600  else
601  {
602  pcopy->promisee = (Rval) {NULL, RVAL_TYPE_NOPROMISEE };
603  }
604 
605  pcopy->classes = xstrdup(pp->classes);
607  pcopy->offset.line = pp->offset.line;
608  pcopy->comment = pp->comment ? xstrdup(pp->comment) : NULL;
609  pcopy->conlist = SeqNew(10, ConstraintDestroy);
610  pcopy->org_pp = pp->org_pp;
611 
612  // if this is a class promise, check if it is already set, if so, skip
613  if (strcmp("classes", pp->parent_promise_type->name) == 0)
614  {
615  if (IsDefinedClass(ctx, CanonifyName(pcopy->promiser)))
616  {
618  "Skipping evaluation of classes promise as class '%s' is already set",
619  CanonifyName(pcopy->promiser));
620 
621  *excluded = true;
622  return pcopy;
623  }
624  }
625 
626  /* Look for 'if'/'ifvarclass' exclusion. */
627  {
628  /* We need to make sure to check both 'if' and 'ifvarclass' constraints. */
629  bool checked_if = false;
630  const Constraint *ifvarclass = PromiseGetConstraint(pp, "ifvarclass");
631  if (!ifvarclass)
632  {
633  ifvarclass = PromiseGetConstraint(pp, "if");
634  checked_if = true;
635  }
636 
637  // if - Skip if false or error:
638  while (ifvarclass != NULL)
639  {
640  if (CheckVarClassExpression(ctx, ifvarclass, pcopy) != EXPRESSION_VALUE_TRUE)
641  {
643  {
644  char *ifvarclass_string = RvalToString(ifvarclass->rval);
645  Log(LOG_LEVEL_VERBOSE, "Skipping promise '%s'"
646  " because constraint '%s => %s' is not met",
647  pp->promiser, ifvarclass->lval, ifvarclass_string);
648  free(ifvarclass_string);
649  }
650  *excluded = true;
651  return pcopy;
652  }
653  if (!checked_if)
654  {
655  ifvarclass = PromiseGetConstraint(pp, "if");
656  checked_if = true;
657  }
658  else
659  {
660  ifvarclass = NULL;
661  }
662  }
663  }
664 
665  /* Look for 'unless' exclusion. */
666  {
667  const Constraint *unless = PromiseGetConstraint(pp, "unless");
668 
669  // unless - Skip if true or error:
670  if (unless != NULL)
671  {
672  // If the rval is a function, CheckVarClassExpression will call it
673  // It will evaluate the class expression as well:
674  const ExpressionValue value = CheckVarClassExpression(ctx, unless, pcopy);
675  // If it returns EXPRESSION_VALUE_ERROR, it most likely means there
676  // are unexpanded variables in the rval (possibly in function calls)
677 
678  if ((EvalContextGetPass(ctx) == CF_DONEPASSES-1)
679  && (value == EXPRESSION_VALUE_ERROR))
680  {
681  char *unless_string = RvalToString(unless->rval);
682  // The rval is most likely a string or a function call,
683  // with unexpanded variables, for example:
684  // unless => "$(no_such_var)"
685  // We default to NOT skipping (since if would skip).
687  "Not skipping %s promise '%s' with constraint '%s => %s' in last evaluation pass (since if would skip)",
689  pp->promiser,
690  unless->lval,
691  unless_string);
692  free(unless_string);
693  }
694  else if (value != EXPRESSION_VALUE_FALSE)
695  {
697  {
698  char *unless_string = RvalToString(unless->rval);
700  "Skipping promise '%s' because constraint '%s => %s' is not met",
701  pp->promiser, unless->lval, unless_string);
702  free(unless_string);
703  }
704  *excluded = true;
705  return pcopy;
706  }
707  }
708  }
709 
710  /* Look for depends_on exclusion. */
711  {
712  const Constraint *depends_on = PromiseGetConstraint(pp, "depends_on");
713  if (depends_on)
714  {
715  Rval final;
716  if (EvaluateConstraintIteration(ctx, depends_on, &final))
717  {
718  PromiseAppendConstraint(pcopy, depends_on->lval, final, false);
719 
720  if (MissingDependencies(ctx, pcopy))
721  {
722  *excluded = true;
723  return pcopy;
724  }
725  }
726  }
727  }
728 
729  /* Evaluate all constraints. */
730  for (size_t i = 0; i < SeqLength(pp->conlist); i++)
731  {
732  Constraint *cp = SeqAt(pp->conlist, i);
733 
734  // special constraints ifvarclass and depends_on are evaluated before the rest of the constraints
735  if (strcmp(cp->lval, "ifvarclass") == 0 ||
736  strcmp(cp->lval, "if") == 0 ||
737  strcmp(cp->lval, "unless") == 0 ||
738  strcmp(cp->lval, "depends_on") == 0)
739  {
740  continue;
741  }
742 
743  Rval final;
744  if (!EvaluateConstraintIteration(ctx, cp, &final))
745  {
746  continue;
747  }
748 
749  PromiseAppendConstraint(pcopy, cp->lval, final, false);
750 
751  if (strcmp(cp->lval, "comment") == 0)
752  {
753  if (final.type != RVAL_TYPE_SCALAR)
754  {
755  Log(LOG_LEVEL_ERR, "Comments can only be scalar objects, not '%s' in '%s'",
756  RvalTypeToString(final.type), pp->promiser);
757  }
758  else
759  {
760  assert(final.item != NULL); /* it's SCALAR type */
761  DereferenceAndPutComment(pcopy, final.item);
762  }
763  }
764  }
765 
766  return pcopy;
767 }
768 
769 void PromiseRef(LogLevel level, const Promise *pp)
770 {
771  if (pp == NULL)
772  {
773  return;
774  }
775 
776  if (PromiseGetBundle(pp)->source_path)
777  {
778  Log(level, "Promise belongs to bundle '%s' in file '%s' near line %zu", PromiseGetBundle(pp)->name,
779  PromiseGetBundle(pp)->source_path, pp->offset.line);
780  }
781  else
782  {
783  Log(level, "Promise belongs to bundle '%s' near line %zu", PromiseGetBundle(pp)->name,
784  pp->offset.line);
785  }
786 
787  if (pp->comment)
788  {
789  Log(level, "Comment is '%s'", pp->comment);
790  }
791 
792  switch (pp->promisee.type)
793  {
794  case RVAL_TYPE_SCALAR:
795  Log(level, "This was a promise to '%s'", (char *)(pp->promisee.item));
796  break;
797  case RVAL_TYPE_LIST:
798  {
799  Writer *w = StringWriter();
800  RlistWrite(w, pp->promisee.item);
801  char *p = StringWriterClose(w);
802  Log(level, "This was a promise to '%s'", p);
803  free(p);
804  break;
805  }
806  default:
807  break;
808  }
809 }
810 
811 /*******************************************************************/
812 
813 /* Old legacy function from Enterprise, TODO remove static string. */
814 const char *PromiseID(const Promise *pp)
815 {
816  static char id[CF_MAXVARSIZE];
817  char vbuff[CF_MAXVARSIZE];
818  const char *handle = PromiseGetHandle(pp);
819 
820  if (handle)
821  {
822  snprintf(id, CF_MAXVARSIZE, "%s", CanonifyName(handle));
823  }
824  else if (pp && PromiseGetBundle(pp)->source_path)
825  {
826  snprintf(vbuff, CF_MAXVARSIZE, "%s", ReadLastNode(PromiseGetBundle(pp)->source_path));
827  snprintf(id, CF_MAXVARSIZE, "promise_%s_%zu", CanonifyName(vbuff), pp->offset.line);
828  }
829  else
830  {
831  snprintf(id, CF_MAXVARSIZE, "unlabelled_promise");
832  }
833 
834  return id;
835 }
void * xcalloc(size_t nmemb, size_t size)
Definition: alloc-mini.c:51
char * xstrdup(const char *str)
Definition: alloc-mini.c:56
int xasprintf(char **strp, const char *fmt,...)
Definition: alloc.c:71
void FatalError(const EvalContext *ctx, char *s,...)
Definition: audit.c:94
@ RVAL_TYPE_LIST
Definition: cf3.defs.h:607
@ RVAL_TYPE_SCALAR
Definition: cf3.defs.h:606
@ RVAL_TYPE_FNCALL
Definition: cf3.defs.h:608
@ RVAL_TYPE_NOPROMISEE
Definition: cf3.defs.h:610
@ CF_DATA_TYPE_BUNDLE
Definition: cf3.defs.h:378
@ CF_DATA_TYPE_STRING
Definition: cf3.defs.h:369
@ CF_DATA_TYPE_BODY
Definition: cf3.defs.h:377
#define CF_DONEPASSES
Definition: cf3.defs.h:344
void free(void *)
#define CF_MAXVARSIZE
Definition: definitions.h:36
Seq * EvalContextResolveBodyExpression(const EvalContext *ctx, const Policy *policy, const char *callee_reference, const char *callee_type)
Returns a Sequence of const Body* elements, first the body and then its parents.
const Bundle * EvalContextResolveBundleExpression(const EvalContext *ctx, const Policy *policy, const char *callee_reference, const char *callee_type)
Find a bundle for a bundle call, given a callee reference (in the form of ns:bundle),...
bool MissingDependencies(EvalContext *ctx, const Promise *pp)
Definition: eval_context.c:867
ExpressionValue CheckClassExpression(const EvalContext *ctx, const char *context)
Definition: eval_context.c:534
const Body * EvalContextFindFirstMatchingBody(const Policy *policy, const char *type, const char *namespace, const char *name)
void EvalContextStackPushBodyFrame(EvalContext *ctx, const Promise *caller, const Body *body, const Rlist *args)
void EvalContextStackPopFrame(EvalContext *ctx)
bool EvalContextVariablePutSpecial(EvalContext *ctx, SpecialScope scope, const char *lval, const void *value, DataType type, const char *tags)
int EvalContextGetPass(EvalContext *ctx)
static bool IsDefinedClass(const EvalContext *ctx, const char *context)
Definition: eval_context.h:213
Rval EvaluateFinalRval(EvalContext *ctx, const Policy *policy, const char *ns, const char *scope, Rval rval, bool forcelist, const Promise *pp)
Definition: expand.c:610
Rval ExpandBundleReference(EvalContext *ctx, const char *ns, const char *scope, Rval rval)
Definition: expand.c:484
Rval ExpandPrivateRval(EvalContext *ctx, const char *ns, const char *scope, const void *rval_item, RvalType rval_type)
Definition: expand.c:313
char * CanonifyName(const char *str)
Definition: files_names.c:483
const char * ReadLastNode(const char *str)
Definition: files_names.c:539
#define NULL
Definition: getopt1.c:56
JsonElement * JsonObjectCreate(const size_t initialCapacity)
Create a new JSON object.
Definition: json.c:880
void JsonDestroy(JsonElement *const element)
Destroy a JSON element.
Definition: json.c:386
void JsonObjectAppendString(JsonElement *const object, const char *const key, const char *const value)
Append a string field to an object.
Definition: json.c:1055
LogLevel
Definition: log.h:30
LogLevel LogGetGlobalLevel(void)
Definition: logging.c:581
void Log(LogLevel level, const char *fmt,...)
Definition: logging.c:409
@ LOG_LEVEL_ERR
Definition: logging.h:42
@ LOG_LEVEL_DEBUG
Definition: logging.h:47
@ LOG_LEVEL_VERBOSE
Definition: logging.h:46
ExpressionValue
@ EXPRESSION_VALUE_ERROR
@ EXPRESSION_VALUE_TRUE
@ EXPRESSION_VALUE_FALSE
void xsnprintf(char *str, size_t str_size, const char *format,...)
Definition: misc_lib.c:114
#define CF_ASSERT(condition,...)
Definition: misc_lib.h:51
Constraint * PromiseGetConstraint(const Promise *pp, const char *lval)
Get the first effective constraint from the promise, also does some checking.
Definition: policy.c:2944
const Policy * PolicyFromPromise(const Promise *promise)
Convenience function to get the policy object associated with a promise.
Definition: policy.c:477
const char * PromiseGetHandle(const Promise *pp)
Return handle of the promise.
Definition: policy.c:2713
const Policy * PromiseGetPolicy(const Promise *pp)
Definition: policy.c:2676
const Bundle * PromiseGetBundle(const Promise *pp)
Definition: policy.c:2671
Constraint * PromiseAppendConstraint(Promise *pp, const char *lval, Rval rval, bool references_body)
Definition: policy.c:1506
void ConstraintDestroy(Constraint *cp)
Definition: policy.c:2461
bool PromiseBundleOrBodyConstraintExists(const EvalContext *ctx, const char *lval, const Promise *pp)
Definition: policy.c:2593
@ POLICY_ELEMENT_TYPE_PROMISE
Definition: policy.h:41
static void DereferenceAndPutComment(Promise *pp, const char *comment)
Definition: promises.c:543
void CopyBodyConstraintsToPromise(EvalContext *ctx, Promise *pp, const Body *bp)
Definition: promises.c:42
Promise * ExpandDeRefPromise(EvalContext *ctx, const Promise *pp, bool *excluded)
Definition: promises.c:566
static bool EvaluateConstraintIteration(EvalContext *ctx, const Constraint *cp, Rval *rval_out)
Definition: promises.c:467
static void AddDefaultBodiesToPromise(EvalContext *ctx, Promise *promise, const PromiseTypeSyntax *syntax)
Definition: promises.c:436
void PromiseRef(LogLevel level, const Promise *pp)
Definition: promises.c:769
const char * PromiseID(const Promise *pp)
Definition: promises.c:814
static JsonElement * GetBodyRewriter(const EvalContext *ctx, const Body *current_body, const Rval *called_rval, bool in_inheritance_chain)
Definition: promises.c:63
static void AppendExpandedBodies(EvalContext *ctx, Promise *pcopy, const Seq *bodies_and_args, bool flatten_slists, bool expand_body_vars)
Definition: promises.c:123
Promise * DeRefCopyPromise(EvalContext *ctx, const Promise *pp)
Definition: promises.c:205
static ExpressionValue CheckVarClassExpression(const EvalContext *ctx, const Constraint *cp, Promise *pcopy)
Helper function to determine whether the Rval of ifvarclass/if/unless is defined. If the Rval is a fu...
Definition: promises.c:494
Rval RvalCopyRewriter(Rval rval, JsonElement *map)
Definition: rlist.c:469
char * RlistScalarValue(const Rlist *rlist)
Definition: rlist.c:83
FnCall * RvalFnCallValue(Rval rval)
Definition: rlist.c:141
char * RvalScalarValue(Rval rval)
Definition: rlist.c:129
void RlistWrite(Writer *writer, const Rlist *list)
Definition: rlist.c:1318
char * RvalToString(Rval rval)
Definition: rlist.c:1396
void RvalDestroy(Rval rval)
Definition: rlist.c:940
const char * RvalTypeToString(RvalType type)
Definition: rlist.c:176
int RlistLen(const Rlist *start)
Definition: rlist.c:672
void RlistFlatten(EvalContext *ctx, Rlist **list)
Flattens an Rlist by expanding naked scalar list-variable members. Flattening is only one-level deep.
Definition: rlist.c:1534
Rval RvalCopy(Rval rval)
Definition: rlist.c:474
@ SPECIAL_SCOPE_THIS
Definition: scope.h:39
size_t SeqLength(const Seq *seq)
Length of the sequence.
Definition: sequence.c:354
Seq * SeqNew(size_t initialCapacity, void(ItemDestroy)(void *item))
Definition: sequence.c:31
void SeqReverse(Seq *seq)
Reverses the order of the sequence.
Definition: sequence.c:327
void SeqDestroy(Seq *seq)
Destroy an existing Sequence.
Definition: sequence.c:60
static void * SeqAt(const Seq *seq, int i)
Definition: sequence.h:57
char * StringConcatenate(size_t count, const char *first,...)
Definition: string_lib.c:348
#define SAFENULL(str)
Definition: string_lib.h:52
char * strstr(const char *haystack, const char *needle)
Definition: strstr.c:35
Definition: policy.h:85
char * type
Definition: policy.h:88
char * ns
Definition: policy.h:90
Seq * conlist
Definition: policy.h:93
Rlist * args
Definition: policy.h:91
char * name
Definition: policy.h:89
Definition: policy.h:70
char * type
Definition: policy.h:73
const char * lval
Definition: cf3.defs.h:656
const DataType dtype
Definition: cf3.defs.h:657
Rval rval
Definition: policy.h:133
SourceOffset offset
Definition: policy.h:138
Promise * promise
Definition: policy.h:128
char * classes
Definition: policy.h:135
char * lval
Definition: policy.h:132
bool references_body
Definition: policy.h:136
union Constraint_::@22 parent
PolicyElementType type
Definition: policy.h:126
char * name
Definition: fncall.h:32
Rlist * args
Definition: fncall.h:33
Definition: policy.h:53
const ConstraintSyntax * constraints
Definition: cf3.defs.h:679
Bundle * parent_bundle
Definition: policy.h:101
char * name
Definition: policy.h:103
PromiseType * parent_promise_type
Definition: policy.h:111
const Promise * org_pp
Definition: policy.h:119
Seq * conlist
Definition: policy.h:117
char * comment
Definition: policy.h:114
char * promiser
Definition: policy.h:115
char * classes
Definition: policy.h:113
Rval promisee
Definition: policy.h:116
SourceOffset offset
Definition: policy.h:121
Definition: rlist.h:35
Rlist * next
Definition: rlist.h:37
Definition: cf3.defs.h:614
RvalType type
Definition: cf3.defs.h:616
void * item
Definition: cf3.defs.h:615
Sequence data-structure.
Definition: sequence.h:50
size_t line
Definition: policy.h:65
Definition: writer.c:45
const PromiseTypeSyntax * PromiseTypeSyntaxGet(const char *bundle_type, const char *promise_type)
Definition: syntax.c:75
DataType ExpectedDataType(const char *lvalname)
Definition: syntax.c:203
char * StringWriterClose(Writer *writer)
Definition: writer.c:262
Writer * StringWriter(void)
Definition: writer.c:67