static bool MethodsParseTreeCheck(const Promise *pp, Seq *errors) { bool success = true; for (size_t i = 0; i < SeqLength(pp->conlist); i++) { const Constraint *cp = SeqAt(pp->conlist, i); // ensure: if call and callee are resolved, then they have matching arity if (StringSafeEqual(cp->lval, "usebundle")) { if (cp->rval.type == RVAL_TYPE_FNCALL) { const FnCall *call = (const FnCall *)cp->rval.item; const Bundle *callee = PolicyGetBundle(PolicyFromPromise(pp), NULL, "agent", call->name); if (!callee) { callee = PolicyGetBundle(PolicyFromPromise(pp), NULL, "common", call->name); } if (callee) { if (RlistLen(call->args) != RlistLen(callee->args)) { SeqAppend(errors, PolicyErrorNew(POLICY_ELEMENT_TYPE_CONSTRAINT, cp, POLICY_ERROR_METHODS_BUNDLE_ARITY, call->name, RlistLen(callee->args), RlistLen(call->args))); success = false; } } } } } return success; }
static PromiseResult DoVerifyServices(EvalContext *ctx, Attributes a, const Promise *pp) { FnCall *service_bundle = PromiseGetConstraintAsRval(pp, "service_bundle", RVAL_TYPE_FNCALL); PromiseResult result = PROMISE_RESULT_NOOP; if (!service_bundle) { service_bundle = PromiseGetConstraintAsRval(pp, "service_bundle", RVAL_TYPE_SCALAR); } if (!service_bundle) { cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_FAIL, pp, a, "Service '%s' cannot be resolved as a bundle", pp->promiser); return PromiseResultUpdate(result, PROMISE_RESULT_FAIL); } switch (a.service.service_policy) { case SERVICE_POLICY_START: EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "service_policy", "start", CF_DATA_TYPE_STRING, "source=promise"); break; case SERVICE_POLICY_RESTART: EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "service_policy", "restart", CF_DATA_TYPE_STRING, "source=promise"); break; case SERVICE_POLICY_RELOAD: EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "service_policy", "reload", CF_DATA_TYPE_STRING, "source=promise"); break; case SERVICE_POLICY_STOP: case SERVICE_POLICY_DISABLE: default: EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "service_policy", "stop", CF_DATA_TYPE_STRING, "source=promise"); break; } const Bundle *bp = PolicyGetBundle(PolicyFromPromise(pp), NULL, "agent", service_bundle->name); if (!bp) { bp = PolicyGetBundle(PolicyFromPromise(pp), NULL, "common", service_bundle->name); } if (!bp) { cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_FAIL, pp, a, "Service '%s' could not be invoked successfully", pp->promiser); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); } if (!DONTDO) { result = PromiseResultUpdate(result, VerifyMethod(ctx, "service_bundle", a, pp)); // Send list of classes to set privately? } return result; }
static bool VerifyBundleSequence(EvalContext *ctx, const Policy *policy, const GenericAgentConfig *config) { Rlist *rp; char *name; Rval retval; int ok = true; FnCall *fp; if (!EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_BUNDLESEQUENCE, &retval)) { Log(LOG_LEVEL_ERR, " No bundlesequence in the common control body"); return false; } if (retval.type != RVAL_TYPE_LIST) { FatalError(ctx, "Promised bundlesequence was not a list"); } for (rp = (Rlist *) retval.item; rp != NULL; rp = rp->next) { switch (rp->type) { case RVAL_TYPE_SCALAR: name = (char *) rp->item; break; case RVAL_TYPE_FNCALL: fp = (FnCall *) rp->item; name = (char *) fp->name; break; default: name = NULL; ok = false; { Writer *w = StringWriter(); WriterWrite(w, "Illegal item found in bundlesequence '"); RvalWrite(w, (Rval) {rp->item, rp->type}); WriterWrite(w, "'"); Log(LOG_LEVEL_ERR, "%s", StringWriterData(w)); WriterClose(w); } break; } if (strcmp(name, CF_NULL_VALUE) == 0) { continue; } if (!config->ignore_missing_bundles && !PolicyGetBundle(policy, NULL, NULL, name)) { Log(LOG_LEVEL_ERR, "Bundle '%s' listed in the bundlesequence is not a defined bundle", name); ok = false; } } return ok; }
static bool MethodsParseTreeCheck(const Promise *pp, Seq *errors) { bool success = true; for (size_t i = 0; i < SeqLength(pp->conlist); i++) { const Constraint *cp = SeqAt(pp->conlist, i); // ensure: if call and callee are resolved, then they have matching arity if (StringSafeEqual(cp->lval, "usebundle")) { if (cp->rval.type == RVAL_TYPE_FNCALL) { // HACK: exploiting the fact that class-references and call-references are similar FnCall *call = RvalFnCallValue(cp->rval); ClassRef ref = ClassRefParse(call->name); if (!ClassRefIsQualified(ref)) { ClassRefQualify(&ref, PromiseGetNamespace(pp)); } const Bundle *callee = PolicyGetBundle(PolicyFromPromise(pp), ref.ns, "agent", ref.name); if (!callee) { callee = PolicyGetBundle(PolicyFromPromise(pp), ref.ns, "common", ref.name); } ClassRefDestroy(ref); if (callee) { if (RlistLen(call->args) != RlistLen(callee->args)) { SeqAppend(errors, PolicyErrorNew(POLICY_ELEMENT_TYPE_CONSTRAINT, cp, POLICY_ERROR_METHODS_BUNDLE_ARITY, call->name, RlistLen(callee->args), RlistLen(call->args))); success = false; } } } } } return success; }
static bool VerifyBundleSequence(EvalContext *ctx, const Policy *policy, const GenericAgentConfig *config) { Rlist *fallback = NULL; const Rlist *bundlesequence = EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_BUNDLESEQUENCE); if (!bundlesequence) { RlistAppendScalar(&fallback, "main"); bundlesequence = fallback; } const char *name; int ok = true; for (const Rlist *rp = bundlesequence; rp != NULL; rp = rp->next) { switch (rp->val.type) { case RVAL_TYPE_SCALAR: name = RlistScalarValue(rp); break; case RVAL_TYPE_FNCALL: name = RlistFnCallValue(rp)->name; break; default: name = NULL; ok = false; { Writer *w = StringWriter(); WriterWrite(w, "Illegal item found in bundlesequence '"); RvalWrite(w, rp->val); WriterWrite(w, "'"); Log(LOG_LEVEL_ERR, "%s", StringWriterData(w)); WriterClose(w); } continue; } if (strcmp(name, CF_NULL_VALUE) == 0) { continue; } if (!config->ignore_missing_bundles && !PolicyGetBundle(policy, NULL, NULL, name)) { Log(LOG_LEVEL_ERR, "Bundle '%s' listed in the bundlesequence is not a defined bundle", name); ok = false; } } RlistDestroy(fallback); return ok; }
static FnCall *DefaultServiceBundleCall(const Promise *pp, const char *service_policy) { Rlist *args = NULL; FnCall *call = NULL; RlistAppend(&args, pp->promiser, RVAL_TYPE_SCALAR); RlistAppend(&args, service_policy, RVAL_TYPE_SCALAR); Rval name = DefaultBundleConstraint(pp, "service"); if (PolicyGetBundle(PolicyFromPromise(pp), PromiseGetBundle(pp)->ns, "agent", (char *)name.item)) { Log(LOG_LEVEL_VERBOSE, "Found service special bundle %s in ns %s\n", (char *)name.item, PromiseGetBundle(pp)->ns); call = FnCallNew(name.item, args); } else { call = FnCallNew("standard_services", args); } return call; }
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; }
static PromiseResult DoVerifyServices(EvalContext *ctx, Attributes a, Promise *pp) { FnCall *default_bundle = NULL; Rlist *args = NULL; // Need to set up the default service pack to eliminate syntax if (ConstraintGetRvalValue(ctx, "service_bundle", pp, RVAL_TYPE_SCALAR) == NULL) { switch (a.service.service_policy) { case SERVICE_POLICY_START: RlistAppendScalar(&args, pp->promiser); RlistAppendScalar(&args, "start"); break; case SERVICE_POLICY_RESTART: RlistAppendScalar(&args, pp->promiser); RlistAppendScalar(&args, "restart"); break; case SERVICE_POLICY_RELOAD: RlistAppendScalar(&args, pp->promiser); RlistAppendScalar(&args, "reload"); break; case SERVICE_POLICY_STOP: case SERVICE_POLICY_DISABLE: default: RlistAppendScalar(&args, pp->promiser); RlistAppendScalar(&args, "stop"); break; } default_bundle = FnCallNew("standard_services", args); PromiseAppendConstraint(pp, "service_bundle", (Rval) {default_bundle, RVAL_TYPE_FNCALL }, "any", false); a.havebundle = true; } // Set $(this.service_policy) for flexible bundle adaptation switch (a.service.service_policy) { case SERVICE_POLICY_START: EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "service_policy", "start", DATA_TYPE_STRING, "goal=state,source=promise"); break; case SERVICE_POLICY_RESTART: EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "service_policy", "restart", DATA_TYPE_STRING, "goal=state,source=promise"); break; case SERVICE_POLICY_RELOAD: EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "service_policy", "reload", DATA_TYPE_STRING, "goal=state,source=promise"); break; case SERVICE_POLICY_STOP: case SERVICE_POLICY_DISABLE: default: EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "service_policy", "stop", DATA_TYPE_STRING, "goal=state,source=promise"); break; } const Bundle *bp = PolicyGetBundle(PolicyFromPromise(pp), NULL, "agent", default_bundle->name); if (!bp) { bp = PolicyGetBundle(PolicyFromPromise(pp), NULL, "common", default_bundle->name); } PromiseResult result = PROMISE_RESULT_NOOP; if (default_bundle && bp == NULL) { cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_FAIL, pp, a, "Service '%s' could not be invoked successfully", pp->promiser); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); } if (!DONTDO) { result = PromiseResultUpdate(result, VerifyMethod(ctx, "service_bundle", a, pp)); // Send list of classes to set privately? } return result; }
static void KeepPromiseBundles(Policy *policy, GenericAgentConfig *config, const ReportContext *report_context) { Bundle *bp; Rlist *rp, *params; FnCall *fp; char *name; Rval retval; int ok = true; if (config->bundlesequence) { CfOut(cf_inform, "", " >> Using command line specified bundlesequence"); retval = (Rval) { config->bundlesequence, RVAL_TYPE_LIST }; } else if (GetVariable("control_common", "bundlesequence", &retval) == DATA_TYPE_NONE) { // TODO: somewhat frenzied way of telling user about an error CfOut(cf_error, "", " !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); CfOut(cf_error, "", " !! No bundlesequence in the common control body"); CfOut(cf_error, "", " !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); exit(1); } // TODO: should've been checked a long time ago, remove? if (retval.type != RVAL_TYPE_LIST) { FatalError("Promised bundlesequence was not a list"); } for (rp = (Rlist *) retval.item; rp != NULL; rp = rp->next) { switch (rp->type) { case RVAL_TYPE_SCALAR: name = (char *) rp->item; params = NULL; if (strcmp(name, CF_NULL_VALUE) == 0) { continue; } break; case RVAL_TYPE_FNCALL: fp = (FnCall *) rp->item; name = (char *) fp->name; params = (Rlist *) fp->args; break; default: name = NULL; params = NULL; CfOut(cf_error, "", "Illegal item found in bundlesequence: "); ShowRval(stdout, (Rval) {rp->item, rp->type}); printf(" = %c\n", rp->type); ok = false; break; } if (!config->ignore_missing_bundles) { if (!(PolicyGetBundle(policy, NULL, "agent", name) || (PolicyGetBundle(policy, NULL, "common", name)))) { CfOut(cf_error, "", "Bundle \"%s\" listed in the bundlesequence was not found\n", name); ok = false; } } } if (!ok) { FatalError("Errors in agent bundles"); } if (VERBOSE || DEBUG) { printf("%s> -> Bundlesequence => ", VPREFIX); ShowRval(stdout, retval); printf("\n"); } /* If all is okay, go ahead and evaluate */ for (rp = (Rlist *) retval.item; rp != NULL; rp = rp->next) { switch (rp->type) { case RVAL_TYPE_FNCALL: fp = (FnCall *) rp->item; name = (char *) fp->name; params = (Rlist *) fp->args; break; default: name = (char *) rp->item; params = NULL; break; } if ((bp = PolicyGetBundle(policy, NULL, "agent", name)) || (bp = PolicyGetBundle(policy, NULL, "common", name))) { char ns[CF_BUFSIZE]; snprintf(ns,CF_BUFSIZE,"%s_meta", name); NewScope(ns); SetBundleOutputs(bp->name); AugmentScope(bp->name, bp->ns, bp->args, params); BannerBundle(bp, params); THIS_BUNDLE = bp->name; DeletePrivateClassContext(); // Each time we change bundle ScheduleAgentOperations(bp, report_context); ResetBundleOutputs(bp->name); } } }
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; }
static void test_policy_json_to_from(void) { EvalContext *ctx = EvalContextNew(); Policy *policy = NULL; { Policy *original = LoadPolicy("benchmark.cf"); JsonElement *json = PolicyToJson(original); PolicyDestroy(original); policy = PolicyFromJson(json); JsonDestroy(json); } assert_true(policy); assert_int_equal(1, SeqLength(policy->bundles)); assert_int_equal(2, SeqLength(policy->bodies)); { Bundle *main_bundle = PolicyGetBundle(policy, NULL, "agent", "main"); assert_true(main_bundle); { { PromiseType *files = BundleGetPromiseType(main_bundle, "files"); assert_true(files); assert_int_equal(1, SeqLength(files->promises)); for (size_t i = 0; i < SeqLength(files->promises); i++) { Promise *promise = SeqAt(files->promises, i); if (strcmp("/tmp/stuff", promise->promiser) == 0) { assert_string_equal("any", promise->classes); assert_int_equal(2, SeqLength(promise->conlist)); { Constraint *create = PromiseGetConstraint(ctx, promise, "create"); assert_true(create); assert_string_equal("create", create->lval); assert_string_equal("true", RvalScalarValue(create->rval)); } { Constraint *create = PromiseGetConstraint(ctx, promise, "perms"); assert_true(create); assert_string_equal("perms", create->lval); assert_string_equal("myperms", RvalScalarValue(create->rval)); } } else { fprintf(stderr, "Found unknown promise"); fail(); } } } { const char* reportOutput[2] = { "Hello, CFEngine", "Hello, world" }; const char* reportClass[2] = { "cfengine", "any" }; PromiseType *reports = BundleGetPromiseType(main_bundle, "reports"); assert_true(reports); assert_int_equal(2, SeqLength(reports->promises)); for (size_t i = 0; i < SeqLength(reports->promises); i++) { Promise *promise = SeqAt(reports->promises, i); if (strcmp(reportOutput[i], promise->promiser) == 0) { assert_string_equal(reportClass[i], promise->classes); assert_int_equal(1, SeqLength(promise->conlist)); { Constraint *friend_pattern = SeqAt(promise->conlist, 0); assert_true(friend_pattern); assert_string_equal("friend_pattern", friend_pattern->lval); assert_int_equal(RVAL_TYPE_FNCALL, friend_pattern->rval.type); FnCall *fn = RvalFnCallValue(friend_pattern->rval); assert_string_equal("hash", fn->name); assert_int_equal(2, RlistLen(fn->args)); } } else { fprintf(stderr, "Found unknown promise"); fail(); } } } } } { Body *myperms = PolicyGetBody(policy, NULL, "perms", "myperms"); assert_true(myperms); { Seq *mode_cps = BodyGetConstraint(myperms, "mode"); assert_int_equal(1, SeqLength(mode_cps)); Constraint *mode = SeqAt(mode_cps, 0); assert_string_equal("mode", mode->lval); assert_string_equal("555", RvalScalarValue(mode->rval)); } } PolicyDestroy(policy); EvalContextDestroy(ctx); }
static void DoVerifyServices(EvalContext *ctx, Attributes a, Promise *pp, const ReportContext *report_context) { FnCall *default_bundle = NULL; Rlist *args = NULL; // Need to set up the default service pack to eliminate syntax if (ConstraintGetRvalValue(ctx, "service_bundle", pp, RVAL_TYPE_SCALAR) == NULL) { switch (a.service.service_policy) { case SERVICE_POLICY_START: RlistAppendScalar(&args, pp->promiser); RlistAppendScalar(&args, "start"); break; case SERVICE_POLICY_RESTART: RlistAppendScalar(&args, pp->promiser); RlistAppendScalar(&args, "restart"); break; case SERVICE_POLICY_RELOAD: RlistAppendScalar(&args, pp->promiser); RlistAppendScalar(&args, "restart"); break; case SERVICE_POLICY_STOP: case SERVICE_POLICY_DISABLE: default: RlistAppendScalar(&args, pp->promiser); RlistAppendScalar(&args, "stop"); break; } default_bundle = FnCallNew("default:standard_services", args); PromiseAppendConstraint(pp, "service_bundle", (Rval) {default_bundle, RVAL_TYPE_FNCALL }, "any", false); a.havebundle = true; } // Set $(this.service_policy) for flexible bundle adaptation switch (a.service.service_policy) { case SERVICE_POLICY_START: ScopeNewScalar("this", "service_policy", "start", DATA_TYPE_STRING); break; case SERVICE_POLICY_RESTART: ScopeNewScalar("this", "service_policy", "restart", DATA_TYPE_STRING); break; case SERVICE_POLICY_RELOAD: ScopeNewScalar("this", "service_policy", "reload", DATA_TYPE_STRING); break; case SERVICE_POLICY_STOP: case SERVICE_POLICY_DISABLE: default: ScopeNewScalar("this", "service_policy", "stop", DATA_TYPE_STRING); break; } const Bundle *bp = PolicyGetBundle(PolicyFromPromise(pp), NULL, "agent", default_bundle->name); if (!bp) { bp = PolicyGetBundle(PolicyFromPromise(pp), NULL, "common", default_bundle->name); } if (default_bundle && bp == NULL) { cfPS(ctx, OUTPUT_LEVEL_INFORM, CF_FAIL, "", pp, a, " !! Service %s could not be invoked successfully\n", pp->promiser); } if (!DONTDO) { VerifyMethod(ctx, "service_bundle", a, pp, report_context); // Send list of classes to set privately? } }
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; }