diff --git a/cf-agent/cf-agent.c b/cf-agent/cf-agent.c index c30c049724..a5b7e73da1 100644 --- a/cf-agent/cf-agent.c +++ b/cf-agent/cf-agent.c @@ -1601,7 +1601,7 @@ static void AllClassesReport(const EvalContext *ctx) PromiseResult ScheduleAgentOperations(EvalContext *ctx, const Bundle *bp) // NB - this function can be called recursively through "methods" { - if (EvalContextIsClassicOrder(ctx)) + if (EvalContextIsClassicOrder(ctx, bp)) { return ScheduleAgentOperationsNormalOrder(ctx, bp); } diff --git a/cf-agent/files_editline.c b/cf-agent/files_editline.c index a78fa1d62d..cad57925d7 100644 --- a/cf-agent/files_editline.c +++ b/cf-agent/files_editline.c @@ -182,7 +182,7 @@ Bundle *MakeTemporaryBundleFromTemplate(EvalContext *ctx, Policy *policy, const char bundlename[CF_MAXVARSIZE]; snprintf(bundlename, CF_MAXVARSIZE, "temp_cf_bundle_%s", CanonifyName(a->edit_template)); - bp = PolicyAppendBundle(policy, "default", bundlename, "edit_line", NULL, NULL); + bp = PolicyAppendBundle(policy, "default", bundlename, "edit_line", NULL, NULL, EVAL_ORDER_UNDEFINED); } assert(bp); diff --git a/cf-monitord/env_monitor.c b/cf-monitord/env_monitor.c index 1d2bc7f3e9..d46bb96674 100644 --- a/cf-monitord/env_monitor.c +++ b/cf-monitord/env_monitor.c @@ -272,7 +272,7 @@ void MonitorStartServer(EvalContext *ctx, const Policy *policy) Policy *monitor_cfengine_policy = PolicyNew(); Promise *pp = NULL; { - Bundle *bp = PolicyAppendBundle(monitor_cfengine_policy, NamespaceDefault(), "monitor_cfengine_bundle", "agent", NULL, NULL); + Bundle *bp = PolicyAppendBundle(monitor_cfengine_policy, NamespaceDefault(), "monitor_cfengine_bundle", "agent", NULL, NULL, EVAL_ORDER_UNDEFINED); BundleSection *sp = BundleAppendSection(bp, "monitor_cfengine"); pp = BundleSectionAppendPromise(sp, "the monitor daemon", (Rval) { NULL, RVAL_TYPE_NOPROMISEE }, NULL, NULL); diff --git a/cf-monitord/history.c b/cf-monitord/history.c index 9d720cc675..e0375b2a1b 100644 --- a/cf-monitord/history.c +++ b/cf-monitord/history.c @@ -400,7 +400,7 @@ void HistoryUpdate(EvalContext *ctx, const Averages *const newvals) Policy *history_db_policy = PolicyNew(); Promise *pp = NULL; { - Bundle *bp = PolicyAppendBundle(history_db_policy, NamespaceDefault(), "history_db_bundle", "agent", NULL, NULL); + Bundle *bp = PolicyAppendBundle(history_db_policy, NamespaceDefault(), "history_db_bundle", "agent", NULL, NULL, EVAL_ORDER_UNDEFINED); BundleSection *sp = BundleAppendSection(bp, "history_db"); pp = BundleSectionAppendPromise(sp, "the long term memory", (Rval) { NULL, RVAL_TYPE_NOPROMISEE }, NULL, NULL); diff --git a/cf-serverd/cf-serverd-functions.c b/cf-serverd/cf-serverd-functions.c index 783bba7222..8e4d352780 100644 --- a/cf-serverd/cf-serverd-functions.c +++ b/cf-serverd/cf-serverd-functions.c @@ -543,7 +543,7 @@ static CfLock AcquireServerLock(EvalContext *ctx, { Bundle *bp = PolicyAppendBundle(server_policy, NamespaceDefault(), "server_cfengine_bundle", "agent", - NULL, NULL); + NULL, NULL, EVAL_ORDER_UNDEFINED); BundleSection *sp = BundleAppendSection(bp, "server_cfengine"); pp = BundleSectionAppendPromise(sp, config->input_file, diff --git a/libpromises/cf3.defs.h b/libpromises/cf3.defs.h index c3e6e87aee..63469802d1 100644 --- a/libpromises/cf3.defs.h +++ b/libpromises/cf3.defs.h @@ -690,6 +690,13 @@ typedef struct SyntaxStatus status; } PromiseTypeSyntax; +typedef enum +{ + EVAL_ORDER_UNDEFINED = 0, + EVAL_ORDER_CLASSIC, + EVAL_ORDER_TOP_DOWN +} EvalOrder; + /*************************************************************************/ typedef struct Constraint_ Constraint; diff --git a/libpromises/cf3parse_logic.h b/libpromises/cf3parse_logic.h index 871cd9a47a..2f98c58601 100644 --- a/libpromises/cf3parse_logic.h +++ b/libpromises/cf3parse_logic.h @@ -1011,6 +1011,24 @@ static inline void ParserHandleBlockAttributeRval() P.current_namespace = xstrdup(P.rval.item); } } + if (StringEqual(P.lval, "evaluation_order")) + { + if (P.rval.type != RVAL_TYPE_SCALAR) + { + yyerror("evaluation_order must be a constant scalar string"); + } + else + { + if (StringEqual(P.rval.item, "classic")) + { + P.current_evaluation_order = EVAL_ORDER_CLASSIC; + } + else if (StringEqual(P.rval.item, "top_down")) + { + P.current_evaluation_order = EVAL_ORDER_TOP_DOWN; + } + } + } } RvalDestroy(P.rval); @@ -1038,7 +1056,8 @@ static inline void ParserBeginBundleBody() P.blockid, P.blocktype, P.useargs, - P.filename); + P.filename, + P.current_evaluation_order); P.currentbundle->offset.line = CURRENT_BLOCKID_LINE; P.currentbundle->offset.start = P.offsets.last_block_id; } diff --git a/libpromises/eval_context.c b/libpromises/eval_context.c index f498df4a49..a611e28c09 100644 --- a/libpromises/eval_context.c +++ b/libpromises/eval_context.c @@ -207,8 +207,8 @@ struct EvalContext_ Seq *events; } profiler; - EvalContextEvalOrder common_eval_order; - EvalContextEvalOrder agent_eval_order; + EvalOrder common_eval_order; + EvalOrder agent_eval_order; }; void EvalContextSetConfig(EvalContext *ctx, const GenericAgentConfig *config) @@ -4062,21 +4062,54 @@ void EvalContextProfilingEnd(EvalContext *ctx, const Policy *policy) // ############################################################## -void EvalContextSetCommonEvalOrder(EvalContext *ctx, EvalContextEvalOrder eval_order) +void EvalContextSetCommonEvalOrder(EvalContext *ctx, EvalOrder eval_order) { assert(ctx != NULL); ctx->common_eval_order = eval_order; } -void EvalContextSetAgentEvalOrder(EvalContext *ctx, EvalContextEvalOrder eval_order) +void EvalContextSetAgentEvalOrder(EvalContext *ctx, EvalOrder eval_order) { assert(ctx != NULL); ctx->agent_eval_order = eval_order; } -bool EvalContextIsClassicOrder(EvalContext *ctx) +const char *EvalContextEvaluationOrderToString(EvalOrder evaluation_order) +{ + if (evaluation_order == EVAL_ORDER_CLASSIC) + { + return "classic"; + } + if (evaluation_order == EVAL_ORDER_TOP_DOWN) + { + return "top_down"; + } + return "undefined"; +} + +EvalOrder EvalContextEvaluationOrderFromString(const char *evaluation_order_string) +{ + if (StringEqual(evaluation_order_string, "classic")) + { + return EVAL_ORDER_CLASSIC; + } + if (StringEqual(evaluation_order_string, "top_down")) + { + return EVAL_ORDER_TOP_DOWN; + } + return EVAL_ORDER_UNDEFINED; +} + +bool EvalContextIsClassicOrder(EvalContext *ctx, const Bundle *bp) { assert(ctx != NULL); + assert(bp != NULL); + + if (bp->evaluation_order != EVAL_ORDER_UNDEFINED) + { + // bundle evaluation order overrides common or agent control + return bp->evaluation_order == EVAL_ORDER_CLASSIC; + } if (ctx->config->agent_type != AGENT_TYPE_AGENT) { diff --git a/libpromises/eval_context.h b/libpromises/eval_context.h index 63b30e7611..5ddff4fa76 100644 --- a/libpromises/eval_context.h +++ b/libpromises/eval_context.h @@ -124,13 +124,6 @@ typedef enum EVAL_OPTION_FULL = 0xFFFFFFFF } EvalContextOption; -typedef enum -{ - EVAL_ORDER_UNDEFINED = 0, - EVAL_ORDER_CLASSIC, - EVAL_ORDER_TOP_DOWN -} EvalContextEvalOrder; - EvalContext *EvalContextNew(void); void EvalContextDestroy(EvalContext *ctx); @@ -459,8 +452,10 @@ void EvalContextSetProfiling(EvalContext *ctx, bool profiling); void EvalContextProfilingStart(EvalContext *ctx); void EvalContextProfilingEnd(EvalContext *ctx, const Policy *policy); -void EvalContextSetCommonEvalOrder(EvalContext *ctx, EvalContextEvalOrder eval_order); -void EvalContextSetAgentEvalOrder(EvalContext *ctx, EvalContextEvalOrder eval_order); -bool EvalContextIsClassicOrder(EvalContext *ctx); +void EvalContextSetCommonEvalOrder(EvalContext *ctx, EvalOrder eval_order); +void EvalContextSetAgentEvalOrder(EvalContext *ctx, EvalOrder eval_order); +const char *EvalContextEvaluationOrderToString(EvalOrder evaluation_order); +EvalOrder EvalContextEvaluationOrderFromString(const char *evaluation_order_string); +bool EvalContextIsClassicOrder(EvalContext *ctx, const Bundle *bp); #endif diff --git a/libpromises/evalfunction.c b/libpromises/evalfunction.c index aa560386a8..8985f5c5de 100644 --- a/libpromises/evalfunction.c +++ b/libpromises/evalfunction.c @@ -4883,7 +4883,7 @@ static FnCallResult FnCallSelectServers(EvalContext *ctx, Policy *select_server_policy = PolicyNew(); { Bundle *bp = PolicyAppendBundle(select_server_policy, NamespaceDefault(), - "select_server_bundle", "agent", NULL, NULL); + "select_server_bundle", "agent", NULL, NULL, EVAL_ORDER_UNDEFINED); BundleSection *sp = BundleAppendSection(bp, "select_server"); BundleSectionAppendPromise(sp, "function", (Rval) { NULL, RVAL_TYPE_NOPROMISEE }, NULL, NULL); diff --git a/libpromises/expand.c b/libpromises/expand.c index 4b28b60f42..0246e1ff74 100644 --- a/libpromises/expand.c +++ b/libpromises/expand.c @@ -844,6 +844,7 @@ void BundleResolve(EvalContext *ctx, const Bundle *bundle) static void ResolveControlBody(EvalContext *ctx, GenericAgentConfig *config, const Body *control_body) { + assert(control_body != NULL); const char *filename = control_body->source_path; assert(CFG_CONTROLBODY[COMMON_CONTROL_MAX].lval == NULL); @@ -1025,8 +1026,9 @@ static void ResolveControlBody(EvalContext *ctx, GenericAgentConfig *config, /* Ignored */ } - if (StringEqual(lval, CFG_CONTROLBODY[COMMON_CONTROL_EVALUATION_ORDER].lval)) + if (StringEqual(lval, CFG_CONTROLBODY[COMMON_CONTROL_EVALUATION_ORDER].lval) && !StringEqual(control_body->type, "file")) { + /* evaluation_order in file control is already handled in the parser */ Log(LOG_LEVEL_VERBOSE, "SET evaluation %s", RvalScalarValue(evaluated_rval)); diff --git a/libpromises/mod_common.c b/libpromises/mod_common.c index 24e9788bce..17e0485b5e 100644 --- a/libpromises/mod_common.c +++ b/libpromises/mod_common.c @@ -413,6 +413,7 @@ const ConstraintSyntax file_control_constraints[] = /* enum cfh_control */ { ConstraintSyntaxNewString("namespace", "[a-zA-Z_][a-zA-Z0-9_]*", "Switch to a private namespace to protect current file from duplicate definitions", SYNTAX_STATUS_NORMAL), ConstraintSyntaxNewStringList("inputs", ".*", "List of additional filenames to parse for promises", SYNTAX_STATUS_NORMAL), + ConstraintSyntaxNewString("evaluation_order", "(classic|top_down)", "Order of evaluation of promises", SYNTAX_STATUS_NORMAL), ConstraintSyntaxNewNull() }; diff --git a/libpromises/parser.c b/libpromises/parser.c index f1b661e0fe..0206bd4293 100644 --- a/libpromises/parser.c +++ b/libpromises/parser.c @@ -76,6 +76,8 @@ static void ParserStateReset(ParserState *p, bool discard) free(p->current_namespace); p->current_namespace = xstrdup("default"); + p->current_evaluation_order = EVAL_ORDER_UNDEFINED; + p->currentid[0] = '\0'; if (p->currentstring) { @@ -104,8 +106,12 @@ static void ParserStateReset(ParserState *p, bool discard) static void ParserStateClean(ParserState *p) { + assert(p != NULL); + free(p->current_namespace); p->current_namespace = NULL; + + p->current_evaluation_order = EVAL_ORDER_UNDEFINED; } Policy *ParserParseFile(AgentType agent_type, const char *path, unsigned int warnings, unsigned int warnings_error) diff --git a/libpromises/parser_state.h b/libpromises/parser_state.h index 22dd090903..c12c842c24 100644 --- a/libpromises/parser_state.h +++ b/libpromises/parser_state.h @@ -62,6 +62,7 @@ typedef struct char *promiser; void *promisee; + EvalOrder current_evaluation_order; char *current_namespace; char currentid[CF_MAXVARSIZE]; char currenttype[CF_MAXVARSIZE]; diff --git a/libpromises/policy.c b/libpromises/policy.c index 4eab215ec0..7fc2dd0722 100644 --- a/libpromises/policy.c +++ b/libpromises/policy.c @@ -1307,7 +1307,7 @@ void BundleSectionDestroy(BundleSection *section) Bundle *PolicyAppendBundle(Policy *policy, const char *ns, const char *name, const char *type, - const Rlist *args, const char *source_path) + const Rlist *args, const char *source_path, const EvalOrder evaluation_order) { Bundle *bundle = xcalloc(1, sizeof(Bundle)); @@ -1323,6 +1323,7 @@ Bundle *PolicyAppendBundle(Policy *policy, bundle->sections = SeqNew(10, BundleSectionDestroy); bundle->custom_sections = SeqNew(10, BundleSectionDestroy); bundle->all_promises = SeqNew(10, NULL); + bundle->evaluation_order = evaluation_order; return bundle; } @@ -1907,6 +1908,7 @@ static JsonElement *BundleContextsToJson(const Seq *promises) */ JsonElement *BundleToJson(const Bundle *bundle) { + assert(bundle != NULL); JsonElement *json_bundle = JsonObjectCreate(10); if (bundle->source_path) @@ -1918,6 +1920,7 @@ JsonElement *BundleToJson(const Bundle *bundle) JsonObjectAppendString(json_bundle, "namespace", bundle->ns); JsonObjectAppendString(json_bundle, "name", bundle->name); JsonObjectAppendString(json_bundle, "bundleType", bundle->type); + JsonObjectAppendString(json_bundle, "evaluation_order", EvalContextEvaluationOrderToString(bundle->evaluation_order)); { JsonElement *json_args = JsonArrayCreate(10); @@ -2302,6 +2305,7 @@ static Bundle *PolicyAppendBundleJson(Policy *policy, JsonElement *json_bundle) const char *name = JsonObjectGetAsString(json_bundle, "name"); const char *type = JsonObjectGetAsString(json_bundle, "bundleType"); const char *source_path = JsonObjectGetAsString(json_bundle, "sourcePath"); + const char *evaluation_order_string = JsonObjectGetAsString(json_bundle, "evaluation_order"); Rlist *args = NULL; { @@ -2312,7 +2316,7 @@ static Bundle *PolicyAppendBundleJson(Policy *policy, JsonElement *json_bundle) } } - Bundle *bundle = PolicyAppendBundle(policy, ns, name, type, args, source_path); + Bundle *bundle = PolicyAppendBundle(policy, ns, name, type, args, source_path, EvalContextEvaluationOrderFromString(evaluation_order_string)); { JsonElement *json_promise_types = JsonObjectGetAsArray(json_bundle, "promiseTypes"); diff --git a/libpromises/policy.h b/libpromises/policy.h index cbd75a8849..24828fd569 100644 --- a/libpromises/policy.h +++ b/libpromises/policy.h @@ -75,6 +75,7 @@ struct Bundle_ char *type; char *name; char *ns; + EvalOrder evaluation_order; Rlist *args; Seq *sections; @@ -179,7 +180,7 @@ void PolicyErrorWrite(Writer *writer, const PolicyError *error); bool PolicyCheckPartial(const Policy *policy, Seq *errors); bool PolicyCheckRunnable(const EvalContext *ctx, const Policy *policy, Seq *errors); -Bundle *PolicyAppendBundle(Policy *policy, const char *ns, const char *name, const char *type, const Rlist *args, const char *source_path); +Bundle *PolicyAppendBundle(Policy *policy, const char *ns, const char *name, const char *type, const Rlist *args, const char *source_path, const EvalOrder evaluation_order); Body *PolicyAppendBody(Policy *policy, const char *ns, const char *name, const char *type, Rlist *args, const char *source_path, bool is_custom); diff --git a/tests/acceptance/24_cmd_line_arguments/parsed_policy.cf.template b/tests/acceptance/24_cmd_line_arguments/parsed_policy.cf.template index e30fce3927..629d32c585 100644 --- a/tests/acceptance/24_cmd_line_arguments/parsed_policy.cf.template +++ b/tests/acceptance/24_cmd_line_arguments/parsed_policy.cf.template @@ -63,6 +63,7 @@ body common control() { "arguments": [], "bundleType": "agent", + "evaluation_order": "undefined", "line": 7, "name": "test", "namespace": "default", @@ -189,6 +190,7 @@ body common control() { "arguments": [], "bundleType": "agent", + "evaluation_order": "undefined", "line": 7, "name": "test", "namespace": "default", @@ -243,6 +245,7 @@ body common control() { "arguments": [], "bundleType": "common", + "evaluation_order": "undefined", "line": 1, "name": "extra", "namespace": "default", diff --git a/tests/unit/eval_context_test.c b/tests/unit/eval_context_test.c index 712bc3c67f..f2f1e6f0e2 100644 --- a/tests/unit/eval_context_test.c +++ b/tests/unit/eval_context_test.c @@ -53,7 +53,7 @@ static void test_class_persistence(void) // e.g. by a class promise in a bundle with a namespace { Policy *p = PolicyNew(); - Bundle *bp = PolicyAppendBundle(p, "ns1", "bundle1", "agent", NULL, NULL); + Bundle *bp = PolicyAppendBundle(p, "ns1", "bundle1", "agent", NULL, NULL, EVAL_ORDER_UNDEFINED); EvalContextStackPushBundleFrame(ctx, bp, NULL, false); EvalContextHeapPersistentSave(ctx, "class2", 5, CONTEXT_STATE_POLICY_PRESERVE, "x"); diff --git a/tests/unit/expand_test.c b/tests/unit/expand_test.c index 45715b4d11..3ae7a168ad 100644 --- a/tests/unit/expand_test.c +++ b/tests/unit/expand_test.c @@ -81,7 +81,7 @@ static void test_map_iterators_from_rval_empty(void **state) EvalContext *ctx = *state; Policy *p = PolicyNew(); - Bundle *bp = PolicyAppendBundle(p, "default", "none", "agent", NULL, NULL); + Bundle *bp = PolicyAppendBundle(p, "default", "none", "agent", NULL, NULL, EVAL_ORDER_UNDEFINED); Rlist *lists = NULL; Rlist *scalars = NULL; @@ -99,7 +99,7 @@ static void test_map_iterators_from_rval_literal(void **state) { EvalContext *ctx = *state; Policy *p = PolicyNew(); - Bundle *bp = PolicyAppendBundle(p, "default", "none", "agent", NULL, NULL); + Bundle *bp = PolicyAppendBundle(p, "default", "none", "agent", NULL, NULL, EVAL_ORDER_UNDEFINED); Rlist *lists = NULL; Rlist *scalars = NULL; @@ -117,7 +117,7 @@ static void test_map_iterators_from_rval_naked_list_var(void **state) { EvalContext *ctx = *state; Policy *p = PolicyNew(); - Bundle *bp = PolicyAppendBundle(p, "default", "scope", "agent", NULL, NULL); + Bundle *bp = PolicyAppendBundle(p, "default", "scope", "agent", NULL, NULL, EVAL_ORDER_UNDEFINED); { Rlist *list = NULL; @@ -190,7 +190,7 @@ static void test_map_iterators_from_rval_naked_list_var_namespace(void **state) { EvalContext *ctx = *state; Policy *p = PolicyNew(); - Bundle *bp = PolicyAppendBundle(p, "ns", "scope", "agent", NULL, NULL); + Bundle *bp = PolicyAppendBundle(p, "ns", "scope", "agent", NULL, NULL, EVAL_ORDER_UNDEFINED); { Rlist *list = NULL; @@ -421,7 +421,7 @@ static void test_expand_promise_array_with_scalar_arg(void **state) } Policy *policy = PolicyNew(); - Bundle *bundle = PolicyAppendBundle(policy, NamespaceDefault(), "bundle", "agent", NULL, NULL); + Bundle *bundle = PolicyAppendBundle(policy, NamespaceDefault(), "bundle", "agent", NULL, NULL, EVAL_ORDER_UNDEFINED); BundleSection *section = BundleAppendSection(bundle, "dummy"); Promise *promise = BundleSectionAppendPromise(section, "$(foo[$(bar)])", (Rval) { NULL, RVAL_TYPE_NOPROMISEE }, "any", NULL); @@ -476,7 +476,7 @@ static void test_expand_promise_slist(void **state) Policy *policy = PolicyNew(); - Bundle *bundle = PolicyAppendBundle(policy, NamespaceDefault(), "bundle", "agent", NULL, NULL); + Bundle *bundle = PolicyAppendBundle(policy, NamespaceDefault(), "bundle", "agent", NULL, NULL, EVAL_ORDER_UNDEFINED); BundleSection *section = BundleAppendSection(bundle, "dummy"); Promise *promise = BundleSectionAppendPromise(section, "$(foo)", (Rval) { NULL, RVAL_TYPE_NOPROMISEE }, "any", NULL); @@ -544,7 +544,7 @@ static void test_expand_promise_array_with_slist_arg(void **state) Policy *policy = PolicyNew(); - Bundle *bundle = PolicyAppendBundle(policy, NamespaceDefault(), "bundle", "agent", NULL, NULL); + Bundle *bundle = PolicyAppendBundle(policy, NamespaceDefault(), "bundle", "agent", NULL, NULL, EVAL_ORDER_UNDEFINED); BundleSection *section = BundleAppendSection(bundle, "dummy"); Promise *promise = BundleSectionAppendPromise(section, "$(arr[$(keys)])", (Rval) { NULL, RVAL_TYPE_NOPROMISEE }, "any", NULL); diff --git a/tests/unit/iteration_test.c b/tests/unit/iteration_test.c index 3d0dac1727..58a5185925 100644 --- a/tests/unit/iteration_test.c +++ b/tests/unit/iteration_test.c @@ -244,7 +244,7 @@ static void IteratorPrepare_TestHelper( EvalContext *evalctx = EvalContextNew(); Policy *policy = PolicyNew(); Bundle *bundle = PolicyAppendBundle(policy, "ns1", "bundle1", "agent", - NULL, NULL); + NULL, NULL, EVAL_ORDER_UNDEFINED); BundleSection *section = BundleAppendSection(bundle, "dummy"); Promise *promise = BundleSectionAppendPromise(section, promiser, (Rval) { NULL, RVAL_TYPE_NOPROMISEE }, diff --git a/tests/unit/var_expressions_test.c b/tests/unit/var_expressions_test.c index 9850be4ca1..d3e97e2593 100644 --- a/tests/unit/var_expressions_test.c +++ b/tests/unit/var_expressions_test.c @@ -106,7 +106,7 @@ static void test_array_with_dot_colon_in_index(void) static void test_special_scope(void) { Policy *p = PolicyNew(); - Bundle *bp = PolicyAppendBundle(p, "ns", "b", "agent", NULL, NULL); + Bundle *bp = PolicyAppendBundle(p, "ns", "b", "agent", NULL, NULL, EVAL_ORDER_UNDEFINED); { VarRef *ref = VarRefParseFromBundle("c.lval", bp);