static void test_expand_scalar_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); } Buffer *res = BufferNew(); ExpandScalar(ctx, "default", "bundle", "a$(foo[$(bar)])b", res); assert_string_equal("afirstb", BufferData(res)); BufferDestroy(res); }
static void test_expand_scalar_two_scalars_nested(void **state) { EvalContext *ctx = *state; { VarRef *lval = VarRefParse("default:bundle.one"); EvalContextVariablePut(ctx, lval, "first", CF_DATA_TYPE_STRING, NULL); VarRefDestroy(lval); } { VarRef *lval = VarRefParse("default:bundle.two"); EvalContextVariablePut(ctx, lval, "one", CF_DATA_TYPE_STRING, NULL); VarRefDestroy(lval); } Buffer *res = BufferNew(); ExpandScalar(ctx, "default", "bundle", "a $($(two))b", res); assert_string_equal("a firstb", BufferData(res)); BufferDestroy(res); }
static void PutHandleVariable(EvalContext *ctx, const Promise *pp) { char *handle_s; const char *existing_handle = PromiseGetHandle(pp); if (existing_handle != NULL) { // This ordering is necessary to get automated canonification handle_s = ExpandScalar(ctx, NULL, "this", existing_handle, NULL); CanonifyNameInPlace(handle_s); } else { handle_s = xstrdup(PromiseID(pp)); /* default handle */ } EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "handle", handle_s, CF_DATA_TYPE_STRING, "source=promise"); free(handle_s); }
Rval ExpandPrivateRval(EvalContext *ctx, const char *ns, const char *scope, const void *rval_item, RvalType rval_type) { Rval returnval; returnval.item = NULL; returnval.type = RVAL_TYPE_NOPROMISEE; switch (rval_type) { case RVAL_TYPE_SCALAR: { Buffer *buffer = BufferNew(); ExpandScalar(ctx, ns, scope, rval_item, buffer); returnval = (Rval) { BufferClose(buffer), RVAL_TYPE_SCALAR }; } break; case RVAL_TYPE_LIST: returnval.item = ExpandList(ctx, ns, scope, rval_item, true); returnval.type = RVAL_TYPE_LIST; break; case RVAL_TYPE_FNCALL: returnval.item = ExpandFnCall(ctx, ns, scope, rval_item); returnval.type = RVAL_TYPE_FNCALL; break; case RVAL_TYPE_CONTAINER: returnval = RvalNew(rval_item, RVAL_TYPE_CONTAINER); break; case RVAL_TYPE_NOPROMISEE: break; } return returnval; }
Rval ExpandBundleReference(EvalContext *ctx, const char *ns, const char *scope, Rval rval) { // Allocates new memory for the copy switch (rval.type) { case RVAL_TYPE_SCALAR: { Buffer *buffer = BufferNew(); ExpandScalar(ctx, ns, scope, RvalScalarValue(rval), buffer); return (Rval) { BufferClose(buffer), RVAL_TYPE_SCALAR }; } case RVAL_TYPE_FNCALL: return (Rval) {ExpandFnCall(ctx, ns, scope, RvalFnCallValue(rval)), RVAL_TYPE_FNCALL}; case RVAL_TYPE_CONTAINER: case RVAL_TYPE_LIST: case RVAL_TYPE_NOPROMISEE: return RvalNew(NULL, RVAL_TYPE_NOPROMISEE); } assert(false); return RvalNew(NULL, RVAL_TYPE_NOPROMISEE); }
bool EvalContextVariableGet(const EvalContext *ctx, VarRef lval, Rval *rval_out, DataType *type_out) { if (lval.lval == NULL) { if (rval_out) { *rval_out = (Rval) {NULL, RVAL_TYPE_SCALAR }; } if (type_out) { *type_out = DATA_TYPE_NONE; } return false; } char expanded_lval[CF_MAXVARSIZE] = ""; if (!IsExpandable(lval.lval)) { strncpy(expanded_lval, lval.lval, CF_MAXVARSIZE - 1); } else { char buffer[CF_EXPANDSIZE] = ""; if (ExpandScalar(ctx, lval.scope, lval.lval, buffer)) { strncpy(expanded_lval, buffer, CF_MAXVARSIZE - 1); } else { if (rval_out) { *rval_out = (Rval) {(char *) lval.lval, RVAL_TYPE_SCALAR }; } if (type_out) { *type_out = DATA_TYPE_NONE; } return false; } } Scope *get_scope = NULL; char lookup_key[CF_MAXVARSIZE] = ""; { char scopeid[CF_MAXVARSIZE] = ""; if (IsQualifiedVariable(expanded_lval)) { scopeid[0] = '\0'; sscanf(expanded_lval, "%[^.].", scopeid); strlcpy(lookup_key, expanded_lval + strlen(scopeid) + 1, sizeof(lookup_key)); } else { strlcpy(lookup_key, expanded_lval, sizeof(lookup_key)); strlcpy(scopeid, lval.scope, sizeof(scopeid)); } if (lval.ns != NULL && strchr(scopeid, CF_NS) == NULL && strcmp(lval.ns, "default") != 0) { char buffer[CF_EXPANDSIZE] = ""; sprintf(buffer, "%s%c%s", lval.ns, CF_NS, scopeid); strlcpy(scopeid, buffer, sizeof(scopeid)); } get_scope = ScopeGet(scopeid); } if (!get_scope) { if (rval_out) { *rval_out = (Rval) {(char *) lval.lval, RVAL_TYPE_SCALAR }; } if (type_out) { *type_out = DATA_TYPE_NONE; } return false; } CfAssoc *assoc = HashLookupElement(get_scope->hashtable, lookup_key); if (!assoc) { if (rval_out) { *rval_out = (Rval) {(char *) lval.lval, RVAL_TYPE_SCALAR }; } if (type_out) { *type_out = DATA_TYPE_NONE; } return false; } if (rval_out) { *rval_out = assoc->rval; } if (type_out) { *type_out = assoc->dtype; assert(*type_out != DATA_TYPE_NONE); } return true; }
bool ExpandScalar(const EvalContext *ctx, const char *ns, const char *scope, const char *string, Buffer *out) { assert(string); bool returnval = true; if (strlen(string) == 0) { return false; } // TODO: cleanup, optimize this mess Buffer *var = BufferNew(); Buffer *current_item = BufferNew(); Buffer *temp = BufferNew(); for (const char *sp = string; /* No exit */ ; sp++) /* check for varitems */ { char varstring = false; size_t increment = 0; if (*sp == '\0') { break; } BufferZero(current_item); ExtractScalarPrefix(current_item, sp, strlen(sp)); BufferAppend(out, BufferData(current_item), BufferSize(current_item)); sp += BufferSize(current_item); if (*sp == '\0') { break; } BufferZero(var); if (*sp == '$') { switch (*(sp + 1)) { case '(': varstring = ')'; ExtractScalarReference(var, sp, strlen(sp), false); if (BufferSize(var) == 0) { BufferAppendChar(out, '$'); continue; } break; case '{': varstring = '}'; ExtractScalarReference(var, sp, strlen(sp), false); if (BufferSize(var) == 0) { BufferAppendChar(out, '$'); continue; } break; default: BufferAppendChar(out, '$'); continue; } } BufferZero(current_item); { BufferZero(temp); ExtractScalarReference(temp, sp, strlen(sp), true); if (IsCf3VarString(BufferData(temp))) { ExpandScalar(ctx, ns, scope, BufferData(temp), current_item); } else { BufferAppend(current_item, BufferData(temp), BufferSize(temp)); } } increment = BufferSize(var) - 1; char name[CF_MAXVARSIZE] = ""; if (!IsExpandable(BufferData(current_item))) { DataType type = DATA_TYPE_NONE; const void *value = NULL; { VarRef *ref = VarRefParseFromNamespaceAndScope(BufferData(current_item), ns, scope, CF_NS, '.'); value = EvalContextVariableGet(ctx, ref, &type); VarRefDestroy(ref); } if (value) { switch (type) { case DATA_TYPE_STRING: case DATA_TYPE_INT: case DATA_TYPE_REAL: BufferAppend(out, value, strlen(value)); break; case DATA_TYPE_STRING_LIST: case DATA_TYPE_INT_LIST: case DATA_TYPE_REAL_LIST: case DATA_TYPE_NONE: if (varstring == '}') { snprintf(name, CF_MAXVARSIZE, "${%s}", BufferData(current_item)); } else { snprintf(name, CF_MAXVARSIZE, "$(%s)", BufferData(current_item)); } BufferAppend(out, name, strlen(name)); returnval = false; break; default: Log(LOG_LEVEL_DEBUG, "Returning Unknown Scalar ('%s' => '%s')", string, BufferData(out)); BufferDestroy(var); BufferDestroy(current_item); BufferDestroy(temp); return false; } } else { if (varstring == '}') { snprintf(name, CF_MAXVARSIZE, "${%s}", BufferData(current_item)); } else { snprintf(name, CF_MAXVARSIZE, "$(%s)", BufferData(current_item)); } BufferAppend(out, name, strlen(name)); returnval = false; } } sp += increment; BufferZero(current_item); } BufferDestroy(var); BufferDestroy(current_item); BufferDestroy(temp); return returnval; }
/** * Expand a #string into Buffer #out, returning the pointer to the string * itself, inside the Buffer #out. If #out is NULL then the buffer will be * created and destroyed internally. * * @retval NULL something went wrong */ char *ExpandScalar(const EvalContext *ctx, const char *ns, const char *scope, const char *string, Buffer *out) { bool out_belongs_to_us = false; if (out == NULL) { out = BufferNew(); out_belongs_to_us = true; } assert(string != NULL); assert(out != NULL); Buffer *current_item = BufferNew(); for (const char *sp = string; *sp != '\0'; sp++) { BufferClear(current_item); ExtractScalarPrefix(current_item, sp, strlen(sp)); BufferAppend(out, BufferData(current_item), BufferSize(current_item)); sp += BufferSize(current_item); if (*sp == '\0') { break; } BufferClear(current_item); char varstring = sp[1]; ExtractScalarReference(current_item, sp, strlen(sp), true); sp += BufferSize(current_item) + 2; if (IsCf3VarString(BufferData(current_item))) { Buffer *temp = BufferCopy(current_item); BufferClear(current_item); ExpandScalar(ctx, ns, scope, BufferData(temp), current_item); BufferDestroy(temp); } if (!IsExpandable(BufferData(current_item))) { VarRef *ref = VarRefParseFromNamespaceAndScope( BufferData(current_item), ns, scope, CF_NS, '.'); DataType value_type; const void *value = EvalContextVariableGet(ctx, ref, &value_type); VarRefDestroy(ref); switch (DataTypeToRvalType(value_type)) { case RVAL_TYPE_SCALAR: assert(value != NULL); BufferAppendString(out, value); continue; break; case RVAL_TYPE_CONTAINER: { assert(value != NULL); const JsonElement *jvalue = value; /* instead of casts */ if (JsonGetElementType(jvalue) == JSON_ELEMENT_TYPE_PRIMITIVE) { BufferAppendString(out, JsonPrimitiveGetAsString(jvalue)); continue; } break; } default: /* TODO Log() */ break; } } if (varstring == '{') { BufferAppendF(out, "${%s}", BufferData(current_item)); } else { BufferAppendF(out, "$(%s)", BufferData(current_item)); } } BufferDestroy(current_item); LogDebug(LOG_MOD_EXPAND, "ExpandScalar( %s : %s . %s ) => %s", SAFENULL(ns), SAFENULL(scope), string, BufferData(out)); return out_belongs_to_us ? BufferClose(out) : BufferGet(out); }
enum cfdatatype GetVariable(const char *scope, const char *lval, Rval *returnv) { Scope *ptr = NULL; char scopeid[CF_MAXVARSIZE], vlval[CF_MAXVARSIZE], sval[CF_MAXVARSIZE]; char expbuf[CF_EXPANDSIZE]; CfAssoc *assoc; CfDebug("\nGetVariable(%s,%s) type=(to be determined)\n", scope, lval); if (lval == NULL) { *returnv = (Rval) {NULL, CF_SCALAR}; return cf_notype; } if (!IsExpandable(lval)) { strncpy(sval, lval, CF_MAXVARSIZE - 1); } else { if (ExpandScalar(lval, expbuf)) { strncpy(sval, expbuf, CF_MAXVARSIZE - 1); } else { /* C type system does not allow us to express the fact that returned value may contain immutable string. */ *returnv = (Rval) {(char *) lval, CF_SCALAR}; CfDebug("Couldn't expand array-like variable (%s) due to undefined dependencies\n", lval); return cf_notype; } } if (IsQualifiedVariable(sval)) { scopeid[0] = '\0'; sscanf(sval, "%[^.].%s", scopeid, vlval); CfDebug("Variable identifier %s is prefixed with scope id %s\n", vlval, scopeid); ptr = GetScope(scopeid); } else { strlcpy(vlval, sval, sizeof(vlval)); strlcpy(scopeid, scope, sizeof(scopeid)); } CfDebug("Looking for %s.%s\n", scopeid, vlval); if (ptr == NULL) { /* Assume current scope */ strcpy(vlval, lval); ptr = GetScope(scopeid); } if (ptr == NULL) { CfDebug("Scope for variable \"%s.%s\" does not seem to exist\n", scope, lval); /* C type system does not allow us to express the fact that returned value may contain immutable string. */ *returnv = (Rval) {(char *) lval, CF_SCALAR}; return cf_notype; } CfDebug("GetVariable(%s,%s): using scope '%s' for variable '%s'\n", scopeid, vlval, ptr->scope, vlval); assoc = HashLookupElement(ptr->hashtable, vlval); if (assoc == NULL) { CfDebug("No such variable found %s.%s\n\n", scopeid, lval); /* C type system does not allow us to express the fact that returned value may contain immutable string. */ *returnv = (Rval) {(char *) lval, CF_SCALAR}; return cf_notype; } CfDebug("return final variable type=%s, value={\n", CF_DATATYPES[assoc->dtype]); if (DEBUG) { ShowRval(stdout, assoc->rval); } CfDebug("}\n"); *returnv = assoc->rval; return assoc->dtype; }
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; }
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 ExpandPromiseAndDo(EvalContext *ctx, const Promise *pp, Rlist *lists, Rlist *containers, PromiseActuator *ActOnPromise, void *param) { const char *handle = PromiseGetHandle(pp); char v[CF_MAXVARSIZE]; EvalContextStackPushPromiseFrame(ctx, pp, true); PromiseIterator *iter_ctx = NULL; for (iter_ctx = PromiseIteratorNew(ctx, pp, lists, containers); PromiseIteratorHasMore(iter_ctx); PromiseIteratorNext(iter_ctx)) { EvalContextStackPushPromiseIterationFrame(ctx, iter_ctx); char number[CF_SMALLBUF]; /* Allow $(this.handle) etc variables */ if (PromiseGetBundle(pp)->source_path) { EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promise_filename",PromiseGetBundle(pp)->source_path, DATA_TYPE_STRING); snprintf(number, CF_SMALLBUF, "%zu", pp->offset.line); EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promise_linenumber", number, DATA_TYPE_STRING); } snprintf(v, CF_MAXVARSIZE, "%d", (int) getuid()); EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser_uid", v, DATA_TYPE_INT); snprintf(v, CF_MAXVARSIZE, "%d", (int) getgid()); EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser_gid", v, DATA_TYPE_INT); EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "bundle", PromiseGetBundle(pp)->name, DATA_TYPE_STRING); EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "namespace", PromiseGetNamespace(pp), DATA_TYPE_STRING); /* Must expand $(this.promiser) here for arg dereferencing in things like edit_line and methods, but we might have to adjust again later if the value changes -- need to qualify this so we don't expand too early for some other promsies */ if (pp->has_subbundles) { EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser", pp->promiser, DATA_TYPE_STRING); } if (handle) { char tmp[CF_EXPANDSIZE]; // This ordering is necessary to get automated canonification ExpandScalar(ctx, NULL, "this", handle, tmp); CanonifyNameInPlace(tmp); Log(LOG_LEVEL_DEBUG, "Expanded handle to '%s'", tmp); EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "handle", tmp, DATA_TYPE_STRING); } else { EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "handle", PromiseID(pp), DATA_TYPE_STRING); } Promise *pexp = ExpandDeRefPromise(ctx, pp); assert(ActOnPromise); ActOnPromise(ctx, pexp, param); if (strcmp(pp->parent_promise_type->name, "vars") == 0 || strcmp(pp->parent_promise_type->name, "meta") == 0) { VerifyVarPromise(ctx, pexp, true); } PromiseDestroy(pexp); EvalContextStackPopFrame(ctx); } PromiseIteratorDestroy(iter_ctx); EvalContextStackPopFrame(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; }
int VerifyMethod(char *attrname, Attributes a, Promise *pp) { Bundle *bp; void *vp; FnCall *fp; char method_name[CF_EXPANDSIZE]; Rlist *params = NULL; int retval = false; CfLock thislock; char lockname[CF_BUFSIZE]; if (a.havebundle) { if ((vp = GetConstraintValue(attrname, pp, CF_FNCALL))) { fp = (FnCall *) vp; ExpandScalar(fp->name, method_name); params = fp->args; } else if ((vp = GetConstraintValue(attrname, pp, CF_SCALAR))) { ExpandScalar((char *) vp, method_name); params = NULL; } else { return false; } } GetLockName(lockname, "method", pp->promiser, params); thislock = AcquireLock(lockname, VUQNAME, CFSTARTTIME, a, pp, false); if (thislock.lock == NULL) { return false; } PromiseBanner(pp); if ((bp = GetBundle(method_name, "agent"))) { char *bp_stack = THIS_BUNDLE; BannerSubBundle(bp, params); DeleteScope(bp->name); NewScope(bp->name); HashVariables(bp->name); AugmentScope(bp->name, bp->args, params); THIS_BUNDLE = bp->name; PushPrivateClassContext(); retval = ScheduleAgentOperations(bp); PopPrivateClassContext(); THIS_BUNDLE = bp_stack; if (retval) { cfPS(cf_verbose, CF_NOP, "", pp, a, " -> Method invoked successfully\n"); } else { cfPS(cf_inform, CF_FAIL, "", pp, a, " !! Method could not be invoked successfully\n"); } DeleteFromScope(bp->name, bp->args); } else { if (IsCf3VarString(method_name)) { CfOut(cf_error, "", " !! A variable seems to have been used for the name of the method. In this case, the promiser also needs to contain the uique name of the method"); } if (bp && bp->name) { cfPS(cf_error, CF_FAIL, "", pp, a, " !! Method \"%s\" was used but was not defined!\n", bp->name); } else { cfPS(cf_error, CF_FAIL, "", pp, a, " !! A method attempted to use a bundle \"%s\" that was apparently not defined!\n", method_name); } } YieldCurrentLock(thislock); return retval; }
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; }
bool ExpandScalar(const EvalContext *ctx, const char *ns, const char *scope, const char *string, Buffer *out) { assert(string); if (strlen(string) == 0) { return true; } bool fully_expanded = true; Buffer *current_item = BufferNew(); for (const char *sp = string; *sp != '\0'; sp++) { BufferClear(current_item); ExtractScalarPrefix(current_item, sp, strlen(sp)); BufferAppend(out, BufferData(current_item), BufferSize(current_item)); sp += BufferSize(current_item); if (*sp == '\0') { break; } BufferClear(current_item); char varstring = sp[1]; ExtractScalarReference(current_item, sp, strlen(sp), true); sp += BufferSize(current_item) + 2; if (IsCf3VarString(BufferData(current_item))) { Buffer *temp = BufferCopy(current_item); BufferClear(current_item); ExpandScalar(ctx, ns, scope, BufferData(temp), current_item); BufferDestroy(temp); } if (!IsExpandable(BufferData(current_item))) { DataType type = CF_DATA_TYPE_NONE; const void *value = NULL; { VarRef *ref = VarRefParseFromNamespaceAndScope(BufferData(current_item), ns, scope, CF_NS, '.'); value = EvalContextVariableGet(ctx, ref, &type); VarRefDestroy(ref); } if (value) { switch (DataTypeToRvalType(type)) { case RVAL_TYPE_SCALAR: BufferAppendString(out, value); continue; case RVAL_TYPE_CONTAINER: if (JsonGetElementType((JsonElement*)value) == JSON_ELEMENT_TYPE_PRIMITIVE) { BufferAppendString(out, JsonPrimitiveGetAsString((JsonElement*)value)); continue; } break; default: break; } } } if (varstring == '{') { BufferAppendF(out, "${%s}", BufferData(current_item)); } else { BufferAppendF(out, "$(%s)", BufferData(current_item)); } } BufferDestroy(current_item); return fully_expanded; }