static void test_expand_list_nested(void **state) { EvalContext *ctx = *state; { VarRef *lval = VarRefParse("default:bundle.i"); EvalContextVariablePut(ctx, lval, "one", CF_DATA_TYPE_STRING, NULL); VarRefDestroy(lval); } { VarRef *lval = VarRefParse("default:bundle.inner[one]"); Rlist *list = NULL; RlistAppendScalar(&list, "foo"); EvalContextVariablePut(ctx, lval, list, CF_DATA_TYPE_STRING_LIST, NULL); RlistDestroy(list); VarRefDestroy(lval); } Rlist *outer = NULL; RlistAppendScalar(&outer, "@{inner[$(i)]}"); Rlist *expanded = ExpandList(ctx, "default", "bundle", outer, true); assert_int_equal(1, RlistLen(expanded)); assert_string_equal("foo", RlistScalarValue(expanded)); RlistDestroy(outer); RlistDestroy(expanded); }
static void test_remove(void) { VariableTable *t = ReferenceTable(); { VarRef *ref = VarRefParse("scope1.array[one]"); assert_true(VariableTableRemove(t, ref)); assert_true(VariableTableGet(t, ref) == NULL); assert_false(VariableTableRemove(t, ref)); assert_int_equal(11, VariableTableCount(t, NULL, NULL, NULL)); VarRefDestroy(ref); } { VarRef *ref = VarRefParse("ns1:scope1.lval1"); assert_true(VariableTableRemove(t, ref)); assert_true(VariableTableGet(t, ref) == NULL); assert_false(VariableTableRemove(t, ref)); assert_int_equal(10, VariableTableCount(t, NULL, NULL, NULL)); VarRefDestroy(ref); } VariableTableDestroy(t); }
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 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 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); }
PromiseIterator *PromiseIteratorNew(EvalContext *ctx, const Promise *pp, const Rlist *lists, const Rlist *containers) { PromiseIterator *iter = xmalloc(sizeof(PromiseIterator)); iter->vars = SeqNew(RlistLen(lists), DeleteAssoc); iter->var_states = SeqNew(RlistLen(lists), NULL); iter->has_null_list = false; for (const Rlist *rp = lists; rp != NULL; rp = rp->next) { VarRef *ref = VarRefParseFromBundle(RlistScalarValue(rp), PromiseGetBundle(pp)); DataType dtype = CF_DATA_TYPE_NONE; const void *value = EvalContextVariableGet(ctx, ref, &dtype); if (!value) { Log(LOG_LEVEL_ERR, "Couldn't locate variable '%s' apparently in '%s'", RlistScalarValue(rp), PromiseGetBundle(pp)->name); VarRefDestroy(ref); continue; } VarRefDestroy(ref); CfAssoc *new_var = NewAssoc(RlistScalarValue(rp), (Rval) { (void *)value, DataTypeToRvalType(dtype) }, dtype); iter->has_null_list |= !AppendIterationVariable(iter, new_var); } for (const Rlist *rp = containers; rp; rp = rp->next) { VarRef *ref = VarRefParseFromBundle(RlistScalarValue(rp), PromiseGetBundle(pp)); DataType dtype = CF_DATA_TYPE_NONE; const JsonElement *value = EvalContextVariableGet(ctx, ref, &dtype); if (!value) { Log(LOG_LEVEL_ERR, "Couldn't locate variable '%s' apparently in '%s'", RlistScalarValue(rp), PromiseGetBundle(pp)->name); VarRefDestroy(ref); continue; } VarRefDestroy(ref); assert(dtype == CF_DATA_TYPE_CONTAINER); /* Mimics NewAssoc() but bypassing extra copying of ->rval: */ CfAssoc *new_var = xmalloc(sizeof(CfAssoc)); new_var->lval = xstrdup(RlistScalarValue(rp)); new_var->rval = (Rval) { ContainerToRlist(value), RVAL_TYPE_LIST }; new_var->dtype = CF_DATA_TYPE_STRING_LIST; iter->has_null_list |= !AppendIterationVariable(iter, new_var); } // We now have a control list of list-variables, with internal state in state_ptr return iter; }
static void CopyLocalizedReferencesToBundleScope(EvalContext *ctx, const Bundle *bundle, const Rlist *ref_names) { for (const Rlist *rp = ref_names; rp != NULL; rp = rp->next) { const char *mangled = RlistScalarValue(rp); char *demangled = xstrdup(mangled); DeMangleVarRefString(demangled, strlen(demangled)); if (strchr(RlistScalarValue(rp), CF_MAPPEDLIST)) { VarRef *demangled_ref = VarRefParseFromBundle(demangled, bundle); DataType value_type; const void *value = EvalContextVariableGet(ctx, demangled_ref, &value_type); if (!value) { ProgrammingError("Couldn't find extracted variable '%s'", mangled); } VarRef *mangled_ref = VarRefParseFromBundle(mangled, bundle); switch (DataTypeToRvalType(value_type)) { case RVAL_TYPE_LIST: { Rlist *list = RlistCopy(value); RlistFlatten(ctx, &list); EvalContextVariablePut(ctx, mangled_ref, list, value_type, "source=agent"); RlistDestroy(list); } break; case RVAL_TYPE_CONTAINER: case RVAL_TYPE_SCALAR: EvalContextVariablePut(ctx, mangled_ref, value, value_type, "source=agent"); break; case RVAL_TYPE_FNCALL: case RVAL_TYPE_NOPROMISEE: ProgrammingError("Illegal rval type in switch %d", DataTypeToRvalType(value_type)); } VarRefDestroy(mangled_ref); VarRefDestroy(demangled_ref); } free(demangled); } }
static VersionCmpResult RunCmpCommand(EvalContext *ctx, const char *command, const char *v1, const char *v2, Attributes a, const Promise *pp, PromiseResult *result) { Buffer *expanded_command = BufferNew(); { VarRef *ref_v1 = VarRefParseFromScope("v1", PACKAGES_CONTEXT); EvalContextVariablePut(ctx, ref_v1, v1, CF_DATA_TYPE_STRING, "source=promise"); VarRef *ref_v2 = VarRefParseFromScope("v2", PACKAGES_CONTEXT); EvalContextVariablePut(ctx, ref_v2, v2, CF_DATA_TYPE_STRING, "source=promise"); ExpandScalar(ctx, NULL, PACKAGES_CONTEXT, command, expanded_command); EvalContextVariableRemove(ctx, ref_v1); VarRefDestroy(ref_v1); EvalContextVariableRemove(ctx, ref_v2); VarRefDestroy(ref_v2); } FILE *pfp = a.packages.package_commands_useshell ? cf_popen_sh(BufferData(expanded_command), "w") : cf_popen(BufferData(expanded_command), "w", true); if (pfp == NULL) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Can not start package version comparison command '%s'. (cf_popen: %s)", BufferData(expanded_command), GetErrorStr()); *result = PromiseResultUpdate(*result, PROMISE_RESULT_FAIL); BufferDestroy(expanded_command); return VERCMP_ERROR; } Log(LOG_LEVEL_VERBOSE, "Executing '%s'", BufferData(expanded_command)); int retcode = cf_pclose(pfp); if (retcode == -1) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Error during package version comparison command execution '%s'. (cf_pclose: %s)", BufferData(expanded_command), GetErrorStr()); *result = PromiseResultUpdate(*result, PROMISE_RESULT_FAIL); BufferDestroy(expanded_command); return VERCMP_ERROR; } BufferDestroy(expanded_command); return retcode == 0; }
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 Rval ExpandListEntry(EvalContext *ctx, const char *ns, const char *scope, int expandnaked, Rval entry) { if (entry.type == RVAL_TYPE_SCALAR && IsNakedVar(entry.item, '@')) { if (expandnaked) { char naked[CF_MAXVARSIZE]; GetNaked(naked, entry.item); if (!IsExpandable(naked)) { VarRef *ref = VarRefParseFromScope(naked, scope); DataType value_type = CF_DATA_TYPE_NONE; const void *value = EvalContextVariableGet(ctx, ref, &value_type); VarRefDestroy(ref); if (value) { return ExpandPrivateRval(ctx, ns, scope, value, DataTypeToRvalType(value_type)); } } } else { return RvalNew(entry.item, RVAL_TYPE_SCALAR); } } return ExpandPrivateRval(ctx, ns, scope, entry.item, entry.type); }
static bool PutVar(VariableTable *table, char *var_str) { VarRef *ref = VarRefParse(var_str); Rval rval = (Rval) { var_str, RVAL_TYPE_SCALAR }; bool ret = VariableTablePut(table, ref, &rval, CF_DATA_TYPE_STRING, NULL, NULL); VarRefDestroy(ref); return ret; }
static VariableTable *ReferenceTable(void) { VariableTable *t = VariableTableNew(); assert_false(PutVar(t, "scope1.lval1")); assert_false(PutVar(t, "scope1.lval2")); assert_false(PutVar(t, "scope2.lval1")); { VarRef *ref = VarRefParse("scope1.array[one]"); Rval rval = (Rval) { "scope1.array[one]", RVAL_TYPE_SCALAR }; assert_false(VariableTablePut(t, ref, &rval, DATA_TYPE_STRING, NULL, NULL)); VarRefDestroy(ref); } { VarRef *ref = VarRefParse("scope1.array[two]"); Rval rval = (Rval) { "scope1.array[two]", RVAL_TYPE_SCALAR }; assert_false(VariableTablePut(t, ref, &rval, DATA_TYPE_STRING, NULL, NULL)); VarRefDestroy(ref); } { VarRef *ref = VarRefParse("scope1.array[two][three]"); Rval rval = (Rval) { "scope1.array[two][three]", RVAL_TYPE_SCALAR }; assert_false(VariableTablePut(t, ref, &rval, DATA_TYPE_STRING, NULL, NULL)); VarRefDestroy(ref); } { VarRef *ref = VarRefParse("scope1.array[two][four]"); Rval rval = (Rval) { "scope1.array[two][four]", RVAL_TYPE_SCALAR }; assert_false(VariableTablePut(t, ref, &rval, DATA_TYPE_STRING, NULL, NULL)); VarRefDestroy(ref); } assert_false(PutVar(t, "ns1:scope1.lval1")); assert_false(PutVar(t, "ns1:scope1.lval2")); assert_false(PutVar(t, "ns1:scope2.lval1")); return t; }
static void CheckToString(const char *str) { VarRef ref = VarRefParse(str); char *out = VarRefToString(ref); assert_string_equal(str, out); free(out); VarRefDestroy(ref); }
static void KeepPromises(EvalContext *ctx, const Policy *policy) { Rval retval; Seq *constraints = ControlBodyConstraints(policy, AGENT_TYPE_MONITOR); if (constraints) { for (size_t i = 0; i < SeqLength(constraints); i++) { Constraint *cp = SeqAt(constraints, i); if (!IsDefinedClass(ctx, cp->classes, NULL)) { continue; } VarRef *ref = VarRefParseFromScope(cp->lval, "control_monitor"); if (!EvalContextVariableGet(ctx, ref, &retval, NULL)) { Log(LOG_LEVEL_ERR, "Unknown lval '%s' in monitor control body", cp->lval); VarRefDestroy(ref); continue; } VarRefDestroy(ref); if (strcmp(cp->lval, CFM_CONTROLBODY[MONITOR_CONTROL_HISTOGRAMS].lval) == 0) { /* Keep accepting this option for backward compatibility. */ } if (strcmp(cp->lval, CFM_CONTROLBODY[MONITOR_CONTROL_TCP_DUMP].lval) == 0) { MonNetworkSnifferEnable(BooleanFromString(retval.item)); } if (strcmp(cp->lval, CFM_CONTROLBODY[MONITOR_CONTROL_FORGET_RATE].lval) == 0) { sscanf(retval.item, "%lf", &FORGETRATE); Log(LOG_LEVEL_DEBUG, "forget rate %f", FORGETRATE); } } } }
static void TestGet(VariableTable *t, const char *ref_str) { VarRef *ref = VarRefParse(ref_str); Variable *v = VariableTableGet(t, ref); assert_true(v != NULL); assert_int_equal(0, VarRefCompare(ref, v->ref)); assert_string_equal(ref_str, RvalScalarValue(v->rval)); VarRefDestroy(ref); }
static void test_qualified_array(void **state) { VarRef ref = VarRefParse("scope.lval[x]"); assert_false(ref.ns); assert_string_equal("scope", ref.scope); assert_string_equal("lval", ref.lval); assert_int_equal(1, ref.num_indices); assert_string_equal("x", ref.indices[0]); VarRefDestroy(ref); }
static void test_plain_variable_with_no_stuff_in_it(void **state) { VarRef ref = VarRefParse("foo"); assert_false(ref.ns); assert_false(ref.scope); assert_string_equal("foo", ref.lval); assert_int_equal(0, ref.num_indices); assert_false(ref.indices); VarRefDestroy(ref); }
static void test_dotted_array(void **state) { VarRef ref = VarRefParse("ns:scope.lval[la.la]"); assert_string_equal("ns", ref.ns); assert_string_equal("scope", ref.scope); assert_string_equal("lval", ref.lval); assert_int_equal(1, ref.num_indices); assert_string_equal("la.la", ref.indices[0]); VarRefDestroy(ref); }
static void test_full(void **state) { VarRef ref = VarRefParse("ns:scope.lval"); assert_string_equal("ns", ref.ns); assert_string_equal("scope", ref.scope); assert_string_equal("lval", ref.lval); assert_int_equal(0, ref.num_indices); assert_false(ref.indices); VarRefDestroy(ref); }
static VersionCmpResult RunCmpCommand(EvalContext *ctx, const char *command, const char *v1, const char *v2, Attributes a, Promise *pp) { char expanded_command[CF_EXPANDSIZE]; { VarRef *ref_v1 = VarRefParseFromScope("v1", "cf_pack_context"); EvalContextVariablePut(ctx, ref_v1, (Rval) { v1, RVAL_TYPE_SCALAR }, DATA_TYPE_STRING); VarRef *ref_v2 = VarRefParseFromScope("v2", "cf_pack_context"); EvalContextVariablePut(ctx, ref_v2, (Rval) { v2, RVAL_TYPE_SCALAR }, DATA_TYPE_STRING); ExpandScalar(ctx, NULL, "cf_pack_context", command, expanded_command); EvalContextVariableRemove(ctx, ref_v1); VarRefDestroy(ref_v1); EvalContextVariableRemove(ctx, ref_v2); VarRefDestroy(ref_v2); } FILE *pfp = a.packages.package_commands_useshell ? cf_popen_sh(expanded_command, "w") : cf_popen(expanded_command, "w", true); if (pfp == NULL) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Can not start package version comparison command '%s'. (cf_popen: %s)", expanded_command, GetErrorStr()); return VERCMP_ERROR; } Log(LOG_LEVEL_VERBOSE, "Executing '%s'", expanded_command); int retcode = cf_pclose(pfp); if (retcode == -1) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Error during package version comparison command execution '%s'. (cf_pclose: %s)", expanded_command, GetErrorStr()); return VERCMP_ERROR; } return retcode == 0; }
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 test_levels(void **state) { VarRef ref = VarRefParse("ns:scope.lval[x][y][z]"); assert_string_equal("ns", ref.ns); assert_string_equal("scope", ref.scope); assert_string_equal("lval", ref.lval); assert_int_equal(3, ref.num_indices); assert_string_equal("x", ref.indices[0]); assert_string_equal("y", ref.indices[1]); assert_string_equal("z", ref.indices[2]); VarRefDestroy(ref); }
/** * @WARNING Don't call ScopeDelete*() before this, it's unnecessary. */ void ScopeNewSpecial(EvalContext *ctx, SpecialScope scope, const char *lval, const void *rval, DataType dt) { Rval rvald; VarRef *ref = VarRefParseFromScope(lval, SpecialScopeToString(scope)); if (EvalContextVariableGet(ctx, ref, &rvald, NULL)) { ScopeDeleteSpecial(scope, lval); } EvalContextVariablePut(ctx, ref, (Rval) { rval, DataTypeToRvalType(dt) }, dt); VarRefDestroy(ref); }
Rlist *ExpandList(EvalContext *ctx, const char *ns, const char *scope, const Rlist *list, int expandnaked) { Rlist *start = NULL; Rval returnval; for (const Rlist *rp = list; rp != NULL; rp = rp->next) { if (!expandnaked && (rp->val.type == RVAL_TYPE_SCALAR) && IsNakedVar(RlistScalarValue(rp), '@')) { returnval = RvalNew(RlistScalarValue(rp), RVAL_TYPE_SCALAR); } else if ((rp->val.type == RVAL_TYPE_SCALAR) && IsNakedVar(RlistScalarValue(rp), '@')) { char naked[CF_MAXVARSIZE]; GetNaked(naked, RlistScalarValue(rp)); if (!IsExpandable(naked)) { VarRef *ref = VarRefParseFromScope(naked, scope); DataType value_type = DATA_TYPE_NONE; const void *value = EvalContextVariableGet(ctx, ref, &value_type); if (value) { returnval = ExpandPrivateRval(ctx, ns, scope, value, DataTypeToRvalType(value_type)); } else { returnval = ExpandPrivateRval(ctx, ns, scope, rp->val.item, rp->val.type); } VarRefDestroy(ref); } else { returnval = ExpandPrivateRval(ctx, ns, scope, rp->val.item, rp->val.type); } } else { returnval = ExpandPrivateRval(ctx, ns, scope, rp->val.item, rp->val.type); } RlistAppend(&start, returnval.item, returnval.type); RvalDestroy(returnval); } return start; }
void RlistFlatten(EvalContext *ctx, Rlist **list) { for (Rlist *rp = *list; rp != NULL;) { if (rp->val.type != RVAL_TYPE_SCALAR) { rp = rp->next; continue; } char naked[CF_BUFSIZE] = ""; if (IsNakedVar(RlistScalarValue(rp), '@')) { GetNaked(naked, RlistScalarValue(rp)); if (!IsExpandable(naked)) { Rval rv; VarRef *ref = VarRefParse(naked); bool var_found = EvalContextVariableGet(ctx, ref, &rv, NULL); VarRefDestroy(ref); if (var_found) { switch (rv.type) { case RVAL_TYPE_LIST: for (const Rlist *srp = rv.item; srp != NULL; srp = srp->next) { RlistAppendRval(list, RvalCopy(srp->val)); } Rlist *next = rp->next; RlistDestroyEntry(list, rp); rp = next; continue; default: ProgrammingError("List variable does not resolve to a list"); RlistAppendRval(list, RvalCopy(rp->val)); break; } } } } rp = rp->next; } }
static void test_replace(void) { VariableTable *t = ReferenceTable(); VarRef *ref = VarRefParse("scope1.lval1"); TestGet(t, "scope1.lval1"); Rval rval = (Rval) { "foo", RVAL_TYPE_SCALAR }; assert_true(VariableTablePut(t, ref, &rval, CF_DATA_TYPE_STRING, NULL, NULL)); Variable *v = VariableTableGet(t, ref); assert_true(v != NULL); assert_string_equal("foo", RvalScalarValue(v->rval)); VarRefDestroy(ref); VariableTableDestroy(t); }
static Rval ExpandListEntry(EvalContext *ctx, const char *ns, const char *scope, int expandnaked, Rval entry) { if (entry.type == RVAL_TYPE_SCALAR && IsNakedVar(entry.item, '@')) { if (expandnaked) { char naked[CF_MAXVARSIZE]; GetNaked(naked, entry.item); if (IsExpandable(naked)) { char *exp = ExpandScalar(ctx, ns, scope, naked, NULL); strlcpy(naked, exp, sizeof(naked)); /* TODO err */ free(exp); } /* Check again, it might have changed. */ if (!IsExpandable(naked)) { VarRef *ref = VarRefParseFromScope(naked, scope); DataType value_type; const void *value = EvalContextVariableGet(ctx, ref, &value_type); VarRefDestroy(ref); if (value_type != CF_DATA_TYPE_NONE) /* variable found? */ { return ExpandPrivateRval(ctx, ns, scope, value, DataTypeToRvalType(value_type)); } } } else { return RvalNew(entry.item, RVAL_TYPE_SCALAR); } } return ExpandPrivateRval(ctx, ns, scope, entry.item, entry.type); }
void RlistFlatten(EvalContext *ctx, Rlist **list) { Rlist *prev = NULL, *next; for (Rlist *rp = *list; rp != NULL; rp = next) { next = rp->next; if (rp->val.type != RVAL_TYPE_SCALAR) { prev = rp; continue; } char naked[CF_BUFSIZE] = ""; if (IsNakedVar(RlistScalarValue(rp), '@')) { GetNaked(naked, RlistScalarValue(rp)); if (!IsExpandable(naked)) { VarRef *ref = VarRefParse(naked); DataType value_type = CF_DATA_TYPE_NONE; const void *value = EvalContextVariableGet(ctx, ref, &value_type); VarRefDestroy(ref); if (value) { switch (DataTypeToRvalType(value_type)) { case RVAL_TYPE_LIST: { RlistDestroyEntry(list, rp); for (const Rlist *srp = value; srp != NULL; srp = srp->next) { Rlist *nrp = xmalloc(sizeof(Rlist)); nrp->val = RvalCopy(srp->val); nrp->next = next; if (prev) { prev->next = nrp; } else { *list = nrp; } prev = nrp; } } continue; default: Log(LOG_LEVEL_WARNING, "Attempted to dereference variable '%s' using @ but variable did not resolve to a list", RlistScalarValue(rp)); break; } } } } prev = rp; } }
PromiseResult VerifyVarPromise(EvalContext *ctx, const Promise *pp, bool allow_duplicates) { ConvergeVariableOptions opts = CollectConvergeVariableOptions(ctx, pp, allow_duplicates); if (!opts.should_converge) { return PROMISE_RESULT_NOOP; } Attributes a = { {0} }; // More consideration needs to be given to using these //a.transaction = GetTransactionConstraints(pp); a.classes = GetClassDefinitionConstraints(ctx, pp); VarRef *ref = VarRefParseFromBundle(pp->promiser, PromiseGetBundle(pp)); if (strcmp("meta", pp->parent_promise_type->name) == 0) { VarRefSetMeta(ref, true); } DataType existing_value_type = CF_DATA_TYPE_NONE; const void *const existing_value = IsExpandable(pp->promiser) ? NULL : EvalContextVariableGet(ctx, ref, &existing_value_type); PromiseResult result = PROMISE_RESULT_NOOP; Rval rval = opts.cp_save->rval; if (rval.item != NULL) { DataType data_type = DataTypeFromString(opts.cp_save->lval); if (opts.cp_save->rval.type == RVAL_TYPE_FNCALL) { FnCall *fp = RvalFnCallValue(rval); const FnCallType *fn = FnCallTypeGet(fp->name); if (!fn) { assert(false && "Canary: should have been caught before this point"); FatalError(ctx, "While setting variable '%s' in bundle '%s', unknown function '%s'", pp->promiser, PromiseGetBundle(pp)->name, fp->name); } if (fn->dtype != DataTypeFromString(opts.cp_save->lval)) { FatalError(ctx, "While setting variable '%s' in bundle '%s', variable declared type '%s' but function '%s' returns type '%s'", pp->promiser, PromiseGetBundle(pp)->name, opts.cp_save->lval, fp->name, DataTypeToString(fn->dtype)); } if (existing_value_type != CF_DATA_TYPE_NONE) { // Already did this VarRefDestroy(ref); return PROMISE_RESULT_NOOP; } FnCallResult res = FnCallEvaluate(ctx, PromiseGetPolicy(pp), fp, pp); if (res.status == FNCALL_FAILURE) { /* We do not assign variables to failed fn calls */ RvalDestroy(res.rval); VarRefDestroy(ref); return PROMISE_RESULT_NOOP; } else { rval = res.rval; } } else { Buffer *conv = BufferNew(); bool malformed = false, misprint = false; if (strcmp(opts.cp_save->lval, "int") == 0) { long int asint = IntFromString(opts.cp_save->rval.item); if (asint == CF_NOINT) { malformed = true; } else if (0 > BufferPrintf(conv, "%ld", asint)) { misprint = true; } else { rval = RvalNew(BufferData(conv), opts.cp_save->rval.type); } } else if (strcmp(opts.cp_save->lval, "real") == 0) { double real_value; if (!DoubleFromString(opts.cp_save->rval.item, &real_value)) { malformed = true; } else if (0 > BufferPrintf(conv, "%lf", real_value)) { misprint = true; } else { rval = RvalNew(BufferData(conv), opts.cp_save->rval.type); } } else { rval = RvalCopy(opts.cp_save->rval); } BufferDestroy(conv); if (malformed) { /* Arises when opts->cp_save->rval.item isn't yet expanded. */ /* Has already been logged by *FromString */ VarRefDestroy(ref); return PromiseResultUpdate(result, PROMISE_RESULT_FAIL); } else if (misprint) { /* Even though no problems with memory allocation can * get here, there might be other problems. */ UnexpectedError("Problems writing to buffer"); VarRefDestroy(ref); return PROMISE_RESULT_NOOP; } else if (rval.type == RVAL_TYPE_LIST) { Rlist *rval_list = RvalRlistValue(rval); RlistFlatten(ctx, &rval_list); rval.item = rval_list; } } if (Epimenides(ctx, PromiseGetBundle(pp)->ns, PromiseGetBundle(pp)->name, pp->promiser, rval, 0)) { Log(LOG_LEVEL_ERR, "Variable '%s' contains itself indirectly - an unkeepable promise", pp->promiser); exit(EXIT_FAILURE); } else { /* See if the variable needs recursively expanding again */ Rval returnval = EvaluateFinalRval(ctx, PromiseGetPolicy(pp), ref->ns, ref->scope, rval, true, pp); RvalDestroy(rval); // freed before function exit rval = returnval; } if (existing_value_type != CF_DATA_TYPE_NONE) { if (!opts.ok_redefine) /* only on second iteration, else we ignore broken promises */ { if (THIS_AGENT_TYPE == AGENT_TYPE_COMMON && !CompareRval(existing_value, DataTypeToRvalType(existing_value_type), rval.item, rval.type)) { switch (rval.type) { case RVAL_TYPE_SCALAR: Log(LOG_LEVEL_VERBOSE, "Redefinition of a constant scalar '%s', was '%s' now '%s'", pp->promiser, (const char *)existing_value, RvalScalarValue(rval)); PromiseRef(LOG_LEVEL_VERBOSE, pp); break; case RVAL_TYPE_LIST: { Log(LOG_LEVEL_VERBOSE, "Redefinition of a constant list '%s'", pp->promiser); Writer *w = StringWriter(); RlistWrite(w, existing_value); char *oldstr = StringWriterClose(w); Log(LOG_LEVEL_VERBOSE, "Old value '%s'", oldstr); free(oldstr); w = StringWriter(); RlistWrite(w, rval.item); char *newstr = StringWriterClose(w); Log(LOG_LEVEL_VERBOSE, " New value '%s'", newstr); free(newstr); PromiseRef(LOG_LEVEL_VERBOSE, pp); } break; case RVAL_TYPE_CONTAINER: case RVAL_TYPE_FNCALL: case RVAL_TYPE_NOPROMISEE: break; } } RvalDestroy(rval); VarRefDestroy(ref); return result; } } if (IsCf3VarString(pp->promiser)) { // Unexpanded variables, we don't do anything with RvalDestroy(rval); VarRefDestroy(ref); return result; } if (!IsValidVariableName(pp->promiser)) { Log(LOG_LEVEL_ERR, "Variable identifier contains illegal characters"); PromiseRef(LOG_LEVEL_ERR, pp); RvalDestroy(rval); VarRefDestroy(ref); return result; } if (rval.type == RVAL_TYPE_LIST) { if (opts.drop_undefined) { for (Rlist *rp = RvalRlistValue(rval); rp; rp = rp->next) { if (IsNakedVar(RlistScalarValue(rp), '@')) { free(rp->val.item); rp->val.item = xstrdup(CF_NULL_VALUE); } } } for (const Rlist *rp = RvalRlistValue(rval); rp; rp = rp->next) { switch (rp->val.type) { case RVAL_TYPE_SCALAR: break; default: // Cannot assign variable because value is a list containing a non-scalar item VarRefDestroy(ref); RvalDestroy(rval); return result; } } } if (ref->num_indices > 0) { if (data_type == CF_DATA_TYPE_CONTAINER) { char *lval_str = VarRefToString(ref, true); Log(LOG_LEVEL_ERR, "Cannot assign a container to an indexed variable name '%s'. Should be assigned to '%s' instead", lval_str, ref->lval); free(lval_str); VarRefDestroy(ref); RvalDestroy(rval); return result; } else { DataType existing_type = CF_DATA_TYPE_NONE; VarRef *base_ref = VarRefCopyIndexless(ref); if (EvalContextVariableGet(ctx, ref, &existing_type) && existing_type == CF_DATA_TYPE_CONTAINER) { char *lval_str = VarRefToString(ref, true); char *base_ref_str = VarRefToString(base_ref, true); Log(LOG_LEVEL_ERR, "Cannot assign value to indexed variable name '%s', because a container is already assigned to the base name '%s'", lval_str, base_ref_str); free(lval_str); free(base_ref_str); VarRefDestroy(base_ref); VarRefDestroy(ref); RvalDestroy(rval); return result; } VarRefDestroy(base_ref); } } DataType required_datatype = DataTypeFromString(opts.cp_save->lval); if (rval.type != DataTypeToRvalType(required_datatype)) { char *ref_str = VarRefToString(ref, true); char *value_str = RvalToString(rval); Log(LOG_LEVEL_ERR, "Variable '%s' expected a variable of type '%s', but was given incompatible value '%s'", ref_str, DataTypeToString(required_datatype), value_str); PromiseRef(LOG_LEVEL_ERR, pp); free(ref_str); free(value_str); VarRefDestroy(ref); RvalDestroy(rval); return PromiseResultUpdate(result, PROMISE_RESULT_FAIL); } if (!EvalContextVariablePut(ctx, ref, rval.item, required_datatype, "source=promise")) { Log(LOG_LEVEL_VERBOSE, "Unable to converge %s.%s value (possibly empty or infinite regression)", ref->scope, pp->promiser); PromiseRef(LOG_LEVEL_VERBOSE, pp); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); } else { Rlist *promise_meta = PromiseGetConstraintAsList(ctx, "meta", pp); if (promise_meta) { StringSet *class_meta = EvalContextVariableTags(ctx, ref); Buffer *print; for (const Rlist *rp = promise_meta; rp; rp = rp->next) { StringSetAdd(class_meta, xstrdup(RlistScalarValue(rp))); print = StringSetToBuffer(class_meta, ','); Log(LOG_LEVEL_DEBUG, "Added tag %s to class %s, tags now [%s]", RlistScalarValue(rp), pp->promiser, BufferData(print)); BufferDestroy(print); } } } } else { Log(LOG_LEVEL_ERR, "Variable %s has no promised value", pp->promiser); Log(LOG_LEVEL_ERR, "Rule from %s at/before line %llu", PromiseGetBundle(pp)->source_path, (unsigned long long)opts.cp_save->offset.line); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); } /* * FIXME: Variable promise are exempt from normal evaluation logic still, so * they are not pushed to evaluation stack before being evaluated. Due to * this reason, we cannot call cfPS here to set classes, as it will error * out with ProgrammingError. * * In order to support 'classes' body for variables as well, we call * ClassAuditLog explicitly. */ ClassAuditLog(ctx, pp, a, result); VarRefDestroy(ref); RvalDestroy(rval); return result; }