static Policy *LoadPolicyFile(EvalContext *ctx, GenericAgentConfig *config, const char *policy_file, StringSet *parsed_files, StringSet *failed_files) { Policy *policy = Cf3ParseFile(config, policy_file); StringSetAdd(parsed_files, xstrdup(policy_file)); if (!policy) { StringSetAdd(failed_files, xstrdup(policy_file)); return NULL; } PolicyResolve(ctx, policy, config); Body *body_common_control = PolicyGetBody(policy, NULL, "common", "control"); Body *body_file_control = PolicyGetBody(policy, NULL, "file", "control"); if (body_common_control) { Seq *potential_inputs = BodyGetConstraint(body_common_control, "inputs"); Constraint *cp = EffectiveConstraint(ctx, potential_inputs); SeqDestroy(potential_inputs); if (cp) { Policy *aux_policy = LoadPolicyInputFiles(ctx, config, RvalRlistValue(cp->rval), parsed_files, failed_files); if (aux_policy) { policy = PolicyMerge(policy, aux_policy); } } } PolicyResolve(ctx, policy, config); if (body_file_control) { Seq *potential_inputs = BodyGetConstraint(body_file_control, "inputs"); Constraint *cp = EffectiveConstraint(ctx, potential_inputs); SeqDestroy(potential_inputs); if (cp) { Policy *aux_policy = LoadPolicyInputFiles(ctx, config, RvalRlistValue(cp->rval), parsed_files, failed_files); if (aux_policy) { policy = PolicyMerge(policy, aux_policy); } } } return policy; }
void RvalWrite(Writer *writer, Rval rval) { if (rval.item == NULL) { return; } switch (rval.type) { case RVAL_TYPE_SCALAR: ScalarWrite(writer, RvalScalarValue(rval)); break; case RVAL_TYPE_LIST: RlistWrite(writer, RvalRlistValue(rval)); break; case RVAL_TYPE_FNCALL: FnCallWrite(writer, RvalFnCallValue(rval)); break; case RVAL_TYPE_NOPROMISEE: WriterWrite(writer, "(no-one)"); break; case RVAL_TYPE_CONTAINER: JsonWrite(writer, RvalContainerValue(rval), 0); break; } }
void RvalWrite(Writer *writer, Rval rval) { if (rval.item == NULL) { return; } switch (rval.type) { case RVAL_TYPE_SCALAR: ScalarWrite(writer, RvalScalarValue(rval)); break; case RVAL_TYPE_LIST: RlistWrite(writer, RvalRlistValue(rval)); break; case RVAL_TYPE_FNCALL: FnCallPrint(writer, RvalFnCallValue(rval)); break; case RVAL_TYPE_NOPROMISEE: WriterWrite(writer, "(no-one)"); break; default: ProgrammingError("Unknown rval type %c", rval.type); } }
Rval ExpandDanglers(EvalContext *ctx, const char *ns, const char *scope, Rval rval, const Promise *pp) { assert(ctx); assert(pp); switch (rval.type) { case RVAL_TYPE_SCALAR: if (IsCf3VarString(RvalScalarValue(rval))) { return EvaluateFinalRval(ctx, PromiseGetPolicy(pp), ns, scope, rval, false, pp); } else { return RvalCopy(rval); } break; case RVAL_TYPE_LIST: { Rlist *result_list = RlistCopy(RvalRlistValue(rval)); RlistFlatten(ctx, &result_list); return RvalNew(result_list, RVAL_TYPE_LIST); } break; case RVAL_TYPE_CONTAINER: case RVAL_TYPE_FNCALL: case RVAL_TYPE_NOPROMISEE: return RvalCopy(rval); } ProgrammingError("Unhandled Rval type"); }
void RvalDestroy(Rval rval) { if (rval.item == NULL) { return; } switch (rval.type) { case RVAL_TYPE_SCALAR: ThreadLock(cft_lock); free(RvalScalarValue(rval)); ThreadUnlock(cft_lock); return; case RVAL_TYPE_LIST: RlistDestroy(RvalRlistValue(rval)); return; case RVAL_TYPE_FNCALL: FnCallDestroy(RvalFnCallValue(rval)); break; case RVAL_TYPE_CONTAINER: JsonDestroy(RvalContainerValue(rval)); break; case RVAL_TYPE_NOPROMISEE: return; } }
static bool Epimenides(EvalContext *ctx, const char *ns, const char *scope, const char *var, Rval rval, int level) { switch (rval.type) { case RVAL_TYPE_SCALAR: if (StringContainsVar(RvalScalarValue(rval), var)) { Log(LOG_LEVEL_ERR, "Scalar variable '%s' contains itself (non-convergent) '%s'", var, RvalScalarValue(rval)); return true; } if (IsCf3VarString(RvalScalarValue(rval))) { Buffer *exp = BufferNew(); ExpandScalar(ctx, ns, scope, RvalScalarValue(rval), exp); if (strcmp(BufferData(exp), RvalScalarValue(rval)) == 0) { BufferDestroy(exp); return false; } if (level > 3) { BufferDestroy(exp); return false; } if (Epimenides(ctx, ns, scope, var, (Rval) { BufferGet(exp), RVAL_TYPE_SCALAR}, level + 1)) { BufferDestroy(exp); return true; } BufferDestroy(exp); } break; case RVAL_TYPE_LIST: for (const Rlist *rp = RvalRlistValue(rval); rp != NULL; rp = rp->next) { if (Epimenides(ctx, ns, scope, var, rp->val, level)) { return true; } } break; case RVAL_TYPE_CONTAINER: case RVAL_TYPE_FNCALL: case RVAL_TYPE_NOPROMISEE: return false; } return false; }
static Policy *LoadPolicyInputFiles(EvalContext *ctx, GenericAgentConfig *config, const Rlist *inputs, StringSet *parsed_files_and_checksums, StringSet *failed_files) { Policy *policy = PolicyNew(); for (const Rlist *rp = inputs; rp; rp = rp->next) { if (rp->val.type != RVAL_TYPE_SCALAR) { Log(LOG_LEVEL_ERR, "Non-file object in inputs list"); continue; } const char *unresolved_input = RlistScalarValue(rp); if (strcmp(CF_NULL_VALUE, unresolved_input) == 0) { continue; } if (IsExpandable(unresolved_input)) { PolicyResolve(ctx, policy, config); } Rval resolved_input = EvaluateFinalRval(ctx, policy, NULL, "sys", rp->val, true, NULL); Policy *aux_policy = NULL; switch (resolved_input.type) { case RVAL_TYPE_SCALAR: if (IsCf3VarString(RvalScalarValue(resolved_input))) { Log(LOG_LEVEL_ERR, "Unresolved variable '%s' in input list, cannot parse", RvalScalarValue(resolved_input)); break; } aux_policy = LoadPolicyFile(ctx, config, GenericAgentResolveInputPath(config, RvalScalarValue(resolved_input)), parsed_files_and_checksums, failed_files); break; case RVAL_TYPE_LIST: aux_policy = LoadPolicyInputFiles(ctx, config, RvalRlistValue(resolved_input), parsed_files_and_checksums, failed_files); break; default: ProgrammingError("Unknown type in input list for parsing: %d", resolved_input.type); break; } if (aux_policy) { policy = PolicyMerge(policy, aux_policy); } RvalDestroy(resolved_input); } return policy; }
static bool AppendIterationVariable(PromiseIterator *iter, CfAssoc *new_var) { Rlist *state = RvalRlistValue(new_var->rval); // move to the first non-null value state = FirstRealEntry(state); SeqAppend(iter->vars, new_var); SeqAppend(iter->var_states, state); return state != NULL; }
/** * Recursively go down the #rval and run PromiseIteratorPrepare() to take note * of all iterables and mangle all rvals than need to be mangled before * iterating. */ static void MapIteratorsFromRval(EvalContext *ctx, PromiseIterator *iterctx, Rval rval) { switch (rval.type) { case RVAL_TYPE_SCALAR: PromiseIteratorPrepare(iterctx, ctx, RvalScalarValue(rval)); break; case RVAL_TYPE_LIST: for (const Rlist *rp = RvalRlistValue(rval); rp != NULL; rp = rp->next) { MapIteratorsFromRval(ctx, iterctx, rp->val); } break; case RVAL_TYPE_FNCALL: { char *fn_name = RvalFnCallValue(rval)->name; /* Check function name. */ PromiseIteratorPrepare(iterctx, ctx, fn_name); /* Check each of the function arguments. */ /* EXCEPT on functions that use special variables: the mangled * variables would never be resolved if they contain inner special * variables (for example "$(bundle.A[$(this.k)])" and the returned * slist would contained mangled vars like "bundle#A[1]" which would * never resolve in future iterations. By skipping the iteration * engine for now, the function returns an slist with unmangled * entries, and the iteration engine works correctly on the next * pass! */ if (strcmp(fn_name, "maplist") != 0 && strcmp(fn_name, "mapdata") != 0 && strcmp(fn_name, "maparray")!= 0) { for (Rlist *rp = RvalFnCallValue(rval)->args; rp != NULL; rp = rp->next) { MapIteratorsFromRval(ctx, iterctx, rp->val); } } break; } case RVAL_TYPE_CONTAINER: case RVAL_TYPE_NOPROMISEE: break; } }
static void ResolvePackageManagerBody(EvalContext *ctx, const Body *pm_body) { PackageModuleBody *new_manager = xcalloc(1, sizeof(PackageModuleBody)); new_manager->name = SafeStringDuplicate(pm_body->name); for (size_t i = 0; i < SeqLength(pm_body->conlist); i++) { Constraint *cp = SeqAt(pm_body->conlist, i); Rval returnval = {0}; if (IsDefinedClass(ctx, cp->classes)) { returnval = ExpandPrivateRval(ctx, NULL, "body", cp->rval.item, cp->rval.type); } if (returnval.item == NULL || returnval.type == RVAL_TYPE_NOPROMISEE) { Log(LOG_LEVEL_VERBOSE, "have invalid constraint while resolving" "package promise body: %s", cp->lval); RvalDestroy(returnval); continue; } if (strcmp(cp->lval, "query_installed_ifelapsed") == 0) { new_manager->installed_ifelapsed = (int)IntFromString(RvalScalarValue(returnval)); } else if (strcmp(cp->lval, "query_updates_ifelapsed") == 0) { new_manager->updates_ifelapsed = (int)IntFromString(RvalScalarValue(returnval)); } else if (strcmp(cp->lval, "default_options") == 0) { new_manager->options = RlistCopy(RvalRlistValue(returnval)); } else { /* This should be handled by the parser. */ assert(0); } RvalDestroy(returnval); } AddPackageModuleToContext(ctx, new_manager); }
unsigned RvalHash(Rval rval, unsigned seed, unsigned max) { switch (rval.type) { case RVAL_TYPE_SCALAR: return StringHash(RvalScalarValue(rval), seed, max); case RVAL_TYPE_FNCALL: return FnCallHash(RvalFnCallValue(rval), seed, max); case RVAL_TYPE_LIST: return RlistHash(RvalRlistValue(rval), seed, max); case RVAL_TYPE_NOPROMISEE: return (seed + 1) % max; default: ProgrammingError("Unhandled case in switch: %d", rval.type); } }
unsigned RvalHash(Rval rval, unsigned seed) { switch (rval.type) { case RVAL_TYPE_SCALAR: return StringHash(RvalScalarValue(rval), seed); case RVAL_TYPE_FNCALL: return FnCallHash(RvalFnCallValue(rval), seed); case RVAL_TYPE_LIST: return RlistHash(RvalRlistValue(rval), seed); case RVAL_TYPE_NOPROMISEE: /* TODO modulus operation is biasing results. */ return (seed + 1); default: ProgrammingError("Unhandled case in switch: %d", rval.type); } }
JsonElement *RvalToJson(Rval rval) { assert(rval.item); switch (rval.type) { case RVAL_TYPE_SCALAR: return JsonStringCreate(RvalScalarValue(rval)); case RVAL_TYPE_LIST: return RlistToJson(RvalRlistValue(rval)); case RVAL_TYPE_FNCALL: return FnCallToJson(RvalFnCallValue(rval)); default: assert(false && "Invalid rval type"); return JsonStringCreate(""); } }
void MapIteratorsFromRval(EvalContext *ctx, const Bundle *bundle, Rval rval, Rlist **scalars, Rlist **lists, Rlist **containers) { assert(rval.item); if (rval.item == NULL) { return; } switch (rval.type) { case RVAL_TYPE_SCALAR: { char *val = RvalScalarValue(rval); size_t val_len = strlen(val); ExpandAndMapIteratorsFromScalar(ctx, bundle, val, val_len, 0, scalars, lists, containers, NULL); } break; case RVAL_TYPE_LIST: for (const Rlist *rp = RvalRlistValue(rval); rp; rp = rp->next) { MapIteratorsFromRval(ctx, bundle, rp->val, scalars, lists, containers); } break; case RVAL_TYPE_FNCALL: ExpandAndMapIteratorsFromScalar(ctx, bundle, RvalFnCallValue(rval)->name, strlen(RvalFnCallValue(rval)->name), 0, scalars, lists, containers, NULL); for (const Rlist *rp = RvalFnCallValue(rval)->args; rp; rp = rp->next) { Log(LOG_LEVEL_DEBUG, "Looking at arg for function-like object '%s'", RvalFnCallValue(rval)->name); MapIteratorsFromRval(ctx, bundle, rp->val, scalars, lists, containers); } break; case RVAL_TYPE_CONTAINER: case RVAL_TYPE_NOPROMISEE: Log(LOG_LEVEL_DEBUG, "Unknown Rval type for scope '%s'", bundle->name); break; } }
JsonElement *RvalToJson(Rval rval) { assert(rval.item); switch (rval.type) { case RVAL_TYPE_SCALAR: return JsonStringCreate(RvalScalarValue(rval)); case RVAL_TYPE_LIST: return RlistToJson(RvalRlistValue(rval)); case RVAL_TYPE_FNCALL: return FnCallToJson(RvalFnCallValue(rval)); case RVAL_TYPE_CONTAINER: return JsonCopy(RvalContainerValue(rval)); case RVAL_TYPE_NOPROMISEE: assert(false); return JsonObjectCreate(1); } assert(false); return NULL; }
static bool VariableStateReset(PromiseIterator *iter, size_t index) { assert(index < SeqLength(iter->var_states)); CfAssoc *var = SeqAt(iter->vars, index); switch (var->rval.type) { case RVAL_TYPE_LIST: { Rlist *state = RvalRlistValue(var->rval); // find the first valid value, return false if there is none state = FirstRealEntry(state); SeqSet(iter->var_states, index, state); return state != NULL; } break; default: ProgrammingError("Unhandled case in switch"); } return false; }
Rval EvaluateFinalRval(EvalContext *ctx, const Policy *policy, const char *ns, const char *scope, Rval rval, bool forcelist, const Promise *pp) { assert(ctx); assert(policy); Rval returnval, newret; if ((rval.type == RVAL_TYPE_SCALAR) && IsNakedVar(rval.item, '@')) /* Treat lists specially here */ { char naked[CF_MAXVARSIZE]; GetNaked(naked, rval.item); if (!IsExpandable(naked)) { VarRef *ref = VarRefParseFromScope(naked, scope); DataType value_type = DATA_TYPE_NONE; const void *value = EvalContextVariableGet(ctx, ref, &value_type); if (!value || DataTypeToRvalType(value_type) != RVAL_TYPE_LIST) { returnval = ExpandPrivateRval(ctx, NULL, "this", rval.item, rval.type); } else { returnval.item = ExpandList(ctx, ns, scope, value, true); returnval.type = RVAL_TYPE_LIST; } VarRefDestroy(ref); } else { returnval = ExpandPrivateRval(ctx, NULL, "this", rval.item, rval.type); } } else { if (forcelist) /* We are replacing scalar @(name) with list */ { returnval = ExpandPrivateRval(ctx, ns, scope, rval.item, rval.type); } else { if (FnCallIsBuiltIn(rval)) { returnval = RvalCopy(rval); } else { returnval = ExpandPrivateRval(ctx, NULL, "this", rval.item, rval.type); } } } switch (returnval.type) { case RVAL_TYPE_SCALAR: case RVAL_TYPE_CONTAINER: break; case RVAL_TYPE_LIST: for (Rlist *rp = RvalRlistValue(returnval); rp; rp = rp->next) { if (rp->val.type == RVAL_TYPE_FNCALL) { FnCall *fp = RlistFnCallValue(rp); FnCallResult res = FnCallEvaluate(ctx, policy, fp, pp); FnCallDestroy(fp); rp->val = res.rval; } else { if (EvalContextStackCurrentPromise(ctx)) { if (IsCf3VarString(RlistScalarValue(rp))) { newret = ExpandPrivateRval(ctx, NULL, "this", rp->val.item, rp->val.type); free(rp->val.item); rp->val.item = newret.item; } } } /* returnval unchanged */ } break; case RVAL_TYPE_FNCALL: if (FnCallIsBuiltIn(returnval)) { FnCall *fp = RvalFnCallValue(returnval); returnval = FnCallEvaluate(ctx, policy, fp, pp).rval; FnCallDestroy(fp); } break; default: returnval.item = NULL; returnval.type = RVAL_TYPE_NOPROMISEE; break; } return returnval; }
static void test_rval_to_list2(void) { Rval rval = { NULL, RVAL_TYPE_LIST }; assert_false(RvalRlistValue(rval)); }
static void test_rval_to_list(void) { Rval rval = { NULL, RVAL_TYPE_SCALAR }; expect_assert_failure(RvalRlistValue(rval)); }
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; }
Promise *DeRefCopyPromise(EvalContext *ctx, const Promise *pp) { Promise *pcopy; Rval returnval; if (pp->promisee.item) { CfDebug("CopyPromise(%s->", pp->promiser); if (DEBUG) { RvalShow(stdout, pp->promisee); } CfDebug("\n"); } else { CfDebug("CopyPromise(%s->)\n", pp->promiser); } 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; CfDebug("Copying promise constraints\n\n"); /* 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; char *bodyname = NULL; /* A body template reference could look like a scalar or fn to the parser w/w () */ Policy *policy = PolicyFromPromise(pp); Seq *bodies = policy ? policy->bodies : NULL; switch (cp->rval.type) { case RVAL_TYPE_SCALAR: bodyname = (char *) cp->rval.item; if (cp->references_body) { bp = IsBody(bodies, PromiseGetNamespace(pp), bodyname); } fp = NULL; break; case RVAL_TYPE_FNCALL: fp = (FnCall *) cp->rval.item; bodyname = fp->name; bp = IsBody(bodies, PromiseGetNamespace(pp), bodyname); break; default: bp = NULL; fp = NULL; bodyname = NULL; break; } /* First case is: we have a body template to expand lval = body(args), .. */ if (bp) { EvalContextStackPushBodyFrame(ctx, bp); if (strcmp(bp->type, cp->lval) != 0) { Log(LOG_LEVEL_ERR, "Body type mismatch for body reference \"%s\" in promise at line %zu of %s (%s != %s)\n", bodyname, pp->offset.line, PromiseGetBundle(pp)->source_path, bp->type, cp->lval); } /* Keep the referent body type as a boolean for convenience when checking later */ { Constraint *cp_copy = PromiseAppendConstraint(pcopy, cp->lval, (Rval) {xstrdup("true"), RVAL_TYPE_SCALAR }, cp->classes, false); cp_copy->offset = cp->offset; } CfDebug("Handling body-lval \"%s\"\n", cp->lval); 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 %zu of %s", bodyname, pp->offset.line, PromiseGetBundle(pp)->source_path); } if (fp && bp && fp->args && bp->args && !ScopeMapBodyArgs(ctx, "body", fp->args, bp->args)) { Log(LOG_LEVEL_ERR, "Number of arguments does not match for body reference \"%s\" in promise at line %zu of %s\n", bodyname, pp->offset.line, PromiseGetBundle(pp)->source_path); } for (size_t k = 0; k < SeqLength(bp->conlist); k++) { Constraint *scp = SeqAt(bp->conlist, k); CfDebug("Doing arg-mapped sublval = %s (promises.c)\n", scp->lval); returnval = ExpandPrivateRval(ctx, "body", scp->rval); { Constraint *scp_copy = PromiseAppendConstraint(pcopy, scp->lval, returnval, scp->classes, false); scp_copy->offset = scp->offset; } } ScopeClear("body"); } 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 %zu of %s (possible unquoted literal value)", bodyname, pp->offset.line, PromiseGetBundle(pp)->source_path); } else { for (size_t k = 0; k < SeqLength(bp->conlist); k++) { Constraint *scp = SeqAt(bp->conlist, k); CfDebug("Doing sublval = %s (promises.c)\n", scp->lval); 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, scp->classes, false); scp_copy->offset = scp->offset; } } } } EvalContextStackPopFrame(ctx); } else { Policy *policy = PolicyFromPromise(pp); if (cp->references_body && !IsBundle(policy->bundles, bodyname)) { Log(LOG_LEVEL_ERR, "Apparent body \"%s()\" was undeclared, but used in a promise near line %zu of %s (possible unquoted literal value)", bodyname, pp->offset.line, PromiseGetBundle(pp)->source_path); } 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, cp->classes, false); cp_copy->offset = cp->offset; } } } return pcopy; }
Promise *DeRefCopyPromise(EvalContext *ctx, const Promise *pp) { Promise *pcopy; Rval returnval; 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 %zu of file '%s', '%s' does not equal '%s'", body_name, 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, PromiseGetNamespace(pcopy))) { 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 %zu of file '%s'", body_name, pp->offset.line, PromiseGetBundle(pp)->source_path); } for (size_t k = 0; k < SeqLength(bp->conlist); k++) { Constraint *scp = SeqAt(bp->conlist, k); returnval = ExpandPrivateRval(ctx, NULL, "body", scp->rval.item, scp->rval.type); if (IsDefinedClass(ctx, scp->classes, PromiseGetNamespace(pcopy))) { 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 %zu of %s (possible unquoted literal value)", body_name, pp->offset.line, PromiseGetBundle(pp)->source_path); } else { for (size_t k = 0; k < SeqLength(bp->conlist); k++) { Constraint *scp = SeqAt(bp->conlist, k); Rval newrv = RvalCopy(scp->rval); if (newrv.type == RVAL_TYPE_LIST) { Rlist *new_list = RvalRlistValue(newrv); RlistFlatten(ctx, &new_list); newrv.item = new_list; } if (IsDefinedClass(ctx, scp->classes, PromiseGetNamespace(pcopy))) { 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 %zu of %s (possible unquoted literal value)", body_name, pp->offset.line, PromiseGetBundle(pp)->source_path); } Rval newrv = RvalCopy(cp->rval); if (newrv.type == RVAL_TYPE_LIST) { Rlist *new_list = RvalRlistValue(newrv); RlistFlatten(ctx, &new_list); newrv.item = new_list; } if (IsDefinedClass(ctx, cp->classes, PromiseGetNamespace(pcopy))) { Constraint *cp_copy = PromiseAppendConstraint(pcopy, cp->lval, newrv, false); cp_copy->offset = cp->offset; } } } return pcopy; }
void ScopeMapBodyArgs(EvalContext *ctx, const Body *body, const Rlist *args) { const Rlist *arg = NULL; const Rlist *param = NULL; for (arg = args, param = body->args; arg != NULL && param != NULL; arg = arg->next, param = param->next) { DataType arg_type = CF_DATA_TYPE_NONE; switch (arg->val.type) { case RVAL_TYPE_SCALAR: arg_type = StringDataType(ctx, RlistScalarValue(arg)); break; case RVAL_TYPE_FNCALL: { const FnCallType *fn = FnCallTypeGet(RlistFnCallValue(arg)->name); if (!fn) { FatalError(ctx, "Argument '%s' given to body '%s' is not a valid function", RlistFnCallValue(arg)->name, body->name); } arg_type = fn->dtype; } break; default: FatalError(ctx, "Cannot derive data type from Rval type %c", arg->val.type); } switch (arg->val.type) { case RVAL_TYPE_SCALAR: { const char *lval = RlistScalarValue(param); VarRef *ref = VarRefParseFromNamespaceAndScope(lval, NULL, "body", CF_NS, '.'); EvalContextVariablePut(ctx, ref, RvalScalarValue(arg->val), arg_type, "source=body"); VarRefDestroy(ref); } break; case RVAL_TYPE_LIST: { const char *lval = RlistScalarValue(param); VarRef *ref = VarRefParseFromNamespaceAndScope(lval, NULL, "body", CF_NS, '.'); EvalContextVariablePut(ctx, ref, RvalRlistValue(arg->val), arg_type, "source=body"); VarRefDestroy(ref); } break; case RVAL_TYPE_FNCALL: { FnCall *fp = RlistFnCallValue(arg); arg_type = CF_DATA_TYPE_NONE; { const FnCallType *fncall_type = FnCallTypeGet(fp->name); if (fncall_type) { arg_type = fncall_type->dtype; } } FnCallResult res = FnCallEvaluate(ctx, body->parent_policy, fp, NULL); if (res.status == FNCALL_FAILURE && THIS_AGENT_TYPE != AGENT_TYPE_COMMON) { Log(LOG_LEVEL_VERBOSE, "Embedded function argument does not resolve to a name - probably too many evaluation levels for '%s'", fp->name); } else { const char *lval = RlistScalarValue(param); void *rval = res.rval.item; VarRef *ref = VarRefParseFromNamespaceAndScope(lval, NULL, "body", CF_NS, '.'); EvalContextVariablePut(ctx, ref, rval, arg_type, "source=body"); VarRefDestroy(ref); } RvalDestroy(res.rval); } break; default: /* Nothing else should happen */ ProgrammingError("Software error: something not a scalar/function in argument literal"); } } }
static Policy *LoadPolicyFile(EvalContext *ctx, GenericAgentConfig *config, const char *policy_file, StringSet *parsed_files_and_checksums, StringSet *failed_files) { Policy *policy = NULL; unsigned char digest[EVP_MAX_MD_SIZE + 1] = { 0 }; char hashbuffer[EVP_MAX_MD_SIZE * 4] = { 0 }; char hashprintbuffer[CF_BUFSIZE] = { 0 }; HashFile(policy_file, digest, CF_DEFAULT_DIGEST); snprintf(hashprintbuffer, CF_BUFSIZE - 1, "{checksum}%s", HashPrintSafe(CF_DEFAULT_DIGEST, true, digest, hashbuffer)); Log(LOG_LEVEL_DEBUG, "Hashed policy file %s to %s", policy_file, hashprintbuffer); if (StringSetContains(parsed_files_and_checksums, policy_file)) { Log(LOG_LEVEL_VERBOSE, "Skipping loading of duplicate policy file %s", policy_file); return NULL; } else if (StringSetContains(parsed_files_and_checksums, hashprintbuffer)) { Log(LOG_LEVEL_VERBOSE, "Skipping loading of duplicate (detected by hash) policy file %s", policy_file); return NULL; } else { Log(LOG_LEVEL_DEBUG, "Loading policy file %s", policy_file); } policy = Cf3ParseFile(config, policy_file); // we keep the checksum and the policy file name to help debugging StringSetAdd(parsed_files_and_checksums, xstrdup(policy_file)); StringSetAdd(parsed_files_and_checksums, xstrdup(hashprintbuffer)); if (policy) { Seq *errors = SeqNew(10, free); if (!PolicyCheckPartial(policy, errors)) { Writer *writer = FileWriter(stderr); for (size_t i = 0; i < errors->length; i++) { PolicyErrorWrite(writer, errors->data[i]); } WriterClose(writer); SeqDestroy(errors); StringSetAdd(failed_files, xstrdup(policy_file)); return NULL; } SeqDestroy(errors); } else { StringSetAdd(failed_files, xstrdup(policy_file)); return NULL; } PolicyResolve(ctx, policy, config); Body *body_common_control = PolicyGetBody(policy, NULL, "common", "control"); Body *body_file_control = PolicyGetBody(policy, NULL, "file", "control"); if (body_common_control) { Seq *potential_inputs = BodyGetConstraint(body_common_control, "inputs"); Constraint *cp = EffectiveConstraint(ctx, potential_inputs); SeqDestroy(potential_inputs); if (cp) { Policy *aux_policy = LoadPolicyInputFiles(ctx, config, RvalRlistValue(cp->rval), parsed_files_and_checksums, failed_files); if (aux_policy) { policy = PolicyMerge(policy, aux_policy); } } } if (body_file_control) { Seq *potential_inputs = BodyGetConstraint(body_file_control, "inputs"); Constraint *cp = EffectiveConstraint(ctx, potential_inputs); SeqDestroy(potential_inputs); if (cp) { Policy *aux_policy = LoadPolicyInputFiles(ctx, config, RvalRlistValue(cp->rval), parsed_files_and_checksums, failed_files); if (aux_policy) { policy = PolicyMerge(policy, aux_policy); } } } return policy; }
Rval EvaluateFinalRval(EvalContext *ctx, const Policy *policy, const char *ns, const char *scope, Rval rval, bool forcelist, const Promise *pp) { assert(ctx); assert(policy); Rval returnval; /* Treat lists specially. */ if (rval.type == RVAL_TYPE_SCALAR && IsNakedVar(rval.item, '@')) { char naked[CF_MAXVARSIZE]; GetNaked(naked, rval.item); if (IsExpandable(naked)) /* example: @(blah_$(blue)) */ { returnval = ExpandPrivateRval(ctx, NULL, "this", rval.item, rval.type); } else { VarRef *ref = VarRefParseFromScope(naked, scope); DataType value_type; const void *value = EvalContextVariableGet(ctx, ref, &value_type); VarRefDestroy(ref); if (DataTypeToRvalType(value_type) == RVAL_TYPE_LIST) { returnval.item = ExpandList(ctx, ns, scope, value, true); returnval.type = RVAL_TYPE_LIST; } else { returnval = ExpandPrivateRval(ctx, NULL, "this", rval.item, rval.type); } } } else if (forcelist) /* We are replacing scalar @(name) with list */ { returnval = ExpandPrivateRval(ctx, ns, scope, rval.item, rval.type); } else if (FnCallIsBuiltIn(rval)) { returnval = RvalCopy(rval); } else { returnval = ExpandPrivateRval(ctx, NULL, "this", rval.item, rval.type); } switch (returnval.type) { case RVAL_TYPE_SCALAR: case RVAL_TYPE_CONTAINER: break; case RVAL_TYPE_LIST: for (Rlist *rp = RvalRlistValue(returnval); rp; rp = rp->next) { switch (rp->val.type) { case RVAL_TYPE_FNCALL: { FnCall *fp = RlistFnCallValue(rp); rp->val = FnCallEvaluate(ctx, policy, fp, pp).rval; FnCallDestroy(fp); break; } case RVAL_TYPE_SCALAR: if (EvalContextStackCurrentPromise(ctx) && IsCf3VarString(RlistScalarValue(rp))) { void *prior = rp->val.item; rp->val = ExpandPrivateRval(ctx, NULL, "this", prior, RVAL_TYPE_SCALAR); free(prior); } /* else: returnval unchanged. */ break; default: assert(!"Bad type for entry in Rlist"); } } break; case RVAL_TYPE_FNCALL: if (FnCallIsBuiltIn(returnval)) { FnCall *fp = RvalFnCallValue(returnval); returnval = FnCallEvaluate(ctx, policy, fp, pp).rval; FnCallDestroy(fp); } break; default: assert(returnval.item == NULL); /* else we're leaking it */ returnval.item = NULL; returnval.type = RVAL_TYPE_NOPROMISEE; break; } return returnval; }
void KeepControlPromises(Policy *policy) { Rval retval; Rlist *rp; Seq *constraints = ControlBodyConstraints(policy, AGENT_TYPE_AGENT); if (constraints) { for (size_t i = 0; i < SeqLength(constraints); i++) { Constraint *cp = SeqAt(constraints, i); if (IsExcluded(cp->classes, NULL)) { continue; } if (GetVariable("control_common", cp->lval, &retval) != DATA_TYPE_NONE) { /* Already handled in generic_agent */ continue; } if (GetVariable("control_agent", cp->lval, &retval) == DATA_TYPE_NONE) { CfOut(OUTPUT_LEVEL_ERROR, "", "Unknown lval %s in agent control body", cp->lval); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_maxconnections].lval) == 0) { CFA_MAXTHREADS = (int) IntFromString(retval.item); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET maxconnections = %d\n", CFA_MAXTHREADS); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_checksum_alert_time].lval) == 0) { CF_PERSISTENCE = (int) IntFromString(retval.item); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET checksum_alert_time = %d\n", CF_PERSISTENCE); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_agentfacility].lval) == 0) { SetFacility(retval.item); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_agentaccess].lval) == 0) { ACCESSLIST = (Rlist *) retval.item; CheckAgentAccess(ACCESSLIST, InputFiles(policy)); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_refresh_processes].lval) == 0) { Rlist *rp; if (VERBOSE) { printf("%s> SET refresh_processes when starting: ", VPREFIX); for (rp = (Rlist *) retval.item; rp != NULL; rp = rp->next) { printf(" %s", (char *) rp->item); PrependItem(&PROCESSREFRESH, rp->item, NULL); } printf("\n"); } continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_abortclasses].lval) == 0) { Rlist *rp; CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET Abort classes from ...\n"); for (rp = (Rlist *) retval.item; rp != NULL; rp = rp->next) { char name[CF_MAXVARSIZE] = ""; strncpy(name, rp->item, CF_MAXVARSIZE - 1); AddAbortClass(name, cp->classes); } continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_abortbundleclasses].lval) == 0) { Rlist *rp; CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET Abort bundle classes from ...\n"); for (rp = (Rlist *) retval.item; rp != NULL; rp = rp->next) { char name[CF_MAXVARSIZE] = ""; strncpy(name, rp->item, CF_MAXVARSIZE - 1); if (!IsItemIn(ABORTBUNDLEHEAP, name)) { AppendItem(&ABORTBUNDLEHEAP, name, cp->classes); } } continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_addclasses].lval) == 0) { Rlist *rp; CfOut(OUTPUT_LEVEL_VERBOSE, "", "-> Add classes ...\n"); for (rp = (Rlist *) retval.item; rp != NULL; rp = rp->next) { CfOut(OUTPUT_LEVEL_VERBOSE, "", " -> ... %s\n", RlistScalarValue(rp)); NewClass(rp->item, NULL); } continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_auditing].lval) == 0) { CfOut(OUTPUT_LEVEL_VERBOSE, "", "This option does nothing and is retained for compatibility reasons"); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_alwaysvalidate].lval) == 0) { ALWAYS_VALIDATE = BooleanFromString(retval.item); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET alwaysvalidate = %d\n", ALWAYS_VALIDATE); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_allclassesreport].lval) == 0) { ALLCLASSESREPORT = BooleanFromString(retval.item); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET allclassesreport = %d\n", ALLCLASSESREPORT); } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_secureinput].lval) == 0) { CFPARANOID = BooleanFromString(retval.item); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET secure input = %d\n", CFPARANOID); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_binarypaddingchar].lval) == 0) { CfOut(OUTPUT_LEVEL_VERBOSE, "", "binarypaddingchar is obsolete and does nothing\n"); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_bindtointerface].lval) == 0) { strncpy(BINDINTERFACE, retval.item, CF_BUFSIZE - 1); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET bindtointerface = %s\n", BINDINTERFACE); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_hashupdates].lval) == 0) { bool enabled = BooleanFromString(retval.item); SetChecksumUpdates(enabled); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET ChecksumUpdates %d\n", enabled); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_exclamation].lval) == 0) { CfOut(OUTPUT_LEVEL_VERBOSE, "", "exclamation control is deprecated and does not do anything\n"); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_childlibpath].lval) == 0) { char output[CF_BUFSIZE]; snprintf(output, CF_BUFSIZE, "LD_LIBRARY_PATH=%s", (char *) retval.item); if (putenv(xstrdup(output)) == 0) { CfOut(OUTPUT_LEVEL_VERBOSE, "", "Setting %s\n", output); } continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_defaultcopytype].lval) == 0) { DEFAULT_COPYTYPE = (char *) retval.item; CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET defaultcopytype = %s\n", DEFAULT_COPYTYPE); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_fsinglecopy].lval) == 0) { SINGLE_COPY_LIST = (Rlist *) retval.item; CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET file single copy list\n"); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_fautodefine].lval) == 0) { SetFileAutoDefineList(RvalRlistValue(retval)); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET file auto define list\n"); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_dryrun].lval) == 0) { DONTDO = BooleanFromString(retval.item); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET dryrun = %c\n", DONTDO); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_inform].lval) == 0) { INFORM = BooleanFromString(retval.item); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET inform = %c\n", INFORM); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_verbose].lval) == 0) { VERBOSE = BooleanFromString(retval.item); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET inform = %c\n", VERBOSE); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_repository].lval) == 0) { SetRepositoryLocation(retval.item); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET repository = %s\n", RvalScalarValue(retval)); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_skipidentify].lval) == 0) { bool enabled = BooleanFromString(retval.item); SetSkipIdentify(enabled); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET skipidentify = %d\n", (int) enabled); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_suspiciousnames].lval) == 0) { for (rp = (Rlist *) retval.item; rp != NULL; rp = rp->next) { AddFilenameToListOfSuspicious(RlistScalarValue(rp)); CfOut(OUTPUT_LEVEL_VERBOSE, "", "-> Considering %s as suspicious file", RlistScalarValue(rp)); } continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_repchar].lval) == 0) { char c = *(char *) retval.item; SetRepositoryChar(c); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET repchar = %c\n", c); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_mountfilesystems].lval) == 0) { CF_MOUNTALL = BooleanFromString(retval.item); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET mountfilesystems = %d\n", CF_MOUNTALL); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_editfilesize].lval) == 0) { EDITFILESIZE = IntFromString(retval.item); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET EDITFILESIZE = %d\n", EDITFILESIZE); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_ifelapsed].lval) == 0) { VIFELAPSED = IntFromString(retval.item); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET ifelapsed = %d\n", VIFELAPSED); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_expireafter].lval) == 0) { VEXPIREAFTER = IntFromString(retval.item); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET ifelapsed = %d\n", VEXPIREAFTER); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_timeout].lval) == 0) { CONNTIMEOUT = IntFromString(retval.item); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET timeout = %jd\n", (intmax_t) CONNTIMEOUT); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_max_children].lval) == 0) { CFA_BACKGROUND_LIMIT = IntFromString(retval.item); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET MAX_CHILDREN = %d\n", CFA_BACKGROUND_LIMIT); if (CFA_BACKGROUND_LIMIT > 10) { CfOut(OUTPUT_LEVEL_ERROR, "", "Silly value for max_children in agent control promise (%d > 10)", CFA_BACKGROUND_LIMIT); CFA_BACKGROUND_LIMIT = 1; } continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_syslog].lval) == 0) { CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET syslog = %d\n", BooleanFromString(retval.item)); continue; } if (strcmp(cp->lval, CFA_CONTROLBODY[cfa_environment].lval) == 0) { Rlist *rp; CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET environment variables from ...\n"); for (rp = (Rlist *) retval.item; rp != NULL; rp = rp->next) { if (putenv(rp->item) != 0) { CfOut(OUTPUT_LEVEL_ERROR, "putenv", "Failed to set environment variable %s", RlistScalarValue(rp)); } } continue; } } } if (GetVariable("control_common", CFG_CONTROLBODY[cfg_lastseenexpireafter].lval, &retval) != DATA_TYPE_NONE) { LASTSEENEXPIREAFTER = IntFromString(retval.item) * 60; } if (GetVariable("control_common", CFG_CONTROLBODY[cfg_fips_mode].lval, &retval) != DATA_TYPE_NONE) { FIPS_MODE = BooleanFromString(retval.item); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET FIPS_MODE = %d\n", FIPS_MODE); } if (GetVariable("control_common", CFG_CONTROLBODY[cfg_syslog_port].lval, &retval) != DATA_TYPE_NONE) { SetSyslogPort(IntFromString(retval.item)); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET syslog_port to %s", RvalScalarValue(retval)); } if (GetVariable("control_common", CFG_CONTROLBODY[cfg_syslog_host].lval, &retval) != DATA_TYPE_NONE) { SetSyslogHost(Hostname2IPString(retval.item)); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET syslog_host to %s", Hostname2IPString(retval.item)); } #ifdef HAVE_NOVA Nova_Initialize(); #endif }
void ScopeMapBodyArgs(EvalContext *ctx, const Body *body, const Rlist *args) { const Rlist *arg = NULL; const Rlist *param = NULL; for (arg = args, param = body->args; arg != NULL && param != NULL; arg = arg->next, param = param->next) { DataType arg_type = StringDataType(ctx, RlistScalarValue(arg)); DataType param_type = StringDataType(ctx, RlistScalarValue(param)); if (arg_type != param_type) { Log(LOG_LEVEL_ERR, "Type mismatch between logical/formal parameters %s/%s", RlistScalarValue(arg), RlistScalarValue(param)); Log(LOG_LEVEL_ERR, "%s is %s whereas %s is %s", RlistScalarValue(arg), DataTypeToString(arg_type), RlistScalarValue(param), DataTypeToString(param_type)); } switch (arg->val.type) { case RVAL_TYPE_SCALAR: { const char *lval = RlistScalarValue(param); VarRef *ref = VarRefParseFromNamespaceAndScope(lval, NULL, "body", CF_NS, '.'); EvalContextVariablePut(ctx, ref, RvalScalarValue(arg->val), arg_type, "source=body"); } break; case RVAL_TYPE_LIST: { const char *lval = RlistScalarValue(param); VarRef *ref = VarRefParseFromNamespaceAndScope(lval, NULL, "body", CF_NS, '.'); EvalContextVariablePut(ctx, ref, RvalRlistValue(arg->val), arg_type, "source=body"); VarRefDestroy(ref); } break; case RVAL_TYPE_FNCALL: { FnCall *fp = RlistFnCallValue(arg); arg_type = DATA_TYPE_NONE; { const FnCallType *fncall_type = FnCallTypeGet(fp->name); if (fncall_type) { arg_type = fncall_type->dtype; } } FnCallResult res = FnCallEvaluate(ctx, fp, NULL); if (res.status == FNCALL_FAILURE && THIS_AGENT_TYPE != AGENT_TYPE_COMMON) { Log(LOG_LEVEL_VERBOSE, "Embedded function argument does not resolve to a name - probably too many evaluation levels for '%s'", fp->name); } else { FnCallDestroy(fp); const char *lval = RlistScalarValue(param); void *rval = res.rval.item; VarRef *ref = VarRefParseFromNamespaceAndScope(lval, NULL, "body", CF_NS, '.'); EvalContextVariablePut(ctx, ref, rval, arg_type, "source=body"); VarRefDestroy(ref); } } break; default: /* Nothing else should happen */ ProgrammingError("Software error: something not a scalar/function in argument literal"); } } }
/** * Evaluate the relevant control body, and set the * relevant fields in #ctx and #config. */ static void ResolveControlBody(EvalContext *ctx, GenericAgentConfig *config, const Body *control_body) { const char *filename = control_body->source_path; assert(CFG_CONTROLBODY[COMMON_CONTROL_MAX].lval == NULL); const ConstraintSyntax *body_syntax = NULL; for (int i = 0; CONTROL_BODIES[i].constraints != NULL; i++) { body_syntax = CONTROL_BODIES[i].constraints; if (strcmp(control_body->type, CONTROL_BODIES[i].body_type) == 0) { break; } } if (body_syntax == NULL) { FatalError(ctx, "Unknown control body: %s", control_body->type); } char *scope; assert(strcmp(control_body->name, "control") == 0); xasprintf(&scope, "control_%s", control_body->type); Log(LOG_LEVEL_DEBUG, "Initiate control variable convergence for scope '%s'", scope); EvalContextStackPushBodyFrame(ctx, NULL, control_body, NULL); for (size_t i = 0; i < SeqLength(control_body->conlist); i++) { const char *lval; Rval evaluated_rval; size_t lineno; /* Use nested scope to constrain cp. */ { Constraint *cp = SeqAt(control_body->conlist, i); lval = cp->lval; lineno = cp->offset.line; if (!IsDefinedClass(ctx, cp->classes)) { continue; } if (strcmp(lval, CFG_CONTROLBODY[COMMON_CONTROL_BUNDLESEQUENCE].lval) == 0) { evaluated_rval = ExpandPrivateRval(ctx, NULL, scope, cp->rval.item, cp->rval.type); } else { evaluated_rval = EvaluateFinalRval(ctx, control_body->parent_policy, NULL, scope, cp->rval, true, NULL); } } /* Close scope: assert we only use evaluated_rval, not cp->rval. */ VarRef *ref = VarRefParseFromScope(lval, scope); EvalContextVariableRemove(ctx, ref); DataType rval_proper_datatype = ConstraintSyntaxGetDataType(body_syntax, lval); if (evaluated_rval.type != DataTypeToRvalType(rval_proper_datatype)) { Log(LOG_LEVEL_ERR, "Attribute '%s' in %s:%zu is of wrong type, skipping", lval, filename, lineno); VarRefDestroy(ref); RvalDestroy(evaluated_rval); continue; } bool success = EvalContextVariablePut( ctx, ref, evaluated_rval.item, rval_proper_datatype, "source=promise"); if (!success) { Log(LOG_LEVEL_ERR, "Attribute '%s' in %s:%zu can't be added, skipping", lval, filename, lineno); VarRefDestroy(ref); RvalDestroy(evaluated_rval); continue; } VarRefDestroy(ref); if (strcmp(lval, CFG_CONTROLBODY[COMMON_CONTROL_OUTPUT_PREFIX].lval) == 0) { strlcpy(VPREFIX, RvalScalarValue(evaluated_rval), sizeof(VPREFIX)); } if (strcmp(lval, CFG_CONTROLBODY[COMMON_CONTROL_DOMAIN].lval) == 0) { strlcpy(VDOMAIN, RvalScalarValue(evaluated_rval), sizeof(VDOMAIN)); Log(LOG_LEVEL_VERBOSE, "SET domain = %s", VDOMAIN); EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_SYS, "domain"); EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_SYS, "fqhost"); snprintf(VFQNAME, CF_MAXVARSIZE, "%s.%s", VUQNAME, VDOMAIN); EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "fqhost", VFQNAME, CF_DATA_TYPE_STRING, "inventory,source=agent,attribute_name=Host name"); EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "domain", VDOMAIN, CF_DATA_TYPE_STRING, "source=agent"); EvalContextClassPutHard(ctx, VDOMAIN, "source=agent"); } if (strcmp(lval, CFG_CONTROLBODY[COMMON_CONTROL_IGNORE_MISSING_INPUTS].lval) == 0) { Log(LOG_LEVEL_VERBOSE, "SET ignore_missing_inputs %s", RvalScalarValue(evaluated_rval)); config->ignore_missing_inputs = BooleanFromString( RvalScalarValue(evaluated_rval)); } if (strcmp(lval, CFG_CONTROLBODY[COMMON_CONTROL_IGNORE_MISSING_BUNDLES].lval) == 0) { Log(LOG_LEVEL_VERBOSE, "SET ignore_missing_bundles %s", RvalScalarValue(evaluated_rval)); config->ignore_missing_bundles = BooleanFromString( RvalScalarValue(evaluated_rval)); } if (strcmp(lval, CFG_CONTROLBODY[COMMON_CONTROL_CACHE_SYSTEM_FUNCTIONS].lval) == 0) { Log(LOG_LEVEL_VERBOSE, "SET cache_system_functions %s", RvalScalarValue(evaluated_rval)); bool cache_system_functions = BooleanFromString( RvalScalarValue(evaluated_rval)); EvalContextSetEvalOption(ctx, EVAL_OPTION_CACHE_SYSTEM_FUNCTIONS, cache_system_functions); } if (strcmp(lval, CFG_CONTROLBODY[COMMON_CONTROL_PROTOCOL_VERSION].lval) == 0) { config->protocol_version = ProtocolVersionParse( RvalScalarValue(evaluated_rval)); Log(LOG_LEVEL_VERBOSE, "SET common protocol_version: %s", PROTOCOL_VERSION_STRING[config->protocol_version]); } /* Those are package_inventory and package_module common control body options */ if (strcmp(lval, CFG_CONTROLBODY[COMMON_CONTROL_PACKAGE_INVENTORY].lval) == 0) { AddDefaultInventoryToContext(ctx, RvalRlistValue(evaluated_rval)); Log(LOG_LEVEL_VERBOSE, "SET common package_inventory list"); } if (strcmp(lval, CFG_CONTROLBODY[COMMON_CONTROL_PACKAGE_MODULE].lval) == 0) { AddDefaultPackageModuleToContext(ctx, RvalScalarValue(evaluated_rval)); Log(LOG_LEVEL_VERBOSE, "SET common package_module: %s", RvalScalarValue(evaluated_rval)); } if (strcmp(lval, CFG_CONTROLBODY[COMMON_CONTROL_GOALPATTERNS].lval) == 0) { /* Ignored */ } RvalDestroy(evaluated_rval); } EvalContextStackPopFrame(ctx); free(scope); }
static Policy *LoadPolicyFile(EvalContext *ctx, GenericAgentConfig *config, const char *policy_file, StringSet *parsed_files_and_checksums, StringSet *failed_files) { unsigned char digest[EVP_MAX_MD_SIZE + 1] = { 0 }; char hashbuffer[CF_HOSTKEY_STRING_SIZE] = { 0 }; char hashprintbuffer[CF_BUFSIZE] = { 0 }; HashFile(policy_file, digest, CF_DEFAULT_DIGEST); snprintf(hashprintbuffer, CF_BUFSIZE - 1, "{checksum}%s", HashPrintSafe(hashbuffer, sizeof(hashbuffer), digest, CF_DEFAULT_DIGEST, true)); Log(LOG_LEVEL_DEBUG, "Hashed policy file %s to %s", policy_file, hashprintbuffer); if (StringSetContains(parsed_files_and_checksums, policy_file)) { Log(LOG_LEVEL_VERBOSE, "Skipping loading of duplicate policy file %s", policy_file); return NULL; } else if (StringSetContains(parsed_files_and_checksums, hashprintbuffer)) { Log(LOG_LEVEL_VERBOSE, "Skipping loading of duplicate (detected by hash) policy file %s", policy_file); return NULL; } else { Log(LOG_LEVEL_DEBUG, "Loading policy file %s", policy_file); } Policy *policy = Cf3ParseFile(config, policy_file); // we keep the checksum and the policy file name to help debugging StringSetAdd(parsed_files_and_checksums, xstrdup(policy_file)); StringSetAdd(parsed_files_and_checksums, xstrdup(hashprintbuffer)); if (policy) { Seq *errors = SeqNew(10, free); if (!PolicyCheckPartial(policy, errors)) { Writer *writer = FileWriter(stderr); for (size_t i = 0; i < errors->length; i++) { PolicyErrorWrite(writer, errors->data[i]); } WriterClose(writer); SeqDestroy(errors); StringSetAdd(failed_files, xstrdup(policy_file)); PolicyDestroy(policy); return NULL; } SeqDestroy(errors); } else { StringSetAdd(failed_files, xstrdup(policy_file)); return NULL; } PolicyResolve(ctx, policy, config); DataType def_inputs_type = CF_DATA_TYPE_NONE; VarRef *inputs_ref = VarRefParse("def.augment_inputs"); const void *def_inputs = EvalContextVariableGet(ctx, inputs_ref, &def_inputs_type); VarRefDestroy(inputs_ref); if (RVAL_TYPE_CONTAINER == DataTypeToRvalType(def_inputs_type) && NULL != def_inputs) { const JsonElement *el; JsonIterator iter = JsonIteratorInit((JsonElement*) def_inputs); while ((el = JsonIteratorNextValueByType(&iter, JSON_ELEMENT_TYPE_PRIMITIVE, true))) { char *input = JsonPrimitiveToString(el); Log(LOG_LEVEL_VERBOSE, "Loading augments from def.augment_inputs: %s", input); Rlist* inputs_rlist = NULL; RlistAppendScalar(&inputs_rlist, input); Policy *aux_policy = LoadPolicyInputFiles(ctx, config, inputs_rlist, parsed_files_and_checksums, failed_files); if (aux_policy) { policy = PolicyMerge(policy, aux_policy); } RlistDestroy(inputs_rlist); free(input); } } Body *body_common_control = PolicyGetBody(policy, NULL, "common", "control"); Body *body_file_control = PolicyGetBody(policy, NULL, "file", "control"); if (body_common_control) { Seq *potential_inputs = BodyGetConstraint(body_common_control, "inputs"); Constraint *cp = EffectiveConstraint(ctx, potential_inputs); SeqDestroy(potential_inputs); if (cp) { Policy *aux_policy = LoadPolicyInputFiles(ctx, config, RvalRlistValue(cp->rval), parsed_files_and_checksums, failed_files); if (aux_policy) { policy = PolicyMerge(policy, aux_policy); } } } if (body_file_control) { Seq *potential_inputs = BodyGetConstraint(body_file_control, "inputs"); Constraint *cp = EffectiveConstraint(ctx, potential_inputs); SeqDestroy(potential_inputs); if (cp) { Policy *aux_policy = LoadPolicyInputFiles(ctx, config, RvalRlistValue(cp->rval), parsed_files_and_checksums, failed_files); if (aux_policy) { policy = PolicyMerge(policy, aux_policy); } } } return policy; }
void VerifyVarPromise(EvalContext *ctx, const Promise *pp, bool allow_duplicates) { ConvergeVariableOptions opts = CollectConvergeVariableOptions(ctx, pp, allow_duplicates); if (!opts.should_converge) { return; } char *scope = NULL; if (strcmp("meta", pp->parent_promise_type->name) == 0) { scope = StringConcatenate(2, PromiseGetBundle(pp)->name, "_meta"); } else { scope = xstrdup(PromiseGetBundle(pp)->name); } //More consideration needs to be given to using these //a.transaction = GetTransactionConstraints(pp); Attributes a = { {0} }; a.classes = GetClassDefinitionConstraints(ctx, pp); Rval existing_var_rval; DataType existing_var_type = DATA_TYPE_NONE; EvalContextVariableGet(ctx, (VarRef) { NULL, scope, pp->promiser }, &existing_var_rval, &existing_var_type); Buffer *qualified_scope = BufferNew(); int result = 0; if (strcmp(PromiseGetNamespace(pp), "default") == 0) { result = BufferSet(qualified_scope, scope, strlen(scope)); if (result < 0) { /* * Even though there will be no problems with memory allocation, there * might be other problems. */ UnexpectedError("Problems writing to buffer"); free(scope); BufferDestroy(&qualified_scope); return; } } else { if (strchr(scope, ':') == NULL) { result = BufferPrintf(qualified_scope, "%s:%s", PromiseGetNamespace(pp), scope); if (result < 0) { /* * Even though there will be no problems with memory allocation, there * might be other problems. */ UnexpectedError("Problems writing to buffer"); free(scope); BufferDestroy(&qualified_scope); return; } } else { result = BufferSet(qualified_scope, scope, strlen(scope)); if (result < 0) { /* * Even though there will be no problems with memory allocation, there * might be other problems. */ UnexpectedError("Problems writing to buffer"); free(scope); BufferDestroy(&qualified_scope); return; } } } PromiseResult promise_result; Rval rval = opts.cp_save->rval; if (rval.item != NULL) { FnCall *fp = (FnCall *) rval.item; if (opts.cp_save->rval.type == RVAL_TYPE_FNCALL) { if (existing_var_type != DATA_TYPE_NONE) { // Already did this free(scope); BufferDestroy(&qualified_scope); return; } FnCallResult res = FnCallEvaluate(ctx, fp, pp); if (res.status == FNCALL_FAILURE) { /* We do not assign variables to failed fn calls */ RvalDestroy(res.rval); free(scope); BufferDestroy(&qualified_scope); return; } else { rval = res.rval; } } else { Buffer *conv = BufferNew(); if (strcmp(opts.cp_save->lval, "int") == 0) { result = BufferPrintf(conv, "%ld", IntFromString(opts.cp_save->rval.item)); if (result < 0) { /* * Even though there will be no problems with memory allocation, there * might be other problems. */ UnexpectedError("Problems writing to buffer"); free(scope); BufferDestroy(&qualified_scope); BufferDestroy(&conv); return; } rval = RvalCopy((Rval) {(char *)BufferData(conv), opts.cp_save->rval.type}); } else if (strcmp(opts.cp_save->lval, "real") == 0) { double real_value = 0.0; if (DoubleFromString(opts.cp_save->rval.item, &real_value)) { result = BufferPrintf(conv, "%lf", real_value); } else { result = BufferPrintf(conv, "(double conversion error)"); } if (result < 0) { /* * Even though there will be no problems with memory allocation, there * might be other problems. */ UnexpectedError("Problems writing to buffer"); free(scope); BufferDestroy(&conv); BufferDestroy(&qualified_scope); return; } rval = RvalCopy((Rval) {(char *)BufferData(conv), opts.cp_save->rval.type}); } else { rval = RvalCopy(opts.cp_save->rval); } if (rval.type == RVAL_TYPE_LIST) { Rlist *rval_list = RvalRlistValue(rval); RlistFlatten(ctx, &rval_list); rval.item = rval_list; } BufferDestroy(&conv); } if (Epimenides(ctx, PromiseGetBundle(pp)->name, pp->promiser, rval, 0)) { Log(LOG_LEVEL_ERR, "Variable \"%s\" contains itself indirectly - an unkeepable promise", pp->promiser); exit(1); } else { /* See if the variable needs recursively expanding again */ Rval returnval = EvaluateFinalRval(ctx, BufferData(qualified_scope), rval, true, pp); RvalDestroy(rval); // freed before function exit rval = returnval; } if (existing_var_type != DATA_TYPE_NONE) { if (opts.ok_redefine) /* only on second iteration, else we ignore broken promises */ { ScopeDeleteVariable(BufferData(qualified_scope), pp->promiser); } else if ((THIS_AGENT_TYPE == AGENT_TYPE_COMMON) && (CompareRval(existing_var_rval, rval) == false)) { switch (rval.type) { case RVAL_TYPE_SCALAR: Log(LOG_LEVEL_VERBOSE, "Redefinition of a constant scalar \"%s\" (was %s now %s)", pp->promiser, RvalScalarValue(existing_var_rval), 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_var_rval.item); 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; default: break; } } } if (IsCf3VarString(pp->promiser)) { // Unexpanded variables, we don't do anything with RvalDestroy(rval); free(scope); BufferDestroy(&qualified_scope); return; } if (!FullTextMatch("[a-zA-Z0-9_\200-\377.]+(\\[.+\\])*", pp->promiser)) { Log(LOG_LEVEL_ERR, "Variable identifier contains illegal characters"); PromiseRef(LOG_LEVEL_ERR, pp); RvalDestroy(rval); free(scope); BufferDestroy(&qualified_scope); return; } if (opts.drop_undefined && rval.type == RVAL_TYPE_LIST) { for (Rlist *rp = rval.item; rp != NULL; rp = rp->next) { if (IsNakedVar(rp->item, '@')) { free(rp->item); rp->item = xstrdup(CF_NULL_VALUE); } } } if (!EvalContextVariablePut(ctx, (VarRef) { NULL, BufferData(qualified_scope), pp->promiser }, rval, DataTypeFromString(opts.cp_save->lval))) { Log(LOG_LEVEL_VERBOSE, "Unable to converge %s.%s value (possibly empty or infinite regression)", BufferData(qualified_scope), pp->promiser); PromiseRef(LOG_LEVEL_VERBOSE, pp); promise_result = PROMISE_RESULT_FAIL; } else { promise_result = PROMISE_RESULT_CHANGE; } } else { Log(LOG_LEVEL_ERR, "Variable %s has no promised value", pp->promiser); Log(LOG_LEVEL_ERR, "Rule from %s at/before line %zu", PromiseGetBundle(pp)->source_path, opts.cp_save->offset.line); promise_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, promise_result); free(scope); BufferDestroy(&qualified_scope); RvalDestroy(rval); }