static int CompareRval(Rval rval1, Rval rval2) { if (rval1.type != rval2.type) { return -1; } switch (rval1.type) { case RVAL_TYPE_SCALAR: if (IsCf3VarString((char *) rval1.item) || IsCf3VarString((char *) rval2.item)) { return -1; // inconclusive } if (strcmp(rval1.item, rval2.item) != 0) { return false; } break; case RVAL_TYPE_LIST: return CompareRlist(rval1.item, rval2.item); case RVAL_TYPE_FNCALL: return -1; default: return -1; } return true; }
bool UnresolvedVariables(const CfAssoc *ap, RvalType rtype) { if (ap == NULL) { return false; } switch (rtype) { case RVAL_TYPE_SCALAR: return IsCf3VarString(ap->rval.item); case RVAL_TYPE_LIST: { for (const Rlist *rp = ap->rval.item; rp != NULL; rp = rp->next) { if (IsCf3VarString(rp->item)) { return true; } } } return false; default: return false; } }
int UnresolvedVariables(CfAssoc *ap, char rtype) { Rlist *list, *rp; if (ap == NULL) { return false; } switch (rtype) { case CF_SCALAR: return IsCf3VarString(ap->rval.item); case CF_LIST: list = (Rlist *) ap->rval.item; for (rp = list; rp != NULL; rp = rp->next) { if (IsCf3VarString(rp->item)) { return true; } } return false; default: return false; } }
bool RlistEqual(const Rlist *list1, const Rlist *list2) { const Rlist *rp1, *rp2; for (rp1 = list1, rp2 = list2; rp1 != NULL && rp2 != NULL; rp1 = rp1->next, rp2 = rp2->next) { if (rp1->val.item != NULL && rp2->val.item != NULL) { if (rp1->val.type == RVAL_TYPE_FNCALL || rp2->val.type == RVAL_TYPE_FNCALL) { return false; // inconclusive } const Rlist *rc1 = rp1; const Rlist *rc2 = rp2; // Check for list nesting with { fncall(), "x" ... } if (rp1->val.type == RVAL_TYPE_LIST) { rc1 = rp1->val.item; } if (rp2->val.type == RVAL_TYPE_LIST) { rc2 = rp2->val.item; } if (IsCf3VarString(rc1->val.item) || IsCf3VarString(rp2->val.item)) { return false; // inconclusive } if (strcmp(rc1->val.item, rc2->val.item) != 0) { return false; } } else if ((rp1->val.item != NULL && rp2->val.item == NULL) || (rp1->val.item == NULL && rp2->val.item != NULL)) { return false; } else { assert(rp1->val.item == NULL && rp2->val.item == NULL); } } return true; }
// FIX: this function is a mixture of Equal/Compare (boolean/diff). // somebody is bound to misuse this at some point static int CompareRlist(const Rlist *list1, const Rlist *list2) { const Rlist *rp1, *rp2; for (rp1 = list1, rp2 = list2; rp1 != NULL && rp2 != NULL; rp1 = rp1->next, rp2 = rp2->next) { if (rp1->val.item && rp2->val.item) { const Rlist *rc1, *rc2; if (rp1->val.type == RVAL_TYPE_FNCALL || rp2->val.type == RVAL_TYPE_FNCALL) { return -1; // inconclusive } rc1 = rp1; rc2 = rp2; // Check for list nesting with { fncall(), "x" ... } if (rp1->val.type == RVAL_TYPE_LIST) { rc1 = rp1->val.item; } if (rp2->val.type == RVAL_TYPE_LIST) { rc2 = rp2->val.item; } if (IsCf3VarString(rc1->val.item) || IsCf3VarString(rp2->val.item)) { return -1; // inconclusive } if (strcmp(rc1->val.item, rc2->val.item) != 0) { return false; } } else { return false; } } return true; }
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"); }
static bool Epimenides(EvalContext *ctx, const char *scope, const char *var, Rval rval, int level) { Rlist *rp, *list; char exp[CF_EXPANDSIZE]; switch (rval.type) { case RVAL_TYPE_SCALAR: if (StringContainsVar(rval.item, var)) { CfOut(OUTPUT_LEVEL_ERROR, "", "Scalar variable \"%s\" contains itself (non-convergent): %s", var, (char *) rval.item); return true; } if (IsCf3VarString(rval.item)) { ExpandScalar(ctx, scope, rval.item, exp); if (strcmp(exp, (const char *) rval.item) == 0) { return false; } CfDebug("bling %d-%s: (look for %s) in \"%s\" => %s \n", level, scope, var, (const char *) rval.item, exp); if (level > 3) { return false; } if (Epimenides(ctx, scope, var, (Rval) {exp, RVAL_TYPE_SCALAR}, level + 1)) { return true; } } break; case RVAL_TYPE_LIST: list = (Rlist *) rval.item; for (rp = list; rp != NULL; rp = rp->next) { if (Epimenides(ctx, scope, var, (Rval) {rp->item, rp->type}, level)) { return true; } } break; default: return false; } return false; }
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 void CheckParseReal(char *lval, char *s, const char *range) { Item *split; double max = (double) CF_LOWINIT, min = (double) CF_HIGHINIT, val; int n; char output[CF_BUFSIZE]; CfDebug("\nCheckParseReal(%s => %s/%s)\n", lval, s, range); if (s == NULL) { return; } if (strcmp(s, "inf") == 0) { ReportError("keyword \"inf\" has an integer value, cannot be used as real"); return; } if (IsCf3VarString(s)) { CfDebug("Validation: Unable to verify syntax of real %s due to variable expansion at this stage\n", s); return; } /* Numeric types are registered by range separated by comma str "min,max" */ split = SplitString(range, ','); if ((n = ListLen(split)) != 2) { FatalError("INTERN:format specifier for real rvalues is not ok for lval %s - %d items", lval, n); } sscanf(split->name, "%lf", &min); sscanf(split->next->name, "%lf", &max); DeleteItemList(split); if (min == CF_HIGHINIT || max == CF_LOWINIT) { FatalError("INTERN:could not parse format specifier for int rvalues for lval %s", lval); } val = Str2Double(s); if (val > max || val < min) { snprintf(output, CF_BUFSIZE, "Real item on rhs of lval \'%s\' give as {%s => %.3lf} is out of bounds (should be in [%s])", lval, s, val, range); ReportError(output); } CfDebug("CheckParseReal - syntax verified\n\n"); }
static int CheckParseString(char *lval, char *s, const char *range) { char output[CF_BUFSIZE]; CfDebug("\nCheckParseString(%s => %s/%s)\n", lval, s, range); if (s == NULL) { return true; } if (strlen(range) == 0) { return true; } if (IsNakedVar(s, '@') || IsNakedVar(s, '$')) { CfDebug("Validation: Unable to verify variable expansion of %s at this stage\n", s); return false; } /* Deal with complex strings as special cases */ if (strcmp(lval, "mode") == 0 || strcmp(lval, "search_mode") == 0) { mode_t plus, minus; if (!ParseModeString(s, &plus, &minus)) { snprintf(output, CF_BUFSIZE, "Error parsing Unix permission string %s)", s); ReportError(output); return false; } } if (FullTextMatch(range, s)) { return true; } if (IsCf3VarString(s)) { CfDebug("Validation: Unable to verify syntax of %s due to variable expansion at this stage\n", s); } else { snprintf(output, CF_BUFSIZE, "Scalar item in %s => { %s } in rvalue is out of bounds (value should match pattern %s)", lval, s, range); ReportError(output); return false; } return true; }
static bool Epimenides(EvalContext *ctx, const char *ns, const char *scope, const char *var, Rval rval, int level) { Rlist *rp, *list; char exp[CF_EXPANDSIZE]; switch (rval.type) { case RVAL_TYPE_SCALAR: if (StringContainsVar(rval.item, var)) { Log(LOG_LEVEL_ERR, "Scalar variable '%s' contains itself (non-convergent) '%s'", var, (char *) rval.item); return true; } if (IsCf3VarString(rval.item)) { ExpandScalar(ctx, ns, scope, rval.item, exp); if (strcmp(exp, (const char *) rval.item) == 0) { return false; } if (level > 3) { return false; } if (Epimenides(ctx, ns, scope, var, (Rval) {exp, RVAL_TYPE_SCALAR}, level + 1)) { return true; } } break; case RVAL_TYPE_LIST: list = (Rlist *) rval.item; for (rp = list; rp != NULL; rp = rp->next) { if (Epimenides(ctx, ns, scope, var, (Rval) {rp->item, rp->type}, level)) { return true; } } break; default: return false; } return false; }
static SyntaxTypeMatch CheckParseInt(const char *lval, const char *s, const char *range) { Item *split; int n; long max = CF_LOWINIT, min = CF_HIGHINIT, val; /* Numeric types are registered by range separated by comma str "min,max" */ CfDebug("\nCheckParseInt(%s => %s/%s)\n", lval, s, range); split = SplitString(range, ','); if ((n = ListLen(split)) != 2) { ProgrammingError("INTERN: format specifier for int rvalues is not ok for lval %s - got %d items", lval, n); } sscanf(split->name, "%ld", &min); if (strcmp(split->next->name, "inf") == 0) { max = CF_INFINITY; } else { sscanf(split->next->name, "%ld", &max); } DeleteItemList(split); if (min == CF_HIGHINIT || max == CF_LOWINIT) { ProgrammingError("INTERN: could not parse format specifier for int rvalues for lval %s", lval); } if (IsCf3VarString(s)) { return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED; } val = IntFromString(s); if (val == CF_NOINT) { return SYNTAX_TYPE_MATCH_ERROR_INT_PARSE; } if (val > max || val < min) { return SYNTAX_TYPE_MATCH_ERROR_INT_OUT_OF_RANGE; } CfDebug("CheckParseInt - syntax verified\n\n"); return SYNTAX_TYPE_MATCH_OK; }
static SyntaxTypeMatch CheckParseString(const char *lval, const char *s, const char *range) { if (s == NULL) { return SYNTAX_TYPE_MATCH_OK; } if (strlen(range) == 0) { return SYNTAX_TYPE_MATCH_OK; } if (IsNakedVar(s, '@') || IsNakedVar(s, '$')) { return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED; } /* Deal with complex strings as special cases */ if (strcmp(lval, "mode") == 0 || strcmp(lval, "search_mode") == 0) { mode_t plus, minus; if (!ParseModeString(s, &plus, &minus)) { return SYNTAX_TYPE_MATCH_ERROR_STRING_UNIX_PERMISSION; } } /* FIXME: review this strcmp. Moved out from StringMatch */ if (!strcmp(range, s) || StringMatchFull(range, s)) { return SYNTAX_TYPE_MATCH_OK; } if (IsCf3VarString(s)) { return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED; } else if ('\0' == s[0]) { return SYNTAX_TYPE_MATCH_ERROR_EMPTY_SCALAR_OUT_OF_RANGE; } else if (!strcmp(range, CF_ABSPATHRANGE)) { return SYNTAX_TYPE_MATCH_ERROR_ABSOLUTE_PATH; } else { return SYNTAX_TYPE_MATCH_ERROR_SCALAR_OUT_OF_RANGE; } return SYNTAX_TYPE_MATCH_OK; }
static int CompareRval(const void *rval1_item, RvalType rval1_type, const void *rval2_item, RvalType rval2_type) { if (rval1_type != rval2_type) { return -1; } switch (rval1_type) { case RVAL_TYPE_SCALAR: if (IsCf3VarString(rval1_item) || IsCf3VarString(rval2_item)) { return -1; // inconclusive } if (strcmp(rval1_item, rval2_item) != 0) { return false; } break; case RVAL_TYPE_LIST: return CompareRlist(rval1_item, rval2_item); case RVAL_TYPE_FNCALL: return -1; default: return -1; } return true; }
static SyntaxTypeMatch CheckParseReal(const char *lval, const char *s, const char *range) { Item *split; double max = (double) CF_LOWINIT, min = (double) CF_HIGHINIT, val; int n; CfDebug("\nCheckParseReal(%s => %s/%s)\n", lval, s, range); if (strcmp(s, "inf") == 0) { return SYNTAX_TYPE_MATCH_ERROR_REAL_INF; } if (IsCf3VarString(s)) { return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED; } /* Numeric types are registered by range separated by comma str "min,max" */ split = SplitString(range, ','); if ((n = ListLen(split)) != 2) { ProgrammingError("Format specifier for real rvalues is not ok for lval %s - %d items", lval, n); } sscanf(split->name, "%lf", &min); sscanf(split->next->name, "%lf", &max); DeleteItemList(split); if (min == CF_HIGHINIT || max == CF_LOWINIT) { ProgrammingError("Could not parse format specifier for int rvalues for lval %s", lval); } if (!DoubleFromString(s, &val)) { return SYNTAX_TYPE_MATCH_ERROR_REAL_OUT_OF_RANGE; } if (val > max || val < min) { return SYNTAX_TYPE_MATCH_ERROR_REAL_OUT_OF_RANGE; } return SYNTAX_TYPE_MATCH_OK; }
static SyntaxTypeMatch CheckParseString(const char *lval, const char *s, const char *range) { CfDebug("\nCheckParseString(%s => %s/%s)\n", lval, s, range); if (s == NULL) { return SYNTAX_TYPE_MATCH_OK; } if (strlen(range) == 0) { return SYNTAX_TYPE_MATCH_OK; } if (IsNakedVar(s, '@') || IsNakedVar(s, '$')) { return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED; } /* Deal with complex strings as special cases */ if (strcmp(lval, "mode") == 0 || strcmp(lval, "search_mode") == 0) { mode_t plus, minus; if (!ParseModeString(s, &plus, &minus)) { return SYNTAX_TYPE_MATCH_ERROR_STRING_UNIX_PERMISSION; } } if (FullTextMatch(range, s)) { return SYNTAX_TYPE_MATCH_OK; } if (IsCf3VarString(s)) { return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED; } else { return SYNTAX_TYPE_MATCH_ERROR_SCALAR_OUT_OF_RANGE; } return SYNTAX_TYPE_MATCH_OK; }
FnCall *ExpandFnCall(EvalContext *ctx, const char *ns, const char *scope, const FnCall *f) { FnCall *result = NULL; if (IsCf3VarString(f->name)) { // e.g. usebundle => $(m)(arg0, arg1); Buffer *buf = BufferNewWithCapacity(CF_MAXVARSIZE); ExpandScalar(ctx, ns, scope, f->name, buf); result = FnCallNew(BufferData(buf), ExpandList(ctx, ns, scope, f->args, false)); BufferDestroy(buf); } else { result = FnCallNew(f->name, ExpandList(ctx, ns, scope, f->args, false)); } return result; }
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 CheckParseIntRange(char *lval, char *s, const char *range) { Item *split, *ip, *rangep; int n; long max = CF_LOWINIT, min = CF_HIGHINIT, val; char output[CF_BUFSIZE]; if (s == NULL) { return; } /* Numeric types are registered by range separated by comma str "min,max" */ CfDebug("\nCheckParseIntRange(%s => %s/%s)\n", lval, s, range); if (*s == '[' || *s == '(') { ReportError("Range specification should not be enclosed in brackets - just \"a,b\""); return; } split = SplitString(range, ','); if ((n = ListLen(split)) != 2) { FatalError("INTERN:format specifier %s for irange rvalues is not ok for lval %s - got %d items", range, lval, n); } sscanf(split->name, "%ld", &min); if (strcmp(split->next->name, "inf") == 0) { max = CF_INFINITY; } else { sscanf(split->next->name, "%ld", &max); } DeleteItemList(split); if (min == CF_HIGHINIT || max == CF_LOWINIT) { FatalError("INTERN: could not parse irange format specifier for int rvalues for lval %s", lval); } if (IsCf3VarString(s)) { CfDebug("Validation: Unable to verify syntax of int \'%s\' due to variable expansion at this stage\n", s); return; } rangep = SplitString(s, ','); if ((n = ListLen(rangep)) != 2) { snprintf(output, CF_BUFSIZE, "Int range format specifier for lval %s should be of form \"a,b\" but got %d items", lval, n); ReportError(output); DeleteItemList(rangep); return; } for (ip = rangep; ip != NULL; ip = ip->next) { val = Str2Int(ip->name); if (val > max || val < min) { snprintf(output, CF_BUFSIZE, "Int range item on rhs of lval \'%s\' given as {%s => %ld} is out of bounds (should be in [%s])", lval, s, val, range); ReportError(output); DeleteItemList(rangep); return; } } DeleteItemList(rangep); CfDebug("CheckParseIntRange - syntax verified\n\n"); }
int VerifyMethod(EvalContext *ctx, char *attrname, Attributes a, Promise *pp) { Bundle *bp; void *vp; FnCall *fp; char method_name[CF_EXPANDSIZE], qualified_method[CF_BUFSIZE], *method_deref; Rlist *params = NULL; int retval = false; CfLock thislock; char lockname[CF_BUFSIZE]; if (a.havebundle) { if ((vp = ConstraintGetRvalValue(ctx, attrname, pp, RVAL_TYPE_FNCALL))) { fp = (FnCall *) vp; ExpandScalar(ctx, PromiseGetBundle(pp)->name, fp->name, method_name); params = fp->args; } else if ((vp = ConstraintGetRvalValue(ctx, attrname, pp, RVAL_TYPE_SCALAR))) { ExpandScalar(ctx, PromiseGetBundle(pp)->name, (char *) vp, method_name); params = NULL; } else { return false; } } GetLockName(lockname, "method", pp->promiser, params); thislock = AcquireLock(ctx, lockname, VUQNAME, CFSTARTTIME, a.transaction, pp, false); if (thislock.lock == NULL) { return false; } PromiseBanner(pp); if (strncmp(method_name,"default:",strlen("default:")) == 0) // CF_NS == ':' { method_deref = strchr(method_name, CF_NS) + 1; } else if ((strchr(method_name, CF_NS) == NULL) && (strcmp(PromiseGetNamespace(pp), "default") != 0)) { snprintf(qualified_method, CF_BUFSIZE, "%s%c%s", PromiseGetNamespace(pp), CF_NS, method_name); method_deref = qualified_method; } else { method_deref = method_name; } bp = PolicyGetBundle(PolicyFromPromise(pp), NULL, "agent", method_deref); if (!bp) { bp = PolicyGetBundle(PolicyFromPromise(pp), NULL, "common", method_deref); } if (bp) { BannerSubBundle(bp, params); EvalContextStackPushBundleFrame(ctx, bp, a.inherit); ScopeClear(bp->name); BundleHashVariables(ctx, bp); ScopeAugment(ctx, bp, pp, params); retval = ScheduleAgentOperations(ctx, bp); GetReturnValue(ctx, bp->name, pp); EvalContextStackPopFrame(ctx); switch (retval) { case PROMISE_RESULT_FAIL: cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_FAIL, pp, a, "Method failed in some repairs or aborted"); break; case PROMISE_RESULT_CHANGE: cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_CHANGE, pp, a, "Method invoked repairs"); break; default: cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "Method verified"); break; } for (const Rlist *rp = bp->args; rp; rp = rp->next) { const char *lval = rp->item; ScopeDeleteScalar((VarRef) { NULL, bp->name, lval }); } } else { if (IsCf3VarString(method_name)) { Log(LOG_LEVEL_ERR, "A variable seems to have been used for the name of the method. In this case, the promiser also needs to contain the unique name of the method"); } if (bp && (bp->name)) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Method '%s' was used but was not defined", bp->name); } else { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "A method attempted to use a bundle '%s' that was apparently not defined", method_name); } } YieldCurrentLock(thislock); return retval; }
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); }
PromiseResult VerifyMethod(EvalContext *ctx, const Rval call, Attributes a, const Promise *pp) { const Rlist *args = NULL; Buffer *method_name = BufferNew(); switch (call.type) { case RVAL_TYPE_FNCALL: { const FnCall *fp = RvalFnCallValue(call); ExpandScalar(ctx, PromiseGetBundle(pp)->ns, PromiseGetBundle(pp)->name, fp->name, method_name); args = fp->args; int arg_index = 0; while (args) { ++arg_index; if (strcmp(args->val.item, CF_NULL_VALUE) == 0) { Log(LOG_LEVEL_DEBUG, "Skipping invokation of method '%s' due to null-values in argument '%d'", fp->name, arg_index); BufferDestroy(method_name); return PROMISE_RESULT_SKIPPED; } args = args->next; } args = fp->args; EvalContextSetBundleArgs(ctx, args); } break; case RVAL_TYPE_SCALAR: { ExpandScalar(ctx, PromiseGetBundle(pp)->ns, PromiseGetBundle(pp)->name, RvalScalarValue(call), method_name); args = NULL; } break; default: BufferDestroy(method_name); return PROMISE_RESULT_NOOP; } char lockname[CF_BUFSIZE]; GetLockName(lockname, "method", pp->promiser, args); CfLock thislock = AcquireLock(ctx, lockname, VUQNAME, CFSTARTTIME, a.transaction, pp, false); if (thislock.lock == NULL) { BufferDestroy(method_name); return PROMISE_RESULT_SKIPPED; } PromiseBanner(ctx, pp); const Bundle *bp = EvalContextResolveBundleExpression(ctx, PromiseGetPolicy(pp), BufferData(method_name), "agent"); if (!bp) { bp = EvalContextResolveBundleExpression(ctx, PromiseGetPolicy(pp), BufferData(method_name), "common"); } PromiseResult result = PROMISE_RESULT_NOOP; if (bp) { if (a.transaction.action == cfa_warn) // don't skip for dry-runs (ie ignore DONTDO) { result = PROMISE_RESULT_WARN; cfPS(ctx, LOG_LEVEL_WARNING, result, pp, a, "Bundle '%s' should be invoked, but only a warning was promised!", BufferData(method_name)); } else { BundleBanner(bp, args); EvalContextStackPushBundleFrame(ctx, bp, args, a.inherit); /* Clear all array-variables that are already set in the sub-bundle. Otherwise, array-data accumulates between multiple bundle evaluations. Note: for bundles invoked multiple times via bundlesequence, array data *does* accumulate. */ VariableTableIterator *iter = EvalContextVariableTableIteratorNew(ctx, bp->ns, bp->name, NULL); Variable *var; while ((var = VariableTableIteratorNext(iter))) { if (!var->ref->num_indices) { continue; } EvalContextVariableRemove(ctx, var->ref); } VariableTableIteratorDestroy(iter); BundleResolve(ctx, bp); result = ScheduleAgentOperations(ctx, bp); GetReturnValue(ctx, bp, pp); EvalContextStackPopFrame(ctx); switch (result) { case PROMISE_RESULT_SKIPPED: // if a bundle returns 'skipped', meaning that all promises were locked in the bundle, // we explicitly consider the method 'kept' result = PROMISE_RESULT_NOOP; // intentional fallthru case PROMISE_RESULT_NOOP: cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "Method '%s' verified", bp->name); break; case PROMISE_RESULT_WARN: cfPS(ctx, LOG_LEVEL_WARNING, PROMISE_RESULT_WARN, pp, a, "Method '%s' invoked repairs, but only warnings promised", bp->name); break; case PROMISE_RESULT_CHANGE: cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_CHANGE, pp, a, "Method '%s' invoked repairs", bp->name); break; case PROMISE_RESULT_FAIL: case PROMISE_RESULT_DENIED: cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Method '%s' failed in some repairs", bp->name); break; default: // PROMISE_RESULT_INTERRUPTED, TIMEOUT cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_FAIL, pp, a, "Method '%s' aborted in some repairs", bp->name); break; } } for (const Rlist *rp = bp->args; rp; rp = rp->next) { const char *lval = RlistScalarValue(rp); VarRef *ref = VarRefParseFromBundle(lval, bp); EvalContextVariableRemove(ctx, ref); VarRefDestroy(ref); } } else { if (IsCf3VarString(BufferData(method_name))) { Log(LOG_LEVEL_ERR, "A variable seems to have been used for the name of the method. In this case, the promiser also needs to contain the unique name of the method"); } cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "A method attempted to use a bundle '%s' that was apparently not defined", BufferData(method_name)); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); } YieldCurrentLock(thislock); BufferDestroy(method_name); EndBundleBanner(bp); return result; }
static SyntaxTypeMatch CheckParseIntRange(const char *lval, const char *s, const char *range) { Item *split, *ip, *rangep; int n; long long max = CF_LOWINIT, min = CF_HIGHINIT; // Numeric types are registered by range separated by comma str "min,max" if (*s == '[' || *s == '(') { return SYNTAX_TYPE_MATCH_ERROR_RANGE_BRACKETED; } split = SplitString(range, ','); if ((n = ListLen(split)) != 2) { ProgrammingError("Format specifier %s for irange rvalues is not ok for lval %s - got %d items", range, lval, n); } sscanf(split->name, "%lld", &min); if (strcmp(split->next->name, "inf") == 0) { max = CF_INFINITY; } else { sscanf(split->next->name, "%lld", &max); } DeleteItemList(split); if (min == CF_HIGHINIT || max == CF_LOWINIT) { ProgrammingError("Could not parse irange format specifier for int rvalues for lval %s", lval); } if (IsCf3VarString(s)) { return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED; } rangep = SplitString(s, ','); if ((n = ListLen(rangep)) != 2) { return SYNTAX_TYPE_MATCH_ERROR_RANGE_MULTIPLE_ITEMS; } for (ip = rangep; ip != NULL; ip = ip->next) { long val = IntFromString(ip->name); if (val > max || val < min) { return SYNTAX_TYPE_MATCH_ERROR_INT_OUT_OF_RANGE; } } DeleteItemList(rangep); return SYNTAX_TYPE_MATCH_OK; }
static SyntaxTypeMatch CheckParseRealRange(const char *lval, const char *s, const char *range) { Item *split, *rangep, *ip; double max = (double) CF_LOWINIT, min = (double) CF_HIGHINIT, val; int n; if (*s == '[' || *s == '(') { return SYNTAX_TYPE_MATCH_ERROR_RANGE_BRACKETED; } if (strcmp(s, "inf") == 0) { return SYNTAX_TYPE_MATCH_ERROR_REAL_INF; } if (IsCf3VarString(s)) { return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED; } /* Numeric types are registered by range separated by comma str "min,max" */ split = SplitString(range, ','); if ((n = ListLen(split)) != 2) { ProgrammingError("Format specifier for real rvalues is not ok for lval %s - %d items", lval, n); } sscanf(split->name, "%lf", &min); sscanf(split->next->name, "%lf", &max); DeleteItemList(split); if (min == CF_HIGHINIT || max == CF_LOWINIT) { ProgrammingError("Could not parse format specifier for int rvalues for lval %s", lval); } rangep = SplitString(s, ','); if ((n = ListLen(rangep)) != 2) { return SYNTAX_TYPE_MATCH_ERROR_RANGE_MULTIPLE_ITEMS; } for (ip = rangep; ip != NULL; ip = ip->next) { if (!DoubleFromString(ip->name, &val)) { return SYNTAX_TYPE_MATCH_ERROR_REAL_OUT_OF_RANGE; } if (val > max || val < min) { return SYNTAX_TYPE_MATCH_ERROR_REAL_OUT_OF_RANGE; } } DeleteItemList(rangep); return SYNTAX_TYPE_MATCH_OK; }
/** * Expand a #string into Buffer #out, returning the pointer to the string * itself, inside the Buffer #out. If #out is NULL then the buffer will be * created and destroyed internally. * * @retval NULL something went wrong */ char *ExpandScalar(const EvalContext *ctx, const char *ns, const char *scope, const char *string, Buffer *out) { bool out_belongs_to_us = false; if (out == NULL) { out = BufferNew(); out_belongs_to_us = true; } assert(string != NULL); assert(out != NULL); Buffer *current_item = BufferNew(); for (const char *sp = string; *sp != '\0'; sp++) { BufferClear(current_item); ExtractScalarPrefix(current_item, sp, strlen(sp)); BufferAppend(out, BufferData(current_item), BufferSize(current_item)); sp += BufferSize(current_item); if (*sp == '\0') { break; } BufferClear(current_item); char varstring = sp[1]; ExtractScalarReference(current_item, sp, strlen(sp), true); sp += BufferSize(current_item) + 2; if (IsCf3VarString(BufferData(current_item))) { Buffer *temp = BufferCopy(current_item); BufferClear(current_item); ExpandScalar(ctx, ns, scope, BufferData(temp), current_item); BufferDestroy(temp); } if (!IsExpandable(BufferData(current_item))) { VarRef *ref = VarRefParseFromNamespaceAndScope( BufferData(current_item), ns, scope, CF_NS, '.'); DataType value_type; const void *value = EvalContextVariableGet(ctx, ref, &value_type); VarRefDestroy(ref); switch (DataTypeToRvalType(value_type)) { case RVAL_TYPE_SCALAR: assert(value != NULL); BufferAppendString(out, value); continue; break; case RVAL_TYPE_CONTAINER: { assert(value != NULL); const JsonElement *jvalue = value; /* instead of casts */ if (JsonGetElementType(jvalue) == JSON_ELEMENT_TYPE_PRIMITIVE) { BufferAppendString(out, JsonPrimitiveGetAsString(jvalue)); continue; } break; } default: /* TODO Log() */ break; } } if (varstring == '{') { BufferAppendF(out, "${%s}", BufferData(current_item)); } else { BufferAppendF(out, "$(%s)", BufferData(current_item)); } } BufferDestroy(current_item); LogDebug(LOG_MOD_EXPAND, "ExpandScalar( %s : %s . %s ) => %s", SAFENULL(ns), SAFENULL(scope), string, BufferData(out)); return out_belongs_to_us ? BufferClose(out) : BufferGet(out); }
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; }
bool ExpandScalar(const EvalContext *ctx, const char *ns, const char *scope, const char *string, Buffer *out) { assert(string); bool returnval = true; if (strlen(string) == 0) { return false; } // TODO: cleanup, optimize this mess Buffer *var = BufferNew(); Buffer *current_item = BufferNew(); Buffer *temp = BufferNew(); for (const char *sp = string; /* No exit */ ; sp++) /* check for varitems */ { char varstring = false; size_t increment = 0; if (*sp == '\0') { break; } BufferZero(current_item); ExtractScalarPrefix(current_item, sp, strlen(sp)); BufferAppend(out, BufferData(current_item), BufferSize(current_item)); sp += BufferSize(current_item); if (*sp == '\0') { break; } BufferZero(var); if (*sp == '$') { switch (*(sp + 1)) { case '(': varstring = ')'; ExtractScalarReference(var, sp, strlen(sp), false); if (BufferSize(var) == 0) { BufferAppendChar(out, '$'); continue; } break; case '{': varstring = '}'; ExtractScalarReference(var, sp, strlen(sp), false); if (BufferSize(var) == 0) { BufferAppendChar(out, '$'); continue; } break; default: BufferAppendChar(out, '$'); continue; } } BufferZero(current_item); { BufferZero(temp); ExtractScalarReference(temp, sp, strlen(sp), true); if (IsCf3VarString(BufferData(temp))) { ExpandScalar(ctx, ns, scope, BufferData(temp), current_item); } else { BufferAppend(current_item, BufferData(temp), BufferSize(temp)); } } increment = BufferSize(var) - 1; char name[CF_MAXVARSIZE] = ""; if (!IsExpandable(BufferData(current_item))) { DataType type = DATA_TYPE_NONE; const void *value = NULL; { VarRef *ref = VarRefParseFromNamespaceAndScope(BufferData(current_item), ns, scope, CF_NS, '.'); value = EvalContextVariableGet(ctx, ref, &type); VarRefDestroy(ref); } if (value) { switch (type) { case DATA_TYPE_STRING: case DATA_TYPE_INT: case DATA_TYPE_REAL: BufferAppend(out, value, strlen(value)); break; case DATA_TYPE_STRING_LIST: case DATA_TYPE_INT_LIST: case DATA_TYPE_REAL_LIST: case DATA_TYPE_NONE: if (varstring == '}') { snprintf(name, CF_MAXVARSIZE, "${%s}", BufferData(current_item)); } else { snprintf(name, CF_MAXVARSIZE, "$(%s)", BufferData(current_item)); } BufferAppend(out, name, strlen(name)); returnval = false; break; default: Log(LOG_LEVEL_DEBUG, "Returning Unknown Scalar ('%s' => '%s')", string, BufferData(out)); BufferDestroy(var); BufferDestroy(current_item); BufferDestroy(temp); return false; } } else { if (varstring == '}') { snprintf(name, CF_MAXVARSIZE, "${%s}", BufferData(current_item)); } else { snprintf(name, CF_MAXVARSIZE, "$(%s)", BufferData(current_item)); } BufferAppend(out, name, strlen(name)); returnval = false; } } sp += increment; BufferZero(current_item); } BufferDestroy(var); BufferDestroy(current_item); BufferDestroy(temp); return returnval; }
static void CheckParseInt(char *lval, char *s, const char *range) { Item *split; int n; long max = CF_LOWINIT, min = CF_HIGHINIT, val; char output[CF_BUFSIZE]; /* Numeric types are registered by range separated by comma str "min,max" */ CfDebug("\nCheckParseInt(%s => %s/%s)\n", lval, s, range); if (s == NULL) { return; } split = SplitString(range, ','); if ((n = ListLen(split)) != 2) { FatalError("INTERN: format specifier for int rvalues is not ok for lval %s - got %d items", lval, n); } sscanf(split->name, "%ld", &min); if (strcmp(split->next->name, "inf") == 0) { max = CF_INFINITY; } else { sscanf(split->next->name, "%ld", &max); } DeleteItemList(split); if (min == CF_HIGHINIT || max == CF_LOWINIT) { FatalError("INTERN: could not parse format specifier for int rvalues for lval %s", lval); } if (IsCf3VarString(s)) { CfDebug("Validation: Unable to verify syntax of int \'%s\' due to variable expansion at this stage\n", s); return; } val = Str2Int(s); if (val == CF_NOINT) { snprintf(output, CF_BUFSIZE, "Int item on rhs of lval \'%s\' given as \'%s\' could not be parsed", lval, s); ReportError(output); return; } if (val > max || val < min) { snprintf(output, CF_BUFSIZE, "Int item on rhs of lval \'%s\' given as {%s => %ld} is out of bounds (should be in [%s])", lval, s, val, range); ReportError(output); return; } CfDebug("CheckParseInt - syntax verified\n\n"); }
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; }