void PolicyResolve(EvalContext *ctx, const Policy *policy, GenericAgentConfig *config) { for (size_t i = 0; i < SeqLength(policy->bundles); i++) { Bundle *bundle = SeqAt(policy->bundles, i); if (strcmp("common", bundle->type) == 0) { EvalContextStackPushBundleFrame(ctx, bundle, NULL, false); BundleResolve(ctx, bundle); EvalContextStackPopFrame(ctx); } } for (size_t i = 0; i < SeqLength(policy->bundles); i++) { Bundle *bundle = SeqAt(policy->bundles, i); if (strcmp("common", bundle->type) != 0) { EvalContextStackPushBundleFrame(ctx, bundle, NULL, false); BundleResolve(ctx, bundle); EvalContextStackPopFrame(ctx); } } for (size_t i = 0; i < SeqLength(policy->bodies); i++) { Body *bdp = SeqAt(policy->bodies, i); if (strcmp(bdp->name, "control") == 0) { ResolveControlBody(ctx, config, bdp); } } }
void PolicyResolve(EvalContext *ctx, const Policy *policy, GenericAgentConfig *config) { /* PRE-EVAL: common bundles: classes,vars. */ for (size_t i = 0; i < SeqLength(policy->bundles); i++) { Bundle *bundle = SeqAt(policy->bundles, i); if (strcmp("common", bundle->type) == 0) { EvalContextStackPushBundleFrame(ctx, bundle, NULL, false); BundleResolve(ctx, bundle); /* PRE-EVAL classes,vars */ EvalContextStackPopFrame(ctx); } } /* * HACK: yet another pre-eval pass here, WHY? TODO remove, but test fails: * 00_basics/03_bodies/dynamic_inputs_findfiles.cf */ #if 1 /* PRE-EVAL: non-common bundles: only vars. */ for (size_t i = 0; i < SeqLength(policy->bundles); i++) { Bundle *bundle = SeqAt(policy->bundles, i); if (strcmp("common", bundle->type) != 0) { EvalContextStackPushBundleFrame(ctx, bundle, NULL, false); BundleResolve(ctx, bundle); /* PRE-EVAL vars */ EvalContextStackPopFrame(ctx); } } #endif for (size_t i = 0; i < SeqLength(policy->bodies); i++) { Body *bdp = SeqAt(policy->bodies, i); if (strcmp(bdp->name, "control") == 0) { ResolveControlBody(ctx, config, bdp); } /* Collect all package managers data from policy as we don't know yet * which ones we will use. */ else if (strcmp(bdp->type, "package_module") == 0) { ResolvePackageManagerBody(ctx, bdp); } } }
static void test_expand_promise_array_with_scalar_arg(void **state) { EvalContext *ctx = *state; { VarRef *lval = VarRefParse("default:bundle.foo[one]"); EvalContextVariablePut(ctx, lval, "first", CF_DATA_TYPE_STRING, NULL); VarRefDestroy(lval); } { VarRef *lval = VarRefParse("default:bundle.bar"); EvalContextVariablePut(ctx, lval, "one", CF_DATA_TYPE_STRING, NULL); VarRefDestroy(lval); } Policy *policy = PolicyNew(); Bundle *bundle = PolicyAppendBundle(policy, NamespaceDefault(), "bundle", "agent", NULL, NULL); PromiseType *promise_type = BundleAppendPromiseType(bundle, "dummy"); Promise *promise = PromiseTypeAppendPromise(promise_type, "$(foo[$(bar)])", (Rval) { NULL, RVAL_TYPE_NOPROMISEE }, "any", NULL); EvalContextStackPushBundleFrame(ctx, bundle, NULL, false); EvalContextStackPushPromiseTypeFrame(ctx, promise_type); ExpandPromise(ctx, promise, actuator_expand_promise_array_with_scalar_arg, NULL); EvalContextStackPopFrame(ctx); EvalContextStackPopFrame(ctx); PolicyDestroy(policy); }
static void test_expand_promise_slist(void **state) { actuator_state = 0; EvalContext *ctx = *state; { VarRef *lval = VarRefParse("default:bundle.foo"); Rlist *list = NULL; RlistAppendScalar(&list, "a"); RlistAppendScalar(&list, "b"); EvalContextVariablePut(ctx, lval, list, CF_DATA_TYPE_STRING_LIST, NULL); RlistDestroy(list); VarRefDestroy(lval); } Policy *policy = PolicyNew(); Bundle *bundle = PolicyAppendBundle(policy, NamespaceDefault(), "bundle", "agent", NULL, NULL); PromiseType *promise_type = BundleAppendPromiseType(bundle, "dummy"); Promise *promise = PromiseTypeAppendPromise(promise_type, "$(foo)", (Rval) { NULL, RVAL_TYPE_NOPROMISEE }, "any", NULL); EvalContextStackPushBundleFrame(ctx, bundle, NULL, false); EvalContextStackPushPromiseTypeFrame(ctx, promise_type); ExpandPromise(ctx, promise, actuator_expand_promise_slist, NULL); EvalContextStackPopFrame(ctx); EvalContextStackPopFrame(ctx); assert_int_equal(2, actuator_state); PolicyDestroy(policy); }
static PromiseResult RenderTemplateCFEngine(EvalContext *ctx, const Promise *pp, const Rlist *bundle_args, Attributes a, EditContext *edcontext) { PromiseResult result = PROMISE_RESULT_NOOP; Policy *tmp_policy = PolicyNew(); Bundle *bp = NULL; if ((bp = MakeTemporaryBundleFromTemplate(ctx, tmp_policy, a, pp, &result))) { BannerSubBundle(bp, bundle_args); a.haveeditline = true; EvalContextStackPushBundleFrame(ctx, bp, bundle_args, a.edits.inherit); BundleResolve(ctx, bp); ScheduleEditLineOperations(ctx, bp, a, pp, edcontext); EvalContextStackPopFrame(ctx); if (edcontext->num_edits == 0) { edcontext->num_edits++; } } PolicyDestroy(tmp_policy); return result; }
static void EvaluateBundle(EvalContext *ctx, const Bundle *bp, const char * const *seq) { EvalContextStackPushBundleFrame(ctx, bp, NULL, false); for (int type = 0; seq[type] != NULL; type++) { const PromiseType *sp = BundleGetPromiseType((Bundle *)bp, seq[type]); /* Some promise types might not be there. */ if (!sp || SeqLength(sp->promises) == 0) { Log(LOG_LEVEL_DEBUG, "No promise type %s in bundle %s", seq[type], bp->name); continue; } EvalContextStackPushPromiseTypeFrame(ctx, sp); for (size_t ppi = 0; ppi < SeqLength(sp->promises); ppi++) { Promise *pp = SeqAt(sp->promises, ppi); ExpandPromise(ctx, pp, KeepServerPromise, NULL); } EvalContextStackPopFrame(ctx); } /* Check if we are having some other promise types which we * should evaluate. THIS IS ONLY FOR BACKWARD COMPATIBILITY! */ for (size_t j = 0; j < SeqLength(bp->promise_types); j++) { PromiseType *sp = SeqAt(bp->promise_types, j); /* Skipping evaluation of promise as this was evaluated in * loop above. */ if (!IsPromiseTypeNotInTypeSequence(sp->name, seq)) { Log(LOG_LEVEL_DEBUG, "Skipping subsequent evaluation of " "promise type %s in bundle %s", sp->name, bp->name); continue; } Log(LOG_LEVEL_WARNING, "Trying to evaluate unsupported/obsolete " "promise type %s in %s bundle %s", sp->name, bp->type, bp->name); EvalContextStackPushPromiseTypeFrame(ctx, sp); for (size_t ppi = 0; ppi < SeqLength(sp->promises); ppi++) { Promise *pp = SeqAt(sp->promises, ppi); ExpandPromise(ctx, pp, KeepServerPromise, NULL); } EvalContextStackPopFrame(ctx); } EvalContextStackPopFrame(ctx); }
static void KeepPromiseBundles(EvalContext *ctx, const Policy *policy) { /* Dial up the generic promise expansion with a callback */ CleanReportBookFilterSet(); for (size_t i = 0; i < SeqLength(policy->bundles); i++) { Bundle *bp = SeqAt(policy->bundles, i); if ((strcmp(bp->type, CF_AGENTTYPES[AGENT_TYPE_SERVER]) == 0) || (strcmp(bp->type, CF_AGENTTYPES[AGENT_TYPE_COMMON]) == 0)) { if (RlistLen(bp->args) > 0) { Log(LOG_LEVEL_WARNING, "Cannot implicitly evaluate bundle '%s %s', as this bundle takes arguments.", bp->type, bp->name); continue; } BannerBundle(bp, NULL); EvalContextStackPushBundleFrame(ctx, bp, NULL, false); for (size_t j = 0; j < SeqLength(bp->promise_types); j++) { PromiseType *sp = SeqAt(bp->promise_types, j); if ((strcmp(sp->name, "access") != 0) && (strcmp(sp->name, "roles") != 0)) { continue; } BannerPromiseType(bp->name, sp->name, 0); EvalContextStackPushPromiseTypeFrame(ctx, sp); for (size_t ppi = 0; ppi < SeqLength(sp->promises); ppi++) { Promise *pp = SeqAt(sp->promises, ppi); ExpandPromise(ctx, pp, KeepServerPromise, NULL); } EvalContextStackPopFrame(ctx); } EvalContextStackPopFrame(ctx); } } }
static void KeepPromiseBundles(EvalContext *ctx, Policy *policy, const ReportContext *report_context) { /* Dial up the generic promise expansion with a callback */ for (size_t i = 0; i < SeqLength(policy->bundles); i++) { Bundle *bp = SeqAt(policy->bundles, i); if ((strcmp(bp->type, CF_AGENTTYPES[AGENT_TYPE_SERVER]) == 0) || (strcmp(bp->type, CF_AGENTTYPES[AGENT_TYPE_COMMON]) == 0)) { if (RlistLen(bp->args) > 0) { CfOut(OUTPUT_LEVEL_ERROR, "", "Cannot implicitly evaluate bundle %s %s, as this bundle takes arguments.", bp->type, bp->name); continue; } BannerBundle(bp, NULL); for (size_t j = 0; j < SeqLength(bp->promise_types); j++) { PromiseType *sp = SeqAt(bp->promise_types, j); if ((strcmp(sp->name, "access") != 0) && (strcmp(sp->name, "roles") != 0)) { continue; } BannerPromiseType(bp->name, sp->name, 0); EvalContextStackPushBundleFrame(ctx, bp, false); ScopeAugment(ctx, bp, NULL); for (size_t ppi = 0; ppi < SeqLength(sp->promises); ppi++) { Promise *pp = SeqAt(sp->promises, ppi); ExpandPromise(ctx, pp, KeepServerPromise, report_context); } EvalContextStackPopFrame(ctx); } } } }
static void VerifyPromises(EvalContext *ctx, Policy *policy, GenericAgentConfig *config) { /* Now look once through ALL the bundles themselves */ for (size_t i = 0; i < SeqLength(policy->bundles); i++) { Bundle *bp = SeqAt(policy->bundles, i); EvalContextStackPushBundleFrame(ctx, bp, NULL, false); for (size_t j = 0; j < SeqLength(bp->promise_types); j++) { PromiseType *sp = SeqAt(bp->promise_types, j); for (size_t ppi = 0; ppi < SeqLength(sp->promises); ppi++) { Promise *pp = SeqAt(sp->promises, ppi); ExpandPromise(ctx, pp, CommonEvalPromise, NULL); } } EvalContextStackPopFrame(ctx); } PolicyResolve(ctx, policy, config); // TODO: need to move this inside PolicyCheckRunnable eventually. if (!config->bundlesequence && config->check_runnable) { // only verify policy-defined bundlesequence for cf-agent, cf-promises if ((config->agent_type == AGENT_TYPE_AGENT) || (config->agent_type == AGENT_TYPE_COMMON)) { if (!VerifyBundleSequence(ctx, policy, config)) { FatalError(ctx, "Errors in promise bundles"); } } } }
static void test_map_iterators_from_rval_naked_list_var_namespace(void **state) { EvalContext *ctx = *state; Policy *p = PolicyNew(); Bundle *bp = PolicyAppendBundle(p, "ns", "scope", "agent", NULL, NULL); { Rlist *list = NULL; RlistAppend(&list, "jersey", RVAL_TYPE_SCALAR); VarRef *lval = VarRefParse("ns:scope.jwow"); EvalContextVariablePut(ctx, lval, list, CF_DATA_TYPE_STRING_LIST, NULL); VarRefDestroy(lval); RlistDestroy(list); } EvalContextStackPushBundleFrame(ctx, bp, NULL, false); { Rlist *lists = NULL; Rlist *scalars = NULL; Rlist *containers = NULL; MapIteratorsFromRval(ctx, bp, (Rval) { "${jwow}", RVAL_TYPE_SCALAR }, &scalars, &lists, &containers); assert_int_equal(1, RlistLen(lists)); assert_string_equal("jwow", RlistScalarValue(lists)); assert_int_equal(0, RlistLen(scalars)); assert_int_equal(0, RlistLen(containers)); RlistDestroy(lists); } { Rlist *lists = NULL; Rlist *scalars = NULL; Rlist *containers = NULL; char *str = xstrdup("${scope.jwow}"); MapIteratorsFromRval(ctx, bp, (Rval) { str, RVAL_TYPE_SCALAR }, &scalars, &lists, &containers); assert_string_equal("${scope#jwow}", str); free(str); assert_int_equal(1, RlistLen(lists)); assert_string_equal("scope#jwow", RlistScalarValue(lists)); assert_int_equal(0, RlistLen(scalars)); assert_int_equal(0, RlistLen(containers)); RlistDestroy(lists); } { Rlist *lists = NULL; Rlist *scalars = NULL; Rlist *containers = NULL; char *str = xstrdup("${ns:scope.jwow}"); MapIteratorsFromRval(ctx, bp, (Rval) { str, RVAL_TYPE_SCALAR }, &scalars, &lists, &containers); assert_string_equal("${ns*scope#jwow}", str); free(str); assert_int_equal(1, RlistLen(lists)); assert_string_equal("ns*scope#jwow", RlistScalarValue(lists)); assert_int_equal(0, RlistLen(scalars)); assert_int_equal(0, RlistLen(containers)); RlistDestroy(lists); } EvalContextStackPopFrame(ctx); PolicyDestroy(p); }
static void test_class_persistence(void) { EvalContext *ctx = EvalContextNew(); // simulate old version { CF_DB *dbp; PersistentClassInfo i; assert_true(OpenDB(&dbp, dbid_state)); i.expires = UINT_MAX; i.policy = CONTEXT_STATE_POLICY_RESET; WriteDB(dbp, "old", &i, sizeof(PersistentClassInfo)); CloseDB(dbp); } // e.g. by monitoring EvalContextHeapPersistentSave(ctx, "class1", 3, CONTEXT_STATE_POLICY_PRESERVE, "a,b"); // e.g. by a class promise in a bundle with a namespace { Policy *p = PolicyNew(); Bundle *bp = PolicyAppendBundle(p, "ns1", "bundle1", "agent", NULL, NULL); EvalContextStackPushBundleFrame(ctx, bp, NULL, false); EvalContextHeapPersistentSave(ctx, "class2", 5, CONTEXT_STATE_POLICY_PRESERVE, "x"); EvalContextStackPopFrame(ctx); PolicyDestroy(p); } EvalContextHeapPersistentLoadAll(ctx); { const Class *cls = EvalContextClassGet(ctx, "default", "old"); assert_true(cls != NULL); assert_string_equal("old", cls->name); assert_true(cls->tags != NULL); assert_int_equal(1, StringSetSize(cls->tags)); assert_true(StringSetContains(cls->tags, "source=persistent")); } { const Class *cls = EvalContextClassGet(ctx, "default", "class1"); assert_true(cls != NULL); assert_string_equal("class1", cls->name); assert_true(cls->tags != NULL); assert_int_equal(3, StringSetSize(cls->tags)); assert_true(StringSetContains(cls->tags, "source=persistent")); assert_true(StringSetContains(cls->tags, "a")); assert_true(StringSetContains(cls->tags, "b")); } { const Class *cls = EvalContextClassGet(ctx, "ns1", "class2"); assert_true(cls != NULL); assert_string_equal("ns1", cls->ns); assert_string_equal("class2", cls->name); assert_true(cls->tags != NULL); assert_int_equal(2, StringSetSize(cls->tags)); assert_true(StringSetContains(cls->tags, "source=persistent")); assert_true(StringSetContains(cls->tags, "x")); } EvalContextDestroy(ctx); }
PromiseResult VerifyMethod(EvalContext *ctx, const Rval call, Attributes a, const Promise *pp) { assert(a.havebundle); const Rlist *args = NULL; Buffer *method_name = BufferNew(); switch (call.type) { case RVAL_TYPE_FNCALL: { const FnCall *fp = RvalFnCallValue(call); ExpandScalar(ctx, PromiseGetBundle(pp)->ns, PromiseGetBundle(pp)->name, fp->name, method_name); args = fp->args; } break; case RVAL_TYPE_SCALAR: { ExpandScalar(ctx, PromiseGetBundle(pp)->ns, PromiseGetBundle(pp)->name, RvalScalarValue(call), method_name); args = NULL; } break; default: BufferDestroy(method_name); return PROMISE_RESULT_NOOP; } char lockname[CF_BUFSIZE]; GetLockName(lockname, "method", pp->promiser, args); CfLock thislock = AcquireLock(ctx, lockname, VUQNAME, CFSTARTTIME, a.transaction, pp, false); if (thislock.lock == NULL) { BufferDestroy(method_name); return PROMISE_RESULT_SKIPPED; } PromiseBanner(pp); const Bundle *bp = EvalContextResolveBundleExpression(ctx, PromiseGetPolicy(pp), BufferData(method_name), "agent"); if (!bp) { bp = EvalContextResolveBundleExpression(ctx, PromiseGetPolicy(pp), BufferData(method_name), "common"); } PromiseResult result = PROMISE_RESULT_NOOP; if (bp) { if (a.transaction.action == cfa_warn) // don't skip for dry-runs (ie ignore DONTDO) { result = PROMISE_RESULT_WARN; cfPS(ctx, LOG_LEVEL_ERR, result, pp, a, "Bundle '%s' should be invoked, but only a warning was promised!", BufferData(method_name)); } else { BannerSubBundle(bp, args); EvalContextStackPushBundleFrame(ctx, bp, args, a.inherit); BundleResolve(ctx, bp); result = ScheduleAgentOperations(ctx, bp); GetReturnValue(ctx, bp, pp); EvalContextStackPopFrame(ctx); switch (result) { case PROMISE_RESULT_SKIPPED: // if a bundle returns 'skipped', meaning that all promises were locked in the bundle, // we explicitly consider the method 'kept' result = PROMISE_RESULT_NOOP; // intentional fallthru case PROMISE_RESULT_NOOP: cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "Method '%s' verified", bp->name); break; case PROMISE_RESULT_WARN: cfPS(ctx, LOG_LEVEL_WARNING, PROMISE_RESULT_WARN, pp, a, "Method '%s' invoked repairs, but only warnings promised", bp->name); break; case PROMISE_RESULT_CHANGE: cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_CHANGE, pp, a, "Method '%s' invoked repairs", bp->name); break; case PROMISE_RESULT_FAIL: case PROMISE_RESULT_DENIED: cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Method '%s' failed in some repairs", bp->name); break; default: // PROMISE_RESULT_INTERRUPTED, TIMEOUT cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_FAIL, pp, a, "Method '%s' aborted in some repairs", bp->name); break; } } for (const Rlist *rp = bp->args; rp; rp = rp->next) { const char *lval = RlistScalarValue(rp); VarRef *ref = VarRefParseFromBundle(lval, bp); EvalContextVariableRemove(ctx, ref); VarRefDestroy(ref); } } else { if (IsCf3VarString(BufferData(method_name))) { Log(LOG_LEVEL_ERR, "A variable seems to have been used for the name of the method. In this case, the promiser also needs to contain the unique name of the method"); } cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "A method attempted to use a bundle '%s' that was apparently not defined", BufferData(method_name)); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); } YieldCurrentLock(thislock); BufferDestroy(method_name); return result; }
PromiseResult ScheduleEditOperation(EvalContext *ctx, char *filename, Attributes a, const Promise *pp) { void *vp; FnCall *fp; Rlist *args = NULL; char edit_bundle_name[CF_BUFSIZE], lockname[CF_BUFSIZE]; CfLock thislock; snprintf(lockname, CF_BUFSIZE - 1, "fileedit-%s", filename); thislock = AcquireLock(ctx, lockname, VUQNAME, CFSTARTTIME, a.transaction, pp, false); if (thislock.lock == NULL) { return PROMISE_RESULT_SKIPPED; } EditContext *edcontext = NewEditContext(filename, a); PromiseResult result = PROMISE_RESULT_NOOP; if (edcontext == NULL) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "File '%s' was marked for editing but could not be opened", filename); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); goto exit; } const Policy *policy = PolicyFromPromise(pp); if (a.haveeditline) { if ((vp = PromiseGetConstraintAsRval(pp, "edit_line", RVAL_TYPE_FNCALL))) { fp = (FnCall *) vp; strcpy(edit_bundle_name, fp->name); args = fp->args; } else if ((vp = PromiseGetConstraintAsRval(pp, "edit_line", RVAL_TYPE_SCALAR))) { strcpy(edit_bundle_name, (char *) vp); args = NULL; } else { goto exit; } Log(LOG_LEVEL_VERBOSE, "Handling file edits in edit_line bundle '%s'", edit_bundle_name); const Bundle *bp = EvalContextResolveBundleExpression(ctx, policy, edit_bundle_name, "edit_line"); if (bp) { BannerSubBundle(bp, args); EvalContextStackPushBundleFrame(ctx, bp, args, a.edits.inherit); BundleResolve(ctx, bp); ScheduleEditLineOperations(ctx, bp, a, pp, edcontext); EvalContextStackPopFrame(ctx); } else { Log(LOG_LEVEL_ERR, "Did not find bundle '%s' for edit operation", edit_bundle_name); } } if (a.haveeditxml) { if ((vp = PromiseGetConstraintAsRval(pp, "edit_xml", RVAL_TYPE_FNCALL))) { fp = (FnCall *) vp; strcpy(edit_bundle_name, fp->name); args = fp->args; } else if ((vp = PromiseGetConstraintAsRval(pp, "edit_xml", RVAL_TYPE_SCALAR))) { strcpy(edit_bundle_name, (char *) vp); args = NULL; } else { goto exit; } Log(LOG_LEVEL_VERBOSE, "Handling file edits in edit_xml bundle '%s'", edit_bundle_name); const Bundle *bp = EvalContextResolveBundleExpression(ctx, policy, edit_bundle_name, "edit_xml"); if (bp) { BannerSubBundle(bp, args); EvalContextStackPushBundleFrame(ctx, bp, args, a.edits.inherit); BundleResolve(ctx, bp); ScheduleEditXmlOperations(ctx, bp, a, pp, edcontext); EvalContextStackPopFrame(ctx); } } if (a.edit_template) { if (!a.template_method || strcmp("cfengine", a.template_method) == 0) { PromiseResult render_result = RenderTemplateCFEngine(ctx, pp, args, a, edcontext); result = PromiseResultUpdate(result, render_result); } else if (strcmp("mustache", a.template_method) == 0) { PromiseResult render_result = RenderTemplateMustache(ctx, pp, a, edcontext); result = PromiseResultUpdate(result, render_result); } } exit: FinishEditContext(ctx, edcontext, a, pp, &result); YieldCurrentLock(thislock); return result; }
Policy *LoadPolicy(EvalContext *ctx, GenericAgentConfig *config) { StringSet *parsed_files_and_checksums = StringSetNew(); StringSet *failed_files = StringSetNew(); Policy *policy = LoadPolicyFile(ctx, config, config->input_file, parsed_files_and_checksums, failed_files); if (StringSetSize(failed_files) > 0) { Log(LOG_LEVEL_ERR, "There are syntax errors in policy files"); exit(EXIT_FAILURE); } StringSetDestroy(parsed_files_and_checksums); StringSetDestroy(failed_files); { Seq *errors = SeqNew(100, PolicyErrorDestroy); if (PolicyCheckPartial(policy, errors)) { if (!config->bundlesequence && (PolicyIsRunnable(policy) || config->check_runnable)) { Log(LOG_LEVEL_VERBOSE, "Running full policy integrity checks"); PolicyCheckRunnable(ctx, policy, errors, config->ignore_missing_bundles); } } if (SeqLength(errors) > 0) { Writer *writer = FileWriter(stderr); for (size_t i = 0; i < errors->length; i++) { PolicyErrorWrite(writer, errors->data[i]); } WriterClose(writer); exit(EXIT_FAILURE); // TODO: do not exit } SeqDestroy(errors); } if (LogGetGlobalLevel() >= LOG_LEVEL_VERBOSE) { ShowContext(ctx); } if (policy) { for (size_t i = 0; i < SeqLength(policy->bundles); i++) { Bundle *bp = SeqAt(policy->bundles, i); EvalContextStackPushBundleFrame(ctx, bp, NULL, false); for (size_t j = 0; j < SeqLength(bp->promise_types); j++) { PromiseType *sp = SeqAt(bp->promise_types, j); EvalContextStackPushPromiseTypeFrame(ctx, sp); for (size_t ppi = 0; ppi < SeqLength(sp->promises); ppi++) { Promise *pp = SeqAt(sp->promises, ppi); ExpandPromise(ctx, pp, CommonEvalPromise, NULL); } EvalContextStackPopFrame(ctx); } EvalContextStackPopFrame(ctx); } PolicyResolve(ctx, policy, config); // TODO: need to move this inside PolicyCheckRunnable eventually. if (!config->bundlesequence && config->check_runnable) { // only verify policy-defined bundlesequence for cf-agent, cf-promises if ((config->agent_type == AGENT_TYPE_AGENT) || (config->agent_type == AGENT_TYPE_COMMON)) { if (!VerifyBundleSequence(ctx, policy, config)) { FatalError(ctx, "Errors in promise bundles: could not verify bundlesequence"); } } } } JsonElement *validated_doc = ReadReleaseIdFileFromInputs(); if (validated_doc) { const char *release_id = JsonObjectGetAsString(validated_doc, "releaseId"); if (release_id) { policy->release_id = xstrdup(release_id); } JsonDestroy(validated_doc); } return policy; }
int VerifyMethod(EvalContext *ctx, char *attrname, Attributes a, Promise *pp) { Bundle *bp; void *vp; FnCall *fp; char method_name[CF_EXPANDSIZE], qualified_method[CF_BUFSIZE], *method_deref; Rlist *params = NULL; int retval = false; CfLock thislock; char lockname[CF_BUFSIZE]; if (a.havebundle) { if ((vp = ConstraintGetRvalValue(ctx, attrname, pp, RVAL_TYPE_FNCALL))) { fp = (FnCall *) vp; ExpandScalar(ctx, PromiseGetBundle(pp)->name, fp->name, method_name); params = fp->args; } else if ((vp = ConstraintGetRvalValue(ctx, attrname, pp, RVAL_TYPE_SCALAR))) { ExpandScalar(ctx, PromiseGetBundle(pp)->name, (char *) vp, method_name); params = NULL; } else { return false; } } GetLockName(lockname, "method", pp->promiser, params); thislock = AcquireLock(ctx, lockname, VUQNAME, CFSTARTTIME, a.transaction, pp, false); if (thislock.lock == NULL) { return false; } PromiseBanner(pp); if (strncmp(method_name,"default:",strlen("default:")) == 0) // CF_NS == ':' { method_deref = strchr(method_name, CF_NS) + 1; } else if ((strchr(method_name, CF_NS) == NULL) && (strcmp(PromiseGetNamespace(pp), "default") != 0)) { snprintf(qualified_method, CF_BUFSIZE, "%s%c%s", PromiseGetNamespace(pp), CF_NS, method_name); method_deref = qualified_method; } else { method_deref = method_name; } bp = PolicyGetBundle(PolicyFromPromise(pp), NULL, "agent", method_deref); if (!bp) { bp = PolicyGetBundle(PolicyFromPromise(pp), NULL, "common", method_deref); } if (bp) { BannerSubBundle(bp, params); EvalContextStackPushBundleFrame(ctx, bp, a.inherit); ScopeClear(bp->name); BundleHashVariables(ctx, bp); ScopeAugment(ctx, bp, pp, params); retval = ScheduleAgentOperations(ctx, bp); GetReturnValue(ctx, bp->name, pp); EvalContextStackPopFrame(ctx); switch (retval) { case PROMISE_RESULT_FAIL: cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_FAIL, pp, a, "Method failed in some repairs or aborted"); break; case PROMISE_RESULT_CHANGE: cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_CHANGE, pp, a, "Method invoked repairs"); break; default: cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "Method verified"); break; } for (const Rlist *rp = bp->args; rp; rp = rp->next) { const char *lval = rp->item; ScopeDeleteScalar((VarRef) { NULL, bp->name, lval }); } } else { if (IsCf3VarString(method_name)) { Log(LOG_LEVEL_ERR, "A variable seems to have been used for the name of the method. In this case, the promiser also needs to contain the unique name of the method"); } if (bp && (bp->name)) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Method '%s' was used but was not defined", bp->name); } else { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "A method attempted to use a bundle '%s' that was apparently not defined", method_name); } } YieldCurrentLock(thislock); return retval; }
PromiseResult VerifyMethod(EvalContext *ctx, const Rval call, Attributes a, const Promise *pp) { const Rlist *args = NULL; Buffer *method_name = BufferNew(); switch (call.type) { case RVAL_TYPE_FNCALL: { const FnCall *fp = RvalFnCallValue(call); ExpandScalar(ctx, PromiseGetBundle(pp)->ns, PromiseGetBundle(pp)->name, fp->name, method_name); args = fp->args; int arg_index = 0; while (args) { ++arg_index; if (strcmp(args->val.item, CF_NULL_VALUE) == 0) { Log(LOG_LEVEL_DEBUG, "Skipping invokation of method '%s' due to null-values in argument '%d'", fp->name, arg_index); BufferDestroy(method_name); return PROMISE_RESULT_SKIPPED; } args = args->next; } args = fp->args; EvalContextSetBundleArgs(ctx, args); } break; case RVAL_TYPE_SCALAR: { ExpandScalar(ctx, PromiseGetBundle(pp)->ns, PromiseGetBundle(pp)->name, RvalScalarValue(call), method_name); args = NULL; } break; default: BufferDestroy(method_name); return PROMISE_RESULT_NOOP; } char lockname[CF_BUFSIZE]; GetLockName(lockname, "method", pp->promiser, args); CfLock thislock = AcquireLock(ctx, lockname, VUQNAME, CFSTARTTIME, a.transaction, pp, false); if (thislock.lock == NULL) { BufferDestroy(method_name); return PROMISE_RESULT_SKIPPED; } PromiseBanner(ctx, pp); const Bundle *bp = EvalContextResolveBundleExpression(ctx, PromiseGetPolicy(pp), BufferData(method_name), "agent"); if (!bp) { bp = EvalContextResolveBundleExpression(ctx, PromiseGetPolicy(pp), BufferData(method_name), "common"); } PromiseResult result = PROMISE_RESULT_NOOP; if (bp) { if (a.transaction.action == cfa_warn) // don't skip for dry-runs (ie ignore DONTDO) { result = PROMISE_RESULT_WARN; cfPS(ctx, LOG_LEVEL_WARNING, result, pp, a, "Bundle '%s' should be invoked, but only a warning was promised!", BufferData(method_name)); } else { BundleBanner(bp, args); EvalContextStackPushBundleFrame(ctx, bp, args, a.inherit); /* Clear all array-variables that are already set in the sub-bundle. Otherwise, array-data accumulates between multiple bundle evaluations. Note: for bundles invoked multiple times via bundlesequence, array data *does* accumulate. */ VariableTableIterator *iter = EvalContextVariableTableIteratorNew(ctx, bp->ns, bp->name, NULL); Variable *var; while ((var = VariableTableIteratorNext(iter))) { if (!var->ref->num_indices) { continue; } EvalContextVariableRemove(ctx, var->ref); } VariableTableIteratorDestroy(iter); BundleResolve(ctx, bp); result = ScheduleAgentOperations(ctx, bp); GetReturnValue(ctx, bp, pp); EvalContextStackPopFrame(ctx); switch (result) { case PROMISE_RESULT_SKIPPED: // if a bundle returns 'skipped', meaning that all promises were locked in the bundle, // we explicitly consider the method 'kept' result = PROMISE_RESULT_NOOP; // intentional fallthru case PROMISE_RESULT_NOOP: cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "Method '%s' verified", bp->name); break; case PROMISE_RESULT_WARN: cfPS(ctx, LOG_LEVEL_WARNING, PROMISE_RESULT_WARN, pp, a, "Method '%s' invoked repairs, but only warnings promised", bp->name); break; case PROMISE_RESULT_CHANGE: cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_CHANGE, pp, a, "Method '%s' invoked repairs", bp->name); break; case PROMISE_RESULT_FAIL: case PROMISE_RESULT_DENIED: cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Method '%s' failed in some repairs", bp->name); break; default: // PROMISE_RESULT_INTERRUPTED, TIMEOUT cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_FAIL, pp, a, "Method '%s' aborted in some repairs", bp->name); break; } } for (const Rlist *rp = bp->args; rp; rp = rp->next) { const char *lval = RlistScalarValue(rp); VarRef *ref = VarRefParseFromBundle(lval, bp); EvalContextVariableRemove(ctx, ref); VarRefDestroy(ref); } } else { if (IsCf3VarString(BufferData(method_name))) { Log(LOG_LEVEL_ERR, "A variable seems to have been used for the name of the method. In this case, the promiser also needs to contain the unique name of the method"); } cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "A method attempted to use a bundle '%s' that was apparently not defined", BufferData(method_name)); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); } YieldCurrentLock(thislock); BufferDestroy(method_name); EndBundleBanner(bp); return result; }
static void KeepPromiseBundles(EvalContext *ctx, const Policy *policy) { /* Dial up the generic promise expansion with a callback */ CleanReportBookFilterSet(); for (size_t i = 0; i < SeqLength(policy->bundles); i++) { Bundle *bp = SeqAt(policy->bundles, i); if ((strcmp(bp->type, CF_AGENTTYPES[AGENT_TYPE_SERVER]) == 0) || (strcmp(bp->type, CF_AGENTTYPES[AGENT_TYPE_COMMON]) == 0)) { if (RlistLen(bp->args) > 0) { Log(LOG_LEVEL_WARNING, "Cannot implicitly evaluate bundle '%s %s', as this bundle takes arguments.", bp->type, bp->name); continue; } EvalContextStackPushBundleFrame(ctx, bp, NULL, false); for (int type = 0; SERVER_TYPESEQUENCE[type] != NULL; type++) { const PromiseType *sp = BundleGetPromiseType((Bundle *)bp, SERVER_TYPESEQUENCE[type]); /* Some promise types might not be there. */ if (!sp || SeqLength(sp->promises) == 0) { Log(LOG_LEVEL_DEBUG, "No promise type %s in bundle %s", SERVER_TYPESEQUENCE[type], bp->name); continue; } EvalContextStackPushPromiseTypeFrame(ctx, sp); for (size_t ppi = 0; ppi < SeqLength(sp->promises); ppi++) { Promise *pp = SeqAt(sp->promises, ppi); ExpandPromise(ctx, pp, KeepServerPromise, NULL); } EvalContextStackPopFrame(ctx); } /* Check if we are having some other promise types which we * should evaluate. THIS IS ONLY FOR BACKWARD COMPATIBILITY! */ for (size_t j = 0; j < SeqLength(bp->promise_types); j++) { PromiseType *sp = SeqAt(bp->promise_types, j); /* Skipping evaluation of promise as this was evaluated in * loop above. */ if (!IsPromiseTypeNotInServerTypeSequence(sp->name)) { Log(LOG_LEVEL_DEBUG, "Skipping subsequent evaluation of " "promise type %s in bundle %s", sp->name, bp->name); continue; } Log(LOG_LEVEL_WARNING, "Trying to evaluate unsupported/obsolete " "promise type %s in bundle %s", sp->name, bp->name); EvalContextStackPushPromiseTypeFrame(ctx, sp); for (size_t ppi = 0; ppi < SeqLength(sp->promises); ppi++) { Promise *pp = SeqAt(sp->promises, ppi); ExpandPromise(ctx, pp, KeepServerPromise, NULL); } EvalContextStackPopFrame(ctx); } EvalContextStackPopFrame(ctx); } } }
PromiseResult ScheduleEditOperation(EvalContext *ctx, char *filename, Attributes a, Promise *pp) { void *vp; FnCall *fp; Rlist *args = NULL; char edit_bundle_name[CF_BUFSIZE], lockname[CF_BUFSIZE], qualified_edit[CF_BUFSIZE], *method_deref; CfLock thislock; snprintf(lockname, CF_BUFSIZE - 1, "fileedit-%s", filename); thislock = AcquireLock(ctx, lockname, VUQNAME, CFSTARTTIME, a.transaction, pp, false); if (thislock.lock == NULL) { return PROMISE_RESULT_NOOP; } EditContext *edcontext = NewEditContext(filename, a); PromiseResult result = PROMISE_RESULT_NOOP; if (edcontext == NULL) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "File '%s' was marked for editing but could not be opened", filename); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); goto exit; } Policy *policy = PolicyFromPromise(pp); if (a.haveeditline) { if ((vp = ConstraintGetRvalValue(ctx, "edit_line", pp, RVAL_TYPE_FNCALL))) { fp = (FnCall *) vp; strcpy(edit_bundle_name, fp->name); args = fp->args; } else if ((vp = ConstraintGetRvalValue(ctx, "edit_line", pp, RVAL_TYPE_SCALAR))) { strcpy(edit_bundle_name, (char *) vp); args = NULL; } else { goto exit; } if (strncmp(edit_bundle_name,"default:",strlen("default:")) == 0) // CF_NS == ':' { method_deref = strchr(edit_bundle_name, CF_NS) + 1; } else if ((strchr(edit_bundle_name, CF_NS) == NULL) && (strcmp(PromiseGetNamespace(pp), "default") != 0)) { snprintf(qualified_edit, CF_BUFSIZE, "%s%c%s", PromiseGetNamespace(pp), CF_NS, edit_bundle_name); method_deref = qualified_edit; } else { method_deref = edit_bundle_name; } Log(LOG_LEVEL_VERBOSE, "Handling file edits in edit_line bundle '%s'", method_deref); Bundle *bp = NULL; if ((bp = PolicyGetBundle(policy, NULL, "edit_line", method_deref))) { BannerSubBundle(bp, args); EvalContextStackPushBundleFrame(ctx, bp, args, a.edits.inherit); BundleResolve(ctx, bp); ScheduleEditLineOperations(ctx, bp, a, pp, edcontext); EvalContextStackPopFrame(ctx); } else { Log(LOG_LEVEL_ERR, "Did not find method '%s' in bundle '%s' for edit operation", method_deref, edit_bundle_name); } } if (a.haveeditxml) { if ((vp = ConstraintGetRvalValue(ctx, "edit_xml", pp, RVAL_TYPE_FNCALL))) { fp = (FnCall *) vp; strcpy(edit_bundle_name, fp->name); args = fp->args; } else if ((vp = ConstraintGetRvalValue(ctx, "edit_xml", pp, RVAL_TYPE_SCALAR))) { strcpy(edit_bundle_name, (char *) vp); args = NULL; } else { goto exit; } if (strncmp(edit_bundle_name,"default:",strlen("default:")) == 0) // CF_NS == ':' { method_deref = strchr(edit_bundle_name, CF_NS) + 1; } else { method_deref = edit_bundle_name; } Log(LOG_LEVEL_VERBOSE, "Handling file edits in edit_xml bundle '%s'", method_deref); Bundle *bp = NULL; if ((bp = PolicyGetBundle(policy, NULL, "edit_xml", method_deref))) { BannerSubBundle(bp, args); EvalContextStackPushBundleFrame(ctx, bp, args, a.edits.inherit); BundleResolve(ctx, bp); ScheduleEditXmlOperations(ctx, bp, a, pp, edcontext); EvalContextStackPopFrame(ctx); } } if (a.edit_template) { if (!a.template_method || strcmp("cfengine", a.template_method) == 0) { Policy *tmp_policy = PolicyNew(); Bundle *bp = NULL; if ((bp = MakeTemporaryBundleFromTemplate(ctx, tmp_policy, a, pp, &result))) { BannerSubBundle(bp, args); a.haveeditline = true; EvalContextStackPushBundleFrame(ctx, bp, args, a.edits.inherit); BundleResolve(ctx, bp); ScheduleEditLineOperations(ctx, bp, a, pp, edcontext); EvalContextStackPopFrame(ctx); } PolicyDestroy(tmp_policy); } else if (strcmp("mustache", a.template_method) == 0) { if (!FileCanOpen(a.edit_template, "r")) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Template file '%s' could not be opened for reading", a.edit_template); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); goto exit; } Writer *ouput_writer = NULL; { FILE *output_file = fopen(pp->promiser, "w"); if (!output_file) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Output file '%s' could not be opened for writing", pp->promiser); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); goto exit; } ouput_writer = FileWriter(output_file); } Writer *template_writer = FileRead(a.edit_template, SIZE_MAX, NULL); if (!template_writer) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Could not read template file '%s'", a.edit_template); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); WriterClose(ouput_writer); goto exit; } JsonElement *default_template_data = NULL; if (!a.template_data) { a.template_data = default_template_data = DefaultTemplateData(ctx); } if (!MustacheRender(ouput_writer, StringWriterData(template_writer), a.template_data)) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Error rendering mustache template '%s'", a.edit_template); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); WriterClose(template_writer); WriterClose(ouput_writer); goto exit; } JsonDestroy(default_template_data); WriterClose(template_writer); WriterClose(ouput_writer); } } exit: result = PromiseResultUpdate(result, FinishEditContext(ctx, edcontext, a, pp)); YieldCurrentLock(thislock); return result; }
PromiseResult VerifyMethod(EvalContext *ctx, char *attrname, Attributes a, const Promise *pp) { Bundle *bp; void *vp; FnCall *fp; char method_name[CF_EXPANDSIZE]; Rlist *args = NULL; CfLock thislock; char lockname[CF_BUFSIZE]; if (a.havebundle) { if ((vp = PromiseGetConstraintAsRval(pp, attrname, RVAL_TYPE_FNCALL))) { fp = (FnCall *) vp; ExpandScalar(ctx, PromiseGetBundle(pp)->ns, PromiseGetBundle(pp)->name, fp->name, method_name); args = fp->args; } else if ((vp = PromiseGetConstraintAsRval(pp, attrname, RVAL_TYPE_SCALAR))) { ExpandScalar(ctx, PromiseGetBundle(pp)->ns, PromiseGetBundle(pp)->name, (char *) vp, method_name); args = NULL; } else { return PROMISE_RESULT_NOOP; } } GetLockName(lockname, "method", pp->promiser, args); thislock = AcquireLock(ctx, lockname, VUQNAME, CFSTARTTIME, a.transaction, pp, false); if (thislock.lock == NULL) { return PROMISE_RESULT_SKIPPED; } PromiseBanner(pp); char ns[CF_MAXVARSIZE] = ""; char bundle_name[CF_MAXVARSIZE] = ""; SplitScopeName(method_name, ns, bundle_name); bp = PolicyGetBundle(PolicyFromPromise(pp), EmptyString(ns) ? NULL : ns, "agent", bundle_name); if (!bp) { bp = PolicyGetBundle(PolicyFromPromise(pp), EmptyString(ns) ? NULL : ns, "common", bundle_name); } PromiseResult result = PROMISE_RESULT_NOOP; if (bp) { BannerSubBundle(bp, args); EvalContextStackPushBundleFrame(ctx, bp, args, a.inherit); BundleResolve(ctx, bp); result = ScheduleAgentOperations(ctx, bp); GetReturnValue(ctx, bp, pp); EvalContextStackPopFrame(ctx); switch (result) { case PROMISE_RESULT_FAIL: cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_FAIL, pp, a, "Method '%s' failed in some repairs or aborted", bp->name); break; case PROMISE_RESULT_CHANGE: cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_CHANGE, pp, a, "Method '%s' invoked repairs", bp->name); break; default: cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "Method '%s' verified", bp->name); break; } for (const Rlist *rp = bp->args; rp; rp = rp->next) { const char *lval = RlistScalarValue(rp); VarRef *ref = VarRefParseFromBundle(lval, bp); EvalContextVariableRemove(ctx, ref); VarRefDestroy(ref); } } else { if (IsCf3VarString(method_name)) { Log(LOG_LEVEL_ERR, "A variable seems to have been used for the name of the method. In this case, the promiser also needs to contain the unique name of the method"); } if (bp && (bp->name)) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Method '%s' was used but was not defined", bp->name); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); } else { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "A method attempted to use a bundle '%s' that was apparently not defined", method_name); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); } } YieldCurrentLock(thislock); return result; }