void VerifyEnvironmentsPromise(EvalContext *ctx, Promise *pp) { Attributes a = { {0} }; CfLock thislock; Promise *pexp; a = GetEnvironmentsAttributes(ctx, pp); if (EnvironmentsSanityChecks(a, pp)) { thislock = AcquireLock(ctx, "virtual", VUQNAME, CFSTARTTIME, a.transaction, pp, false); if (thislock.lock == NULL) { return; } PromiseBanner(pp); ScopeNewSpecialScalar(ctx, "this", "promiser", pp->promiser, DATA_TYPE_STRING); pexp = ExpandDeRefPromise(ctx, "this", pp); VerifyEnvironments(ctx, a, pp); PromiseDestroy(pexp); } YieldCurrentLock(thislock); }
PromiseResult ExpandPromise(EvalContext *ctx, const Promise *pp, PromiseActuator *ActOnPromise, void *param) { Rlist *lists = NULL; Rlist *scalars = NULL; Rlist *containers = NULL; Promise *pcopy = DeRefCopyPromise(ctx, pp); MapIteratorsFromRval(ctx, PromiseGetBundle(pp), (Rval) { pcopy->promiser, RVAL_TYPE_SCALAR }, &scalars, &lists, &containers); if (pcopy->promisee.item != NULL) { MapIteratorsFromRval(ctx, PromiseGetBundle(pp), pp->promisee, &scalars, &lists, &containers); } for (size_t i = 0; i < SeqLength(pcopy->conlist); i++) { Constraint *cp = SeqAt(pcopy->conlist, i); MapIteratorsFromRval(ctx, PromiseGetBundle(pp), cp->rval, &scalars, &lists, &containers); } CopyLocalizedReferencesToBundleScope(ctx, PromiseGetBundle(pp), lists); CopyLocalizedReferencesToBundleScope(ctx, PromiseGetBundle(pp), scalars); CopyLocalizedReferencesToBundleScope(ctx, PromiseGetBundle(pp), containers); PromiseResult result = ExpandPromiseAndDo(ctx, pcopy, lists, containers, ActOnPromise, param); PromiseDestroy(pcopy); RlistDestroy(lists); RlistDestroy(scalars); RlistDestroy(containers); return result; }
PromiseResult ExpandPromise(EvalContext *ctx, const Promise *pp, PromiseActuator *ActOnPromise, void *param) { Log(LOG_LEVEL_VERBOSE, "Evaluating promise '%s'", pp->promiser); if (!IsDefinedClass(ctx, pp->classes)) { if (LEGACY_OUTPUT) { Log(LOG_LEVEL_VERBOSE, ". . . . . . . . . . . . . . . . . . . . . . . . . . . . "); Log(LOG_LEVEL_VERBOSE, "Skipping whole next promise (%s), as context %s is not relevant", pp->promiser, pp->classes); Log(LOG_LEVEL_VERBOSE, ". . . . . . . . . . . . . . . . . . . . . . . . . . . . "); } else { Log(LOG_LEVEL_VERBOSE, "Skipping next promise '%s', as context '%s' is not relevant", pp->promiser, pp->classes); } return PROMISE_RESULT_SKIPPED; } Rlist *lists = NULL; Rlist *scalars = NULL; Rlist *containers = NULL; Promise *pcopy = DeRefCopyPromise(ctx, pp); MapIteratorsFromRval(ctx, PromiseGetBundle(pp), (Rval) { pcopy->promiser, RVAL_TYPE_SCALAR }, &scalars, &lists, &containers); if (pcopy->promisee.item != NULL) { MapIteratorsFromRval(ctx, PromiseGetBundle(pp), pp->promisee, &scalars, &lists, &containers); } for (size_t i = 0; i < SeqLength(pcopy->conlist); i++) { Constraint *cp = SeqAt(pcopy->conlist, i); MapIteratorsFromRval(ctx, PromiseGetBundle(pp), cp->rval, &scalars, &lists, &containers); } CopyLocalizedReferencesToBundleScope(ctx, PromiseGetBundle(pp), lists); CopyLocalizedReferencesToBundleScope(ctx, PromiseGetBundle(pp), scalars); CopyLocalizedReferencesToBundleScope(ctx, PromiseGetBundle(pp), containers); PromiseResult result = ExpandPromiseAndDo(ctx, pcopy, lists, containers, ActOnPromise, param); PromiseDestroy(pcopy); RlistDestroy(lists); RlistDestroy(scalars); RlistDestroy(containers); return result; }
PromiseResult ExpandPromise(EvalContext *ctx, const Promise *pp, PromiseActuator *act_on_promise, void *param) { if (!IsDefinedClass(ctx, pp->classes)) { return PROMISE_RESULT_SKIPPED; } /* 1. Copy the promise while expanding '@' slists and body arguments * (including body inheritance). */ Promise *pcopy = DeRefCopyPromise(ctx, pp); EvalContextStackPushPromiseFrame(ctx, pcopy, true); PromiseIterator *iterctx = PromiseIteratorNew(pcopy); /* 2. Parse all strings (promiser-promisee-constraints), find all unexpanded variables, mangle them if needed (if they are namespaced/scoped), and start the iteration engine (iterctx) to iterate over slists and containers. */ MapIteratorsFromRval(ctx, iterctx, (Rval) { pcopy->promiser, RVAL_TYPE_SCALAR }); if (pcopy->promisee.item != NULL) { MapIteratorsFromRval(ctx, iterctx, pcopy->promisee); } for (size_t i = 0; i < SeqLength(pcopy->conlist); i++) { Constraint *cp = SeqAt(pcopy->conlist, i); MapIteratorsFromRval(ctx, iterctx, cp->rval); } /* 3. GO! */ PutHandleVariable(ctx, pcopy); PromiseResult result = ExpandPromiseAndDo(ctx, iterctx, act_on_promise, param); EvalContextStackPopFrame(ctx); PromiseIteratorDestroy(iterctx); PromiseDestroy(pcopy); return result; }
void ExpandPromise(EvalContext *ctx, Promise *pp, PromiseActuator *ActOnPromise, void *param) { Rlist *listvars = NULL; Rlist *scalars = NULL; Promise *pcopy; // Set a default for packages here...general defaults that need to come before //fix me wth a general function SetMissingDefaults SetAnyMissingDefaults(ctx, pp); ScopeClear("match"); /* in case we expand something expired accidentially */ EvalContextStackPushPromiseFrame(ctx, pp); pcopy = DeRefCopyPromise(ctx, pp); MapIteratorsFromRval(ctx, PromiseGetBundle(pp)->name, &listvars, &scalars, (Rval) { pcopy->promiser, RVAL_TYPE_SCALAR }); if (pcopy->promisee.item != NULL) { MapIteratorsFromRval(ctx, PromiseGetBundle(pp)->name, &listvars, &scalars, pp->promisee); } for (size_t i = 0; i < SeqLength(pcopy->conlist); i++) { Constraint *cp = SeqAt(pcopy->conlist, i); MapIteratorsFromRval(ctx, PromiseGetBundle(pp)->name, &listvars, &scalars, cp->rval); } CopyLocalizedIteratorsToThisScope(ctx, PromiseGetBundle(pp)->name, listvars); CopyLocalizedScalarsToThisScope(ctx, PromiseGetBundle(pp)->name, scalars); ScopePushThis(); ExpandPromiseAndDo(ctx, pcopy, listvars, ActOnPromise, param); ScopePopThis(); PromiseDestroy(pcopy); RlistDestroy(listvars); RlistDestroy(scalars); EvalContextStackPopFrame(ctx); }
void ExpandPromise(EvalContext *ctx, Promise *pp, PromiseActuator *ActOnPromise, void *param) { Rlist *lists = NULL; Rlist *scalars = NULL; Rlist *containers = NULL; // Set a default for packages here...general defaults that need to come before //fix me wth a general function SetMissingDefaults SetAnyMissingDefaults(ctx, pp); Promise *pcopy = DeRefCopyPromise(ctx, pp); MapIteratorsFromRval(ctx, PromiseGetBundle(pp)->name, (Rval) { pcopy->promiser, RVAL_TYPE_SCALAR }, &scalars, &lists, &containers); if (pcopy->promisee.item != NULL) { MapIteratorsFromRval(ctx, PromiseGetBundle(pp)->name, pp->promisee, &scalars, &lists, &containers); } for (size_t i = 0; i < SeqLength(pcopy->conlist); i++) { Constraint *cp = SeqAt(pcopy->conlist, i); MapIteratorsFromRval(ctx, PromiseGetBundle(pp)->name, cp->rval,&scalars, &lists, &containers); } CopyLocalizedReferencesToBundleScope(ctx, PromiseGetBundle(pp), lists); CopyLocalizedReferencesToBundleScope(ctx, PromiseGetBundle(pp), scalars); CopyLocalizedReferencesToBundleScope(ctx, PromiseGetBundle(pp), containers); ExpandPromiseAndDo(ctx, pcopy, lists, containers, ActOnPromise, param); PromiseDestroy(pcopy); RlistDestroy(lists); RlistDestroy(scalars); RlistDestroy(containers); }
PromiseResult VerifyEnvironmentsPromise(EvalContext *ctx, const Promise *pp) { CfLock thislock; Attributes a = GetEnvironmentsAttributes(ctx, pp); PromiseResult result = PROMISE_RESULT_NOOP; if (EnvironmentsSanityChecks(a, pp)) { thislock = AcquireLock(ctx, "virtual", VUQNAME, CFSTARTTIME, a.transaction, pp, false); if (thislock.lock == NULL) { return PROMISE_RESULT_NOOP; } PromiseBanner(ctx, pp); bool excluded = false; Promise *pexp = ExpandDeRefPromise(ctx, pp, &excluded); if (excluded) { result = PROMISE_RESULT_SKIPPED; } else { result = VerifyEnvironments(ctx, a, pp); } PromiseDestroy(pexp); } YieldCurrentLock(thislock); return result; }
/* Might be called back from NovaWin_StartExecService */ void StartServer(Policy *policy, GenericAgentConfig *config, ExecConfig *exec_config, const ReportContext *report_context) { #if !defined(__MINGW32__) time_t now = time(NULL); #endif Promise *pp = NewPromise("exec_cfengine", "the executor agent"); Attributes dummyattr; CfLock thislock; pthread_attr_init(&threads_attrs); pthread_attr_setdetachstate(&threads_attrs, PTHREAD_CREATE_DETACHED); pthread_attr_setstacksize(&threads_attrs, (size_t)2048*1024); Banner("Starting executor"); memset(&dummyattr, 0, sizeof(dummyattr)); dummyattr.restart_class = "nonce"; dummyattr.transaction.ifelapsed = CF_EXEC_IFELAPSED; dummyattr.transaction.expireafter = CF_EXEC_EXPIREAFTER; if (!ONCE) { thislock = AcquireLock(pp->promiser, VUQNAME, CFSTARTTIME, dummyattr, pp, false); if (thislock.lock == NULL) { PromiseDestroy(pp); return; } /* Kill previous instances of cf-execd if those are still running */ Apoptosis(); /* FIXME: kludge. This code re-sets "last" lock to the one we have acquired a few lines before. If the cf-execd is terminated, this lock will be removed, and subsequent restart of cf-execd won't fail. The culprit is Apoptosis(), which creates a promise and executes it, taking locks during it, so CFLOCK/CFLAST/CFLOG get reset. Proper fix is to keep all the taken locks in the memory, and release all of them during process termination. */ strcpy(CFLOCK, thislock.lock); strcpy(CFLAST, thislock.last ? thislock.last : ""); strcpy(CFLOG, thislock.log ? thislock.log : ""); } #ifdef __MINGW32__ if (!NO_FORK) { CfOut(OUTPUT_LEVEL_VERBOSE, "", "Windows does not support starting processes in the background - starting in foreground"); } #else /* !__MINGW32__ */ if ((!NO_FORK) && (fork() != 0)) { CfOut(OUTPUT_LEVEL_INFORM, "", "cf-execd starting %.24s\n", cf_ctime(&now)); _exit(0); } if (!NO_FORK) { ActAsDaemon(0); } #endif /* !__MINGW32__ */ WritePID("cf-execd.pid"); signal(SIGINT, HandleSignalsForDaemon); signal(SIGTERM, HandleSignalsForDaemon); signal(SIGHUP, SIG_IGN); signal(SIGPIPE, SIG_IGN); signal(SIGUSR1, HandleSignalsForDaemon); signal(SIGUSR2, HandleSignalsForDaemon); umask(077); if (ONCE) { CfOut(OUTPUT_LEVEL_VERBOSE, "", "Sleeping for splaytime %d seconds\n\n", SPLAYTIME); sleep(SPLAYTIME); LocalExec(exec_config); CloseLog(); } else { while (!IsPendingTermination()) { if (ScheduleRun(&policy, config, exec_config, report_context)) { CfOut(OUTPUT_LEVEL_VERBOSE, "", "Sleeping for splaytime %d seconds\n\n", SPLAYTIME); sleep(SPLAYTIME); if (!LocalExecInThread(exec_config)) { CfOut(OUTPUT_LEVEL_INFORM, "", "Unable to run agent in thread, falling back to blocking execution"); LocalExec(exec_config); } } } YieldCurrentLock(thislock); } }
void LocateFilePromiserGroup(EvalContext *ctx, char *wildpath, Promise *pp, void (*fnptr) (EvalContext *ctx, char *path, Promise *ptr)) { Item *path, *ip, *remainder = NULL; char pbuffer[CF_BUFSIZE]; struct stat statbuf; int count = 0, lastnode = false, expandregex = false; uid_t agentuid = getuid(); int create = PromiseGetConstraintAsBoolean(ctx, "create", pp); char *pathtype = ConstraintGetRvalValue(ctx, "pathtype", pp, RVAL_TYPE_SCALAR); /* Do a search for promiser objects matching wildpath */ if ((!IsPathRegex(wildpath)) || (pathtype && (strcmp(pathtype, "literal") == 0))) { Log(LOG_LEVEL_VERBOSE, "Using literal pathtype for '%s'", wildpath); (*fnptr) (ctx, wildpath, pp); return; } else { Log(LOG_LEVEL_VERBOSE, "Using regex pathtype for '%s' (see pathtype)", wildpath); } pbuffer[0] = '\0'; path = SplitString(wildpath, '/'); // require forward slash in regex on all platforms for (ip = path; ip != NULL; ip = ip->next) { if ((ip->name == NULL) || (strlen(ip->name) == 0)) { continue; } if (ip->next == NULL) { lastnode = true; } /* No need to chdir as in recursive descent, since we know about the path here */ if (IsRegex(ip->name)) { remainder = ip->next; expandregex = true; break; } else { expandregex = false; } if (!JoinPath(pbuffer, ip->name)) { Log(LOG_LEVEL_ERR, "Buffer has limited size in LocateFilePromiserGroup"); return; } if (stat(pbuffer, &statbuf) != -1) { if ((S_ISDIR(statbuf.st_mode)) && ((statbuf.st_uid) != agentuid) && ((statbuf.st_uid) != 0)) { Log(LOG_LEVEL_INFO, "Directory '%s' in search path '%s' is controlled by another user (uid %ju) - trusting its content is potentially risky (possible race condition)", pbuffer, wildpath, (uintmax_t)statbuf.st_uid); PromiseRef(LOG_LEVEL_INFO, pp); } } } if (expandregex) /* Expand one regex link and hand down */ { char nextbuffer[CF_BUFSIZE], nextbufferOrig[CF_BUFSIZE], regex[CF_BUFSIZE]; const struct dirent *dirp; Dir *dirh; memset(regex, 0, CF_BUFSIZE); strncpy(regex, ip->name, CF_BUFSIZE - 1); if ((dirh = DirOpen(pbuffer)) == NULL) { // Could be a dummy directory to be created so this is not an error. Log(LOG_LEVEL_VERBOSE, "Using best-effort expanded (but non-existent) file base path '%s'", wildpath); (*fnptr) (ctx, wildpath, pp); DeleteItemList(path); return; } else { count = 0; for (dirp = DirRead(dirh); dirp != NULL; dirp = DirRead(dirh)) { if (!ConsiderLocalFile(dirp->d_name, pbuffer)) { continue; } if ((!lastnode) && (!S_ISDIR(statbuf.st_mode))) { Log(LOG_LEVEL_DEBUG, "Skipping non-directory '%s'", dirp->d_name); continue; } if (FullTextMatch(regex, dirp->d_name)) { Log(LOG_LEVEL_DEBUG, "Link '%s' matched regex '%s'", dirp->d_name, regex); } else { continue; } count++; strncpy(nextbuffer, pbuffer, CF_BUFSIZE - 1); AddSlash(nextbuffer); strcat(nextbuffer, dirp->d_name); for (ip = remainder; ip != NULL; ip = ip->next) { AddSlash(nextbuffer); strcat(nextbuffer, ip->name); } /* The next level might still contain regexs, so go again as long as expansion is not nullpotent */ if ((!lastnode) && (strcmp(nextbuffer, wildpath) != 0)) { LocateFilePromiserGroup(ctx, nextbuffer, pp, fnptr); } else { Promise *pcopy; Log(LOG_LEVEL_VERBOSE, "Using expanded file base path '%s'", nextbuffer); /* Now need to recompute any back references to get the complete path */ snprintf(nextbufferOrig, sizeof(nextbufferOrig), "%s", nextbuffer); MapNameForward(nextbuffer); if (!FullTextMatch(pp->promiser, nextbuffer)) { Log(LOG_LEVEL_DEBUG, "Error recomputing references for '%s' in '%s'", pp->promiser, nextbuffer); } /* If there were back references there could still be match.x vars to expand */ pcopy = ExpandDeRefPromise(ctx, ScopeGetCurrent()->scope, pp); (*fnptr) (ctx, nextbufferOrig, pcopy); PromiseDestroy(pcopy); } } DirClose(dirh); } } else { Log(LOG_LEVEL_VERBOSE, "Using file base path '%s'", pbuffer); (*fnptr) (ctx, pbuffer, pp); } if (count == 0) { Log(LOG_LEVEL_VERBOSE, "No promiser file objects matched as regular expression '%s'", wildpath); if (create) { (*fnptr)(ctx, pp->promiser, pp); } } DeleteItemList(path); }
static void ExpandPromiseAndDo(EvalContext *ctx, const Promise *pp, Rlist *lists, Rlist *containers, PromiseActuator *ActOnPromise, void *param) { const char *handle = PromiseGetHandle(pp); char v[CF_MAXVARSIZE]; EvalContextStackPushPromiseFrame(ctx, pp, true); PromiseIterator *iter_ctx = NULL; for (iter_ctx = PromiseIteratorNew(ctx, pp, lists, containers); PromiseIteratorHasMore(iter_ctx); PromiseIteratorNext(iter_ctx)) { EvalContextStackPushPromiseIterationFrame(ctx, iter_ctx); char number[CF_SMALLBUF]; /* Allow $(this.handle) etc variables */ if (PromiseGetBundle(pp)->source_path) { EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promise_filename",PromiseGetBundle(pp)->source_path, DATA_TYPE_STRING); snprintf(number, CF_SMALLBUF, "%zu", pp->offset.line); EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promise_linenumber", number, DATA_TYPE_STRING); } snprintf(v, CF_MAXVARSIZE, "%d", (int) getuid()); EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser_uid", v, DATA_TYPE_INT); snprintf(v, CF_MAXVARSIZE, "%d", (int) getgid()); EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser_gid", v, DATA_TYPE_INT); EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "bundle", PromiseGetBundle(pp)->name, DATA_TYPE_STRING); EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "namespace", PromiseGetNamespace(pp), DATA_TYPE_STRING); /* Must expand $(this.promiser) here for arg dereferencing in things like edit_line and methods, but we might have to adjust again later if the value changes -- need to qualify this so we don't expand too early for some other promsies */ if (pp->has_subbundles) { EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser", pp->promiser, DATA_TYPE_STRING); } if (handle) { char tmp[CF_EXPANDSIZE]; // This ordering is necessary to get automated canonification ExpandScalar(ctx, NULL, "this", handle, tmp); CanonifyNameInPlace(tmp); Log(LOG_LEVEL_DEBUG, "Expanded handle to '%s'", tmp); EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "handle", tmp, DATA_TYPE_STRING); } else { EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "handle", PromiseID(pp), DATA_TYPE_STRING); } Promise *pexp = ExpandDeRefPromise(ctx, pp); assert(ActOnPromise); ActOnPromise(ctx, pexp, param); if (strcmp(pp->parent_promise_type->name, "vars") == 0 || strcmp(pp->parent_promise_type->name, "meta") == 0) { VerifyVarPromise(ctx, pexp, true); } PromiseDestroy(pexp); EvalContextStackPopFrame(ctx); } PromiseIteratorDestroy(iter_ctx); EvalContextStackPopFrame(ctx); }
int main(int argc, char *argv[]) { Rlist *rp; Promise *pp; #if !defined(__MINGW32__) int count = 0; int status; int pid; #endif GenericAgentConfig *config = CheckOpts(argc, argv); ReportContext *report_context = OpenReports(config->agent_type); GenericAgentDiscoverContext(config, report_context); Policy *policy = GenericAgentLoadPolicy(config->agent_type, config, report_context); CheckLicenses(); ThisAgentInit(); KeepControlPromises(policy); // Set RUNATTR using copy if (BACKGROUND && INTERACTIVE) { CfOut(OUTPUT_LEVEL_ERROR, "", " !! You cannot specify background mode and interactive mode together"); exit(1); } pp = MakeDefaultRunAgentPromise(); /* HvB */ if (HOSTLIST) { rp = HOSTLIST; while (rp != NULL) { #ifdef __MINGW32__ if (BACKGROUND) { CfOut(OUTPUT_LEVEL_VERBOSE, "", "Windows does not support starting processes in the background - starting in foreground"); BACKGROUND = false; } #else if (BACKGROUND) /* parallel */ { if (count <= MAXCHILD) { if (fork() == 0) /* child process */ { HailServer(rp->item, RUNATTR, pp); exit(0); } else /* parent process */ { rp = rp->next; count++; } } else { pid = wait(&status); CfDebug("child = %d, child number = %d\n", pid, count); count--; } } else /* serial */ #endif /* __MINGW32__ */ { HailServer(rp->item, RUNATTR, pp); rp = rp->next; } } /* end while */ } /* end if HOSTLIST */ #ifndef __MINGW32__ if (BACKGROUND) { printf("Waiting for child processes to finish\n"); while (count > 1) { pid = wait(&status); CfOut(OUTPUT_LEVEL_VERBOSE, "", "Child = %d ended, number = %d\n", pid, count); count--; } } #endif PromiseDestroy(pp); GenericAgentConfigDestroy(config); ReportContextDestroy(report_context); return 0; }