Rval ExpandBundleReference(EvalContext *ctx, const char *ns, const char *scope, Rval rval) { // Allocates new memory for the copy switch (rval.type) { case RVAL_TYPE_SCALAR: return (Rval) { ExpandScalar(ctx, ns, scope, RvalScalarValue(rval), NULL), RVAL_TYPE_SCALAR }; case RVAL_TYPE_FNCALL: return (Rval) { ExpandFnCall(ctx, ns, scope, RvalFnCallValue(rval)), RVAL_TYPE_FNCALL }; case RVAL_TYPE_CONTAINER: case RVAL_TYPE_LIST: case RVAL_TYPE_NOPROMISEE: return RvalNew(NULL, RVAL_TYPE_NOPROMISEE); } assert(false); return RvalNew(NULL, RVAL_TYPE_NOPROMISEE); }
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"); }
Rval ExpandPrivateRval(EvalContext *ctx, const char *ns, const char *scope, const void *rval_item, RvalType rval_type) { Rval returnval; returnval.item = NULL; returnval.type = RVAL_TYPE_NOPROMISEE; switch (rval_type) { case RVAL_TYPE_SCALAR: returnval.item = ExpandScalar(ctx, ns, scope, rval_item, NULL); returnval.type = RVAL_TYPE_SCALAR; break; case RVAL_TYPE_LIST: returnval.item = ExpandList(ctx, ns, scope, rval_item, true); returnval.type = RVAL_TYPE_LIST; break; case RVAL_TYPE_FNCALL: returnval.item = ExpandFnCall(ctx, ns, scope, rval_item); returnval.type = RVAL_TYPE_FNCALL; break; case RVAL_TYPE_CONTAINER: returnval = RvalNew(rval_item, RVAL_TYPE_CONTAINER); break; case RVAL_TYPE_NOPROMISEE: break; } return returnval; }
PromiseResult VerifyMethodsPromise(EvalContext *ctx, const Promise *pp) { Attributes a = GetMethodAttributes(ctx, pp); const Constraint *cp; Rval method_name; bool destroy_name; if ((cp = PromiseGetConstraint(pp, "usebundle"))) { method_name = cp->rval; destroy_name = false; } else { method_name = RvalNew(pp->promiser, RVAL_TYPE_SCALAR); destroy_name = true; } PromiseResult result = VerifyMethod(ctx, method_name, a, pp); if (destroy_name) { RvalDestroy(method_name); } return result; }
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); }
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; }
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); }
Rlist *RlistPrepend(Rlist **start, const void *item, RvalType type) { switch (type) { case RVAL_TYPE_LIST: { Rlist *lp = NULL; for (const Rlist *rp = item; rp; rp = rp->next) { lp = RlistPrependRval(start, RvalCopy(rp->val)); } return lp; } case RVAL_TYPE_SCALAR: case RVAL_TYPE_FNCALL: case RVAL_TYPE_CONTAINER: case RVAL_TYPE_NOPROMISEE: return RlistPrependRval(start, RvalNew(item, type)); } assert(false); return NULL; }
Rval ExpandPrivateRval(EvalContext *ctx, const char *ns, const char *scope, const void *rval_item, RvalType rval_type) { Rval returnval; returnval.item = NULL; returnval.type = RVAL_TYPE_NOPROMISEE; switch (rval_type) { case RVAL_TYPE_SCALAR: { Buffer *buffer = BufferNew(); ExpandScalar(ctx, ns, scope, rval_item, buffer); returnval = (Rval) { BufferClose(buffer), RVAL_TYPE_SCALAR }; } break; case RVAL_TYPE_LIST: returnval.item = ExpandList(ctx, ns, scope, rval_item, true); returnval.type = RVAL_TYPE_LIST; break; case RVAL_TYPE_FNCALL: returnval.item = ExpandFnCall(ctx, ns, scope, rval_item); returnval.type = RVAL_TYPE_FNCALL; break; case RVAL_TYPE_CONTAINER: returnval = RvalNew(JsonCopy(rval_item), RVAL_TYPE_CONTAINER); break; case RVAL_TYPE_NOPROMISEE: break; } return returnval; }
Rval RvalCopy(Rval rval) { return RvalNew(rval.item, rval.type); }
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; }
static void ParserStateReset(ParserState *p, bool discard) { p->agent_type = AGENT_TYPE_COMMON; p->warnings = PARSER_WARNING_ALL; p->policy = NULL; int i = CF_MAX_NESTING; while (i-- > 0) /* Clear stacks from top down */ { if (discard) { free(p->currentfnid[i]); RlistDestroy(p->giveargs[i]); FnCallDestroy(p->currentfncall[i]); } else { assert(!p->currentfnid[i]); assert(!p->giveargs[i]); assert(!p->currentfncall[i]); } p->currentfnid[i] = NULL; p->giveargs[i] = NULL; p->currentfncall[i] = NULL; } free(p->current_line); p->current_line = NULL; p->line_no = 1; p->line_pos = 1; p->error_count = 0; p->warning_count = 0; p->list_nesting = 0; p->arg_nesting = 0; free(p->current_namespace); p->current_namespace = xstrdup("default"); p->currentid[0] = '\0'; if (p->currentstring) { free(p->currentstring); } p->currentstring = NULL; p->currenttype[0] = '\0'; if (p->currentclasses) { free(p->currentclasses); } p->currentclasses = NULL; p->currentRlist = NULL; p->currentpromise = NULL; p->currentbody = NULL; if (p->promiser) { free(p->promiser); } p->promiser = NULL; p->blockid[0] = '\0'; p->blocktype[0] = '\0'; p->rval = RvalNew(NULL, RVAL_TYPE_NOPROMISEE); }
// See fncall.c for the usage of allow_all_types. Rlist *RlistAppendAllTypes(Rlist **start, const void *item, RvalType type, bool allow_all_types) { Rlist *lp = *start; switch (type) { case RVAL_TYPE_SCALAR: return RlistAppendScalar(start, item); case RVAL_TYPE_FNCALL: break; case RVAL_TYPE_LIST: if (allow_all_types) { JsonElement* store = JsonArrayCreate(RlistLen(item)); for (const Rlist *rp = item; rp; rp = rp->next) { JsonArrayAppendElement(store, RvalToJson(rp->val)); } return RlistAppendRval(start, (Rval) { store, RVAL_TYPE_CONTAINER }); } for (const Rlist *rp = item; rp; rp = rp->next) { lp = RlistAppendRval(start, RvalCopy(rp->val)); } return lp; case RVAL_TYPE_CONTAINER: if (allow_all_types) { return RlistAppendRval(start, (Rval) { JsonCopy((JsonElement*) item), RVAL_TYPE_CONTAINER }); } // note falls through! default: Log(LOG_LEVEL_DEBUG, "Cannot append %c to rval-list '%s'", type, (char *) item); return NULL; } Rlist *rp = xmalloc(sizeof(Rlist)); rp->val = RvalNew(item, type); rp->next = NULL; if (*start == NULL) { *start = rp; } else { for (lp = *start; lp->next != NULL; lp = lp->next) { } lp->next = rp; } return rp; }
/* Inserts an Rlist node with value #rval, right after the rlist node #node. */ void RlistInsertAfter(Rlist *node, Rval rval) { assert(node != NULL); Rlist new_node = { .val = rval, .next = node->next }; node->next = xmemdup(&new_node, sizeof(new_node)); } Rval RvalNewRewriter(const void *item, RvalType type, JsonElement *map) { switch (type) { case RVAL_TYPE_SCALAR: if (map != NULL && JsonLength(map) > 0 && // do we have a rewrite map? (strstr(item, "$(") || strstr(item, "${"))) // are there unresolved variable references? { // TODO: replace with BufferSearchAndReplace when the // string_replace code is merged. // Sorry about the CF_BUFSIZE ugliness. int max_size = 10*CF_BUFSIZE+1; char *buffer_from = xmalloc(max_size); char *buffer_to = xmalloc(max_size); Buffer *format = BufferNew(); StringCopy(item, buffer_from, max_size); for (int iteration = 0; iteration < 10; iteration++) { bool replacement_made = false; int var_start = -1; char closing_brace = 0; for (int c = 0; c < buffer_from[c]; c++) { if (buffer_from[c] == '$') { if (buffer_from[c+1] == '(') { closing_brace = ')'; } else if (buffer_from[c+1] == '{') { closing_brace = '}'; } if (closing_brace) { c++; var_start = c-1; } } else if (var_start >= 0 && buffer_from[c] == closing_brace) { char saved = buffer_from[c]; buffer_from[c] = '\0'; const char *repl = JsonObjectGetAsString(map, buffer_from + var_start + 2); buffer_from[c] = saved; if (repl) { // Before the replacement. memcpy(buffer_to, buffer_from, var_start); // The actual replacement. int repl_len = strlen(repl); memcpy(buffer_to + var_start, repl, repl_len); // The text after. strlcpy(buffer_to + var_start + repl_len, buffer_from + c + 1, max_size - var_start - repl_len); // Reset location to immediately after the replacement. c = var_start + repl_len - 1; var_start = -1; StringCopy(buffer_to, buffer_from, max_size); closing_brace = 0; replacement_made = true; } } } if (!replacement_made) { break; } } char *ret = xstrdup(buffer_to); BufferDestroy(format); free(buffer_to); free(buffer_from); return (Rval) { ret, RVAL_TYPE_SCALAR }; } else { return (Rval) { xstrdup(item), RVAL_TYPE_SCALAR }; } case RVAL_TYPE_FNCALL: return (Rval) { FnCallCopyRewriter(item, map), RVAL_TYPE_FNCALL }; case RVAL_TYPE_LIST: return (Rval) { RlistCopyRewriter(item, map), RVAL_TYPE_LIST }; case RVAL_TYPE_CONTAINER: return (Rval) { JsonCopy(item), RVAL_TYPE_CONTAINER }; case RVAL_TYPE_NOPROMISEE: return ((Rval) {NULL, type}); } assert(false); return ((Rval) { NULL, RVAL_TYPE_NOPROMISEE }); } Rval RvalNew(const void *item, RvalType type) { return RvalNewRewriter(item, type, NULL); } Rval RvalCopyRewriter(Rval rval, JsonElement *map) { return RvalNewRewriter(rval.item, rval.type, map); } Rval RvalCopy(Rval rval) { return RvalNew(rval.item, rval.type); } /*******************************************************************/ Rlist *RlistCopyRewriter(const Rlist *rp, JsonElement *map) { Rlist *start = NULL; while (rp != NULL) { RlistAppendRval(&start, RvalCopyRewriter(rp->val, map)); rp = rp->next; } return start; }