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