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; }
/** * 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 void ResolveControlBody(EvalContext *ctx, GenericAgentConfig *config, const Body *control_body) { const ConstraintSyntax *body_syntax = NULL; Rval returnval; assert(strcmp(control_body->name, "control") == 0); 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 agent"); } char scope[CF_BUFSIZE]; snprintf(scope, CF_BUFSIZE, "%s_%s", control_body->name, 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++) { Constraint *cp = SeqAt(control_body->conlist, i); if (!IsDefinedClass(ctx, cp->classes)) { continue; } if (strcmp(cp->lval, CFG_CONTROLBODY[COMMON_CONTROL_BUNDLESEQUENCE].lval) == 0) { returnval = ExpandPrivateRval(ctx, NULL, scope, cp->rval.item, cp->rval.type); } else { returnval = EvaluateFinalRval(ctx, control_body->parent_policy, NULL, scope, cp->rval, true, NULL); } VarRef *ref = VarRefParseFromScope(cp->lval, scope); EvalContextVariableRemove(ctx, ref); if (!EvalContextVariablePut(ctx, ref, returnval.item, ConstraintSyntaxGetDataType(body_syntax, cp->lval), "source=promise")) { Log(LOG_LEVEL_ERR, "Rule from %s at/before line %zu", control_body->source_path, cp->offset.line); } VarRefDestroy(ref); if (strcmp(cp->lval, CFG_CONTROLBODY[COMMON_CONTROL_OUTPUT_PREFIX].lval) == 0) { strncpy(VPREFIX, returnval.item, CF_MAXVARSIZE); } if (strcmp(cp->lval, CFG_CONTROLBODY[COMMON_CONTROL_DOMAIN].lval) == 0) { strcpy(VDOMAIN, cp->rval.item); 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, DATA_TYPE_STRING, "inventory,source=agent,group=Host name"); EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "domain", VDOMAIN, DATA_TYPE_STRING, "source=agent"); EvalContextClassPutHard(ctx, VDOMAIN, "source=agent"); } if (strcmp(cp->lval, CFG_CONTROLBODY[COMMON_CONTROL_IGNORE_MISSING_INPUTS].lval) == 0) { Log(LOG_LEVEL_VERBOSE, "SET ignore_missing_inputs %s", RvalScalarValue(cp->rval)); config->ignore_missing_inputs = BooleanFromString(cp->rval.item); } if (strcmp(cp->lval, CFG_CONTROLBODY[COMMON_CONTROL_IGNORE_MISSING_BUNDLES].lval) == 0) { Log(LOG_LEVEL_VERBOSE, "SET ignore_missing_bundles %s", RvalScalarValue(cp->rval)); config->ignore_missing_bundles = BooleanFromString(cp->rval.item); } if (strcmp(cp->lval, CFG_CONTROLBODY[COMMON_CONTROL_CACHE_SYSTEM_FUNCTIONS].lval) == 0) { Log(LOG_LEVEL_VERBOSE, "SET cache_system_functions %s", RvalScalarValue(cp->rval)); bool cache_system_functions = BooleanFromString(RvalScalarValue(cp->rval)); EvalContextSetEvalOption(ctx, EVAL_OPTION_CACHE_SYSTEM_FUNCTIONS, cache_system_functions); } if (strcmp(cp->lval, CFG_CONTROLBODY[COMMON_CONTROL_GOALPATTERNS].lval) == 0) { /* Ignored */ continue; } RvalDestroy(returnval); } EvalContextStackPopFrame(ctx); }
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; }