Example #1
0
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;
}
Example #2
0
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);
    }
}