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; }
Promise *DeRefCopyPromise(EvalContext *ctx, const Promise *pp) { Promise *pcopy; pcopy = xcalloc(1, sizeof(Promise)); if (pp->promiser) { pcopy->promiser = xstrdup(pp->promiser); } if (pp->promisee.item) { pcopy->promisee = RvalCopy(pp->promisee); if (pcopy->promisee.type == RVAL_TYPE_LIST) { Rlist *rval_list = RvalRlistValue(pcopy->promisee); RlistFlatten(ctx, &rval_list); pcopy->promisee.item = rval_list; } } if (pp->classes) { pcopy->classes = xstrdup(pp->classes); } /* FIXME: may it happen? */ if ((pp->promisee.item != NULL && pcopy->promisee.item == NULL)) { ProgrammingError("Unable to copy promise"); } pcopy->parent_promise_type = pp->parent_promise_type; pcopy->offset.line = pp->offset.line; pcopy->comment = pp->comment ? xstrdup(pp->comment) : NULL; pcopy->has_subbundles = pp->has_subbundles; pcopy->conlist = SeqNew(10, ConstraintDestroy); pcopy->org_pp = pp->org_pp; pcopy->offset = pp->offset; /* No further type checking should be necessary here, already done by CheckConstraintTypeMatch */ for (size_t i = 0; i < SeqLength(pp->conlist); i++) { Constraint *cp = SeqAt(pp->conlist, i); Body *bp = NULL; FnCall *fp = NULL; /* A body template reference could look like a scalar or fn to the parser w/w () */ const Policy *policy = PolicyFromPromise(pp); Seq *bodies = policy ? policy->bodies : NULL; char body_ns[CF_MAXVARSIZE] = ""; char body_name[CF_MAXVARSIZE] = ""; switch (cp->rval.type) { case RVAL_TYPE_SCALAR: if (cp->references_body) { SplitScopeName(RvalScalarValue(cp->rval), body_ns, body_name); if (EmptyString(body_ns)) { strncpy(body_ns, PromiseGetNamespace(pp), CF_MAXVARSIZE); } bp = IsBody(bodies, body_ns, body_name); } fp = NULL; break; case RVAL_TYPE_FNCALL: fp = RvalFnCallValue(cp->rval); SplitScopeName(fp->name, body_ns, body_name); if (EmptyString(body_ns)) { strncpy(body_ns, PromiseGetNamespace(pp), CF_MAXVARSIZE); } bp = IsBody(bodies, body_ns, body_name); break; default: bp = NULL; fp = NULL; break; } /* First case is: we have a body template to expand lval = body(args), .. */ if (bp) { EvalContextStackPushBodyFrame(ctx, pcopy, bp, fp ? fp->args : NULL); if (strcmp(bp->type, cp->lval) != 0) { Log(LOG_LEVEL_ERR, "Body type mismatch for body reference '%s' in promise " "at line %llu of file '%s', '%s' does not equal '%s'", body_name, (unsigned long long)pp->offset.line, PromiseGetBundle(pp)->source_path, bp->type, cp->lval); } /* Keep the referent body type as a boolean for convenience when checking later */ if (IsDefinedClass(ctx, cp->classes)) { Constraint *cp_copy = PromiseAppendConstraint(pcopy, cp->lval, (Rval) {xstrdup("true"), RVAL_TYPE_SCALAR }, false); cp_copy->offset = cp->offset; } if (bp->args != NULL) { /* There are arguments to insert */ if (fp == NULL || fp->args == NULL) { Log(LOG_LEVEL_ERR, "Argument mismatch for body reference '%s' in promise " "at line %llu of file '%s'", body_name, (unsigned long long) pp->offset.line, PromiseGetBundle(pp)->source_path); } for (size_t k = 0; k < SeqLength(bp->conlist); k++) { Constraint *scp = SeqAt(bp->conlist, k); if (IsDefinedClass(ctx, scp->classes)) { Rval returnval = ExpandPrivateRval(ctx, NULL, "body", scp->rval.item, scp->rval.type); Constraint *scp_copy = PromiseAppendConstraint(pcopy, scp->lval, returnval, false); scp_copy->offset = scp->offset; } } } else { /* No arguments to deal with or body undeclared */ if (fp != NULL) { Log(LOG_LEVEL_ERR, "An apparent body \"%s()\" was undeclared or could " "have incorrect args, but used in a promise near " "line %llu of %s (possible unquoted literal value)", body_name, (unsigned long long) pp->offset.line, PromiseGetBundle(pp)->source_path); } else { for (size_t k = 0; k < SeqLength(bp->conlist); k++) { Constraint *scp = SeqAt(bp->conlist, k); if (IsDefinedClass(ctx, scp->classes)) { Rval newrv = RvalCopy(scp->rval); if (newrv.type == RVAL_TYPE_LIST) { Rlist *new_list = RvalRlistValue(newrv); RlistFlatten(ctx, &new_list); newrv.item = new_list; } Constraint *scp_copy = PromiseAppendConstraint(pcopy, scp->lval, newrv, false); scp_copy->offset = scp->offset; } } } } EvalContextStackPopFrame(ctx); } else { const Policy *policy = PolicyFromPromise(pp); if (cp->references_body && !IsBundle(policy->bundles, EmptyString(body_ns) ? NULL : body_ns, body_name)) { Log(LOG_LEVEL_ERR, "Apparent body \"%s()\" was undeclared, but " "used in a promise near line %llu of %s " "(possible unquoted literal value)", body_name, (unsigned long long)pp->offset.line, PromiseGetBundle(pp)->source_path); } if (IsDefinedClass(ctx, cp->classes)) { Rval newrv = RvalCopy(cp->rval); if (newrv.type == RVAL_TYPE_LIST) { Rlist *new_list = RvalRlistValue(newrv); RlistFlatten(ctx, &new_list); newrv.item = new_list; } Constraint *cp_copy = PromiseAppendConstraint(pcopy, cp->lval, newrv, false); cp_copy->offset = cp->offset; } } } return pcopy; }