void GenericAgentConfigApply(EvalContext *ctx, const GenericAgentConfig *config) { if (config->heap_soft) { StringSetIterator it = StringSetIteratorInit(config->heap_soft); const char *context = NULL; while ((context = StringSetIteratorNext(&it))) { if (EvalContextHeapContainsHard(ctx, context)) { FatalError(ctx, "cfengine: You cannot use -D to define a reserved class!"); } EvalContextHeapAddSoft(ctx, context, NULL); } } if (config->heap_negated) { StringSetIterator it = StringSetIteratorInit(config->heap_negated); const char *context = NULL; while ((context = StringSetIteratorNext(&it))) { if (EvalContextHeapContainsHard(ctx, context)) { FatalError(ctx, "Cannot negate the reserved class [%s]\n", context); } EvalContextHeapAddNegated(ctx, context); } } switch (LogGetGlobalLevel()) { case LOG_LEVEL_DEBUG: EvalContextHeapAddHard(ctx, "debug_mode"); EvalContextHeapAddHard(ctx, "opt_debug"); // intentional fall case LOG_LEVEL_VERBOSE: EvalContextHeapAddHard(ctx, "verbose_mode"); // intentional fall case LOG_LEVEL_INFO: EvalContextHeapAddHard(ctx, "inform_mode"); break; default: break; } if (config->agent_specific.agent.bootstrap_policy_server) { EvalContextHeapAddHard(ctx, "bootstrap_mode"); } if (config->color) { LoggingSetColor(config->color); } }
StringSetIterator EvalContextStackFrameIteratorSoft(const EvalContext *ctx) { StackFrame *frame = LastStackFrameBundle(ctx); assert(frame); return StringSetIteratorInit(frame->data.bundle.contexts); }
Buffer *StringSetToBuffer(StringSet *set, const char delimiter) { Buffer *buf = BufferNew(); StringSetIterator it = StringSetIteratorInit(set); const char *element = NULL; int pos = 0; int size = StringSetSize(set); char minibuf[2]; minibuf[0] = delimiter; minibuf[1] = '\0'; while ((element = StringSetIteratorNext(&it))) { BufferAppend(buf, element, strlen(element)); if (pos < size-1) { BufferAppend(buf, minibuf, sizeof(char)); } pos++; } return buf; }
static void TransformGidsToGroups(StringSet **list) { StringSet *new_list = StringSetNew(); StringSetIterator i = StringSetIteratorInit(*list); const char *data; for (data = StringSetIteratorNext(&i); data; data = StringSetIteratorNext(&i)) { if (strlen(data) != strspn(data, "0123456789")) { // Cannot possibly be a gid. StringSetAdd(new_list, xstrdup(data)); continue; } // In groups vs gids, groups take precedence. So check if it exists. struct group *group_info = GetGrEntry(data, &EqualGroupName); if (!group_info) { if (errno == 0) { group_info = GetGrEntry(data, &EqualGid); if (!group_info) { if (errno != 0) { Log(LOG_LEVEL_ERR, "Error while checking group name '%s': %s", data, GetErrorStr()); StringSetDestroy(new_list); return; } // Neither group nor gid is found. This will lead to an error later, but we don't // handle that here. } else { // Replace gid with group name. StringSetAdd(new_list, xstrdup(group_info->gr_name)); } } else { Log(LOG_LEVEL_ERR, "Error while checking group name '%s': '%s'", data, GetErrorStr()); StringSetDestroy(new_list); return; } } else { StringSetAdd(new_list, xstrdup(data)); } } StringSet *old_list = *list; *list = new_list; StringSetDestroy(old_list); }
JsonElement *StringSetToJson(const StringSet *set) { JsonElement *arr = JsonArrayCreate(StringSetSize(set)); StringSetIterator it = StringSetIteratorInit((StringSet *)set); const char *el = NULL; while ((el = StringSetIteratorNext(&it))) { JsonArrayAppendString(arr, el); } return arr; }
static size_t StringSetMatchCount(StringSet *set, const char *regex) { size_t count = 0; StringSetIterator it = StringSetIteratorInit(set); const char *context = NULL; while ((context = SetIteratorNext(&it))) { // TODO: used FullTextMatch to avoid regressions, investigate whether StringMatch can be used if (FullTextMatch(regex, context)) { count++; } } return count; }
bool GenericAgentConfigParseWarningOptions(GenericAgentConfig *config, const char *warning_options) { if (strlen(warning_options) == 0) { return false; } if (strcmp("error", warning_options) == 0) { config->agent_specific.common.parser_warnings_error |= PARSER_WARNING_ALL; return true; } const char *options_start = warning_options; bool warnings_as_errors = false; if (StringStartsWith(warning_options, "error=")) { options_start = warning_options + strlen("error="); warnings_as_errors = true; } StringSet *warnings_set = StringSetFromString(options_start, ','); StringSetIterator it = StringSetIteratorInit(warnings_set); const char *warning_str = NULL; while ((warning_str = StringSetIteratorNext(&it))) { int warning = ParserWarningFromString(warning_str); if (warning == -1) { Log(LOG_LEVEL_ERR, "Unrecognized warning '%s'", warning_str); StringSetDestroy(warnings_set); return false; } if (warnings_as_errors) { config->agent_specific.common.parser_warnings_error |= warning; } else { config->agent_specific.common.parser_warnings |= warning; } } StringSetDestroy(warnings_set); return true; }
static void RemoveTimeClass(EvalContext *ctx, const char* tags) { Rlist *tags_rlist = RlistFromSplitString(tags, ','); ClassTableIterator *iter = EvalContextClassTableIteratorNewGlobal(ctx, NULL, true, true); StringSet *global_matches = ClassesMatching(ctx, iter, ".*", tags_rlist, false); ClassTableIteratorDestroy(iter); StringSetIterator it = StringSetIteratorInit(global_matches); const char *element = NULL; while ((element = StringSetIteratorNext(&it))) { EvalContextClassRemove(ctx, NULL, element); } StringSetDestroy(global_matches); RlistDestroy(tags_rlist); }
static bool ScheduleRun(EvalContext *ctx, Policy **policy, GenericAgentConfig *config, ExecConfig *exec_config) { CfOut(OUTPUT_LEVEL_VERBOSE, "", "Sleeping for pulse time %d seconds...\n", CFPULSETIME); sleep(CFPULSETIME); /* 1 Minute resolution is enough */ /* * FIXME: this logic duplicates the one from cf-serverd.c. Unify ASAP. */ if (CheckNewPromises(ctx, config, InputFiles(ctx, *policy)) == RELOAD_FULL) { /* Full reload */ CfOut(OUTPUT_LEVEL_INFORM, "", "Re-reading promise file %s..\n", config->input_file); EvalContextHeapClear(ctx); DeleteItemList(IPADDRESSES); IPADDRESSES = NULL; ScopeDeleteAll(); strcpy(VDOMAIN, "undefined.domain"); POLICY_SERVER[0] = '\0'; PolicyDestroy(*policy); *policy = NULL; SetPolicyServer(ctx, POLICY_SERVER); ScopeNewSpecialScalar(ctx, "sys", "policy_hub", POLICY_SERVER, DATA_TYPE_STRING); GetNameInfo3(ctx, AGENT_TYPE_EXECUTOR); GetInterfacesInfo(ctx, AGENT_TYPE_EXECUTOR); Get3Environment(ctx, AGENT_TYPE_EXECUTOR); BuiltinClasses(ctx); OSClasses(ctx); EvalContextHeapAddHard(ctx, CF_AGENTTYPES[AGENT_TYPE_EXECUTOR]); SetReferenceTime(ctx, true); GenericAgentConfigSetBundleSequence(config, NULL); *policy = GenericAgentLoadPolicy(ctx, config); ExecConfigUpdate(ctx, *policy, exec_config); SetFacility(exec_config->log_facility); } else { /* Environment reload */ EvalContextHeapClear(ctx); DeleteItemList(IPADDRESSES); IPADDRESSES = NULL; ScopeClear("this"); ScopeClear("mon"); ScopeClear("sys"); GetInterfacesInfo(ctx, AGENT_TYPE_EXECUTOR); Get3Environment(ctx, AGENT_TYPE_EXECUTOR); BuiltinClasses(ctx); OSClasses(ctx); SetReferenceTime(ctx, true); } { StringSetIterator it = StringSetIteratorInit(exec_config->schedule); const char *time_context = NULL; while ((time_context = StringSetIteratorNext(&it))) { if (IsDefinedClass(ctx, time_context, NULL)) { CfOut(OUTPUT_LEVEL_VERBOSE, "", "Waking up the agent at %s ~ %s \n", cf_ctime(&CFSTARTTIME), time_context); return true; } } } CfOut(OUTPUT_LEVEL_VERBOSE, "", "Nothing to do at %s\n", cf_ctime(&CFSTARTTIME)); return false; }
StringSetIterator EvalContextStackFrameIteratorSoft(const EvalContext *ctx) { StackFrame *frame = EvalContextStackFrame(ctx); return StringSetIteratorInit(frame->contexts); }
static void TransformGidsToGroups(StringSet **list) { StringSet *new_list = StringSetNew(); StringSetIterator i = StringSetIteratorInit(*list); const char *data; for (data = StringSetIteratorNext(&i); data; data = StringSetIteratorNext(&i)) { if (strlen(data) != strspn(data, "0123456789")) { // Cannot possibly be a gid. StringSetAdd(new_list, xstrdup(data)); continue; } // In groups vs gids, groups take precedence. So check if it exists. errno = 0; struct group *group_info = getgrnam(data); if (!group_info) { switch (errno) { case 0: case ENOENT: case EBADF: case ESRCH: case EWOULDBLOCK: case EPERM: // POSIX is apparently ambiguous here. All values mean "not found". errno = 0; group_info = getgrgid(atoi(data)); if (!group_info) { switch (errno) { case 0: case ENOENT: case EBADF: case ESRCH: case EWOULDBLOCK: case EPERM: // POSIX is apparently ambiguous here. All values mean "not found". // // Neither group nor gid is found. This will lead to an error later, but we don't // handle that here. break; default: Log(LOG_LEVEL_ERR, "Error while checking group name '%s'. (getgrgid: '%s')", data, GetErrorStr()); StringSetDestroy(new_list); return; } } else { // Replace gid with group name. StringSetAdd(new_list, xstrdup(group_info->gr_name)); } break; default: Log(LOG_LEVEL_ERR, "Error while checking group name '%s'. (getgrnam: '%s')", data, GetErrorStr()); StringSetDestroy(new_list); return; } } else { StringSetAdd(new_list, xstrdup(data)); } } StringSet *old_list = *list; *list = new_list; StringSetDestroy(old_list); }
StringSetIterator EvalContextHeapIteratorNegated(const EvalContext *ctx) { return StringSetIteratorInit(ctx->heap_negated); }
StringSetIterator EvalContextHeapIteratorHard(const EvalContext *ctx) { return StringSetIteratorInit(ctx->heap_hard); }
StringSetIterator EvalContextHeapIteratorSoft(const EvalContext *ctx) { return StringSetIteratorInit(ctx->heap_soft); }
StringSet *StringSetAddAllMatching(StringSet* base, const StringSet* filtered, const char *filter_regex) { return StringSetAddAllMatchingIterator(base, StringSetIteratorInit((StringSet*)filtered), filter_regex); }
bool GenericAgentIsPolicyReloadNeeded(const GenericAgentConfig *config, const Policy *policy) { char filename[CF_MAXVARSIZE]; time_t validated_at = ReadPolicyValidatedFileMTime(config); if (validated_at > time(NULL)) { Log(LOG_LEVEL_INFO, "Clock seems to have jumped back in time - mtime of '%s' is newer than current time - touching it", filename); if (utime(filename, NULL) == -1) { Log(LOG_LEVEL_ERR, "Could not touch '%s'. (utime: %s)", filename, GetErrorStr()); } return true; } { struct stat sb; if (stat(config->input_file, &sb) == -1) { Log(LOG_LEVEL_VERBOSE, "There is no readable input file at '%s'. (stat: %s)", config->input_file, GetErrorStr()); return true; } else if (sb.st_mtime > validated_at || sb.st_mtime > config->policy_last_read_attempt) { Log(LOG_LEVEL_VERBOSE, "Input file '%s' has changed since the last policy read attempt", config->input_file); return true; } } // Check the directories first for speed and because non-input/data files should trigger an update snprintf(filename, CF_MAXVARSIZE, "%s/inputs", CFWORKDIR); MapName(filename); if (IsNewerFileTree(filename, config->policy_last_read_attempt)) { Log(LOG_LEVEL_VERBOSE, "Quick search detected file changes"); return true; } if (policy) { const LogLevel missing_inputs_log_level = config->ignore_missing_inputs ? LOG_LEVEL_VERBOSE : LOG_LEVEL_ERR; StringSet *input_files = PolicySourceFiles(policy); StringSetIterator iter = StringSetIteratorInit(input_files); const char *input_file = NULL; bool reload_needed = false; while ((input_file = StringSetIteratorNext(&iter))) { struct stat sb; if (stat(input_file, &sb) == -1) { Log(missing_inputs_log_level, "Unreadable promise proposals at '%s'. (stat: %s)", input_file, GetErrorStr()); reload_needed = true; break; } else if (sb.st_mtime > config->policy_last_read_attempt) { reload_needed = true; break; } } StringSetDestroy(input_files); if (reload_needed) { return true; } } { snprintf(filename, CF_MAXVARSIZE, "%s/policy_server.dat", CFWORKDIR); MapName(filename); struct stat sb; if ((stat(filename, &sb) != -1) && (sb.st_mtime > config->policy_last_read_attempt)) { return true; } } return false; }
static bool SelectProcess(const char *procentry, time_t pstime, char **names, int *start, int *end, const char *process_regex, ProcessSelect a, bool attrselect) { bool result = true; char *column[CF_PROCCOLS]; Rlist *rp; assert(process_regex); StringSet *process_select_attributes = StringSetNew(); memset(column, 0, sizeof(column)); if (!SplitProcLine(procentry, pstime, names, start, end, PS_COLUMN_ALGORITHM[VPSHARDCLASS], column)) { result = false; goto cleanup; } ApplyPlatformExtraTable(names, column); for (int i = 0; names[i] != NULL; i++) { Log(LOG_LEVEL_DEBUG, "In SelectProcess, COL[%s] = '%s'", names[i], column[i]); } if (!SelectProcRegexMatch("CMD", "COMMAND", process_regex, false, names, column)) { result = false; goto cleanup; } if (!attrselect) { // If we are not considering attributes, then the matching is done. goto cleanup; } for (rp = a.owner; rp != NULL; rp = rp->next) { if (SelectProcRegexMatch("USER", "UID", RlistScalarValue(rp), true, names, column)) { StringSetAdd(process_select_attributes, xstrdup("process_owner")); break; } } if (SelectProcRangeMatch("PID", "PID", a.min_pid, a.max_pid, names, column)) { StringSetAdd(process_select_attributes, xstrdup("pid")); } if (SelectProcRangeMatch("PPID", "PPID", a.min_ppid, a.max_ppid, names, column)) { StringSetAdd(process_select_attributes, xstrdup("ppid")); } if (SelectProcRangeMatch("PGID", "PGID", a.min_pgid, a.max_pgid, names, column)) { StringSetAdd(process_select_attributes, xstrdup("pgid")); } if (SelectProcRangeMatch("VSZ", "SZ", a.min_vsize, a.max_vsize, names, column)) { StringSetAdd(process_select_attributes, xstrdup("vsize")); } if (SelectProcRangeMatch("RSS", "RSS", a.min_rsize, a.max_rsize, names, column)) { StringSetAdd(process_select_attributes, xstrdup("rsize")); } if (SelectProcTimeCounterRangeMatch("TIME", "TIME", a.min_ttime, a.max_ttime, names, column)) { StringSetAdd(process_select_attributes, xstrdup("ttime")); } if (SelectProcTimeAbsRangeMatch ("STIME", "START", a.min_stime, a.max_stime, names, column)) { StringSetAdd(process_select_attributes, xstrdup("stime")); } if (SelectProcRangeMatch("NI", "PRI", a.min_pri, a.max_pri, names, column)) { StringSetAdd(process_select_attributes, xstrdup("priority")); } if (SelectProcRangeMatch("NLWP", "NLWP", a.min_thread, a.max_thread, names, column)) { StringSetAdd(process_select_attributes, xstrdup("threads")); } if (SelectProcRegexMatch("S", "STAT", a.status, true, names, column)) { StringSetAdd(process_select_attributes, xstrdup("status")); } if (SelectProcRegexMatch("CMD", "COMMAND", a.command, true, names, column)) { StringSetAdd(process_select_attributes, xstrdup("command")); } if (SelectProcRegexMatch("TTY", "TTY", a.tty, true, names, column)) { StringSetAdd(process_select_attributes, xstrdup("tty")); } if (!a.process_result) { if (StringSetSize(process_select_attributes) == 0) { result = EvalProcessResult("", process_select_attributes); } else { Writer *w = StringWriter(); StringSetIterator iter = StringSetIteratorInit(process_select_attributes); char *attr = StringSetIteratorNext(&iter); WriterWrite(w, attr); while ((attr = StringSetIteratorNext(&iter))) { WriterWriteChar(w, '.'); WriterWrite(w, attr); } result = EvalProcessResult(StringWriterData(w), process_select_attributes); WriterClose(w); } } else { result = EvalProcessResult(a.process_result, process_select_attributes); } cleanup: StringSetDestroy(process_select_attributes); for (int i = 0; column[i] != NULL; i++) { free(column[i]); } return result; }
static bool ScheduleRun(EvalContext *ctx, Policy **policy, GenericAgentConfig *config, ExecdConfig **execd_config, ExecConfig **exec_config) { Log(LOG_LEVEL_VERBOSE, "Sleeping for pulse time %d seconds...", CFPULSETIME); sleep(CFPULSETIME); /* 1 Minute resolution is enough */ /* * FIXME: this logic duplicates the one from cf-serverd.c. Unify ASAP. */ if (CheckNewPromises(config) == RELOAD_FULL) { /* Full reload */ Log(LOG_LEVEL_INFO, "Re-reading promise file '%s'", config->input_file); EvalContextClear(ctx); strcpy(VDOMAIN, "undefined.domain"); PolicyDestroy(*policy); *policy = NULL; { char *existing_policy_server = ReadPolicyServerFile(GetWorkDir()); SetPolicyServer(ctx, existing_policy_server); free(existing_policy_server); } UpdateLastPolicyUpdateTime(ctx); DetectEnvironment(ctx); EvalContextClassPutHard(ctx, CF_AGENTTYPES[AGENT_TYPE_EXECUTOR], "cfe_internal,source=agent"); time_t t = SetReferenceTime(); UpdateTimeClasses(ctx, t); GenericAgentConfigSetBundleSequence(config, NULL); *policy = LoadPolicy(ctx, config); ExecConfigDestroy(*exec_config); ExecdConfigDestroy(*execd_config); *exec_config = ExecConfigNew(!ONCE, ctx, *policy); *execd_config = ExecdConfigNew(ctx, *policy); SetFacility((*execd_config)->log_facility); } else { /* Environment reload */ EvalContextClear(ctx); DetectEnvironment(ctx); time_t t = SetReferenceTime(); UpdateTimeClasses(ctx, t); } { StringSetIterator it = StringSetIteratorInit((*execd_config)->schedule); const char *time_context = NULL; while ((time_context = StringSetIteratorNext(&it))) { if (IsDefinedClass(ctx, time_context)) { Log(LOG_LEVEL_VERBOSE, "Waking up the agent at %s ~ %s", ctime(&CFSTARTTIME), time_context); return true; } } } Log(LOG_LEVEL_VERBOSE, "Nothing to do at %s", ctime(&CFSTARTTIME)); return false; }
static int SelectProcess(EvalContext *ctx, char *procentry, char **names, int *start, int *end, ProcessSelect a) { int result = true, i; char *column[CF_PROCCOLS]; Rlist *rp; StringSet *process_select_attributes = StringSetNew(); if (!SplitProcLine(procentry, names, start, end, column)) { return false; } for (i = 0; names[i] != NULL; i++) { Log(LOG_LEVEL_DEBUG, "In SelectProcess, COL[%s] = '%s'", names[i], column[i]); } for (rp = a.owner; rp != NULL; rp = rp->next) { if (SelectProcRegexMatch(ctx, "USER", "UID", RlistScalarValue(rp), names, column)) { StringSetAdd(process_select_attributes, xstrdup("process_owner")); break; } } if (SelectProcRangeMatch("PID", "PID", a.min_pid, a.max_pid, names, column)) { StringSetAdd(process_select_attributes, xstrdup("pid")); } if (SelectProcRangeMatch("PPID", "PPID", a.min_ppid, a.max_ppid, names, column)) { StringSetAdd(process_select_attributes, xstrdup("ppid")); } if (SelectProcRangeMatch("PGID", "PGID", a.min_pgid, a.max_pgid, names, column)) { StringSetAdd(process_select_attributes, xstrdup("pgid")); } if (SelectProcRangeMatch("VSZ", "SZ", a.min_vsize, a.max_vsize, names, column)) { StringSetAdd(process_select_attributes, xstrdup("vsize")); } if (SelectProcRangeMatch("RSS", "RSS", a.min_rsize, a.max_rsize, names, column)) { StringSetAdd(process_select_attributes, xstrdup("rsize")); } if (SelectProcTimeCounterRangeMatch("TIME", "TIME", a.min_ttime, a.max_ttime, names, column)) { StringSetAdd(process_select_attributes, xstrdup("ttime")); } if (SelectProcTimeAbsRangeMatch ("STIME", "START", a.min_stime, a.max_stime, names, column)) { StringSetAdd(process_select_attributes, xstrdup("stime")); } if (SelectProcRangeMatch("NI", "PRI", a.min_pri, a.max_pri, names, column)) { StringSetAdd(process_select_attributes, xstrdup("priority")); } if (SelectProcRangeMatch("NLWP", "NLWP", a.min_thread, a.max_thread, names, column)) { StringSetAdd(process_select_attributes, xstrdup("threads")); } if (SelectProcRegexMatch(ctx, "S", "STAT", a.status, names, column)) { StringSetAdd(process_select_attributes, xstrdup("status")); } if (SelectProcRegexMatch(ctx, "CMD", "COMMAND", a.command, names, column)) { StringSetAdd(process_select_attributes, xstrdup("command")); } if (SelectProcRegexMatch(ctx, "TTY", "TTY", a.tty, names, column)) { StringSetAdd(process_select_attributes, xstrdup("tty")); } if (!a.process_result) { if (StringSetSize(process_select_attributes) == 0) { result = EvalProcessResult("", process_select_attributes); } else { Writer *w = StringWriter(); StringSetIterator iter = StringSetIteratorInit(process_select_attributes); char *attr = StringSetIteratorNext(&iter); WriterWrite(w, attr); while ((attr = StringSetIteratorNext(&iter))) { WriterWriteChar(w, '.'); WriterWrite(w, attr); } result = EvalProcessResult(StringWriterData(w), process_select_attributes); WriterClose(w); } } else { result = EvalProcessResult(a.process_result, process_select_attributes); } StringSetDestroy(process_select_attributes); for (i = 0; column[i] != NULL; i++) { free(column[i]); } return result; }