int VarClassExcluded(EvalContext *ctx, Promise *pp, char **classes) { Constraint *cp = PromiseGetConstraint(ctx, pp, "ifvarclass"); if (cp == NULL) { return false; } *classes = (char *) ConstraintGetRvalValue(ctx, "ifvarclass", pp, RVAL_TYPE_SCALAR); if (*classes == NULL) { return true; } if (strchr(*classes, '$') || strchr(*classes, '@')) { CfDebug("Class expression did not evaluate"); return true; } if (*classes && IsDefinedClass(ctx, *classes, PromiseGetNamespace(pp))) { return false; } else { return true; } }
static void GetReturnValue(EvalContext *ctx, char *scope, Promise *pp) { char *result = ConstraintGetRvalValue(ctx, "useresult", pp, RVAL_TYPE_SCALAR); if (result) { AssocHashTableIterator i; CfAssoc *assoc; char newname[CF_BUFSIZE]; Scope *ptr; char index[CF_MAXVARSIZE], match[CF_MAXVARSIZE]; if ((ptr = ScopeGet(scope)) == NULL) { Log(LOG_LEVEL_INFO, "useresult was specified but the method returned no data"); return; } i = HashIteratorInit(ptr->hashtable); while ((assoc = HashIteratorNext(&i))) { snprintf(match, CF_MAXVARSIZE - 1, "last-result["); if (strncmp(match, assoc->lval, strlen(match)) == 0) { char *sp; index[0] = '\0'; sscanf(assoc->lval + strlen(match), "%127[^\n]", index); if ((sp = strchr(index, ']'))) { *sp = '\0'; } else { index[strlen(index) - 1] = '\0'; } if (strlen(index) > 0) { snprintf(newname, CF_BUFSIZE, "%s[%s]", result, index); } else { snprintf(newname, CF_BUFSIZE, "%s", result); } EvalContextVariablePut(ctx, (VarRef) { NULL, PromiseGetBundle(pp)->name, newname }, assoc->rval, DATA_TYPE_STRING); } } } }
void EndMeasurePromise(EvalContext *ctx, struct timespec start, Promise *pp) { char id[CF_BUFSIZE], *mid = NULL; mid = ConstraintGetRvalValue(ctx, "measurement_class", pp, RVAL_TYPE_SCALAR); if (mid) { snprintf(id, CF_BUFSIZE, "%s:%s:%.100s", (char *) mid, pp->parent_promise_type->name, pp->promiser); if (Chop(id, CF_EXPANDSIZE) == -1) { Log(LOG_LEVEL_ERR, "Chop was called on a string that seemed to have no terminator"); } EndMeasure(id, start); } }
void MarkPromiseHandleDone(EvalContext *ctx, const Promise *pp) { if (pp == NULL) { return; } char name[CF_BUFSIZE]; char *handle = ConstraintGetRvalValue(ctx, "handle", pp, RVAL_TYPE_SCALAR); if (handle == NULL) { return; } snprintf(name, CF_BUFSIZE, "%s:%s", PromiseGetNamespace(pp), handle); StringSetAdd(ctx->dependency_handles, xstrdup(name)); }
static PromiseResult FindFilePromiserObjects(EvalContext *ctx, Promise *pp) { char *val = ConstraintGetRvalValue(ctx, "pathtype", pp, RVAL_TYPE_SCALAR); int literal = (PromiseGetConstraintAsBoolean(ctx, "copy_from", pp)) || ((val != NULL) && (strcmp(val, "literal") == 0)); /* Check if we are searching over a regular expression */ PromiseResult result = PROMISE_RESULT_NOOP; if (literal) { // Prime the promiser temporarily, may override later EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser", pp->promiser, DATA_TYPE_STRING); result = PromiseResultUpdate(result, VerifyFilePromise(ctx, pp->promiser, pp)); } else // Default is to expand regex paths { result = PromiseResultUpdate(result, LocateFilePromiserGroup(ctx, pp->promiser, pp, VerifyFilePromise)); } return result; }
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; }
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 KeepServerPromise(EvalContext *ctx, Promise *pp, ARG_UNUSED void *param) { char *sp = NULL; assert(param == NULL); if (!IsDefinedClass(ctx, pp->classes, PromiseGetNamespace(pp))) { Log(LOG_LEVEL_VERBOSE, "Skipping whole promise, as context is %s", pp->classes); return; } if (VarClassExcluded(ctx, pp, &sp)) { if (LEGACY_OUTPUT) { Log(LOG_LEVEL_VERBOSE, "\n"); Log(LOG_LEVEL_VERBOSE, ". . . . . . . . . . . . . . . . . . . . . . . . . . . . "); Log(LOG_LEVEL_VERBOSE, "Skipping whole next promise (%s), as var-context %s is not relevant", pp->promiser, sp); Log(LOG_LEVEL_VERBOSE, ". . . . . . . . . . . . . . . . . . . . . . . . . . . . "); } else { Log(LOG_LEVEL_VERBOSE, "Skipping next promise '%s', as var-context '%s' is not relevant", pp->promiser, sp); } return; } if (strcmp(pp->parent_promise_type->name, "classes") == 0) { VerifyClassPromise(ctx, pp, NULL); return; } sp = (char *) ConstraintGetRvalValue(ctx, "resource_type", pp, RVAL_TYPE_SCALAR); if ((strcmp(pp->parent_promise_type->name, "access") == 0) && sp && (strcmp(sp, "literal") == 0)) { KeepLiteralAccessPromise(ctx, pp, "literal"); return; } if ((strcmp(pp->parent_promise_type->name, "access") == 0) && sp && (strcmp(sp, "variable") == 0)) { KeepLiteralAccessPromise(ctx, pp, "variable"); return; } if ((strcmp(pp->parent_promise_type->name, "access") == 0) && sp && (strcmp(sp, "query") == 0)) { KeepQueryAccessPromise(ctx, pp, "query"); KeepReportDataSelectAccessPromise(pp); return; } if ((strcmp(pp->parent_promise_type->name, "access") == 0) && sp && (strcmp(sp, "context") == 0)) { KeepLiteralAccessPromise(ctx, pp, "context"); return; } /* Default behaviour is file access */ if (strcmp(pp->parent_promise_type->name, "access") == 0) { KeepFileAccessPromise(ctx, pp); return; } if (strcmp(pp->parent_promise_type->name, "roles") == 0) { KeepServerRolePromise(ctx, pp); return; } }
void cfPS(OutputLevel level, char status, char *errstr, const Promise *pp, Attributes attr, char *fmt, ...) { va_list ap; char buffer[CF_BUFSIZE], output[CF_BUFSIZE], *v, handle[CF_MAXVARSIZE]; const char *sp; Item *mess = NULL; int verbose; Rval retval; if ((fmt == NULL) || (strlen(fmt) == 0)) { return; } va_start(ap, fmt); vsnprintf(buffer, CF_BUFSIZE - 1, fmt, ap); va_end(ap); if (Chop(buffer, CF_EXPANDSIZE) == -1) { CfOut(OUTPUT_LEVEL_ERROR, "", "Chop was called on a string that seemed to have no terminator"); } AppendItem(&mess, buffer, NULL); if ((errstr == NULL) || (strlen(errstr) > 0)) { snprintf(output, CF_BUFSIZE - 1, " !!! System reports error for %s: \"%s\"", errstr, GetErrorStr()); AppendItem(&mess, output, NULL); } if (level == OUTPUT_LEVEL_ERROR) { if (GetVariable("control_common", "version", &retval) != DATA_TYPE_NONE) { v = (char *) retval.item; } else { v = "not specified"; } if ((sp = ConstraintGetRvalValue("handle", pp, RVAL_TYPE_SCALAR)) || (sp = PromiseID(pp))) { strncpy(handle, sp, CF_MAXVARSIZE - 1); } else { strcpy(handle, "(unknown)"); } if (INFORM || VERBOSE || DEBUG) { snprintf(output, CF_BUFSIZE - 1, "I: Report relates to a promise with handle \"%s\"", handle); AppendItem(&mess, output, NULL); } if (pp && (pp->audit)) { snprintf(output, CF_BUFSIZE - 1, "I: Made in version \'%s\' of \'%s\' near line %zu", v, pp->audit->filename, pp->offset.line); } else { snprintf(output, CF_BUFSIZE - 1, "I: Promise is made internally by cfengine"); } AppendItem(&mess, output, NULL); if (pp != NULL) { switch (pp->promisee.type) { case RVAL_TYPE_SCALAR: snprintf(output, CF_BUFSIZE - 1, "I: The promise was made to: \'%s\'", (char *) pp->promisee.item); AppendItem(&mess, output, NULL); break; case RVAL_TYPE_LIST: snprintf(output, CF_BUFSIZE - 1, "I: The promise was made to (stakeholders): "); RlistPrint(output+strlen(output), CF_BUFSIZE, (Rlist *)pp->promisee.item); AppendItem(&mess, output, NULL); break; default: break; } if (pp->ref) { snprintf(output, CF_BUFSIZE - 1, "I: Comment: %s\n", pp->ref); AppendItem(&mess, output, NULL); } } } verbose = (attr.transaction.report_level == OUTPUT_LEVEL_VERBOSE) || VERBOSE; switch (level) { case OUTPUT_LEVEL_INFORM: if (INFORM || verbose || DEBUG || (attr.transaction.report_level == OUTPUT_LEVEL_INFORM)) { LogList(stdout, mess, verbose); } if (attr.transaction.log_level == OUTPUT_LEVEL_INFORM) { MakeLog(mess, level); } break; case OUTPUT_LEVEL_REPORTING: case OUTPUT_LEVEL_CMDOUT: if (attr.report.to_file) { FileReport(mess, verbose, attr.report.to_file); } else { LogList(stdout, mess, verbose); } if (attr.transaction.log_level == OUTPUT_LEVEL_INFORM) { MakeLog(mess, level); } break; case OUTPUT_LEVEL_VERBOSE: if (verbose || DEBUG) { LogList(stdout, mess, verbose); } if (attr.transaction.log_level == OUTPUT_LEVEL_VERBOSE) { MakeLog(mess, level); } break; case OUTPUT_LEVEL_ERROR: if (attr.report.to_file) { FileReport(mess, verbose, attr.report.to_file); } else { LogList(stdout, mess, verbose); } if (attr.transaction.log_level == OUTPUT_LEVEL_ERROR) { MakeLog(mess, level); } break; case OUTPUT_LEVEL_LOG: MakeLog(mess, level); break; default: break; } if (pp != NULL) { LogPromiseResult(pp->promiser, pp->promisee.type, pp->promisee.item, status, attr.transaction.log_level, mess); } /* Now complete the exits status classes and auditing */ if (pp != NULL) { ClassAuditLog(pp, attr, status, buffer); } DeleteItemList(mess); }
static PromiseResult DoVerifyServices(EvalContext *ctx, Attributes a, Promise *pp) { FnCall *default_bundle = NULL; Rlist *args = NULL; // Need to set up the default service pack to eliminate syntax if (ConstraintGetRvalValue(ctx, "service_bundle", pp, RVAL_TYPE_SCALAR) == NULL) { switch (a.service.service_policy) { case SERVICE_POLICY_START: RlistAppendScalar(&args, pp->promiser); RlistAppendScalar(&args, "start"); break; case SERVICE_POLICY_RESTART: RlistAppendScalar(&args, pp->promiser); RlistAppendScalar(&args, "restart"); break; case SERVICE_POLICY_RELOAD: RlistAppendScalar(&args, pp->promiser); RlistAppendScalar(&args, "reload"); break; case SERVICE_POLICY_STOP: case SERVICE_POLICY_DISABLE: default: RlistAppendScalar(&args, pp->promiser); RlistAppendScalar(&args, "stop"); break; } default_bundle = FnCallNew("standard_services", args); PromiseAppendConstraint(pp, "service_bundle", (Rval) {default_bundle, RVAL_TYPE_FNCALL }, "any", false); a.havebundle = true; } // Set $(this.service_policy) for flexible bundle adaptation switch (a.service.service_policy) { case SERVICE_POLICY_START: EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "service_policy", "start", DATA_TYPE_STRING, "goal=state,source=promise"); break; case SERVICE_POLICY_RESTART: EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "service_policy", "restart", DATA_TYPE_STRING, "goal=state,source=promise"); break; case SERVICE_POLICY_RELOAD: EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "service_policy", "reload", DATA_TYPE_STRING, "goal=state,source=promise"); break; case SERVICE_POLICY_STOP: case SERVICE_POLICY_DISABLE: default: EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "service_policy", "stop", DATA_TYPE_STRING, "goal=state,source=promise"); break; } const Bundle *bp = PolicyGetBundle(PolicyFromPromise(pp), NULL, "agent", default_bundle->name); if (!bp) { bp = PolicyGetBundle(PolicyFromPromise(pp), NULL, "common", default_bundle->name); } PromiseResult result = PROMISE_RESULT_NOOP; if (default_bundle && bp == NULL) { cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_FAIL, pp, a, "Service '%s' could not be invoked successfully", pp->promiser); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); } if (!DONTDO) { result = PromiseResultUpdate(result, VerifyMethod(ctx, "service_bundle", a, pp)); // Send list of classes to set privately? } return result; }
static void KeepServerPromise(EvalContext *ctx, Promise *pp, const ReportContext *report_context) { char *sp = NULL; if (!IsDefinedClass(ctx, pp->classes, PromiseGetNamespace(pp))) { CfOut(OUTPUT_LEVEL_VERBOSE, "", "Skipping whole promise, as context is %s\n", pp->classes); return; } if (VarClassExcluded(ctx, pp, &sp)) { CfOut(OUTPUT_LEVEL_VERBOSE, "", "\n"); CfOut(OUTPUT_LEVEL_VERBOSE, "", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . \n"); CfOut(OUTPUT_LEVEL_VERBOSE, "", "Skipping whole next promise (%s), as var-context %s is not relevant\n", pp->promiser, sp); CfOut(OUTPUT_LEVEL_VERBOSE, "", ". . . . . . . . . . . . . . . . . . . . . . . . . . . . \n"); return; } if (strcmp(pp->parent_promise_type->name, "classes") == 0) { KeepClassContextPromise(ctx, pp, report_context); return; } sp = (char *) ConstraintGetRvalValue(ctx, "resource_type", pp, RVAL_TYPE_SCALAR); if ((strcmp(pp->parent_promise_type->name, "access") == 0) && sp && (strcmp(sp, "literal") == 0)) { KeepLiteralAccessPromise(ctx, pp, "literal"); return; } if ((strcmp(pp->parent_promise_type->name, "access") == 0) && sp && (strcmp(sp, "variable") == 0)) { KeepLiteralAccessPromise(ctx, pp, "variable"); return; } if ((strcmp(pp->parent_promise_type->name, "access") == 0) && sp && (strcmp(sp, "query") == 0)) { KeepQueryAccessPromise(ctx, pp, "query"); return; } if ((strcmp(pp->parent_promise_type->name, "access") == 0) && sp && (strcmp(sp, "context") == 0)) { KeepLiteralAccessPromise(ctx, pp, "context"); return; } /* Default behaviour is file access */ if (strcmp(pp->parent_promise_type->name, "access") == 0) { KeepFileAccessPromise(ctx, pp); return; } if (strcmp(pp->parent_promise_type->name, "roles") == 0) { KeepServerRolePromise(ctx, pp); return; } }
int VerifyMethod(EvalContext *ctx, char *attrname, Attributes a, Promise *pp) { Bundle *bp; void *vp; FnCall *fp; char method_name[CF_EXPANDSIZE], qualified_method[CF_BUFSIZE], *method_deref; Rlist *params = NULL; int retval = false; CfLock thislock; char lockname[CF_BUFSIZE]; if (a.havebundle) { if ((vp = ConstraintGetRvalValue(ctx, attrname, pp, RVAL_TYPE_FNCALL))) { fp = (FnCall *) vp; ExpandScalar(ctx, PromiseGetBundle(pp)->name, fp->name, method_name); params = fp->args; } else if ((vp = ConstraintGetRvalValue(ctx, attrname, pp, RVAL_TYPE_SCALAR))) { ExpandScalar(ctx, PromiseGetBundle(pp)->name, (char *) vp, method_name); params = NULL; } else { return false; } } GetLockName(lockname, "method", pp->promiser, params); thislock = AcquireLock(ctx, lockname, VUQNAME, CFSTARTTIME, a.transaction, pp, false); if (thislock.lock == NULL) { return false; } PromiseBanner(pp); if (strncmp(method_name,"default:",strlen("default:")) == 0) // CF_NS == ':' { method_deref = strchr(method_name, CF_NS) + 1; } else if ((strchr(method_name, CF_NS) == NULL) && (strcmp(PromiseGetNamespace(pp), "default") != 0)) { snprintf(qualified_method, CF_BUFSIZE, "%s%c%s", PromiseGetNamespace(pp), CF_NS, method_name); method_deref = qualified_method; } else { method_deref = method_name; } bp = PolicyGetBundle(PolicyFromPromise(pp), NULL, "agent", method_deref); if (!bp) { bp = PolicyGetBundle(PolicyFromPromise(pp), NULL, "common", method_deref); } if (bp) { BannerSubBundle(bp, params); EvalContextStackPushBundleFrame(ctx, bp, a.inherit); ScopeClear(bp->name); BundleHashVariables(ctx, bp); ScopeAugment(ctx, bp, pp, params); retval = ScheduleAgentOperations(ctx, bp); GetReturnValue(ctx, bp->name, pp); EvalContextStackPopFrame(ctx); switch (retval) { case PROMISE_RESULT_FAIL: cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_FAIL, pp, a, "Method failed in some repairs or aborted"); break; case PROMISE_RESULT_CHANGE: cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_CHANGE, pp, a, "Method invoked repairs"); break; default: cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "Method verified"); break; } for (const Rlist *rp = bp->args; rp; rp = rp->next) { const char *lval = rp->item; ScopeDeleteScalar((VarRef) { NULL, bp->name, lval }); } } else { if (IsCf3VarString(method_name)) { Log(LOG_LEVEL_ERR, "A variable seems to have been used for the name of the method. In this case, the promiser also needs to contain the unique name of the method"); } if (bp && (bp->name)) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Method '%s' was used but was not defined", bp->name); } else { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "A method attempted to use a bundle '%s' that was apparently not defined", method_name); } } YieldCurrentLock(thislock); return retval; }
static LogLevel GetLevelForPromise(const EvalContext *ctx, const Promise *pp, const char *attr_name) { return StringToLogLevel(ConstraintGetRvalValue(ctx, attr_name, pp, RVAL_TYPE_SCALAR)); }
static void DoVerifyServices(EvalContext *ctx, Attributes a, Promise *pp, const ReportContext *report_context) { FnCall *default_bundle = NULL; Rlist *args = NULL; // Need to set up the default service pack to eliminate syntax if (ConstraintGetRvalValue(ctx, "service_bundle", pp, RVAL_TYPE_SCALAR) == NULL) { switch (a.service.service_policy) { case SERVICE_POLICY_START: RlistAppendScalar(&args, pp->promiser); RlistAppendScalar(&args, "start"); break; case SERVICE_POLICY_RESTART: RlistAppendScalar(&args, pp->promiser); RlistAppendScalar(&args, "restart"); break; case SERVICE_POLICY_RELOAD: RlistAppendScalar(&args, pp->promiser); RlistAppendScalar(&args, "restart"); break; case SERVICE_POLICY_STOP: case SERVICE_POLICY_DISABLE: default: RlistAppendScalar(&args, pp->promiser); RlistAppendScalar(&args, "stop"); break; } default_bundle = FnCallNew("default:standard_services", args); PromiseAppendConstraint(pp, "service_bundle", (Rval) {default_bundle, RVAL_TYPE_FNCALL }, "any", false); a.havebundle = true; } // Set $(this.service_policy) for flexible bundle adaptation switch (a.service.service_policy) { case SERVICE_POLICY_START: ScopeNewScalar("this", "service_policy", "start", DATA_TYPE_STRING); break; case SERVICE_POLICY_RESTART: ScopeNewScalar("this", "service_policy", "restart", DATA_TYPE_STRING); break; case SERVICE_POLICY_RELOAD: ScopeNewScalar("this", "service_policy", "reload", DATA_TYPE_STRING); break; case SERVICE_POLICY_STOP: case SERVICE_POLICY_DISABLE: default: ScopeNewScalar("this", "service_policy", "stop", DATA_TYPE_STRING); break; } const Bundle *bp = PolicyGetBundle(PolicyFromPromise(pp), NULL, "agent", default_bundle->name); if (!bp) { bp = PolicyGetBundle(PolicyFromPromise(pp), NULL, "common", default_bundle->name); } if (default_bundle && bp == NULL) { cfPS(ctx, OUTPUT_LEVEL_INFORM, CF_FAIL, "", pp, a, " !! Service %s could not be invoked successfully\n", pp->promiser); } if (!DONTDO) { VerifyMethod(ctx, "service_bundle", a, pp, report_context); // Send list of classes to set privately? } }
static PromiseResult KeepServerPromise(EvalContext *ctx, Promise *pp, ARG_UNUSED void *param) { assert(!param); if (!IsDefinedClass(ctx, pp->classes, PromiseGetNamespace(pp))) { Log(LOG_LEVEL_VERBOSE, "Skipping whole promise, as context is %s", pp->classes); return PROMISE_RESULT_NOOP; } { char *cls = NULL; if (VarClassExcluded(ctx, pp, &cls)) { if (LEGACY_OUTPUT) { Log(LOG_LEVEL_VERBOSE, "\n"); Log(LOG_LEVEL_VERBOSE, ". . . . . . . . . . . . . . . . . . . . . . . . . . . . "); Log(LOG_LEVEL_VERBOSE, "Skipping whole next promise (%s), as var-context %s is not relevant", pp->promiser, cls); Log(LOG_LEVEL_VERBOSE, ". . . . . . . . . . . . . . . . . . . . . . . . . . . . "); } else { Log(LOG_LEVEL_VERBOSE, "Skipping next promise '%s', as var-context '%s' is not relevant", pp->promiser, cls); } return PROMISE_RESULT_NOOP; } } if (strcmp(pp->parent_promise_type->name, "classes") == 0) { return VerifyClassPromise(ctx, pp, NULL); } const char *resource_type = ConstraintGetRvalValue(ctx, "resource_type", pp, RVAL_TYPE_SCALAR); if (resource_type && strcmp(pp->parent_promise_type->name, "access") == 0) { if (strcmp(resource_type, "literal") == 0) { KeepLiteralAccessPromise(ctx, pp, "literal"); return PROMISE_RESULT_NOOP; } else if (strcmp(resource_type, "variable") == 0) { KeepLiteralAccessPromise(ctx, pp, "variable"); return PROMISE_RESULT_NOOP; } else if (strcmp(resource_type, "query") == 0) { KeepQueryAccessPromise(ctx, pp, "query"); KeepReportDataSelectAccessPromise(pp); return PROMISE_RESULT_NOOP; } else if (strcmp(resource_type, "context") == 0) { KeepLiteralAccessPromise(ctx, pp, "context"); return PROMISE_RESULT_NOOP; } } if (strcmp(pp->parent_promise_type->name, "access") == 0) { KeepFileAccessPromise(ctx, pp); return PROMISE_RESULT_NOOP; } else if (strcmp(pp->parent_promise_type->name, "roles") == 0) { KeepServerRolePromise(ctx, pp); return PROMISE_RESULT_NOOP; } return PROMISE_RESULT_NOOP; }