static void test_expand_promise_array_with_scalar_arg(void **state) { EvalContext *ctx = *state; { VarRef *lval = VarRefParse("default:bundle.foo[one]"); EvalContextVariablePut(ctx, lval, "first", CF_DATA_TYPE_STRING, NULL); VarRefDestroy(lval); } { VarRef *lval = VarRefParse("default:bundle.bar"); EvalContextVariablePut(ctx, lval, "one", CF_DATA_TYPE_STRING, NULL); VarRefDestroy(lval); } Policy *policy = PolicyNew(); Bundle *bundle = PolicyAppendBundle(policy, NamespaceDefault(), "bundle", "agent", NULL, NULL); PromiseType *promise_type = BundleAppendPromiseType(bundle, "dummy"); Promise *promise = PromiseTypeAppendPromise(promise_type, "$(foo[$(bar)])", (Rval) { NULL, RVAL_TYPE_NOPROMISEE }, "any", NULL); EvalContextStackPushBundleFrame(ctx, bundle, NULL, false); EvalContextStackPushPromiseTypeFrame(ctx, promise_type); ExpandPromise(ctx, promise, actuator_expand_promise_array_with_scalar_arg, NULL); EvalContextStackPopFrame(ctx); EvalContextStackPopFrame(ctx); PolicyDestroy(policy); }
static PromiseResult RenderTemplateCFEngine(EvalContext *ctx, const Promise *pp, const Rlist *bundle_args, Attributes a, EditContext *edcontext) { PromiseResult result = PROMISE_RESULT_NOOP; Policy *tmp_policy = PolicyNew(); Bundle *bp = NULL; if ((bp = MakeTemporaryBundleFromTemplate(ctx, tmp_policy, a, pp, &result))) { BannerSubBundle(bp, bundle_args); a.haveeditline = true; EvalContextStackPushBundleFrame(ctx, bp, bundle_args, a.edits.inherit); BundleResolve(ctx, bp); ScheduleEditLineOperations(ctx, bp, a, pp, edcontext); EvalContextStackPopFrame(ctx); if (edcontext->num_edits == 0) { edcontext->num_edits++; } } PolicyDestroy(tmp_policy); return result; }
static void test_expand_promise_slist(void **state) { actuator_state = 0; EvalContext *ctx = *state; { VarRef *lval = VarRefParse("default:bundle.foo"); Rlist *list = NULL; RlistAppendScalar(&list, "a"); RlistAppendScalar(&list, "b"); EvalContextVariablePut(ctx, lval, list, CF_DATA_TYPE_STRING_LIST, NULL); RlistDestroy(list); VarRefDestroy(lval); } Policy *policy = PolicyNew(); Bundle *bundle = PolicyAppendBundle(policy, NamespaceDefault(), "bundle", "agent", NULL, NULL); PromiseType *promise_type = BundleAppendPromiseType(bundle, "dummy"); Promise *promise = PromiseTypeAppendPromise(promise_type, "$(foo)", (Rval) { NULL, RVAL_TYPE_NOPROMISEE }, "any", NULL); EvalContextStackPushBundleFrame(ctx, bundle, NULL, false); EvalContextStackPushPromiseTypeFrame(ctx, promise_type); ExpandPromise(ctx, promise, actuator_expand_promise_slist, NULL); EvalContextStackPopFrame(ctx); EvalContextStackPopFrame(ctx); assert_int_equal(2, actuator_state); PolicyDestroy(policy); }
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 test_map_iterators_from_rval_literal(void **state) { EvalContext *ctx = *state; Policy *p = PolicyNew(); Bundle *bp = PolicyAppendBundle(p, "default", "none", "agent", NULL, NULL); Rlist *lists = NULL; Rlist *scalars = NULL; Rlist *containers = NULL; MapIteratorsFromRval(ctx, bp, (Rval) { "snookie", RVAL_TYPE_SCALAR }, &scalars, &lists, &containers); assert_int_equal(0, RlistLen(lists)); assert_int_equal(0, RlistLen(scalars)); assert_int_equal(0, RlistLen(containers)); PolicyDestroy(p); }
Policy *ParserParseFile(AgentType agent_type, const char *path, unsigned int warnings, unsigned int warnings_error) { ParserStateReset(&P, false); P.agent_type = agent_type; P.policy = PolicyNew(); P.warnings = warnings; P.warnings_error = warnings_error; strncpy(P.filename, path, CF_MAXVARSIZE); yyin = safe_fopen(path, "rt"); if (yyin == NULL) { Log(LOG_LEVEL_ERR, "While opening file '%s' for parsing. (fopen: %s)", path, GetErrorStr()); exit(EXIT_FAILURE); } while (!feof(yyin)) { yyparse(); if (ferror(yyin)) { perror("cfengine"); exit(EXIT_FAILURE); } } fclose(yyin); if (P.error_count > 0) { PolicyDestroy(P.policy); ParserStateReset(&P, true); ParserStateClean(&P); return NULL; } Policy *policy = P.policy; ParserStateReset(&P, false); ParserStateClean(&P); return policy; }
Policy *ParserParseFile(const char *path) { ParserStateReset(); P.policy = PolicyNew(); strncpy(P.filename, path, CF_MAXVARSIZE); yyin = fopen(path, "r"); while (!feof(yyin)) { yyparse(); if (ferror(yyin)) { perror("cfengine"); exit(1); } } fclose(yyin); return P.policy; }
/** * @retval >0 Number of threads still working * @retval 0 All threads are done * @retval -1 Server didn't run */ int StartServer(EvalContext *ctx, Policy **policy, GenericAgentConfig *config) { InitSignals(); ServerTLSInitialize(); int sd = SetServerListenState(ctx, QUEUESIZE, SERVER_LISTEN, &InitServer); /* Necessary for our use of select() to work in WaitForIncoming(): */ assert(sd < sizeof(fd_set) * CHAR_BIT && GetSignalPipe() < sizeof(fd_set) * CHAR_BIT); Policy *server_cfengine_policy = PolicyNew(); CfLock thislock = AcquireServerLock(ctx, config, server_cfengine_policy); if (thislock.lock == NULL) { PolicyDestroy(server_cfengine_policy); if (sd >= 0) { cf_closesocket(sd); } return -1; } PrepareServer(sd); CollectCallStart(COLLECT_INTERVAL); while (!IsPendingTermination()) { CollectCallIfDue(ctx); int selected = WaitForIncoming(sd); Log(LOG_LEVEL_DEBUG, "select(): %d", selected); if (selected == -1) { Log(LOG_LEVEL_ERR, "Error while waiting for connections. (select: %s)", GetErrorStr()); break; } else if (selected >= 0) /* timeout or success */ { PolicyUpdateIfSafe(ctx, policy, config); /* Is there a new connection pending at our listening socket? */ if (selected > 0) { AcceptAndHandle(ctx, sd); } } /* else: interrupted, maybe pending termination. */ } Log(LOG_LEVEL_NOTICE, "Cleaning up and exiting..."); CollectCallStop(); if (sd != -1) { Log(LOG_LEVEL_VERBOSE, "Closing listening socket"); cf_closesocket(sd); /* Close listening socket */ } /* This is a graceful exit, give 2 seconds chance to threads. */ int threads_left = WaitOnThreads(); YieldCurrentLock(thislock); PolicyDestroy(server_cfengine_policy); return threads_left; }
/* * The difference between filename and input_input file is that the latter is the file specified by -f or * equivalently the file containing body common control. This will hopefully be squashed in later refactoring. */ static Policy *Cf3ParseFile(const GenericAgentConfig *config, const char *input_path) { struct stat statbuf; if (stat(input_path, &statbuf) == -1) { if (config->ignore_missing_inputs) { return PolicyNew(); } Log(LOG_LEVEL_ERR, "Can't stat file '%s' for parsing. (stat: %s)", input_path, GetErrorStr()); exit(1); } #ifndef _WIN32 if (config->check_not_writable_by_others && (statbuf.st_mode & (S_IWGRP | S_IWOTH))) { Log(LOG_LEVEL_ERR, "File %s (owner %ju) is writable by others (security exception)", input_path, (uintmax_t)statbuf.st_uid); exit(1); } #endif Log(LOG_LEVEL_VERBOSE, "Parsing file '%s'", input_path); if (!FileCanOpen(input_path, "r")) { Log(LOG_LEVEL_ERR, "Can't open file '%s' for parsing", input_path); exit(1); } Policy *policy = NULL; if (StringEndsWith(input_path, ".json")) { char *contents = NULL; if (FileReadMax(&contents, input_path, SIZE_MAX) == -1) { Log(LOG_LEVEL_ERR, "Error reading JSON input file '%s'", input_path); return NULL; } JsonElement *json_policy = NULL; const char *data = contents; // TODO: need to fix JSON parser signature, just silly if (JsonParse(&data, &json_policy) != JSON_PARSE_OK) { Log(LOG_LEVEL_ERR, "Error parsing JSON input file '%s'", input_path); free(contents); return NULL; } policy = PolicyFromJson(json_policy); JsonDestroy(json_policy); free(contents); } else { if (config->agent_type == AGENT_TYPE_COMMON) { policy = ParserParseFile(input_path, config->agent_specific.common.parser_warnings, config->agent_specific.common.parser_warnings_error); } else { policy = ParserParseFile(input_path, 0, 0); } } return policy; }
void StartServer(EvalContext *ctx, Policy **policy, GenericAgentConfig *config) { int sd = -1; fd_set rset; int ret_val; CfLock thislock; time_t last_policy_reload = 0; extern int COLLECT_WINDOW; struct sockaddr_storage cin; socklen_t addrlen = sizeof(cin); MakeSignalPipe(); signal(SIGINT, HandleSignalsForDaemon); signal(SIGTERM, HandleSignalsForDaemon); signal(SIGHUP, SIG_IGN); signal(SIGPIPE, SIG_IGN); signal(SIGUSR1, HandleSignalsForDaemon); signal(SIGUSR2, HandleSignalsForDaemon); ServerTLSInitialize(); sd = SetServerListenState(ctx, QUEUESIZE, SERVER_LISTEN, &InitServer); TransactionContext tc = { .ifelapsed = 0, .expireafter = 1, }; Policy *server_cfengine_policy = PolicyNew(); Promise *pp = NULL; { Bundle *bp = PolicyAppendBundle(server_cfengine_policy, NamespaceDefault(), "server_cfengine_bundle", "agent", NULL, NULL); PromiseType *tp = BundleAppendPromiseType(bp, "server_cfengine"); pp = PromiseTypeAppendPromise(tp, config->input_file, (Rval) { NULL, RVAL_TYPE_NOPROMISEE }, NULL); } assert(pp); thislock = AcquireLock(ctx, pp->promiser, VUQNAME, CFSTARTTIME, tc, pp, false); if (thislock.lock == NULL) { PolicyDestroy(server_cfengine_policy); return; } if (sd != -1) { Log(LOG_LEVEL_VERBOSE, "Listening for connections ..."); } #ifdef __MINGW32__ if (!NO_FORK) { Log(LOG_LEVEL_VERBOSE, "Windows does not support starting processes in the background - starting in foreground"); } #else /* !__MINGW32__ */ if ((!NO_FORK) && (fork() != 0)) { _exit(EXIT_SUCCESS); } if (!NO_FORK) { ActAsDaemon(); } #endif /* !__MINGW32__ */ WritePID("cf-serverd.pid"); /* Andrew Stribblehill <*****@*****.**> -- close sd on exec */ #ifndef __MINGW32__ fcntl(sd, F_SETFD, FD_CLOEXEC); #endif CollectCallStart(COLLECT_INTERVAL); while (!IsPendingTermination()) { /* Note that this loop logic is single threaded, but ACTIVE_THREADS might still change in threads pertaining to service handling */ if (ThreadLock(cft_server_children)) { if (ACTIVE_THREADS == 0) { CheckFileChanges(ctx, policy, config, &last_policy_reload); } ThreadUnlock(cft_server_children); } // Check whether we have established peering with a hub if (CollectCallHasPending()) { int waiting_queue = 0; int new_client = CollectCallGetPending(&waiting_queue); if (waiting_queue > COLLECT_WINDOW) { Log(LOG_LEVEL_INFO, "Closing collect call because it would take" "longer than the allocated window [%d]", COLLECT_WINDOW); } ConnectionInfo *info = ConnectionInfoNew(); if (info) { ConnectionInfoSetSocket(info, new_client); ServerEntryPoint(ctx, POLICY_SERVER, info); CollectCallMarkProcessed(); } } else { /* check if listening is working */ if (sd != -1) { // Look for normal incoming service requests int signal_pipe = GetSignalPipe(); FD_ZERO(&rset); FD_SET(sd, &rset); FD_SET(signal_pipe, &rset); Log(LOG_LEVEL_DEBUG, "Waiting at incoming select..."); struct timeval timeout = { .tv_sec = 60, .tv_usec = 0 }; int max_fd = (sd > signal_pipe) ? (sd + 1) : (signal_pipe + 1); ret_val = select(max_fd, &rset, NULL, NULL, &timeout); // Empty the signal pipe. We don't need the values. unsigned char buf; while (recv(signal_pipe, &buf, 1, 0) > 0) {} if (ret_val == -1) /* Error received from call to select */ { if (errno == EINTR) { continue; } else { Log(LOG_LEVEL_ERR, "select failed. (select: %s)", GetErrorStr()); exit(1); } } else if (!ret_val) /* No data waiting, we must have timed out! */ { continue; } if (FD_ISSET(sd, &rset)) { int new_client = accept(sd, (struct sockaddr *)&cin, &addrlen); if (new_client == -1) { continue; } /* Just convert IP address to string, no DNS lookup. */ char ipaddr[CF_MAX_IP_LEN] = ""; getnameinfo((struct sockaddr *) &cin, addrlen, ipaddr, sizeof(ipaddr), NULL, 0, NI_NUMERICHOST); ConnectionInfo *info = ConnectionInfoNew(); if (info) { ConnectionInfoSetSocket(info, new_client); ServerEntryPoint(ctx, ipaddr, info); } } } } } CollectCallStop(); PolicyDestroy(server_cfengine_policy); } /*********************************************************************/ /* Level 2 */ /*********************************************************************/ int InitServer(size_t queue_size) { int sd = -1; if ((sd = OpenReceiverChannel()) == -1) { Log(LOG_LEVEL_ERR, "Unable to start server"); exit(EXIT_FAILURE); } if (listen(sd, queue_size) == -1) { Log(LOG_LEVEL_ERR, "listen failed. (listen: %s)", GetErrorStr()); exit(EXIT_FAILURE); } return sd; }
/* * The difference between filename and input_input file is that the latter is the file specified by -f or * equivalently the file containing body common control. This will hopefully be squashed in later refactoring. */ Policy *Cf3ParseFile(const GenericAgentConfig *config, const char *input_path) { struct stat statbuf; if (stat(input_path, &statbuf) == -1) { if (config->ignore_missing_inputs) { return PolicyNew(); } Log(LOG_LEVEL_ERR, "Can't stat file '%s' for parsing. (stat: %s)", input_path, GetErrorStr()); exit(EXIT_FAILURE); } else if (S_ISDIR(statbuf.st_mode)) { if (config->ignore_missing_inputs) { return PolicyNew(); } Log(LOG_LEVEL_ERR, "Can't parse directory '%s'.", input_path); exit(EXIT_FAILURE); } #ifndef _WIN32 if (config->check_not_writable_by_others && (statbuf.st_mode & (S_IWGRP | S_IWOTH))) { Log(LOG_LEVEL_ERR, "File %s (owner %ju) is writable by others (security exception)", input_path, (uintmax_t)statbuf.st_uid); exit(EXIT_FAILURE); } #endif Log(LOG_LEVEL_VERBOSE, "BEGIN parsing file: %s", input_path); if (!FileCanOpen(input_path, "r")) { Log(LOG_LEVEL_ERR, "Can't open file '%s' for parsing", input_path); exit(EXIT_FAILURE); } Policy *policy = NULL; if (StringEndsWith(input_path, ".json")) { Writer *contents = FileRead(input_path, SIZE_MAX, NULL); if (!contents) { Log(LOG_LEVEL_ERR, "Error reading JSON input file '%s'", input_path); return NULL; } JsonElement *json_policy = NULL; const char *data = StringWriterData(contents); if (JsonParse(&data, &json_policy) != JSON_PARSE_OK) { Log(LOG_LEVEL_ERR, "Error parsing JSON input file '%s'", input_path); WriterClose(contents); return NULL; } policy = PolicyFromJson(json_policy); JsonDestroy(json_policy); WriterClose(contents); } else { if (config->agent_type == AGENT_TYPE_COMMON) { policy = ParserParseFile(config->agent_type, input_path, config->agent_specific.common.parser_warnings, config->agent_specific.common.parser_warnings_error); } else { policy = ParserParseFile(config->agent_type, input_path, 0, 0); } } Log(LOG_LEVEL_VERBOSE, "END parsing file: %s", input_path); return policy; }
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); { Rlist *list = NULL; RlistAppend(&list, "jersey", RVAL_TYPE_SCALAR); VarRef *lval = VarRefParse("ns:scope.jwow"); EvalContextVariablePut(ctx, lval, list, CF_DATA_TYPE_STRING_LIST, NULL); VarRefDestroy(lval); RlistDestroy(list); } EvalContextStackPushBundleFrame(ctx, bp, NULL, false); { Rlist *lists = NULL; Rlist *scalars = NULL; Rlist *containers = NULL; MapIteratorsFromRval(ctx, bp, (Rval) { "${jwow}", RVAL_TYPE_SCALAR }, &scalars, &lists, &containers); assert_int_equal(1, RlistLen(lists)); assert_string_equal("jwow", RlistScalarValue(lists)); assert_int_equal(0, RlistLen(scalars)); assert_int_equal(0, RlistLen(containers)); RlistDestroy(lists); } { Rlist *lists = NULL; Rlist *scalars = NULL; Rlist *containers = NULL; char *str = xstrdup("${scope.jwow}"); MapIteratorsFromRval(ctx, bp, (Rval) { str, RVAL_TYPE_SCALAR }, &scalars, &lists, &containers); assert_string_equal("${scope#jwow}", str); free(str); assert_int_equal(1, RlistLen(lists)); assert_string_equal("scope#jwow", RlistScalarValue(lists)); assert_int_equal(0, RlistLen(scalars)); assert_int_equal(0, RlistLen(containers)); RlistDestroy(lists); } { Rlist *lists = NULL; Rlist *scalars = NULL; Rlist *containers = NULL; char *str = xstrdup("${ns:scope.jwow}"); MapIteratorsFromRval(ctx, bp, (Rval) { str, RVAL_TYPE_SCALAR }, &scalars, &lists, &containers); assert_string_equal("${ns*scope#jwow}", str); free(str); assert_int_equal(1, RlistLen(lists)); assert_string_equal("ns*scope#jwow", RlistScalarValue(lists)); assert_int_equal(0, RlistLen(scalars)); assert_int_equal(0, RlistLen(containers)); RlistDestroy(lists); } EvalContextStackPopFrame(ctx); PolicyDestroy(p); }
void StartServer(EvalContext *ctx, Policy **policy, GenericAgentConfig *config) { int sd = -1, sd_reply; fd_set rset; struct timeval timeout; int ret_val; CfLock thislock; time_t starttime = time(NULL), last_collect = 0; struct sockaddr_storage cin; socklen_t addrlen = sizeof(cin); signal(SIGINT, HandleSignalsForDaemon); signal(SIGTERM, HandleSignalsForDaemon); signal(SIGHUP, SIG_IGN); signal(SIGPIPE, SIG_IGN); signal(SIGUSR1, HandleSignalsForDaemon); signal(SIGUSR2, HandleSignalsForDaemon); sd = SetServerListenState(ctx, QUEUESIZE); TransactionContext tc = { .ifelapsed = 0, .expireafter = 1, }; Policy *server_cfengine_policy = PolicyNew(); Promise *pp = NULL; { Bundle *bp = PolicyAppendBundle(server_cfengine_policy, NamespaceDefault(), "server_cfengine_bundle", "agent", NULL, NULL); PromiseType *tp = BundleAppendPromiseType(bp, "server_cfengine"); pp = PromiseTypeAppendPromise(tp, config->input_file, (Rval) { NULL, RVAL_TYPE_NOPROMISEE }, NULL); } assert(pp); thislock = AcquireLock(ctx, pp->promiser, VUQNAME, CFSTARTTIME, tc, pp, false); if (thislock.lock == NULL) { PolicyDestroy(server_cfengine_policy); return; } Log(LOG_LEVEL_INFO, "cf-serverd starting %.24s", ctime(&starttime)); if (sd != -1) { Log(LOG_LEVEL_VERBOSE, "Listening for connections ..."); } #ifdef __MINGW32__ if (!NO_FORK) { Log(LOG_LEVEL_VERBOSE, "Windows does not support starting processes in the background - starting in foreground"); } #else /* !__MINGW32__ */ if ((!NO_FORK) && (fork() != 0)) { _exit(0); } if (!NO_FORK) { ActAsDaemon(sd); } #endif /* !__MINGW32__ */ WritePID("cf-serverd.pid"); /* Andrew Stribblehill <*****@*****.**> -- close sd on exec */ #ifndef __MINGW32__ fcntl(sd, F_SETFD, FD_CLOEXEC); #endif while (!IsPendingTermination()) { time_t now = time(NULL); /* Note that this loop logic is single threaded, but ACTIVE_THREADS might still change in threads pertaining to service handling */ if (ThreadLock(cft_server_children)) { if (ACTIVE_THREADS == 0) { CheckFileChanges(ctx, policy, config); } ThreadUnlock(cft_server_children); } // Check whether we should try to establish peering with a hub if ((COLLECT_INTERVAL > 0) && ((now - last_collect) > COLLECT_INTERVAL)) { TryCollectCall(); last_collect = now; continue; } /* check if listening is working */ if (sd != -1) { // Look for normal incoming service requests FD_ZERO(&rset); FD_SET(sd, &rset); /* Set 1 second timeout for select, so that signals are handled in * a timely manner */ timeout.tv_sec = 1; timeout.tv_usec = 0; Log(LOG_LEVEL_DEBUG, "Waiting at incoming select..."); ret_val = select((sd + 1), &rset, NULL, NULL, &timeout); if (ret_val == -1) /* Error received from call to select */ { if (errno == EINTR) { continue; } else { Log(LOG_LEVEL_ERR, "select failed. (select: %s)", GetErrorStr()); exit(1); } } else if (!ret_val) /* No data waiting, we must have timed out! */ { continue; } Log(LOG_LEVEL_VERBOSE, "Accepting a connection"); if ((sd_reply = accept(sd, (struct sockaddr *) &cin, &addrlen)) != -1) { /* Just convert IP address to string, no DNS lookup. */ char ipaddr[CF_MAX_IP_LEN] = ""; getnameinfo((struct sockaddr *) &cin, addrlen, ipaddr, sizeof(ipaddr), NULL, 0, NI_NUMERICHOST); ServerEntryPoint(ctx, sd_reply, ipaddr); } } } PolicyDestroy(server_cfengine_policy); } /*********************************************************************/ /* Level 2 */ /*********************************************************************/ int InitServer(size_t queue_size) { int sd = -1; if ((sd = OpenReceiverChannel()) == -1) { Log(LOG_LEVEL_ERR, "Unable to start server"); exit(1); } if (listen(sd, queue_size) == -1) { Log(LOG_LEVEL_ERR, "listen failed. (listen: %s)", GetErrorStr()); exit(1); } return sd; }
static void test_class_persistence(void) { EvalContext *ctx = EvalContextNew(); // simulate old version { CF_DB *dbp; PersistentClassInfo i; assert_true(OpenDB(&dbp, dbid_state)); i.expires = UINT_MAX; i.policy = CONTEXT_STATE_POLICY_RESET; WriteDB(dbp, "old", &i, sizeof(PersistentClassInfo)); CloseDB(dbp); } // e.g. by monitoring EvalContextHeapPersistentSave(ctx, "class1", 3, CONTEXT_STATE_POLICY_PRESERVE, "a,b"); // e.g. by a class promise in a bundle with a namespace { Policy *p = PolicyNew(); Bundle *bp = PolicyAppendBundle(p, "ns1", "bundle1", "agent", NULL, NULL); EvalContextStackPushBundleFrame(ctx, bp, NULL, false); EvalContextHeapPersistentSave(ctx, "class2", 5, CONTEXT_STATE_POLICY_PRESERVE, "x"); EvalContextStackPopFrame(ctx); PolicyDestroy(p); } EvalContextHeapPersistentLoadAll(ctx); { const Class *cls = EvalContextClassGet(ctx, "default", "old"); assert_true(cls != NULL); assert_string_equal("old", cls->name); assert_true(cls->tags != NULL); assert_int_equal(1, StringSetSize(cls->tags)); assert_true(StringSetContains(cls->tags, "source=persistent")); } { const Class *cls = EvalContextClassGet(ctx, "default", "class1"); assert_true(cls != NULL); assert_string_equal("class1", cls->name); assert_true(cls->tags != NULL); assert_int_equal(3, StringSetSize(cls->tags)); assert_true(StringSetContains(cls->tags, "source=persistent")); assert_true(StringSetContains(cls->tags, "a")); assert_true(StringSetContains(cls->tags, "b")); } { const Class *cls = EvalContextClassGet(ctx, "ns1", "class2"); assert_true(cls != NULL); assert_string_equal("ns1", cls->ns); assert_string_equal("class2", cls->name); assert_true(cls->tags != NULL); assert_int_equal(2, StringSetSize(cls->tags)); assert_true(StringSetContains(cls->tags, "source=persistent")); assert_true(StringSetContains(cls->tags, "x")); } EvalContextDestroy(ctx); }
void MonitorStartServer(EvalContext *ctx, const Policy *policy) { char timekey[CF_SMALLBUF]; Averages averages; Policy *monitor_cfengine_policy = PolicyNew(); Promise *pp = NULL; { Bundle *bp = PolicyAppendBundle(monitor_cfengine_policy, NamespaceDefault(), "monitor_cfengine_bundle", "agent", NULL, NULL); PromiseType *tp = BundleAppendPromiseType(bp, "monitor_cfengine"); pp = PromiseTypeAppendPromise(tp, "the monitor daemon", (Rval) { NULL, RVAL_TYPE_NOPROMISEE }, NULL); } assert(pp); CfLock thislock; #ifdef __MINGW32__ if (!NO_FORK) { Log(LOG_LEVEL_VERBOSE, "Windows does not support starting processes in the background - starting in foreground"); } #else /* !__MINGW32__ */ if ((!NO_FORK) && (fork() != 0)) { Log(LOG_LEVEL_INFO, "cf-monitord: starting"); _exit(0); } if (!NO_FORK) { ActAsDaemon(0); } #endif /* !__MINGW32__ */ TransactionContext tc = { .ifelapsed = 0, .expireafter = 0, }; thislock = AcquireLock(ctx, pp->promiser, VUQNAME, CFSTARTTIME, tc, pp, false); if (thislock.lock == NULL) { PolicyDestroy(monitor_cfengine_policy); return; } WritePID("cf-monitord.pid"); MonNetworkSnifferOpen(); while (!IsPendingTermination()) { GetQ(ctx, policy); snprintf(timekey, sizeof(timekey), "%s", GenTimeKey(time(NULL))); averages = EvalAvQ(ctx, timekey); LeapDetection(); ArmClasses(averages, timekey); ZeroArrivals(); MonNetworkSnifferSniff(ITER, CF_THIS); ITER++; } PolicyDestroy(monitor_cfengine_policy); } /*********************************************************************/ static void GetQ(EvalContext *ctx, const Policy *policy) { MonEntropyClassesReset(); ZeroArrivals(); MonProcessesGatherData(CF_THIS); #ifndef __MINGW32__ MonCPUGatherData(CF_THIS); MonLoadGatherData(CF_THIS); MonDiskGatherData(CF_THIS); MonNetworkGatherData(CF_THIS); MonNetworkSnifferGatherData(); MonTempGatherData(CF_THIS); #endif /* !__MINGW32__ */ MonOtherGatherData(CF_THIS); GatherPromisedMeasures(ctx, policy); }
PromiseResult ScheduleEditOperation(EvalContext *ctx, char *filename, Attributes a, Promise *pp) { void *vp; FnCall *fp; Rlist *args = NULL; char edit_bundle_name[CF_BUFSIZE], lockname[CF_BUFSIZE], qualified_edit[CF_BUFSIZE], *method_deref; CfLock thislock; snprintf(lockname, CF_BUFSIZE - 1, "fileedit-%s", filename); thislock = AcquireLock(ctx, lockname, VUQNAME, CFSTARTTIME, a.transaction, pp, false); if (thislock.lock == NULL) { return PROMISE_RESULT_NOOP; } EditContext *edcontext = NewEditContext(filename, a); PromiseResult result = PROMISE_RESULT_NOOP; if (edcontext == NULL) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "File '%s' was marked for editing but could not be opened", filename); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); goto exit; } Policy *policy = PolicyFromPromise(pp); if (a.haveeditline) { if ((vp = ConstraintGetRvalValue(ctx, "edit_line", pp, RVAL_TYPE_FNCALL))) { fp = (FnCall *) vp; strcpy(edit_bundle_name, fp->name); args = fp->args; } else if ((vp = ConstraintGetRvalValue(ctx, "edit_line", pp, RVAL_TYPE_SCALAR))) { strcpy(edit_bundle_name, (char *) vp); args = NULL; } else { goto exit; } if (strncmp(edit_bundle_name,"default:",strlen("default:")) == 0) // CF_NS == ':' { method_deref = strchr(edit_bundle_name, CF_NS) + 1; } else if ((strchr(edit_bundle_name, CF_NS) == NULL) && (strcmp(PromiseGetNamespace(pp), "default") != 0)) { snprintf(qualified_edit, CF_BUFSIZE, "%s%c%s", PromiseGetNamespace(pp), CF_NS, edit_bundle_name); method_deref = qualified_edit; } else { method_deref = edit_bundle_name; } Log(LOG_LEVEL_VERBOSE, "Handling file edits in edit_line bundle '%s'", method_deref); Bundle *bp = NULL; if ((bp = PolicyGetBundle(policy, NULL, "edit_line", method_deref))) { BannerSubBundle(bp, args); EvalContextStackPushBundleFrame(ctx, bp, args, a.edits.inherit); BundleResolve(ctx, bp); ScheduleEditLineOperations(ctx, bp, a, pp, edcontext); EvalContextStackPopFrame(ctx); } else { Log(LOG_LEVEL_ERR, "Did not find method '%s' in bundle '%s' for edit operation", method_deref, edit_bundle_name); } } if (a.haveeditxml) { if ((vp = ConstraintGetRvalValue(ctx, "edit_xml", pp, RVAL_TYPE_FNCALL))) { fp = (FnCall *) vp; strcpy(edit_bundle_name, fp->name); args = fp->args; } else if ((vp = ConstraintGetRvalValue(ctx, "edit_xml", pp, RVAL_TYPE_SCALAR))) { strcpy(edit_bundle_name, (char *) vp); args = NULL; } else { goto exit; } if (strncmp(edit_bundle_name,"default:",strlen("default:")) == 0) // CF_NS == ':' { method_deref = strchr(edit_bundle_name, CF_NS) + 1; } else { method_deref = edit_bundle_name; } Log(LOG_LEVEL_VERBOSE, "Handling file edits in edit_xml bundle '%s'", method_deref); Bundle *bp = NULL; if ((bp = PolicyGetBundle(policy, NULL, "edit_xml", method_deref))) { BannerSubBundle(bp, args); EvalContextStackPushBundleFrame(ctx, bp, args, a.edits.inherit); BundleResolve(ctx, bp); ScheduleEditXmlOperations(ctx, bp, a, pp, edcontext); EvalContextStackPopFrame(ctx); } } if (a.edit_template) { if (!a.template_method || strcmp("cfengine", a.template_method) == 0) { Policy *tmp_policy = PolicyNew(); Bundle *bp = NULL; if ((bp = MakeTemporaryBundleFromTemplate(ctx, tmp_policy, a, pp, &result))) { BannerSubBundle(bp, args); a.haveeditline = true; EvalContextStackPushBundleFrame(ctx, bp, args, a.edits.inherit); BundleResolve(ctx, bp); ScheduleEditLineOperations(ctx, bp, a, pp, edcontext); EvalContextStackPopFrame(ctx); } PolicyDestroy(tmp_policy); } else if (strcmp("mustache", a.template_method) == 0) { if (!FileCanOpen(a.edit_template, "r")) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Template file '%s' could not be opened for reading", a.edit_template); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); goto exit; } Writer *ouput_writer = NULL; { FILE *output_file = fopen(pp->promiser, "w"); if (!output_file) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Output file '%s' could not be opened for writing", pp->promiser); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); goto exit; } ouput_writer = FileWriter(output_file); } Writer *template_writer = FileRead(a.edit_template, SIZE_MAX, NULL); if (!template_writer) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Could not read template file '%s'", a.edit_template); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); WriterClose(ouput_writer); goto exit; } JsonElement *default_template_data = NULL; if (!a.template_data) { a.template_data = default_template_data = DefaultTemplateData(ctx); } if (!MustacheRender(ouput_writer, StringWriterData(template_writer), a.template_data)) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Error rendering mustache template '%s'", a.edit_template); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); WriterClose(template_writer); WriterClose(ouput_writer); goto exit; } JsonDestroy(default_template_data); WriterClose(template_writer); WriterClose(ouput_writer); } } exit: result = PromiseResultUpdate(result, FinishEditContext(ctx, edcontext, a, pp)); YieldCurrentLock(thislock); return result; }
static Item *MonReSample(EvalContext *ctx, int slot, Attributes a, const Promise *pp, PromiseResult *result) { CfLock thislock; char eventname[CF_BUFSIZE]; char comm[20]; struct timespec start; FILE *fin = NULL; mode_t maskval = 0; if (a.measure.stream_type && strcmp(a.measure.stream_type, "pipe") == 0) { if (!IsExecutable(CommandArg0(pp->promiser))) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "%s promises to be executable but isn't\n", pp->promiser); *result = PromiseResultUpdate(*result, PROMISE_RESULT_FAIL); return NULL; } else { Log(LOG_LEVEL_VERBOSE, "Promiser string contains a valid executable (%s) - ok", CommandArg0(pp->promiser)); } } TransactionContext tc = { .expireafter = a.transaction.expireafter, .ifelapsed = MONITOR_RESTARTED ? 0 : a.transaction.ifelapsed, // Force a measurement if restarted }; CFSTARTTIME = time(NULL); thislock = AcquireLock(ctx, pp->promiser, VUQNAME, CFSTARTTIME, tc, pp, false); if (thislock.lock == NULL) { if (a.measure.history_type && (strcmp(a.measure.history_type, "log") == 0)) { DeleteItemList(ENTERPRISE_DATA[slot].output); ENTERPRISE_DATA[slot].output = NULL; } else { /* If static or time-series, and too soon or busy then use a cached value to avoid artificial gaps in the history */ } MONITOR_RESTARTED = false; return ENTERPRISE_DATA[slot].output; } else { DeleteItemList(ENTERPRISE_DATA[slot].output); ENTERPRISE_DATA[slot].output = NULL; Log(LOG_LEVEL_INFO, "Sampling \'%s\' ...(timeout=%d,owner=%ju,group=%ju)", pp->promiser, a.contain.timeout, (uintmax_t)a.contain.owner, (uintmax_t)a.contain.group); start = BeginMeasure(); CommandPrefix(pp->promiser, comm); if (a.contain.timeout != 0) { SetTimeOut(a.contain.timeout); } /* Stream types */ if (a.measure.stream_type && strcmp(a.measure.stream_type, "file") == 0) { long filepos = 0; struct stat sb; Log(LOG_LEVEL_VERBOSE, "Stream \"%s\" is a plain file", pp->promiser); if (stat(pp->promiser, &sb) == -1) { Log(LOG_LEVEL_INFO, "Unable to find stream '%s'. (stat: %s)", pp->promiser, GetErrorStr()); YieldCurrentLock(thislock); MONITOR_RESTARTED = false; return NULL; } fin = safe_fopen(pp->promiser, "r"); if (a.measure.growing) { filepos = Mon_RestoreFilePosition(pp->promiser); if (sb.st_size >= filepos) { fseek(fin, filepos, SEEK_SET); } } } else if (a.measure.stream_type && strcmp(a.measure.stream_type, "pipe") == 0) { Log(LOG_LEVEL_VERBOSE, "(Setting pipe umask to %jo)", (uintmax_t)a.contain.umask); maskval = umask(a.contain.umask); if (a.contain.umask == 0) { Log(LOG_LEVEL_VERBOSE, "Programming %s running with umask 0! Use umask= to set", pp->promiser); } // Mark: This is strange that we used these wrappers. Currently no way of setting these a.contain.owner = -1; a.contain.group = -1; a.contain.chdir = NULL; a.contain.chroot = NULL; // Mark: they were unset, and would fail for non-root(!) if (a.contain.shelltype == SHELL_TYPE_POWERSHELL) { #ifdef __MINGW32__ fin = cf_popen_powershell_setuid(pp->promiser, "r", a.contain.owner, a.contain.group, a.contain.chdir, a.contain.chroot, false); #else // !__MINGW32__ Log(LOG_LEVEL_ERR, "Powershell is only supported on Windows"); YieldCurrentLock(thislock); MONITOR_RESTARTED = false; return NULL; #endif // !__MINGW32__ } else if (a.contain.shelltype == SHELL_TYPE_USE) { fin = cf_popen_shsetuid(pp->promiser, "r", a.contain.owner, a.contain.group, a.contain.chdir, a.contain.chroot, false); } else { fin = cf_popensetuid(pp->promiser, "r", a.contain.owner, a.contain.group, a.contain.chdir, a.contain.chroot, false); } } /* generic file stream */ if (fin == NULL) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Couldn't open pipe to command '%s'. (cf_popen: %s)", pp->promiser, GetErrorStr()); *result = PromiseResultUpdate(*result, PROMISE_RESULT_FAIL); YieldCurrentLock(thislock); MONITOR_RESTARTED = false; return ENTERPRISE_DATA[slot].output; } size_t line_size = CF_BUFSIZE; char *line = xmalloc(line_size); for (;;) { ssize_t res = CfReadLine(&line, &line_size, fin); if (res == -1) { if (!feof(fin)) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_TIMEOUT, pp, a, "Sample stream '%s'. (fread: %s)", pp->promiser, GetErrorStr()); *result = PromiseResultUpdate(*result, PROMISE_RESULT_TIMEOUT); YieldCurrentLock(thislock); free(line); return ENTERPRISE_DATA[slot].output; } else { break; } } AppendItem(&(ENTERPRISE_DATA[slot].output), line, NULL); } free(line); if (a.measure.stream_type && strcmp(a.measure.stream_type, "file") == 0) { long fileptr = ftell(fin); fclose(fin); Mon_SaveFilePosition(pp->promiser, fileptr); } else if (a.measure.stream_type && strcmp(a.measure.stream_type, "pipe") == 0) { cf_pclose(fin); } } if (a.contain.timeout != 0) { alarm(0); signal(SIGALRM, SIG_DFL); } Log(LOG_LEVEL_INFO, "Collected sample of %s", pp->promiser); umask(maskval); YieldCurrentLock(thislock); MONITOR_RESTARTED = false; snprintf(eventname, CF_BUFSIZE - 1, "Sample(%s)", pp->promiser); EndMeasure(eventname, start); return ENTERPRISE_DATA[slot].output; } /************************************************************************************/ void HistoryUpdate(EvalContext *ctx, Averages newvals) { CfLock thislock; time_t now = time(NULL); /* We do this only once per hour - this should not be changed */ Log(LOG_LEVEL_VERBOSE, "(Updating long-term history database)"); Policy *history_db_policy = PolicyNew(); Promise *pp = NULL; Bundle *bp = PolicyAppendBundle(history_db_policy, NamespaceDefault(), "history_db_bundle", "agent", NULL, NULL); PromiseType *tp = BundleAppendPromiseType(bp, "history_db"); pp = PromiseTypeAppendPromise(tp, "the long term memory", (Rval) { NULL, RVAL_TYPE_NOPROMISEE }, NULL); assert(pp); TransactionContext tc = { .expireafter = 0, .ifelapsed = 59 }; thislock = AcquireLock(ctx, pp->promiser, VUQNAME, now, tc, pp, false); if (thislock.lock == NULL) { PolicyDestroy(history_db_policy); return; } /* Refresh the class context of the agent */ EvalContextClear(ctx); DetectEnvironment(ctx); time_t t = SetReferenceTime(); UpdateTimeClasses(ctx, t); EvalContextHeapPersistentLoadAll(ctx); LoadSystemConstants(ctx); YieldCurrentLock(thislock); PolicyDestroy(history_db_policy); Mon_HistoryUpdate(CFSTARTTIME, &newvals); Mon_DumpSlowlyVaryingObservations(); } /************************************************************************************/ static Item *MonGetMeasurementStream(EvalContext *ctx, Attributes a, const Promise *pp, PromiseResult *result) { int i; for (i = 0; i < CF_DUNBAR_WORK; i++) { if (ENTERPRISE_DATA[i].path == NULL) { break; } if (strcmp(ENTERPRISE_DATA[i].path, pp->promiser) == 0) { ENTERPRISE_DATA[i].output = MonReSample(ctx, i, a, pp, result); return ENTERPRISE_DATA[i].output; } } ENTERPRISE_DATA[i].path = xstrdup(pp->promiser); ENTERPRISE_DATA[i].output = MonReSample(ctx, i, a, pp, result); return ENTERPRISE_DATA[i].output; }