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 RenderTemplateMustache(EvalContext *ctx, const Promise *pp, Attributes a, EditContext *edcontext) { PromiseResult result = PROMISE_RESULT_NOOP; 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); return PromiseResultUpdate(result, PROMISE_RESULT_FAIL); } unsigned char existing_output_digest[EVP_MAX_MD_SIZE + 1] = { 0 }; if (access(pp->promiser, R_OK) == 0) { HashFile(pp->promiser, existing_output_digest, CF_DEFAULT_DIGEST); } int template_fd = safe_open(a.edit_template, O_RDONLY | O_TEXT); Writer *template_writer = NULL; if (template_fd >= 0) { template_writer = FileReadFromFd(template_fd, SIZE_MAX, NULL); close(template_fd); } if (!template_writer) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Could not read template file '%s'", a.edit_template); return PromiseResultUpdate(result, PROMISE_RESULT_FAIL); } JsonElement *default_template_data = NULL; if (!a.template_data) { a.template_data = default_template_data = DefaultTemplateData(ctx); } Buffer *output_buffer = BufferNew(); if (MustacheRender(output_buffer, StringWriterData(template_writer), a.template_data)) { unsigned char rendered_output_digest[EVP_MAX_MD_SIZE + 1] = { 0 }; HashString(BufferData(output_buffer), BufferSize(output_buffer), rendered_output_digest, CF_DEFAULT_DIGEST); if (!HashesMatch(existing_output_digest, rendered_output_digest, CF_DEFAULT_DIGEST)) { if (SaveAsFile(SaveBufferCallback, output_buffer, edcontext->filename, a, edcontext->new_line_mode)) { cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_CHANGE, pp, a, "Updated rendering of '%s' from template mustache template '%s'", pp->promiser, a.edit_template); result = PromiseResultUpdate(result, PROMISE_RESULT_CHANGE); } else { cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_FAIL, pp, a, "Updated rendering of '%s' from template mustache template '%s'", pp->promiser, a.edit_template); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); } } JsonDestroy(default_template_data); WriterClose(template_writer); BufferDestroy(output_buffer); return result; } else { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Error rendering mustache template '%s'", a.edit_template); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); JsonDestroy(default_template_data); WriterClose(template_writer); BufferDestroy(output_buffer); return PromiseResultUpdate(result, PROMISE_RESULT_FAIL); } }