static void test_iterate_indices(void) { VariableTable *t = ReferenceTable(); { VarRef *ref = VarRefParse("default:scope1.array"); VariableTableIterator *iter = VariableTableIteratorNewFromVarRef(t, ref); unsigned short number_of_entries = 0; while (VariableTableIteratorNext(iter)) { number_of_entries++; } assert_int_equal(4, number_of_entries); VariableTableIteratorDestroy(iter); VarRefDestroy(ref); } { VarRef *ref = VarRefParse("default:scope1.array[two]"); VariableTableIterator *iter = VariableTableIteratorNewFromVarRef(t, ref); unsigned short number_of_entries = 0; while (VariableTableIteratorNext(iter)) { number_of_entries++; } assert_int_equal(3, number_of_entries); VariableTableIteratorDestroy(iter); VarRefDestroy(ref); } }
static void RemoveRemotelyInjectedVars(const EvalContext *ctx, const Bundle *bundle) { const Seq *remote_var_promises = EvalContextGetRemoteVarPromises(ctx, bundle->name); if ((remote_var_promises == NULL) || SeqLength(remote_var_promises) == 0) { /* nothing to do here */ return; } size_t promises_length = SeqLength(remote_var_promises); Seq *remove_vars = SeqNew(promises_length, NULL); /* remove variables that have been attempted to be inserted into this * bundle */ /* TODO: this is expensive and should be removed! */ for (size_t i = 0; i < promises_length; i++) { const Promise *pp = (Promise *) SeqAt(remote_var_promises, i); VariableTableIterator *iter = EvalContextVariableTableIteratorNew(ctx, NULL, bundle->name, NULL); const Variable *var = VariableTableIteratorNext(iter); while (var != NULL) { /* variables are stored together with their original promises (org_pp) */ if (var->promise && var->promise->org_pp == pp) { Log(LOG_LEVEL_ERR, "Ignoring remotely-injected variable '%s'", var->ref->lval); /* avoid modifications of the variable table being iterated * over and avoid trying to remove the same variable twice */ SeqAppendOnce(remove_vars, (void *) var, PointerCmp); } var = VariableTableIteratorNext(iter); } VariableTableIteratorDestroy(iter); } /* iteration over the variable table done, time to remove the variables */ size_t remove_vars_length = SeqLength(remove_vars); for (size_t i = 0; i < remove_vars_length; i++) { Variable *var = (Variable *) SeqAt(remove_vars, i); if (var->ref != NULL) { EvalContextVariableRemove(ctx, var->ref); } } SeqDestroy(remove_vars); }
static void GetReturnValue(EvalContext *ctx, const Bundle *callee, const Promise *caller) { char *result = PromiseGetConstraintAsRval(caller, "useresult", RVAL_TYPE_SCALAR); if (result) { VarRef *ref = VarRefParseFromBundle("last-result", callee); VariableTableIterator *iter = EvalContextVariableTableIteratorNew(ctx, ref->ns, ref->scope, ref->lval); Variable *result_var = NULL; while ((result_var = VariableTableIteratorNext(iter))) { assert(result_var->ref->num_indices == 1); if (result_var->ref->num_indices != 1) { continue; } VarRef *new_ref = VarRefParseFromBundle(result, PromiseGetBundle(caller)); VarRefAddIndex(new_ref, result_var->ref->indices[0]); EvalContextVariablePut(ctx, new_ref, result_var->rval.item, result_var->type, "source=bundle"); VarRefDestroy(new_ref); } VarRefDestroy(ref); VariableTableIteratorDestroy(iter); } }
static void ShowVariablesFormatted(EvalContext *ctx) { VariableTableIterator *iter = EvalContextVariableTableIteratorNew(ctx, NULL, NULL, NULL); Variable *v = NULL; Seq *seq = SeqNew(2000, free); while ((v = VariableTableIteratorNext(iter))) { char *varname = VarRefToString(v->ref, true); Writer *w = StringWriter(); switch (DataTypeToRvalType(v->type)) { case RVAL_TYPE_CONTAINER: JsonWriteCompact(w, RvalContainerValue(v->rval)); break; default: RvalWrite(w, v->rval); } const char *var_value; if (StringIsPrintable(StringWriterData(w))) { var_value = StringWriterData(w); } else { var_value = "<non-printable>"; } StringSet *tagset = EvalContextVariableTags(ctx, v->ref); Buffer *tagbuf = StringSetToBuffer(tagset, ','); char *line; xasprintf(&line, "%-40s %-60s %-40s", varname, var_value, BufferData(tagbuf)); SeqAppend(seq, line); BufferDestroy(tagbuf); WriterClose(w); free(varname); } SeqSort(seq, (SeqItemComparator)strcmp, NULL); printf("%-40s %-60s %-40s\n", "Variable name", "Variable value", "Meta tags"); for (size_t i = 0; i < SeqLength(seq); i++) { const char *variable = SeqAt(seq, i); printf("%s\n", variable); } SeqDestroy(seq); VariableTableIteratorDestroy(iter); }
static JsonElement *DefaultTemplateData(const EvalContext *ctx) { JsonElement *hash = JsonObjectCreate(10); { ClassTableIterator *it = EvalContextClassTableIteratorNewGlobal(ctx, NULL, true, true); Class *cls = NULL; while ((cls = ClassTableIteratorNext(it))) { char *key = ClassRefToString(cls->ns, cls->name); JsonObjectAppendBool(hash, key, true); free(key); } ClassTableIteratorDestroy(it); } { ClassTableIterator *it = EvalContextClassTableIteratorNewLocal(ctx); Class *cls = NULL; while ((cls = ClassTableIteratorNext(it))) { char *key = ClassRefToString(cls->ns, cls->name); JsonObjectAppendBool(hash, key, true); free(key); } ClassTableIteratorDestroy(it); } { VariableTableIterator *it = EvalContextVariableTableIteratorNew(ctx, NULL, NULL, NULL); Variable *var = NULL; while ((var = VariableTableIteratorNext(it))) { // TODO: need to get a CallRef, this is bad char *scope_key = ClassRefToString(var->ref->ns, var->ref->scope); JsonElement *scope_obj = JsonObjectGetAsObject(hash, scope_key); if (!scope_obj) { scope_obj = JsonObjectCreate(50); JsonObjectAppendObject(hash, scope_key, scope_obj); } free(scope_key); char *lval_key = VarRefToString(var->ref, false); JsonObjectAppendElement(scope_obj, lval_key, RvalToJson(var->rval)); free(lval_key); } VariableTableIteratorDestroy(it); } return hash; }
// Below test relies on the ordering items in RB tree which is strongly // related to the hash function used. static void test_iterate_indices_ordering_related(void) { VariableTable *t = ReferenceTable(); { VarRef *ref = VarRefParse("default:scope1.array"); VariableTableIterator *iter = VariableTableIteratorNewFromVarRef(t, ref); Variable *v = VariableTableIteratorNext(iter); assert_true(v != NULL); assert_int_equal(1, v->ref->num_indices); assert_string_equal("two", v->ref->indices[0]); v = VariableTableIteratorNext(iter); assert_true(v != NULL); assert_int_equal(2, v->ref->num_indices); assert_string_equal("two", v->ref->indices[0]); assert_string_equal("three", v->ref->indices[1]); v = VariableTableIteratorNext(iter); assert_true(v != NULL); assert_int_equal(2, v->ref->num_indices); assert_string_equal("two", v->ref->indices[0]); assert_string_equal("four", v->ref->indices[1]); v = VariableTableIteratorNext(iter); assert_true(v != NULL); assert_int_equal(1, v->ref->num_indices); assert_string_equal("one", v->ref->indices[0]); assert_false(VariableTableIteratorNext(iter) != NULL); VariableTableIteratorDestroy(iter); VarRefDestroy(ref); } { VarRef *ref = VarRefParse("default:scope1.array[two]"); VariableTableIterator *iter = VariableTableIteratorNewFromVarRef(t, ref); Variable *v = VariableTableIteratorNext(iter); assert_true(v != NULL); assert_int_equal(1, v->ref->num_indices); assert_string_equal("two", v->ref->indices[0]); v = VariableTableIteratorNext(iter); assert_true(v != NULL); assert_int_equal(2, v->ref->num_indices); assert_string_equal("two", v->ref->indices[0]); assert_string_equal("three", v->ref->indices[1]); v = VariableTableIteratorNext(iter); assert_true(v != NULL); assert_int_equal(2, v->ref->num_indices); assert_string_equal("two", v->ref->indices[0]); assert_string_equal("four", v->ref->indices[1]); assert_false(VariableTableIteratorNext(iter) != NULL); VariableTableIteratorDestroy(iter); VarRefDestroy(ref); } VariableTableDestroy(t); }
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; }