static PromiseResult VerifyServices(EvalContext *ctx, Attributes a, const Promise *pp) { CfLock thislock; thislock = AcquireLock(ctx, pp->promiser, VUQNAME, CFSTARTTIME, a.transaction, pp, false); if (thislock.lock == NULL) { return PROMISE_RESULT_SKIPPED; } PromiseBanner(ctx, pp); PromiseResult result = PROMISE_RESULT_SKIPPED; if (strcmp(a.service.service_type, "windows") == 0) { #ifdef __MINGW32__ result = PromiseResultUpdate(result, VerifyWindowsService(ctx, a, pp)); #else Log(LOG_LEVEL_INFO, "Service type windows not supported on this platform."); #endif } else { result = PromiseResultUpdate(result, DoVerifyServices(ctx, a, pp)); } YieldCurrentLock(thislock); return result; }
static void VerifyProcesses(EvalContext *ctx, Attributes a, Promise *pp) { CfLock thislock; char lockname[CF_BUFSIZE]; if (a.restart_class) { snprintf(lockname, CF_BUFSIZE - 1, "proc-%s-%s", pp->promiser, a.restart_class); } else { snprintf(lockname, CF_BUFSIZE - 1, "proc-%s-norestart", pp->promiser); } thislock = AcquireLock(ctx, lockname, VUQNAME, CFSTARTTIME, a.transaction, pp, false); if (thislock.lock == NULL) { return; } EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser", pp->promiser, DATA_TYPE_STRING); PromiseBanner(pp); VerifyProcessOp(ctx, PROCESSTABLE, a, pp); EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser"); YieldCurrentLock(thislock); }
static PromiseResult VerifyProcesses(EvalContext *ctx, Attributes a, const Promise *pp) { CfLock thislock; char lockname[CF_BUFSIZE]; if (a.restart_class) { snprintf(lockname, CF_BUFSIZE - 1, "proc-%s-%s", pp->promiser, a.restart_class); } else { snprintf(lockname, CF_BUFSIZE - 1, "proc-%s-norestart", pp->promiser); } thislock = AcquireLock(ctx, lockname, VUQNAME, CFSTARTTIME, a.transaction, pp, false); if (thislock.lock == NULL) { return PROMISE_RESULT_SKIPPED; } PromiseBanner(pp); PromiseResult result = VerifyProcessOp(ctx, PROCESSTABLE, a, pp); YieldCurrentLock(thislock); return result; }
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); }
static PromiseResult VerifyServices(EvalContext *ctx, Attributes a, Promise *pp) { CfLock thislock; thislock = AcquireLock(ctx, pp->promiser, VUQNAME, CFSTARTTIME, a.transaction, pp, false); if (thislock.lock == NULL) { return PROMISE_RESULT_SKIPPED; } EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser", pp->promiser, DATA_TYPE_STRING, "goal=state,source=promise"); PromiseBanner(pp); PromiseResult result = PROMISE_RESULT_NOOP; if (strcmp(a.service.service_type, "windows") == 0) { result = PromiseResultUpdate(result, VerifyWindowsService(ctx, a, pp)); } else { result = PromiseResultUpdate(result, DoVerifyServices(ctx, a, pp)); } EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser"); YieldCurrentLock(thislock); return result; }
void VerifyEnvironmentsPromise(Promise *pp) { Attributes a = { {0} }; CfLock thislock; Promise *pexp; a = GetEnvironmentsAttributes(pp); if (EnvironmentsSanityChecks(a, pp)) { thislock = AcquireLock("virtual", VUQNAME, CFSTARTTIME, a, pp, false); if (thislock.lock == NULL) { return; } CF_OCCUR++; PromiseBanner(pp); NewScalar("this", "promiser", pp->promiser, cf_str); pexp = ExpandDeRefPromise("this", pp); VerifyEnvironments(a, pp); DeletePromise(pexp); } YieldCurrentLock(thislock); }
static PromiseResult VerifyServices(EvalContext *ctx, Attributes a, const Promise *pp) { CfLock thislock; thislock = AcquireLock(ctx, pp->promiser, VUQNAME, CFSTARTTIME, a.transaction, pp, false); if (thislock.lock == NULL) { return PROMISE_RESULT_SKIPPED; } EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser", pp->promiser, CF_DATA_TYPE_STRING, "source=promise"); PromiseBanner(pp); PromiseResult result = PROMISE_RESULT_NOOP; if (strcmp(a.service.service_type, "windows") == 0) { #ifdef __MINGW32__ result = PromiseResultUpdate(result, VerifyWindowsService(ctx, a, pp)); #else Log(LOG_LEVEL_INFO, "Service type windows not supported on this platform."); #endif } else { result = PromiseResultUpdate(result, DoVerifyServices(ctx, a, pp)); } EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser"); YieldCurrentLock(thislock); return result; }
void VerifyProcesses(Attributes a, Promise *pp) { CfLock thislock; char lockname[CF_BUFSIZE]; if (a.restart_class) { snprintf(lockname, CF_BUFSIZE - 1, "proc-%s-%s", pp->promiser, a.restart_class); } else { snprintf(lockname, CF_BUFSIZE - 1, "proc-%s-norestart", pp->promiser); } thislock = AcquireLock(lockname, VUQNAME, CFSTARTTIME, a, pp, false); if (thislock.lock == NULL) { return; } DeleteScalar("this", "promiser"); NewScalar("this", "promiser", pp->promiser, cf_str); PromiseBanner(pp); VerifyProcessOp(PROCESSTABLE, a, pp); DeleteScalar("this", "promiser"); YieldCurrentLock(thislock); }
void VerifyExecPromise(EvalContext *ctx, Promise *pp) { Attributes a = { {0} }; a = GetExecAttributes(ctx, pp); ScopeNewSpecial(ctx, "this", "promiser", pp->promiser, DATA_TYPE_STRING); if (!SyntaxCheckExec(a, pp)) { // cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, ""); ScopeDeleteSpecial("this", "promiser"); return; } if (PromiseKeptExec(a, pp)) { // cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_NOOP, pp, a, ""); ScopeDeleteSpecial("this", "promiser"); return; } char *lock_name = GetLockNameExec(a, pp); CfLock thislock = AcquireLock(ctx, lock_name, VUQNAME, CFSTARTTIME, a.transaction, pp, false); free(lock_name); if (thislock.lock == NULL) { // cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_FAIL, pp, a, ""); ScopeDeleteSpecial("this", "promiser"); return; } PromiseBanner(pp); switch (RepairExec(ctx, a, pp)) { case ACTION_RESULT_OK: // cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_CHANGE, pp, a, ""); break; case ACTION_RESULT_TIMEOUT: // cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_TIMEOUT, pp, a, ""); break; case ACTION_RESULT_FAILED: // cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_FAIL, pp, a, ""); break; default: ProgrammingError("Unexpected ActionResult value"); } YieldCurrentLock(thislock); ScopeDeleteSpecial("this", "promiser"); }
void VerifyReportPromise(struct Promise *pp) { struct Attributes a = {{0}}; struct CfLock thislock; struct Rlist *rp; char unique_name[CF_EXPANDSIZE]; a = GetReportsAttributes(pp); if (strcmp(pp->classes,"any") == 0) { CfOut(cf_verbose,""," --> Reports promises may not be in class \"any\""); return; } snprintf(unique_name,CF_EXPANDSIZE-1,"%s_%d",pp->promiser,pp->lineno); thislock = AcquireLock(unique_name,VUQNAME,CFSTARTTIME,a,pp,false); if (thislock.lock == NULL) { return; } PromiseBanner(pp); cfPS(cf_verbose,CF_CHG,"",pp,a,"Report: %s", pp->promiser); if (a.report.to_file) { CfFOut(a.report.to_file,cf_error,"","%s",pp->promiser); } else { CfOut(cf_reporting,"","R: %s",pp->promiser); } if (a.report.haveprintfile) { PrintFile(a,pp); } if (a.report.showstate) { for (rp = a.report.showstate; rp != NULL; rp=rp->next) { ShowState(rp->item,a,pp); } } if (a.report.havelastseen) { FriendStatus(a,pp); } YieldCurrentLock(thislock); }
static void LocksCleanup(void) { if (strlen(CFLOCK) > 0) { CfLock best_guess; best_guess.lock = xstrdup(CFLOCK); best_guess.last = xstrdup(CFLAST); best_guess.log = xstrdup(CFLOG); YieldCurrentLock(best_guess); } }
PromiseResult VerifyExecPromise(EvalContext *ctx, const Promise *pp) { Attributes a = GetExecAttributes(ctx, pp); EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser", pp->promiser, DATA_TYPE_STRING, "source=promise"); if (!SyntaxCheckExec(a, pp)) { EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser"); return PROMISE_RESULT_FAIL; } if (PromiseKeptExec(a, pp)) { EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser"); return PROMISE_RESULT_NOOP; } char *lock_name = GetLockNameExec(a, pp); CfLock thislock = AcquireLock(ctx, lock_name, VUQNAME, CFSTARTTIME, a.transaction, pp, false); free(lock_name); if (thislock.lock == NULL) { EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser"); return PROMISE_RESULT_SKIPPED; } PromiseBanner(pp); PromiseResult result = PROMISE_RESULT_NOOP; switch (RepairExec(ctx, a, pp, &result)) { case ACTION_RESULT_OK: result = PromiseResultUpdate(result, PROMISE_RESULT_CHANGE); break; case ACTION_RESULT_TIMEOUT: result = PromiseResultUpdate(result, PROMISE_RESULT_TIMEOUT); break; case ACTION_RESULT_FAILED: result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); break; default: ProgrammingError("Unexpected ActionResult value"); } YieldCurrentLock(thislock); EvalContextVariableRemoveSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser"); return result; }
void VerifyReportPromise(Promise *pp) { Attributes a = { {0} }; CfLock thislock; Rlist *rp; char unique_name[CF_EXPANDSIZE]; a = GetReportsAttributes(pp); snprintf(unique_name, CF_EXPANDSIZE - 1, "%s_%zu", pp->promiser, pp->offset.line); thislock = AcquireLock(unique_name, VUQNAME, CFSTARTTIME, a, pp, false); if (thislock.lock == NULL) { return; } PromiseBanner(pp); cfPS(cf_verbose, CF_CHG, "", pp, a, "Report: %s", pp->promiser); if (a.report.to_file) { CfFOut(a.report.to_file, cf_error, "", "%s", pp->promiser); } else { CfOut(cf_reporting, "", "R: %s", pp->promiser); } if (a.report.haveprintfile) { PrintFile(a, pp); } if (a.report.showstate) { for (rp = a.report.showstate; rp != NULL; rp = rp->next) { ShowState(rp->item); } } if (a.report.havelastseen) { /* Do nothing. Deprecated. */ } YieldCurrentLock(thislock); }
PromiseResult VerifyUsersPromise(EvalContext *ctx, Promise *pp) { Attributes a = { {0} }; CfLock thislock; char lockname[CF_BUFSIZE]; a = GetUserAttributes(ctx, pp); if (!UserSanityCheck(a, pp)) { return PROMISE_RESULT_FAIL; } PromiseBanner(pp); snprintf(lockname, CF_BUFSIZE - 1, "user-%s-%d", pp->promiser, a.users.policy); thislock = AcquireLock(ctx, lockname, VUQNAME, CFSTARTTIME, a.transaction, pp, false); if (thislock.lock == NULL) { return PROMISE_RESULT_SKIPPED; } PromiseResult result = PROMISE_RESULT_NOOP; VerifyOneUsersPromise(pp->promiser, a.users, &result, a.transaction.action, ctx, &a, pp); switch (result) { case PROMISE_RESULT_NOOP: cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_NOOP, pp, a, "User promise kept"); break; case PROMISE_RESULT_FAIL: case PROMISE_RESULT_DENIED: case PROMISE_RESULT_TIMEOUT: case PROMISE_RESULT_INTERRUPTED: case PROMISE_RESULT_WARN: cfPS(ctx, LOG_LEVEL_INFO, result, pp, a, "User promise not kept"); break; case PROMISE_RESULT_CHANGE: cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_CHANGE, pp, a, "User promise repaired"); break; default: ProgrammingError("Unknown promise result"); break; } YieldCurrentLock(thislock); return result; }
void SelfTerminatePrelude(void) { CfLock best_guess; CfOut(cf_verbose, "", "Trying to remove lock - try %s", CFLOCK); best_guess.lock = xstrdup(CFLOCK); best_guess.last = xstrdup(CFLAST); best_guess.log = xstrdup(CFLOG); YieldCurrentLock(best_guess); unlink(PIDFILE); if (THIS_AGENT_TYPE == cf_agent) { EndAudit(); } GenericDeInitialize(); }
void VerifyServices(EvalContext *ctx, Attributes a, Promise *pp, const ReportContext *report_context) { CfLock thislock; // allow to start Cfengine windows executor without license #ifdef __MINGW32__ if ((LICENSES == 0) && (strcmp(WINSERVICE_NAME, pp->promiser) != 0)) { return; } #endif thislock = AcquireLock(pp->promiser, VUQNAME, CFSTARTTIME, a, pp, false); if (thislock.lock == NULL) { return; } ScopeNewScalar("this", "promiser", pp->promiser, DATA_TYPE_STRING); PromiseBanner(ctx, pp); if (strcmp(a.service.service_type, "windows") == 0) { VerifyWindowsService(ctx, a, pp); } else { DoVerifyServices(ctx, a, pp, report_context); } ScopeDeleteScalar("this", "promiser"); YieldCurrentLock(thislock); }
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; }
void VerifyReportPromise(EvalContext *ctx, Promise *pp) { Attributes a = { {0} }; CfLock thislock; char unique_name[CF_EXPANDSIZE]; a = GetReportsAttributes(ctx, pp); snprintf(unique_name, CF_EXPANDSIZE - 1, "%s_%zu", pp->promiser, pp->offset.line); thislock = AcquireLock(ctx, unique_name, VUQNAME, CFSTARTTIME, a.transaction, pp, false); // Handle return values before locks, as we always do this if (a.report.result) { // User-unwritable value last-result contains the useresult if (strlen(a.report.result) > 0) { snprintf(unique_name, CF_BUFSIZE, "last-result[%s]", a.report.result); } else { snprintf(unique_name, CF_BUFSIZE, "last-result"); } VarRef *ref = VarRefParseFromBundle(unique_name, PromiseGetBundle(pp)); EvalContextVariablePut(ctx, ref, (Rval) { pp->promiser, RVAL_TYPE_SCALAR }, DATA_TYPE_STRING); VarRefDestroy(ref); return; } // Now do regular human reports if (thislock.lock == NULL) { return; } PromiseBanner(pp); if (a.transaction.action == cfa_warn) { cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_WARN, pp, a, "Need to repair reports promise: %s", pp->promiser); YieldCurrentLock(thislock); return; } cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_CHANGE, pp, a, "Report: %s", pp->promiser); if (a.report.to_file) { ReportToFile(a.report.to_file, pp->promiser); } else { Log(LOG_LEVEL_NOTICE, "R: %s", pp->promiser); } if (a.report.haveprintfile) { PrintFile(ctx, a, pp); } if (a.report.showstate) { /* Do nothing. Deprecated. */ } if (a.report.havelastseen) { /* Do nothing. Deprecated. */ } YieldCurrentLock(thislock); }
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 PromiseResult VerifyFilePromise(EvalContext *ctx, char *path, Promise *pp) { struct stat osb, oslb, dsb; Attributes a = { {0} }; CfLock thislock; int exists; a = GetFilesAttributes(ctx, pp); if (!FileSanityChecks(ctx, path, a, pp)) { return PROMISE_RESULT_NOOP; } EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser", path, DATA_TYPE_STRING); thislock = AcquireLock(ctx, path, VUQNAME, CFSTARTTIME, a.transaction, pp, false); if (thislock.lock == NULL) { return PROMISE_RESULT_NOOP; } LoadSetuid(a); PromiseResult result = PROMISE_RESULT_NOOP; if (lstat(path, &oslb) == -1) /* Careful if the object is a link */ { if ((a.create) || (a.touch)) { if (!CfCreateFile(ctx, path, pp, a, &result)) { goto exit; } else { exists = (lstat(path, &oslb) != -1); } } exists = false; } else { if ((a.create) || (a.touch)) { cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "File '%s' exists as promised", path); } exists = true; } if ((a.havedelete) && (!exists)) { cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "File '%s' does not exist as promised", path); goto exit; } if (!a.havedepthsearch) /* if the search is trivial, make sure that we are in the parent dir of the leaf */ { char basedir[CF_BUFSIZE]; Log(LOG_LEVEL_DEBUG, "Direct file reference '%s', no search implied", path); snprintf(basedir, sizeof(basedir), "%s", path); if (strcmp(ReadLastNode(basedir), ".") == 0) { // Handle /. notation for deletion of directories ChopLastNode(basedir); ChopLastNode(path); } ChopLastNode(basedir); if (chdir(basedir)) { Log(LOG_LEVEL_ERR, "Failed to chdir into '%s'", basedir); } } if (exists && (!VerifyFileLeaf(ctx, path, &oslb, a, pp, &result))) { if (!S_ISDIR(oslb.st_mode)) { goto exit; } } if (stat(path, &osb) == -1) { if ((a.create) || (a.touch)) { if (!CfCreateFile(ctx, path, pp, a, &result)) { goto exit; } else { exists = true; } } else { exists = false; } } else { if (!S_ISDIR(osb.st_mode)) { if (a.havedepthsearch) { Log(LOG_LEVEL_WARNING, "depth_search (recursion) is promised for a base object '%s' that is not a directory", path); goto exit; } } exists = true; } if (a.link.link_children) { if (stat(a.link.source, &dsb) != -1) { if (!S_ISDIR(dsb.st_mode)) { Log(LOG_LEVEL_ERR, "Cannot promise to link the children of '%s' as it is not a directory!", a.link.source); goto exit; } } } /* Phase 1 - */ if (exists && ((a.havedelete) || (a.haverename) || (a.haveperms) || (a.havechange) || (a.transformer))) { lstat(path, &oslb); /* if doesn't exist have to stat again anyway */ DepthSearch(ctx, path, &oslb, 0, a, pp, oslb.st_dev, &result); /* normally searches do not include the base directory */ if (a.recursion.include_basedir) { int save_search = a.havedepthsearch; /* Handle this node specially */ a.havedepthsearch = false; DepthSearch(ctx, path, &oslb, 0, a, pp, oslb.st_dev, &result); a.havedepthsearch = save_search; } else { /* unless child nodes were repaired, set a promise kept class */ if (!IsDefinedClass(ctx, "repaired" , PromiseGetNamespace(pp))) { cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "Basedir '%s' not promising anything", path); } } if (((a.change.report_changes) == FILE_CHANGE_REPORT_CONTENT_CHANGE) || ((a.change.report_changes) == FILE_CHANGE_REPORT_ALL)) { if (a.havedepthsearch) { PurgeHashes(ctx, NULL, a, pp); } else { PurgeHashes(ctx, path, a, pp); } } } /* Phase 2a - copying is potentially threadable if no followup actions */ if (a.havecopy) { result = PromiseResultUpdate(result, ScheduleCopyOperation(ctx, path, a, pp)); } /* Phase 2b link after copy in case need file first */ if ((a.havelink) && (a.link.link_children)) { result = PromiseResultUpdate(result, ScheduleLinkChildrenOperation(ctx, path, a.link.source, 1, a, pp)); } else if (a.havelink) { result = PromiseResultUpdate(result, ScheduleLinkOperation(ctx, path, a.link.source, a, pp)); } /* Phase 3 - content editing */ if (a.haveedit) { result = PromiseResultUpdate(result, ScheduleEditOperation(ctx, path, a, pp)); } // Once more in case a file has been created as a result of editing or copying exists = (stat(path, &osb) != -1); if (exists && (S_ISREG(osb.st_mode))) { VerifyFileLeaf(ctx, path, &osb, a, pp, &result); } if (!exists && a.havechange) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Promised to monitor '%s' for changes, but file does not exist", path); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); } exit: result = PromiseResultUpdate(result, SaveSetuid(ctx, a, pp)); 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 VerifyStoragePromise(char *path, Promise *pp, const ReportContext *report_context) { Attributes a = { {0} }; CfLock thislock; a = GetStorageAttributes(pp); CF_OCCUR++; #ifdef MINGW if (!a.havemount) { CfOut(cf_verbose, "", "storage.mount is not supported on Windows"); } #endif /* No parameter conflicts here */ if (a.mount.unmount) { if (a.mount.mount_source || a.mount.mount_server) { CfOut(cf_verbose, "", " !! An unmount promise indicates a mount-source information - probably in error\n"); } } else if (a.havemount) { if (a.mount.mount_source == NULL || a.mount.mount_server == NULL) { CfOut(cf_error, "", " !! Insufficient specification in mount promise - need source and server\n"); return; } } thislock = AcquireLock(path, VUQNAME, CFSTARTTIME, a, pp, false); if (thislock.lock == NULL) { return; } /* Do mounts first */ #ifndef MINGW if (!MOUNTEDFSLIST && !LoadMountInfo(&MOUNTEDFSLIST)) { CfOut(cf_error, "", "Couldn't obtain a list of mounted filesystems - aborting\n"); YieldCurrentLock(thislock); return; } if (a.havemount) { VerifyMountPromise(path, a, pp, report_context); } #endif /* NOT MINGW */ /* Then check file system */ if (a.havevolume) { VerifyFileSystem(path, a, pp); if (a.volume.freespace != CF_NOINT) { VerifyFreeSpace(path, a, pp); } if (a.volume.scan_arrivals) { VolumeScanArrivals(path, a, pp); } } YieldCurrentLock(thislock); }
/** * @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; }
PromiseResult VerifyReportPromise(EvalContext *ctx, const Promise *pp) { CfLock thislock; char unique_name[CF_EXPANDSIZE]; Attributes a = GetReportsAttributes(ctx, pp); // We let AcquireLock worry about making a unique name snprintf(unique_name, CF_EXPANDSIZE - 1, "%s", pp->promiser); thislock = AcquireLock(ctx, unique_name, VUQNAME, CFSTARTTIME, a.transaction, pp, false); // Handle return values before locks, as we always do this if (a.report.result) { // User-unwritable value last-result contains the useresult if (strlen(a.report.result) > 0) { snprintf(unique_name, CF_BUFSIZE, "last-result[%s]", a.report.result); } else { snprintf(unique_name, CF_BUFSIZE, "last-result"); } VarRef *ref = VarRefParseFromBundle(unique_name, PromiseGetBundle(pp)); EvalContextVariablePut(ctx, ref, pp->promiser, CF_DATA_TYPE_STRING, "source=bundle"); VarRefDestroy(ref); if (thislock.lock) { YieldCurrentLock(thislock); } return PROMISE_RESULT_NOOP; } if (thislock.lock == NULL) { return PROMISE_RESULT_SKIPPED; } PromiseBanner(ctx, pp); if (a.transaction.action == cfa_warn) { cfPS(ctx, LOG_LEVEL_WARNING, PROMISE_RESULT_WARN, pp, a, "Need to repair reports promise: %s", pp->promiser); YieldCurrentLock(thislock); return PROMISE_RESULT_WARN; } if (a.report.to_file) { ReportToFile(a.report.to_file, pp->promiser); } else { ReportToLog(pp->promiser); } PromiseResult result = PROMISE_RESULT_NOOP; if (a.report.haveprintfile) { if (!PrintFile(a.report.filename, a.report.numlines)) { result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); } } YieldCurrentLock(thislock); ClassAuditLog(ctx, pp, a, result); return result; }
PromiseResult VerifyStoragePromise(EvalContext *ctx, char *path, Promise *pp) { Attributes a = { {0} }; CfLock thislock; a = GetStorageAttributes(ctx, pp); #ifdef __MINGW32__ if (!a.havemount) { Log(LOG_LEVEL_VERBOSE, "storage.mount is not supported on Windows"); } #endif /* No parameter conflicts here */ if (a.mount.unmount) { if ((a.mount.mount_source)) { Log(LOG_LEVEL_VERBOSE, "An unmount promise indicates a mount-source information - probably an error"); } if ((a.mount.mount_server)) { Log(LOG_LEVEL_VERBOSE, "An unmount promise indicates a mount-server information - probably an error"); } } else if (a.havemount) { if ((a.mount.mount_source == NULL) || (a.mount.mount_server == NULL)) { Log(LOG_LEVEL_ERR, "Insufficient specification in mount promise - need source and server"); return PROMISE_RESULT_NOOP; } } thislock = AcquireLock(ctx, path, VUQNAME, CFSTARTTIME, a.transaction, pp, false); if (thislock.lock == NULL) { return PROMISE_RESULT_NOOP; } /* Do mounts first */ PromiseResult result = PROMISE_RESULT_NOOP; #ifndef __MINGW32__ if ((SeqLength(GetGlobalMountedFSList())) && (!LoadMountInfo(GetGlobalMountedFSList()))) { Log(LOG_LEVEL_ERR, "Couldn't obtain a list of mounted filesystems - aborting"); YieldCurrentLock(thislock); return PROMISE_RESULT_NOOP; } if (a.havemount) { result = PromiseResultUpdate(result, VerifyMountPromise(ctx, path, a, pp)); } #endif /* !__MINGW32__ */ /* Then check file system */ if (a.havevolume) { result = PromiseResultUpdate(result, VerifyFileSystem(ctx, path, a, pp)); if (a.volume.freespace != CF_NOINT) { result = PromiseResultUpdate(result, VerifyFreeSpace(ctx, path, a, pp)); } if (a.volume.scan_arrivals) { result = PromiseResultUpdate(result, VolumeScanArrivals(path, a, pp)); } } YieldCurrentLock(thislock); return result; }
void StartServer(Policy *policy, GenericAgentConfig config, const ReportContext *report_context) { int sd = -1, sd_reply; fd_set rset; struct timeval timeout; int ret_val; Promise *pp = NewPromise("server_cfengine", "the server daemon"); Attributes dummyattr = { {0} }; CfLock thislock; time_t starttime = time(NULL), last_collect = 0; #if defined(HAVE_GETADDRINFO) socklen_t addrlen = sizeof(struct sockaddr_in6); struct sockaddr_in6 cin; #else socklen_t addrlen = sizeof(struct sockaddr_in); struct sockaddr_in cin; #endif memset(&dummyattr, 0, sizeof(dummyattr)); signal(SIGINT, HandleSignals); signal(SIGTERM, HandleSignals); signal(SIGHUP, SIG_IGN); signal(SIGPIPE, SIG_IGN); signal(SIGUSR1, HandleSignals); signal(SIGUSR2, HandleSignals); sd = SetServerListenState(QUEUESIZE); dummyattr.transaction.ifelapsed = 0; dummyattr.transaction.expireafter = 1; thislock = AcquireLock(pp->promiser, VUQNAME, CFSTARTTIME, dummyattr, pp, false); if (thislock.lock == NULL) { return; } CfOut(cf_inform, "", "cf-serverd starting %.24s\n", cf_ctime(&starttime)); if (sd != -1) { CfOut(cf_verbose, "", "Listening for connections ...\n"); } #ifdef MINGW if (!NO_FORK) { CfOut(cf_verbose, "", "Windows does not support starting processes in the background - starting in foreground"); } #else /* NOT MINGW */ if ((!NO_FORK) && (fork() != 0)) { exit(0); } if (!NO_FORK) { ActAsDaemon(sd); } #endif /* NOT MINGW */ WritePID("cf-serverd.pid"); /* Andrew Stribblehill <*****@*****.**> -- close sd on exec */ #ifndef MINGW fcntl(sd, F_SETFD, FD_CLOEXEC); #endif while (true) { 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(&policy, config, report_context); } 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); timeout.tv_sec = 10; /* Set a 10 second timeout for select */ timeout.tv_usec = 0; CfDebug(" -> Waiting at incoming select...\n"); ret_val = select((sd + 1), &rset, NULL, NULL, &timeout); if (ret_val == -1) /* Error received from call to select */ { if (errno == EINTR) { continue; } else { CfOut(cf_error, "select", "select failed"); exit(1); } } else if (!ret_val) /* No data waiting, we must have timed out! */ { continue; } CfOut(cf_verbose, "", " -> Accepting a connection\n"); if ((sd_reply = accept(sd, (struct sockaddr *) &cin, &addrlen)) != -1) { char ipaddr[CF_MAXVARSIZE]; memset(ipaddr, 0, CF_MAXVARSIZE); ThreadLock(cft_getaddr); snprintf(ipaddr, CF_MAXVARSIZE - 1, "%s", sockaddr_ntop((struct sockaddr *) &cin)); ThreadUnlock(cft_getaddr); ServerEntryPoint(sd_reply, ipaddr, SV); } } } YieldCurrentLock(thislock); /* We never get here - this is done by a signal handler */ }
void VerifyReportPromise(Promise *pp) { Attributes a = { {0} }; CfLock thislock; Rlist *rp; char unique_name[CF_EXPANDSIZE]; a = GetReportsAttributes(pp); snprintf(unique_name, CF_EXPANDSIZE - 1, "%s_%zu", pp->promiser, pp->offset.line); thislock = AcquireLock(unique_name, VUQNAME, CFSTARTTIME, a, pp, false); // Handle return values before locks, as we always do this if (a.report.result) { // User-unwritable value last-result contains the useresult if (strlen(a.report.result) > 0) { snprintf(unique_name, CF_BUFSIZE, "last-result[%s]", a.report.result); } else { snprintf(unique_name, CF_BUFSIZE, "last-result"); } NewScalar(pp->bundle, unique_name, pp->promiser, cf_str); return; } // Now do regular human reports if (thislock.lock == NULL) { return; } PromiseBanner(pp); cfPS(cf_verbose, CF_CHG, "", pp, a, "Report: %s", pp->promiser); if (a.report.to_file) { CfFOut(a.report.to_file, cf_error, "", "%s", pp->promiser); } else { CfOut(cf_reporting, "", "R: %s", pp->promiser); } if (a.report.haveprintfile) { PrintFile(a, pp); } if (a.report.showstate) { for (rp = a.report.showstate; rp != NULL; rp = rp->next) { ShowState(rp->item); } } if (a.report.havelastseen) { /* Do nothing. Deprecated. */ } YieldCurrentLock(thislock); }
void VerifyStoragePromise(EvalContext *ctx, char *path, Promise *pp, ARG_UNUSED const ReportContext *report_context) /* FIXME: unused param */ { Attributes a = { {0} }; CfLock thislock; a = GetStorageAttributes(ctx, pp); CF_OCCUR++; #ifdef __MINGW32__ if (!a.havemount) { CfOut(OUTPUT_LEVEL_VERBOSE, "", "storage.mount is not supported on Windows"); } #endif /* No parameter conflicts here */ if (a.mount.unmount) { if ((a.mount.mount_source)) { CfOut(OUTPUT_LEVEL_VERBOSE, "", " !! An unmount promise indicates a mount-source information - probably an error\n"); } if ((a.mount.mount_server)) { CfOut(OUTPUT_LEVEL_VERBOSE, "", " !! An unmount promise indicates a mount-server information - probably an error\n"); } } else if (a.havemount) { if ((a.mount.mount_source == NULL) || (a.mount.mount_server == NULL)) { CfOut(OUTPUT_LEVEL_ERROR, "", " !! Insufficient specification in mount promise - need source and server\n"); return; } } thislock = AcquireLock(ctx, path, VUQNAME, CFSTARTTIME, a, pp, false); if (thislock.lock == NULL) { return; } /* Do mounts first */ #ifndef __MINGW32__ if ((!MOUNTEDFSLIST) && (!LoadMountInfo(&MOUNTEDFSLIST))) { CfOut(OUTPUT_LEVEL_ERROR, "", "Couldn't obtain a list of mounted filesystems - aborting\n"); YieldCurrentLock(thislock); return; } if (a.havemount) { VerifyMountPromise(ctx, path, a, pp); } #endif /* !__MINGW32__ */ /* Then check file system */ if (a.havevolume) { VerifyFileSystem(ctx, path, a, pp); if (a.volume.freespace != CF_NOINT) { VerifyFreeSpace(ctx, path, a, pp); } if (a.volume.scan_arrivals) { VolumeScanArrivals(path, a, pp); } } YieldCurrentLock(thislock); }
static void VerifySQLPromise(Attributes a, Promise *pp) { char database[CF_MAXVARSIZE], table[CF_MAXVARSIZE], query[CF_BUFSIZE]; char *sp; int count = 0; CfdbConn cfdb; CfLock thislock; char lockname[CF_BUFSIZE]; snprintf(lockname, CF_BUFSIZE - 1, "db-%s", pp->promiser); thislock = AcquireLock(lockname, VUQNAME, CFSTARTTIME, a, pp, false); if (thislock.lock == NULL) { return; } database[0] = '\0'; table[0] = '\0'; for (sp = pp->promiser; *sp != '\0'; sp++) { if (strchr("./\\", *sp)) { count++; strncpy(table, sp + 1, CF_MAXVARSIZE - 1); sscanf(pp->promiser, "%[^.\\/]", database); if (strlen(database) == 0) { cfPS(cf_error, CF_FAIL, "", pp, a, "SQL database promiser syntax should be of the form \"database.table\""); PromiseRef(cf_error, pp); YieldCurrentLock(thislock); return; } } } if (count > 1) { cfPS(cf_error, CF_FAIL, "", pp, a, "SQL database promiser syntax should be of the form \"database.table\""); PromiseRef(cf_error, pp); } if (strlen(database) == 0) { strncpy(database, pp->promiser, CF_MAXVARSIZE - 1); } if (a.database.operation == NULL) { cfPS(cf_error, CF_FAIL, "", pp, a , "Missing database_operation in database promise"); PromiseRef(cf_error, pp); YieldCurrentLock(thislock); return; } if (strcmp(a.database.operation, "delete") == 0) { /* Just deal with one */ strcpy(a.database.operation, "drop"); } /* Connect to the server */ CfConnectDB(&cfdb, a.database.db_server_type, a.database.db_server_host, a.database.db_server_owner, a.database.db_server_password, database); if (!cfdb.connected) { /* If we haven't said create then db should already exist */ if ((a.database.operation) && (strcmp(a.database.operation, "create") != 0)) { CfOut(cf_error, "", "Could not connect an existing database %s - check server configuration?\n", database); PromiseRef(cf_error, pp); CfCloseDB(&cfdb); YieldCurrentLock(thislock); return; } } /* Check change of existential constraints */ if ((a.database.operation) && (strcmp(a.database.operation, "create") == 0)) { CfConnectDB(&cfdb, a.database.db_server_type, a.database.db_server_host, a.database.db_server_owner, a.database.db_server_password, a.database.db_connect_db); if (!cfdb.connected) { CfOut(cf_error, "", "Could not connect to the sql_db server for %s\n", database); return; } /* Don't drop the db if we really want to drop a table */ if ((strlen(table) == 0) || ((strlen(table) > 0) && (strcmp(a.database.operation, "drop") != 0))) { VerifyDatabasePromise(&cfdb, database, a, pp); } /* Close the database here to commit the change - might have to reopen */ CfCloseDB(&cfdb); } /* Now check the structure of the named table, if any */ if (strlen(table) == 0) { YieldCurrentLock(thislock); return; } CfConnectDB(&cfdb, a.database.db_server_type, a.database.db_server_host, a.database.db_server_owner, a.database.db_server_password, database); if (!cfdb.connected) { CfOut(cf_inform, "", "Database %s is not connected\n", database); } else { snprintf(query, CF_MAXVARSIZE - 1, "%s.%s", database, table); if (VerifyTablePromise(&cfdb, query, a.database.columns, a, pp)) { cfPS(cf_inform, CF_NOP, "", pp, a, " -> Table \"%s\" is as promised", query); } else { cfPS(cf_inform, CF_FAIL, "", pp, a, " -> Table \"%s\" is not as promised", query); } /* Finally check any row constraints on this table */ if (a.database.rows) { CfOut(cf_inform, "", " !! Database row operations are not currently supported. Please contact cfengine with suggestions."); } CfCloseDB(&cfdb); } YieldCurrentLock(thislock); }
static void VerifyExec(Attributes a, Promise *pp) { CfLock thislock; char unsafeLine[CF_BUFSIZE], line[sizeof(unsafeLine) * 2], eventname[CF_BUFSIZE]; char comm[20]; char execstr[CF_EXPANDSIZE]; int outsourced, count = 0; mode_t maskval = 0; FILE *pfp; char cmdOutBuf[CF_BUFSIZE]; int cmdOutBufPos = 0; int lineOutLen; if (!IsExecutable(GetArg0(pp->promiser))) { cfPS(cf_error, CF_FAIL, "", pp, a, "%s promises to be executable but isn't\n", pp->promiser); if (strchr(pp->promiser, ' ')) { CfOut(cf_verbose, "", "Paths with spaces must be inside escaped quoutes (e.g. \\\"%s\\\")", pp->promiser); } return; } else { CfOut(cf_verbose, "", " -> Promiser string contains a valid executable (%s) - ok\n", GetArg0(pp->promiser)); } DeleteScalar("this", "promiser"); NewScalar("this", "promiser", pp->promiser, cf_str); if (a.args) { snprintf(execstr, CF_EXPANDSIZE - 1, "%s %s", pp->promiser, a.args); } else { strncpy(execstr, pp->promiser, CF_BUFSIZE); } thislock = AcquireLock(execstr, VUQNAME, CFSTARTTIME, a, pp, false); if (thislock.lock == NULL) { return; } PromiseBanner(pp); CfOut(cf_inform, "", " -> Executing \'%s\' ...(timeout=%d,owner=%ju,group=%ju)\n", execstr, a.contain.timeout, (uintmax_t)a.contain.owner, (uintmax_t)a.contain.group); BeginMeasure(); if (DONTDO && !a.contain.preview) { CfOut(cf_error, "", "-> Would execute script %s\n", execstr); } else if (a.transaction.action != cfa_fix) { cfPS(cf_error, CF_WARN, "", pp, a, " !! Command \"%s\" needs to be executed, but only warning was promised", execstr); } else { CommPrefix(execstr, comm); if (a.transaction.background) { #ifdef MINGW outsourced = true; #else CfOut(cf_verbose, "", " -> Backgrounding job %s\n", execstr); outsourced = fork(); #endif } else { outsourced = false; } if (outsourced || !a.transaction.background) // work done here: either by child or non-background parent { if (a.contain.timeout != CF_NOINT) { SetTimeOut(a.contain.timeout); } #ifndef MINGW CfOut(cf_verbose, "", " -> (Setting umask to %jo)\n", (uintmax_t)a.contain.umask); maskval = umask(a.contain.umask); if (a.contain.umask == 0) { CfOut(cf_verbose, "", " !! Programming %s running with umask 0! Use umask= to set\n", execstr); } #endif /* NOT MINGW */ if (a.contain.useshell) { pfp = cf_popen_shsetuid(execstr, "r", a.contain.owner, a.contain.group, a.contain.chdir, a.contain.chroot, a.transaction.background); } else { pfp = cf_popensetuid(execstr, "r", a.contain.owner, a.contain.group, a.contain.chdir, a.contain.chroot, a.transaction.background); } if (pfp == NULL) { cfPS(cf_error, CF_FAIL, "cf_popen", pp, a, "!! Couldn't open pipe to command %s\n", execstr); YieldCurrentLock(thislock); return; } while (!feof(pfp)) { if (ferror(pfp)) /* abortable */ { cfPS(cf_error, CF_TIMEX, "ferror", pp, a, "!! Command pipe %s\n", execstr); cf_pclose(pfp); YieldCurrentLock(thislock); return; } CfReadLine(unsafeLine, CF_BUFSIZE - 1, pfp); ReplaceStr(unsafeLine, line, sizeof(line), "%", "%%"); // escape format char if (strstr(line, "cfengine-die")) { break; } if (ferror(pfp)) /* abortable */ { cfPS(cf_error, CF_TIMEX, "ferror", pp, a, "!! Command pipe %s\n", execstr); cf_pclose(pfp); YieldCurrentLock(thislock); return; } if (a.contain.preview) { PreviewProtocolLine(line, execstr); } if (a.module) { ModuleProtocol(execstr, line, !a.contain.nooutput); } else if (!a.contain.nooutput && NonEmptyLine(line)) { lineOutLen = strlen(comm) + strlen(line) + 12; // if buffer is to small for this line, output it directly if (lineOutLen > sizeof(cmdOutBuf)) { CfOut(cf_cmdout, "", "Q: \"...%s\": %s\n", comm, line); } else { if (cmdOutBufPos + lineOutLen > sizeof(cmdOutBuf)) { CfOut(cf_cmdout, "", "%s", cmdOutBuf); cmdOutBufPos = 0; } sprintf(cmdOutBuf + cmdOutBufPos, "Q: \"...%s\": %s\n", comm, line); cmdOutBufPos += (lineOutLen - 1); } count++; } } #ifdef MINGW if (outsourced) // only get return value if we waited for command execution { cf_pclose(pfp); } else { cf_pclose_def(pfp, a, pp); } #else /* NOT MINGW */ cf_pclose_def(pfp, a, pp); #endif } if (count) { if (cmdOutBufPos) { CfOut(cf_cmdout, "", "%s", cmdOutBuf); } CfOut(cf_cmdout, "", "I: Last %d quoted lines were generated by promiser \"%s\"\n", count, execstr); } if (a.contain.timeout != CF_NOINT) { alarm(0); signal(SIGALRM, SIG_DFL); } CfOut(cf_inform, "", " -> Completed execution of %s\n", execstr); #ifndef MINGW umask(maskval); #endif YieldCurrentLock(thislock); snprintf(eventname, CF_BUFSIZE - 1, "Exec(%s)", execstr); #ifndef MINGW if (a.transaction.background && outsourced) { CfOut(cf_verbose, "", " -> Backgrounded command (%s) is done - exiting\n", execstr); exit(0); } #endif /* NOT MINGW */ } }