コード例 #1
0
ファイル: verify_vars.c プロジェクト: baptr/core
static int CompareRval(Rval rval1, Rval rval2)
{
    if (rval1.type != rval2.type)
    {
        return -1;
    }

    switch (rval1.type)
    {
    case RVAL_TYPE_SCALAR:

        if (IsCf3VarString((char *) rval1.item) || IsCf3VarString((char *) rval2.item))
        {
            return -1;          // inconclusive
        }

        if (strcmp(rval1.item, rval2.item) != 0)
        {
            return false;
        }

        break;

    case RVAL_TYPE_LIST:
        return CompareRlist(rval1.item, rval2.item);

    case RVAL_TYPE_FNCALL:
        return -1;

    default:
        return -1;
    }

    return true;
}
コード例 #2
0
ファイル: scope.c プロジェクト: jooooooon/core
bool UnresolvedVariables(const CfAssoc *ap, RvalType rtype)
{
    if (ap == NULL)
    {
        return false;
    }

    switch (rtype)
    {
    case RVAL_TYPE_SCALAR:
        return IsCf3VarString(ap->rval.item);

    case RVAL_TYPE_LIST:
        {
            for (const Rlist *rp = ap->rval.item; rp != NULL; rp = rp->next)
            {
                if (IsCf3VarString(rp->item))
                {
                    return true;
                }
            }
        }
        return false;

    default:
        return false;
    }
}
コード例 #3
0
ファイル: vars.c プロジェクト: rdparker/core
int UnresolvedVariables(CfAssoc *ap, char rtype)
{
    Rlist *list, *rp;

    if (ap == NULL)
    {
        return false;
    }

    switch (rtype)
    {
    case CF_SCALAR:
        return IsCf3VarString(ap->rval.item);

    case CF_LIST:
        list = (Rlist *) ap->rval.item;

        for (rp = list; rp != NULL; rp = rp->next)
        {
            if (IsCf3VarString(rp->item))
            {
                return true;
            }
        }

        return false;

    default:
        return false;
    }
}
コード例 #4
0
ファイル: rlist.c プロジェクト: nickanderson/core
bool RlistEqual(const Rlist *list1, const Rlist *list2)
{
    const Rlist *rp1, *rp2;

    for (rp1 = list1, rp2 = list2; rp1 != NULL && rp2 != NULL; rp1 = rp1->next, rp2 = rp2->next)
    {
        if (rp1->val.item != NULL &&
            rp2->val.item != NULL)
        {
            if (rp1->val.type == RVAL_TYPE_FNCALL || rp2->val.type == RVAL_TYPE_FNCALL)
            {
                return false;      // inconclusive
            }

            const Rlist *rc1 = rp1;
            const Rlist *rc2 = rp2;

            // Check for list nesting with { fncall(), "x" ... }

            if (rp1->val.type == RVAL_TYPE_LIST)
            {
                rc1 = rp1->val.item;
            }

            if (rp2->val.type == RVAL_TYPE_LIST)
            {
                rc2 = rp2->val.item;
            }

            if (IsCf3VarString(rc1->val.item) || IsCf3VarString(rp2->val.item))
            {
                return false;      // inconclusive
            }

            if (strcmp(rc1->val.item, rc2->val.item) != 0)
            {
                return false;
            }
        }
        else if ((rp1->val.item != NULL && rp2->val.item == NULL) ||
                 (rp1->val.item == NULL && rp2->val.item != NULL))
        {
            return false;
        }
        else
        {
            assert(rp1->val.item == NULL && rp2->val.item == NULL);
        }
    }

    return true;
}
コード例 #5
0
ファイル: verify_vars.c プロジェクト: awsiv/core
// FIX: this function is a mixture of Equal/Compare (boolean/diff).
// somebody is bound to misuse this at some point
static int CompareRlist(const Rlist *list1, const Rlist *list2)
{
    const Rlist *rp1, *rp2;

    for (rp1 = list1, rp2 = list2; rp1 != NULL && rp2 != NULL; rp1 = rp1->next, rp2 = rp2->next)
    {
        if (rp1->val.item && rp2->val.item)
        {
            const Rlist *rc1, *rc2;

            if (rp1->val.type == RVAL_TYPE_FNCALL || rp2->val.type == RVAL_TYPE_FNCALL)
            {
                return -1;      // inconclusive
            }

            rc1 = rp1;
            rc2 = rp2;

            // Check for list nesting with { fncall(), "x" ... }

            if (rp1->val.type == RVAL_TYPE_LIST)
            {
                rc1 = rp1->val.item;
            }

            if (rp2->val.type == RVAL_TYPE_LIST)
            {
                rc2 = rp2->val.item;
            }

            if (IsCf3VarString(rc1->val.item) || IsCf3VarString(rp2->val.item))
            {
                return -1;      // inconclusive
            }

            if (strcmp(rc1->val.item, rc2->val.item) != 0)
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }

    return true;
}
コード例 #6
0
ファイル: expand.c プロジェクト: tzz/core
Rval ExpandDanglers(EvalContext *ctx, const char *ns, const char *scope, Rval rval, const Promise *pp)
{
    assert(ctx);
    assert(pp);

    switch (rval.type)
    {
    case RVAL_TYPE_SCALAR:
        if (IsCf3VarString(RvalScalarValue(rval)))
        {
            return EvaluateFinalRval(ctx, PromiseGetPolicy(pp), ns, scope, rval, false, pp);
        }
        else
        {
            return RvalCopy(rval);
        }
        break;

    case RVAL_TYPE_LIST:
        {
            Rlist *result_list = RlistCopy(RvalRlistValue(rval));
            RlistFlatten(ctx, &result_list);
            return RvalNew(result_list, RVAL_TYPE_LIST);
        }
        break;

    case RVAL_TYPE_CONTAINER:
    case RVAL_TYPE_FNCALL:
    case RVAL_TYPE_NOPROMISEE:
        return RvalCopy(rval);
    }

    ProgrammingError("Unhandled Rval type");
}
コード例 #7
0
ファイル: verify_vars.c プロジェクト: jooooooon/core
static bool Epimenides(EvalContext *ctx, const char *scope, const char *var, Rval rval, int level)
{
    Rlist *rp, *list;
    char exp[CF_EXPANDSIZE];

    switch (rval.type)
    {
    case RVAL_TYPE_SCALAR:

        if (StringContainsVar(rval.item, var))
        {
            CfOut(OUTPUT_LEVEL_ERROR, "", "Scalar variable \"%s\" contains itself (non-convergent): %s", var, (char *) rval.item);
            return true;
        }

        if (IsCf3VarString(rval.item))
        {
            ExpandScalar(ctx, scope, rval.item, exp);

            if (strcmp(exp, (const char *) rval.item) == 0)
            {
                return false;
            }

            CfDebug("bling %d-%s: (look for %s) in \"%s\" => %s \n", level, scope, var, (const char *) rval.item,
                    exp);

            if (level > 3)
            {
                return false;
            }

            if (Epimenides(ctx, scope, var, (Rval) {exp, RVAL_TYPE_SCALAR}, level + 1))
            {
                return true;
            }
        }

        break;

    case RVAL_TYPE_LIST:
        list = (Rlist *) rval.item;

        for (rp = list; rp != NULL; rp = rp->next)
        {
            if (Epimenides(ctx, scope, var, (Rval) {rp->item, rp->type}, level))
            {
                return true;
            }
        }
        break;

    default:
        return false;
    }

    return false;
}
コード例 #8
0
ファイル: verify_vars.c プロジェクト: awsiv/core
static bool Epimenides(EvalContext *ctx, const char *ns, const char *scope, const char *var, Rval rval, int level)
{
    switch (rval.type)
    {
    case RVAL_TYPE_SCALAR:

        if (StringContainsVar(RvalScalarValue(rval), var))
        {
            Log(LOG_LEVEL_ERR, "Scalar variable '%s' contains itself (non-convergent) '%s'", var, RvalScalarValue(rval));
            return true;
        }

        if (IsCf3VarString(RvalScalarValue(rval)))
        {
            Buffer *exp = BufferNew();
            ExpandScalar(ctx, ns, scope, RvalScalarValue(rval), exp);

            if (strcmp(BufferData(exp), RvalScalarValue(rval)) == 0)
            {
                BufferDestroy(exp);
                return false;
            }

            if (level > 3)
            {
                BufferDestroy(exp);
                return false;
            }

            if (Epimenides(ctx, ns, scope, var, (Rval) { BufferGet(exp), RVAL_TYPE_SCALAR}, level + 1))
            {
                BufferDestroy(exp);
                return true;
            }

            BufferDestroy(exp);
        }

        break;

    case RVAL_TYPE_LIST:
        for (const Rlist *rp = RvalRlistValue(rval); rp != NULL; rp = rp->next)
        {
            if (Epimenides(ctx, ns, scope, var, rp->val, level))
            {
                return true;
            }
        }
        break;

    case RVAL_TYPE_CONTAINER:
    case RVAL_TYPE_FNCALL:
    case RVAL_TYPE_NOPROMISEE:
        return false;
    }

    return false;
}
コード例 #9
0
ファイル: loading.c プロジェクト: lra/core
static Policy *LoadPolicyInputFiles(EvalContext *ctx, GenericAgentConfig *config, const Rlist *inputs, StringSet *parsed_files_and_checksums, StringSet *failed_files)
{
    Policy *policy = PolicyNew();

    for (const Rlist *rp = inputs; rp; rp = rp->next)
    {
        if (rp->val.type != RVAL_TYPE_SCALAR)
        {
            Log(LOG_LEVEL_ERR, "Non-file object in inputs list");
            continue;
        }

        const char *unresolved_input = RlistScalarValue(rp);

        if (strcmp(CF_NULL_VALUE, unresolved_input) == 0)
        {
            continue;
        }

        if (IsExpandable(unresolved_input))
        {
            PolicyResolve(ctx, policy, config);
        }

        Rval resolved_input = EvaluateFinalRval(ctx, policy, NULL, "sys", rp->val, true, NULL);

        Policy *aux_policy = NULL;
        switch (resolved_input.type)
        {
        case RVAL_TYPE_SCALAR:
            if (IsCf3VarString(RvalScalarValue(resolved_input)))
            {
                Log(LOG_LEVEL_ERR, "Unresolved variable '%s' in input list, cannot parse", RvalScalarValue(resolved_input));
                break;
            }

            aux_policy = LoadPolicyFile(ctx, config, GenericAgentResolveInputPath(config, RvalScalarValue(resolved_input)), parsed_files_and_checksums, failed_files);
            break;

        case RVAL_TYPE_LIST:
            aux_policy = LoadPolicyInputFiles(ctx, config, RvalRlistValue(resolved_input), parsed_files_and_checksums, failed_files);
            break;

        default:
            ProgrammingError("Unknown type in input list for parsing: %d", resolved_input.type);
            break;
        }

        if (aux_policy)
        {
            policy = PolicyMerge(policy, aux_policy);
        }

        RvalDestroy(resolved_input);
    }

    return policy;
}
コード例 #10
0
ファイル: syntax.c プロジェクト: dnaeon/core
static void CheckParseReal(char *lval, char *s, const char *range)
{
    Item *split;
    double max = (double) CF_LOWINIT, min = (double) CF_HIGHINIT, val;
    int n;
    char output[CF_BUFSIZE];

    CfDebug("\nCheckParseReal(%s => %s/%s)\n", lval, s, range);

    if (s == NULL)
    {
        return;
    }

    if (strcmp(s, "inf") == 0)
    {
        ReportError("keyword \"inf\" has an integer value, cannot be used as real");
        return;
    }

    if (IsCf3VarString(s))
    {
        CfDebug("Validation: Unable to verify syntax of real %s due to variable expansion at this stage\n", s);
        return;
    }

/* Numeric types are registered by range separated by comma str "min,max" */

    split = SplitString(range, ',');

    if ((n = ListLen(split)) != 2)
    {
        FatalError("INTERN:format specifier for real rvalues is not ok for lval %s - %d items", lval, n);
    }

    sscanf(split->name, "%lf", &min);
    sscanf(split->next->name, "%lf", &max);
    DeleteItemList(split);

    if (min == CF_HIGHINIT || max == CF_LOWINIT)
    {
        FatalError("INTERN:could not parse format specifier for int rvalues for lval %s", lval);
    }

    val = Str2Double(s);

    if (val > max || val < min)
    {
        snprintf(output, CF_BUFSIZE,
                 "Real item on rhs of lval \'%s\' give as {%s => %.3lf} is out of bounds (should be in [%s])", lval, s,
                 val, range);
        ReportError(output);
    }

    CfDebug("CheckParseReal - syntax verified\n\n");
}
コード例 #11
0
ファイル: syntax.c プロジェクト: dnaeon/core
static int CheckParseString(char *lval, char *s, const char *range)
{
    char output[CF_BUFSIZE];

    CfDebug("\nCheckParseString(%s => %s/%s)\n", lval, s, range);

    if (s == NULL)
    {
        return true;
    }

    if (strlen(range) == 0)
    {
        return true;
    }

    if (IsNakedVar(s, '@') || IsNakedVar(s, '$'))
    {
        CfDebug("Validation: Unable to verify variable expansion of %s at this stage\n", s);
        return false;
    }

/* Deal with complex strings as special cases */

    if (strcmp(lval, "mode") == 0 || strcmp(lval, "search_mode") == 0)
    {
        mode_t plus, minus;

        if (!ParseModeString(s, &plus, &minus))
        {
            snprintf(output, CF_BUFSIZE, "Error parsing Unix permission string %s)", s);
            ReportError(output);
            return false;
        }
    }

    if (FullTextMatch(range, s))
    {
        return true;
    }

    if (IsCf3VarString(s))
    {
        CfDebug("Validation: Unable to verify syntax of %s due to variable expansion at this stage\n", s);
    }
    else
    {
        snprintf(output, CF_BUFSIZE,
                 "Scalar item in %s => { %s } in rvalue is out of bounds (value should match pattern %s)", lval, s,
                 range);
        ReportError(output);
        return false;
    }

    return true;
}
コード例 #12
0
static bool Epimenides(EvalContext *ctx, const char *ns, const char *scope, const char *var, Rval rval, int level)
{
    Rlist *rp, *list;
    char exp[CF_EXPANDSIZE];

    switch (rval.type)
    {
    case RVAL_TYPE_SCALAR:

        if (StringContainsVar(rval.item, var))
        {
            Log(LOG_LEVEL_ERR, "Scalar variable '%s' contains itself (non-convergent) '%s'", var, (char *) rval.item);
            return true;
        }

        if (IsCf3VarString(rval.item))
        {
            ExpandScalar(ctx, ns, scope, rval.item, exp);

            if (strcmp(exp, (const char *) rval.item) == 0)
            {
                return false;
            }

            if (level > 3)
            {
                return false;
            }

            if (Epimenides(ctx, ns, scope, var, (Rval) {exp, RVAL_TYPE_SCALAR}, level + 1))
            {
                return true;
            }
        }

        break;

    case RVAL_TYPE_LIST:
        list = (Rlist *) rval.item;

        for (rp = list; rp != NULL; rp = rp->next)
        {
            if (Epimenides(ctx, ns, scope, var, (Rval) {rp->item, rp->type}, level))
            {
                return true;
            }
        }
        break;

    default:
        return false;
    }

    return false;
}
コード例 #13
0
ファイル: syntax.c プロジェクト: jooooooon/core
static SyntaxTypeMatch CheckParseInt(const char *lval, const char *s, const char *range)
{
    Item *split;
    int n;
    long max = CF_LOWINIT, min = CF_HIGHINIT, val;

/* Numeric types are registered by range separated by comma str "min,max" */
    CfDebug("\nCheckParseInt(%s => %s/%s)\n", lval, s, range);

    split = SplitString(range, ',');

    if ((n = ListLen(split)) != 2)
    {
        ProgrammingError("INTERN: format specifier for int rvalues is not ok for lval %s - got %d items", lval, n);
    }

    sscanf(split->name, "%ld", &min);

    if (strcmp(split->next->name, "inf") == 0)
    {
        max = CF_INFINITY;
    }
    else
    {
        sscanf(split->next->name, "%ld", &max);
    }

    DeleteItemList(split);

    if (min == CF_HIGHINIT || max == CF_LOWINIT)
    {
        ProgrammingError("INTERN: could not parse format specifier for int rvalues for lval %s", lval);
    }

    if (IsCf3VarString(s))
    {
        return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED;
    }

    val = IntFromString(s);

    if (val == CF_NOINT)
    {
        return SYNTAX_TYPE_MATCH_ERROR_INT_PARSE;
    }

    if (val > max || val < min)
    {
        return SYNTAX_TYPE_MATCH_ERROR_INT_OUT_OF_RANGE;
    }

    CfDebug("CheckParseInt - syntax verified\n\n");

    return SYNTAX_TYPE_MATCH_OK;
}
コード例 #14
0
ファイル: syntax.c プロジェクト: markburgess/Cellibrium
static SyntaxTypeMatch CheckParseString(const char *lval, const char *s, const char *range)
{
 if (s == NULL)
    {
    return SYNTAX_TYPE_MATCH_OK;
    }

 if (strlen(range) == 0)
    {
    return SYNTAX_TYPE_MATCH_OK;
    }

 if (IsNakedVar(s, '@') || IsNakedVar(s, '$'))
    {
    return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED;
    }

/* Deal with complex strings as special cases */

 if (strcmp(lval, "mode") == 0 || strcmp(lval, "search_mode") == 0)
    {
    mode_t plus, minus;

    if (!ParseModeString(s, &plus, &minus))
       {
       return SYNTAX_TYPE_MATCH_ERROR_STRING_UNIX_PERMISSION;
       }
    }

 /* FIXME: review this strcmp. Moved out from StringMatch */
 if (!strcmp(range, s) || StringMatchFull(range, s))
    {
    return SYNTAX_TYPE_MATCH_OK;
    }

 if (IsCf3VarString(s))
    {
    return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED;
    }
 else if ('\0' == s[0])
    {
    return SYNTAX_TYPE_MATCH_ERROR_EMPTY_SCALAR_OUT_OF_RANGE;
    }
 else if (!strcmp(range, CF_ABSPATHRANGE))
    {
    return SYNTAX_TYPE_MATCH_ERROR_ABSOLUTE_PATH;
    }
 else
    {
    return SYNTAX_TYPE_MATCH_ERROR_SCALAR_OUT_OF_RANGE;
    }

 return SYNTAX_TYPE_MATCH_OK;
}
コード例 #15
0
ファイル: verify_vars.c プロジェクト: awsiv/core
static int CompareRval(const void *rval1_item, RvalType rval1_type,
                       const void *rval2_item, RvalType rval2_type)
{
    if (rval1_type != rval2_type)
    {
        return -1;
    }

    switch (rval1_type)
    {
    case RVAL_TYPE_SCALAR:

        if (IsCf3VarString(rval1_item) || IsCf3VarString(rval2_item))
        {
            return -1;          // inconclusive
        }

        if (strcmp(rval1_item, rval2_item) != 0)
        {
            return false;
        }

        break;

    case RVAL_TYPE_LIST:
        return CompareRlist(rval1_item, rval2_item);

    case RVAL_TYPE_FNCALL:
        return -1;

    default:
        return -1;
    }

    return true;
}
コード例 #16
0
ファイル: syntax.c プロジェクト: jooooooon/core
static SyntaxTypeMatch CheckParseReal(const char *lval, const char *s, const char *range)
{
    Item *split;
    double max = (double) CF_LOWINIT, min = (double) CF_HIGHINIT, val;
    int n;

    CfDebug("\nCheckParseReal(%s => %s/%s)\n", lval, s, range);

    if (strcmp(s, "inf") == 0)
    {
        return SYNTAX_TYPE_MATCH_ERROR_REAL_INF;
    }

    if (IsCf3VarString(s))
    {
        return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED;
    }

/* Numeric types are registered by range separated by comma str "min,max" */

    split = SplitString(range, ',');

    if ((n = ListLen(split)) != 2)
    {
        ProgrammingError("Format specifier for real rvalues is not ok for lval %s - %d items", lval, n);
    }

    sscanf(split->name, "%lf", &min);
    sscanf(split->next->name, "%lf", &max);
    DeleteItemList(split);

    if (min == CF_HIGHINIT || max == CF_LOWINIT)
    {
        ProgrammingError("Could not parse format specifier for int rvalues for lval %s", lval);
    }

    if (!DoubleFromString(s, &val))
    {
        return SYNTAX_TYPE_MATCH_ERROR_REAL_OUT_OF_RANGE;
    }

    if (val > max || val < min)
    {
        return SYNTAX_TYPE_MATCH_ERROR_REAL_OUT_OF_RANGE;
    }

    return SYNTAX_TYPE_MATCH_OK;
}
コード例 #17
0
ファイル: syntax.c プロジェクト: jooooooon/core
static SyntaxTypeMatch CheckParseString(const char *lval, const char *s, const char *range)
{
    CfDebug("\nCheckParseString(%s => %s/%s)\n", lval, s, range);

    if (s == NULL)
    {
        return SYNTAX_TYPE_MATCH_OK;
    }

    if (strlen(range) == 0)
    {
        return SYNTAX_TYPE_MATCH_OK;
    }

    if (IsNakedVar(s, '@') || IsNakedVar(s, '$'))
    {
        return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED;
    }

/* Deal with complex strings as special cases */

    if (strcmp(lval, "mode") == 0 || strcmp(lval, "search_mode") == 0)
    {
        mode_t plus, minus;

        if (!ParseModeString(s, &plus, &minus))
        {
            return SYNTAX_TYPE_MATCH_ERROR_STRING_UNIX_PERMISSION;
        }
    }

    if (FullTextMatch(range, s))
    {
        return SYNTAX_TYPE_MATCH_OK;
    }

    if (IsCf3VarString(s))
    {
        return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED;
    }
    else
    {
        return SYNTAX_TYPE_MATCH_ERROR_SCALAR_OUT_OF_RANGE;
    }

    return SYNTAX_TYPE_MATCH_OK;
}
コード例 #18
0
ファイル: fncall.c プロジェクト: dstam/core
FnCall *ExpandFnCall(EvalContext *ctx, const char *ns, const char *scope, const FnCall *f)
{
    FnCall *result = NULL;
    if (IsCf3VarString(f->name))
    {
        // e.g. usebundle => $(m)(arg0, arg1);
        Buffer *buf = BufferNewWithCapacity(CF_MAXVARSIZE);
        ExpandScalar(ctx, ns, scope, f->name, buf);

        result = FnCallNew(BufferData(buf), ExpandList(ctx, ns, scope, f->args, false));
        BufferDestroy(buf);
    }
    else
    {
        result = FnCallNew(f->name, ExpandList(ctx, ns, scope, f->args, false));
    }

    return result;
}
コード例 #19
0
ファイル: verify_vars.c プロジェクト: awsiv/core
PromiseResult VerifyVarPromise(EvalContext *ctx, const Promise *pp, bool allow_duplicates)
{
    ConvergeVariableOptions opts = CollectConvergeVariableOptions(ctx, pp, allow_duplicates);
    if (!opts.should_converge)
    {
        return PROMISE_RESULT_NOOP;
    }

    Attributes a = { {0} };
    // More consideration needs to be given to using these
    //a.transaction = GetTransactionConstraints(pp);
    a.classes = GetClassDefinitionConstraints(ctx, pp);

    VarRef *ref = VarRefParseFromBundle(pp->promiser, PromiseGetBundle(pp));
    if (strcmp("meta", pp->parent_promise_type->name) == 0)
    {
        VarRefSetMeta(ref, true);
    }

    DataType existing_value_type = CF_DATA_TYPE_NONE;
    const void *const existing_value =
        IsExpandable(pp->promiser) ? NULL : EvalContextVariableGet(ctx, ref, &existing_value_type);

    PromiseResult result = PROMISE_RESULT_NOOP;
    Rval rval = opts.cp_save->rval;

    if (rval.item != NULL)
    {
        DataType data_type = DataTypeFromString(opts.cp_save->lval);

        if (opts.cp_save->rval.type == RVAL_TYPE_FNCALL)
        {
            FnCall *fp = RvalFnCallValue(rval);
            const FnCallType *fn = FnCallTypeGet(fp->name);
            if (!fn)
            {
                assert(false && "Canary: should have been caught before this point");
                FatalError(ctx, "While setting variable '%s' in bundle '%s', unknown function '%s'",
                           pp->promiser, PromiseGetBundle(pp)->name, fp->name);
            }

            if (fn->dtype != DataTypeFromString(opts.cp_save->lval))
            {
                FatalError(ctx, "While setting variable '%s' in bundle '%s', variable declared type '%s' but function '%s' returns type '%s'",
                           pp->promiser, PromiseGetBundle(pp)->name, opts.cp_save->lval,
                           fp->name, DataTypeToString(fn->dtype));
            }

            if (existing_value_type != CF_DATA_TYPE_NONE)
            {
                // Already did this
                VarRefDestroy(ref);
                return PROMISE_RESULT_NOOP;
            }

            FnCallResult res = FnCallEvaluate(ctx, PromiseGetPolicy(pp), fp, pp);

            if (res.status == FNCALL_FAILURE)
            {
                /* We do not assign variables to failed fn calls */
                RvalDestroy(res.rval);
                VarRefDestroy(ref);
                return PROMISE_RESULT_NOOP;
            }
            else
            {
                rval = res.rval;
            }
        }
        else
        {
            Buffer *conv = BufferNew();
            bool malformed = false, misprint = false;

            if (strcmp(opts.cp_save->lval, "int") == 0)
            {
                long int asint = IntFromString(opts.cp_save->rval.item);
                if (asint == CF_NOINT)
                {
                    malformed = true;
                }
                else if (0 > BufferPrintf(conv, "%ld", asint))
                {
                    misprint = true;
                }
                else
                {
                    rval = RvalNew(BufferData(conv), opts.cp_save->rval.type);
                }
            }
            else if (strcmp(opts.cp_save->lval, "real") == 0)
            {
                double real_value;
                if (!DoubleFromString(opts.cp_save->rval.item, &real_value))
                {
                    malformed = true;
                }
                else if (0 > BufferPrintf(conv, "%lf", real_value))
                {
                    misprint = true;
                }
                else
                {
                    rval = RvalNew(BufferData(conv), opts.cp_save->rval.type);
                }
            }
            else
            {
                rval = RvalCopy(opts.cp_save->rval);
            }
            BufferDestroy(conv);

            if (malformed)
            {
                /* Arises when opts->cp_save->rval.item isn't yet expanded. */
                /* Has already been logged by *FromString */
                VarRefDestroy(ref);
                return PromiseResultUpdate(result, PROMISE_RESULT_FAIL);
            }
            else if (misprint)
            {
                /* Even though no problems with memory allocation can
                 * get here, there might be other problems. */
                UnexpectedError("Problems writing to buffer");
                VarRefDestroy(ref);
                return PROMISE_RESULT_NOOP;
            }
            else if (rval.type == RVAL_TYPE_LIST)
            {
                Rlist *rval_list = RvalRlistValue(rval);
                RlistFlatten(ctx, &rval_list);
                rval.item = rval_list;
            }
        }

        if (Epimenides(ctx, PromiseGetBundle(pp)->ns, PromiseGetBundle(pp)->name, pp->promiser, rval, 0))
        {
            Log(LOG_LEVEL_ERR, "Variable '%s' contains itself indirectly - an unkeepable promise", pp->promiser);
            exit(EXIT_FAILURE);
        }
        else
        {
            /* See if the variable needs recursively expanding again */

            Rval returnval = EvaluateFinalRval(ctx, PromiseGetPolicy(pp), ref->ns, ref->scope, rval, true, pp);

            RvalDestroy(rval);

            // freed before function exit
            rval = returnval;
        }

        if (existing_value_type != CF_DATA_TYPE_NONE)
        {
            if (!opts.ok_redefine)    /* only on second iteration, else we ignore broken promises */
            {
                if (THIS_AGENT_TYPE == AGENT_TYPE_COMMON &&
                     !CompareRval(existing_value, DataTypeToRvalType(existing_value_type),
                                  rval.item, rval.type))
                {
                    switch (rval.type)
                    {
                    case RVAL_TYPE_SCALAR:
                        Log(LOG_LEVEL_VERBOSE, "Redefinition of a constant scalar '%s', was '%s' now '%s'",
                            pp->promiser, (const char *)existing_value, RvalScalarValue(rval));
                        PromiseRef(LOG_LEVEL_VERBOSE, pp);
                        break;

                    case RVAL_TYPE_LIST:
                        {
                            Log(LOG_LEVEL_VERBOSE, "Redefinition of a constant list '%s'", pp->promiser);
                            Writer *w = StringWriter();
                            RlistWrite(w, existing_value);
                            char *oldstr = StringWriterClose(w);
                            Log(LOG_LEVEL_VERBOSE, "Old value '%s'", oldstr);
                            free(oldstr);

                            w = StringWriter();
                            RlistWrite(w, rval.item);
                            char *newstr = StringWriterClose(w);
                            Log(LOG_LEVEL_VERBOSE, " New value '%s'", newstr);
                            free(newstr);
                            PromiseRef(LOG_LEVEL_VERBOSE, pp);
                        }
                        break;

                    case RVAL_TYPE_CONTAINER:
                    case RVAL_TYPE_FNCALL:
                    case RVAL_TYPE_NOPROMISEE:
                        break;
                    }
                }

                RvalDestroy(rval);
                VarRefDestroy(ref);
                return result;
            }
        }

        if (IsCf3VarString(pp->promiser))
        {
            // Unexpanded variables, we don't do anything with
            RvalDestroy(rval);
            VarRefDestroy(ref);
            return result;
        }

        if (!IsValidVariableName(pp->promiser))
        {
            Log(LOG_LEVEL_ERR, "Variable identifier contains illegal characters");
            PromiseRef(LOG_LEVEL_ERR, pp);
            RvalDestroy(rval);
            VarRefDestroy(ref);
            return result;
        }

        if (rval.type == RVAL_TYPE_LIST)
        {
            if (opts.drop_undefined)
            {
                for (Rlist *rp = RvalRlistValue(rval); rp; rp = rp->next)
                {
                    if (IsNakedVar(RlistScalarValue(rp), '@'))
                    {
                        free(rp->val.item);
                        rp->val.item = xstrdup(CF_NULL_VALUE);
                    }
                }
            }

            for (const Rlist *rp = RvalRlistValue(rval); rp; rp = rp->next)
            {
                switch (rp->val.type)
                {
                case RVAL_TYPE_SCALAR:
                    break;

                default:
                    // Cannot assign variable because value is a list containing a non-scalar item
                    VarRefDestroy(ref);
                    RvalDestroy(rval);
                    return result;
                }
            }
        }

        if (ref->num_indices > 0)
        {
            if (data_type == CF_DATA_TYPE_CONTAINER)
            {
                char *lval_str = VarRefToString(ref, true);
                Log(LOG_LEVEL_ERR, "Cannot assign a container to an indexed variable name '%s'. Should be assigned to '%s' instead",
                    lval_str, ref->lval);
                free(lval_str);
                VarRefDestroy(ref);
                RvalDestroy(rval);
                return result;
            }
            else
            {
                DataType existing_type = CF_DATA_TYPE_NONE;
                VarRef *base_ref = VarRefCopyIndexless(ref);
                if (EvalContextVariableGet(ctx, ref, &existing_type) && existing_type == CF_DATA_TYPE_CONTAINER)
                {
                    char *lval_str = VarRefToString(ref, true);
                    char *base_ref_str = VarRefToString(base_ref, true);
                    Log(LOG_LEVEL_ERR, "Cannot assign value to indexed variable name '%s', because a container is already assigned to the base name '%s'",
                        lval_str, base_ref_str);
                    free(lval_str);
                    free(base_ref_str);
                    VarRefDestroy(base_ref);
                    VarRefDestroy(ref);
                    RvalDestroy(rval);
                    return result;
                }
                VarRefDestroy(base_ref);
            }
        }


        DataType required_datatype = DataTypeFromString(opts.cp_save->lval);
        if (rval.type != DataTypeToRvalType(required_datatype))
        {
            char *ref_str = VarRefToString(ref, true);
            char *value_str = RvalToString(rval);
            Log(LOG_LEVEL_ERR, "Variable '%s' expected a variable of type '%s', but was given incompatible value '%s'",
                ref_str, DataTypeToString(required_datatype), value_str);
            PromiseRef(LOG_LEVEL_ERR, pp);
            free(ref_str);
            free(value_str);
            VarRefDestroy(ref);
            RvalDestroy(rval);
            return PromiseResultUpdate(result, PROMISE_RESULT_FAIL);
        }

        if (!EvalContextVariablePut(ctx, ref, rval.item, required_datatype, "source=promise"))
        {
            Log(LOG_LEVEL_VERBOSE,
                "Unable to converge %s.%s value (possibly empty or infinite regression)",
                ref->scope, pp->promiser);
            PromiseRef(LOG_LEVEL_VERBOSE, pp);
            result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL);
        }
        else
        {
            Rlist *promise_meta = PromiseGetConstraintAsList(ctx, "meta", pp);
            if (promise_meta)
            {
                StringSet *class_meta = EvalContextVariableTags(ctx, ref);
                Buffer *print;
                for (const Rlist *rp = promise_meta; rp; rp = rp->next)
                {
                    StringSetAdd(class_meta, xstrdup(RlistScalarValue(rp)));
                    print = StringSetToBuffer(class_meta, ',');
                    Log(LOG_LEVEL_DEBUG,
                        "Added tag %s to class %s, tags now [%s]",
                        RlistScalarValue(rp), pp->promiser, BufferData(print));
                    BufferDestroy(print);
                }
            }
        }
    }
    else
    {
        Log(LOG_LEVEL_ERR, "Variable %s has no promised value", pp->promiser);
        Log(LOG_LEVEL_ERR, "Rule from %s at/before line %llu",
            PromiseGetBundle(pp)->source_path,
            (unsigned long long)opts.cp_save->offset.line);
        result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL);
    }

    /*
     * FIXME: Variable promise are exempt from normal evaluation logic still, so
     * they are not pushed to evaluation stack before being evaluated. Due to
     * this reason, we cannot call cfPS here to set classes, as it will error
     * out with ProgrammingError.
     *
     * In order to support 'classes' body for variables as well, we call
     * ClassAuditLog explicitly.
     */
    ClassAuditLog(ctx, pp, a, result);

    VarRefDestroy(ref);
    RvalDestroy(rval);

    return result;
}
コード例 #20
0
ファイル: syntax.c プロジェクト: dnaeon/core
static void CheckParseIntRange(char *lval, char *s, const char *range)
{
    Item *split, *ip, *rangep;
    int n;
    long max = CF_LOWINIT, min = CF_HIGHINIT, val;
    char output[CF_BUFSIZE];

    if (s == NULL)
    {
        return;
    }

/* Numeric types are registered by range separated by comma str "min,max" */
    CfDebug("\nCheckParseIntRange(%s => %s/%s)\n", lval, s, range);

    if (*s == '[' || *s == '(')
    {
        ReportError("Range specification should not be enclosed in brackets - just \"a,b\"");
        return;
    }

    split = SplitString(range, ',');

    if ((n = ListLen(split)) != 2)
    {
        FatalError("INTERN:format specifier %s for irange rvalues is not ok for lval %s - got %d items", range, lval,
                   n);
    }

    sscanf(split->name, "%ld", &min);

    if (strcmp(split->next->name, "inf") == 0)
    {
        max = CF_INFINITY;
    }
    else
    {
        sscanf(split->next->name, "%ld", &max);
    }

    DeleteItemList(split);

    if (min == CF_HIGHINIT || max == CF_LOWINIT)
    {
        FatalError("INTERN: could not parse irange format specifier for int rvalues for lval %s", lval);
    }

    if (IsCf3VarString(s))
    {
        CfDebug("Validation: Unable to verify syntax of int \'%s\' due to variable expansion at this stage\n", s);
        return;
    }

    rangep = SplitString(s, ',');

    if ((n = ListLen(rangep)) != 2)
    {
        snprintf(output, CF_BUFSIZE,
                 "Int range format specifier for lval %s should be of form \"a,b\" but got %d items", lval, n);
        ReportError(output);
        DeleteItemList(rangep);
        return;
    }

    for (ip = rangep; ip != NULL; ip = ip->next)
    {
        val = Str2Int(ip->name);

        if (val > max || val < min)
        {
            snprintf(output, CF_BUFSIZE,
                     "Int range item on rhs of lval \'%s\' given as {%s => %ld} is out of bounds (should be in [%s])",
                     lval, s, val, range);
            ReportError(output);
            DeleteItemList(rangep);
            return;
        }
    }

    DeleteItemList(rangep);

    CfDebug("CheckParseIntRange - syntax verified\n\n");
}
コード例 #21
0
ファイル: verify_methods.c プロジェクト: jeffali/core
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;
}
コード例 #22
0
ファイル: verify_vars.c プロジェクト: baptr/core
void VerifyVarPromise(EvalContext *ctx, const Promise *pp, bool allow_duplicates)
{
    ConvergeVariableOptions opts = CollectConvergeVariableOptions(ctx, pp, allow_duplicates);
    if (!opts.should_converge)
    {
        return;
    }

    char *scope = NULL;
    if (strcmp("meta", pp->parent_promise_type->name) == 0)
    {
        scope = StringConcatenate(2, PromiseGetBundle(pp)->name, "_meta");
    }
    else
    {
        scope = xstrdup(PromiseGetBundle(pp)->name);
    }

    //More consideration needs to be given to using these
    //a.transaction = GetTransactionConstraints(pp);
    Attributes a = { {0} };
    a.classes = GetClassDefinitionConstraints(ctx, pp);

    Rval existing_var_rval;
    DataType existing_var_type = DATA_TYPE_NONE;
    EvalContextVariableGet(ctx, (VarRef) { NULL, scope, pp->promiser }, &existing_var_rval, &existing_var_type);
    Buffer *qualified_scope = BufferNew();
    int result = 0;
    if (strcmp(PromiseGetNamespace(pp), "default") == 0)
    {
        result = BufferSet(qualified_scope, scope, strlen(scope));
        if (result < 0)
        {
            /*
             * Even though there will be no problems with memory allocation, there
             * might be other problems.
             */
            UnexpectedError("Problems writing to buffer");
            free(scope);
            BufferDestroy(&qualified_scope);
            return;
        }
    }
    else
    {
        if (strchr(scope, ':') == NULL)
        {
            result = BufferPrintf(qualified_scope, "%s:%s", PromiseGetNamespace(pp), scope);
            if (result < 0)
            {
                /*
                 * Even though there will be no problems with memory allocation, there
                 * might be other problems.
                 */
                UnexpectedError("Problems writing to buffer");
                free(scope);
                BufferDestroy(&qualified_scope);
                return;
            }
        }
        else
        {
            result = BufferSet(qualified_scope, scope, strlen(scope));
            if (result < 0)
            {
                /*
                 * Even though there will be no problems with memory allocation, there
                 * might be other problems.
                 */
                UnexpectedError("Problems writing to buffer");
                free(scope);
                BufferDestroy(&qualified_scope);
                return;
            }
        }
    }

    PromiseResult promise_result;

    Rval rval = opts.cp_save->rval;

    if (rval.item != NULL)
    {
        FnCall *fp = (FnCall *) rval.item;

        if (opts.cp_save->rval.type == RVAL_TYPE_FNCALL)
        {
            if (existing_var_type != DATA_TYPE_NONE)
            {
                // Already did this
                free(scope);
                BufferDestroy(&qualified_scope);
                return;
            }

            FnCallResult res = FnCallEvaluate(ctx, fp, pp);

            if (res.status == FNCALL_FAILURE)
            {
                /* We do not assign variables to failed fn calls */
                RvalDestroy(res.rval);
                free(scope);
                BufferDestroy(&qualified_scope);
                return;
            }
            else
            {
                rval = res.rval;
            }
        }
        else
        {
            Buffer *conv = BufferNew();

            if (strcmp(opts.cp_save->lval, "int") == 0)
            {
                result = BufferPrintf(conv, "%ld", IntFromString(opts.cp_save->rval.item));
                if (result < 0)
                {
                    /*
                     * Even though there will be no problems with memory allocation, there
                     * might be other problems.
                     */
                    UnexpectedError("Problems writing to buffer");
                    free(scope);
                    BufferDestroy(&qualified_scope);
                    BufferDestroy(&conv);
                    return;
                }
                rval = RvalCopy((Rval) {(char *)BufferData(conv), opts.cp_save->rval.type});
            }
            else if (strcmp(opts.cp_save->lval, "real") == 0)
            {
                double real_value = 0.0;
                if (DoubleFromString(opts.cp_save->rval.item, &real_value))
                {
                    result = BufferPrintf(conv, "%lf", real_value);
                }
                else
                {
                    result = BufferPrintf(conv, "(double conversion error)");
                }

                if (result < 0)
                {
                    /*
                     * Even though there will be no problems with memory allocation, there
                     * might be other problems.
                     */
                    UnexpectedError("Problems writing to buffer");
                    free(scope);
                    BufferDestroy(&conv);
                    BufferDestroy(&qualified_scope);
                    return;
                }
                rval = RvalCopy((Rval) {(char *)BufferData(conv), opts.cp_save->rval.type});
            }
            else
            {
                rval = RvalCopy(opts.cp_save->rval);
            }

            if (rval.type == RVAL_TYPE_LIST)
            {
                Rlist *rval_list = RvalRlistValue(rval);
                RlistFlatten(ctx, &rval_list);
                rval.item = rval_list;
            }

            BufferDestroy(&conv);
        }

        if (Epimenides(ctx, PromiseGetBundle(pp)->name, pp->promiser, rval, 0))
        {
            Log(LOG_LEVEL_ERR, "Variable \"%s\" contains itself indirectly - an unkeepable promise", pp->promiser);
            exit(1);
        }
        else
        {
            /* See if the variable needs recursively expanding again */

            Rval returnval = EvaluateFinalRval(ctx, BufferData(qualified_scope), rval, true, pp);

            RvalDestroy(rval);

            // freed before function exit
            rval = returnval;
        }

        if (existing_var_type != DATA_TYPE_NONE)
        {
            if (opts.ok_redefine)    /* only on second iteration, else we ignore broken promises */
            {
                ScopeDeleteVariable(BufferData(qualified_scope), pp->promiser);
            }
            else if ((THIS_AGENT_TYPE == AGENT_TYPE_COMMON) && (CompareRval(existing_var_rval, rval) == false))
            {
                switch (rval.type)
                {
                case RVAL_TYPE_SCALAR:
                    Log(LOG_LEVEL_VERBOSE, "Redefinition of a constant scalar \"%s\" (was %s now %s)",
                          pp->promiser, RvalScalarValue(existing_var_rval), RvalScalarValue(rval));
                    PromiseRef(LOG_LEVEL_VERBOSE, pp);
                    break;

                case RVAL_TYPE_LIST:
                    {
                        Log(LOG_LEVEL_VERBOSE, "Redefinition of a constant list \"%s\".", pp->promiser);
                        Writer *w = StringWriter();
                        RlistWrite(w, existing_var_rval.item);
                        char *oldstr = StringWriterClose(w);
                        Log(LOG_LEVEL_VERBOSE, "Old value: %s", oldstr);
                        free(oldstr);

                        w = StringWriter();
                        RlistWrite(w, rval.item);
                        char *newstr = StringWriterClose(w);
                        Log(LOG_LEVEL_VERBOSE, " New value: %s", newstr);
                        free(newstr);
                        PromiseRef(LOG_LEVEL_VERBOSE, pp);
                    }
                    break;

                default:
                    break;
                }
            }
        }

        if (IsCf3VarString(pp->promiser))
        {
            // Unexpanded variables, we don't do anything with
            RvalDestroy(rval);
            free(scope);
            BufferDestroy(&qualified_scope);
            return;
        }

        if (!FullTextMatch("[a-zA-Z0-9_\200-\377.]+(\\[.+\\])*", pp->promiser))
        {
            Log(LOG_LEVEL_ERR, "Variable identifier contains illegal characters");
            PromiseRef(LOG_LEVEL_ERR, pp);
            RvalDestroy(rval);
            free(scope);
            BufferDestroy(&qualified_scope);
            return;
        }

        if (opts.drop_undefined && rval.type == RVAL_TYPE_LIST)
        {
            for (Rlist *rp = rval.item; rp != NULL; rp = rp->next)
            {
                if (IsNakedVar(rp->item, '@'))
                {
                    free(rp->item);
                    rp->item = xstrdup(CF_NULL_VALUE);
                }
            }
        }

        if (!EvalContextVariablePut(ctx, (VarRef) { NULL, BufferData(qualified_scope), pp->promiser }, rval, DataTypeFromString(opts.cp_save->lval)))
        {
            Log(LOG_LEVEL_VERBOSE, "Unable to converge %s.%s value (possibly empty or infinite regression)", BufferData(qualified_scope), pp->promiser);
            PromiseRef(LOG_LEVEL_VERBOSE, pp);
            promise_result = PROMISE_RESULT_FAIL;
        }
        else
        {
            promise_result = PROMISE_RESULT_CHANGE;
        }
    }
    else
    {
        Log(LOG_LEVEL_ERR, "Variable %s has no promised value", pp->promiser);
        Log(LOG_LEVEL_ERR, "Rule from %s at/before line %zu", PromiseGetBundle(pp)->source_path, opts.cp_save->offset.line);
        promise_result = PROMISE_RESULT_FAIL;
    }

    /*
     * FIXME: Variable promise are exempt from normal evaluation logic still, so
     * they are not pushed to evaluation stack before being evaluated. Due to
     * this reason, we cannot call cfPS here to set classes, as it will error
     * out with ProgrammingError.
     *
     * In order to support 'classes' body for variables as well, we call
     * ClassAuditLog explicitly.
     */
    ClassAuditLog(ctx, pp, a, promise_result);

    free(scope);
    BufferDestroy(&qualified_scope);
    RvalDestroy(rval);
}
コード例 #23
0
PromiseResult VerifyMethod(EvalContext *ctx, const Rval call, Attributes a, const Promise *pp)
{
    const Rlist *args = NULL;
    Buffer *method_name = BufferNew();
    switch (call.type)
    {
    case RVAL_TYPE_FNCALL:
    {
        const FnCall *fp = RvalFnCallValue(call);
        ExpandScalar(ctx, PromiseGetBundle(pp)->ns, PromiseGetBundle(pp)->name, fp->name, method_name);
        args = fp->args;
        int arg_index = 0;
        while (args)
        {
           ++arg_index;
           if (strcmp(args->val.item, CF_NULL_VALUE) == 0)
           {
               Log(LOG_LEVEL_DEBUG, "Skipping invokation of method '%s' due to null-values in argument '%d'",
                   fp->name, arg_index);
               BufferDestroy(method_name);
               return PROMISE_RESULT_SKIPPED;
           }
           args = args->next;
        }
        args = fp->args;
        EvalContextSetBundleArgs(ctx, args);
    }
    break;

    case RVAL_TYPE_SCALAR:
    {
        ExpandScalar(ctx, PromiseGetBundle(pp)->ns, PromiseGetBundle(pp)->name,
                     RvalScalarValue(call), method_name);
        args = NULL;
    }
    break;

    default:
        BufferDestroy(method_name);
        return PROMISE_RESULT_NOOP;
    }

    char lockname[CF_BUFSIZE];
    GetLockName(lockname, "method", pp->promiser, args);

    CfLock thislock = AcquireLock(ctx, lockname, VUQNAME, CFSTARTTIME, a.transaction, pp, false);
    if (thislock.lock == NULL)
    {
        BufferDestroy(method_name);
        return PROMISE_RESULT_SKIPPED;
    }

    PromiseBanner(ctx, pp);

    const Bundle *bp = EvalContextResolveBundleExpression(ctx, PromiseGetPolicy(pp), BufferData(method_name), "agent");

    if (!bp)
    {
        bp = EvalContextResolveBundleExpression(ctx, PromiseGetPolicy(pp), BufferData(method_name), "common");
    }

    PromiseResult result = PROMISE_RESULT_NOOP;

    if (bp)
    {
        if (a.transaction.action == cfa_warn) // don't skip for dry-runs (ie ignore DONTDO)
        {
            result = PROMISE_RESULT_WARN;
            cfPS(ctx, LOG_LEVEL_WARNING, result, pp, a, "Bundle '%s' should be invoked, but only a warning was promised!", BufferData(method_name));
        }
        else
        {
            BundleBanner(bp, args);
            EvalContextStackPushBundleFrame(ctx, bp, args, a.inherit);

            /* Clear all array-variables that are already set in the sub-bundle.
               Otherwise, array-data accumulates between multiple bundle evaluations.
               Note: for bundles invoked multiple times via bundlesequence, array
               data *does* accumulate. */
            VariableTableIterator *iter = EvalContextVariableTableIteratorNew(ctx, bp->ns, bp->name, NULL);
            Variable *var;
            while ((var = VariableTableIteratorNext(iter)))
            {
                if (!var->ref->num_indices)
                {
                    continue;
                }
                EvalContextVariableRemove(ctx, var->ref);
            }
            VariableTableIteratorDestroy(iter);

            BundleResolve(ctx, bp);

            result = ScheduleAgentOperations(ctx, bp);

            GetReturnValue(ctx, bp, pp);

            EvalContextStackPopFrame(ctx);
            switch (result)
            {
            case PROMISE_RESULT_SKIPPED:
                // if a bundle returns 'skipped', meaning that all promises were locked in the bundle,
                // we explicitly consider the method 'kept'
                result = PROMISE_RESULT_NOOP;
                // intentional fallthru

            case PROMISE_RESULT_NOOP:
                cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "Method '%s' verified", bp->name);
                break;

            case PROMISE_RESULT_WARN:
                cfPS(ctx, LOG_LEVEL_WARNING, PROMISE_RESULT_WARN, pp, a, "Method '%s' invoked repairs, but only warnings promised", bp->name);
                break;

            case PROMISE_RESULT_CHANGE:
                cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_CHANGE, pp, a, "Method '%s' invoked repairs", bp->name);
                break;

            case PROMISE_RESULT_FAIL:
            case PROMISE_RESULT_DENIED:
                cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Method '%s' failed in some repairs", bp->name);
                break;

            default: // PROMISE_RESULT_INTERRUPTED, TIMEOUT
                cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_FAIL, pp, a, "Method '%s' aborted in some repairs", bp->name);
                break;
            }
        }

        for (const Rlist *rp = bp->args; rp; rp = rp->next)
        {
            const char *lval = RlistScalarValue(rp);
            VarRef *ref = VarRefParseFromBundle(lval, bp);
            EvalContextVariableRemove(ctx, ref);
            VarRefDestroy(ref);
        }
    }
    else
    {
        if (IsCf3VarString(BufferData(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");
        }

        cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a,
             "A method attempted to use a bundle '%s' that was apparently not defined",
             BufferData(method_name));
        result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL);
    }

    YieldCurrentLock(thislock);
    BufferDestroy(method_name);
    EndBundleBanner(bp);

    return result;
}
コード例 #24
0
ファイル: syntax.c プロジェクト: markburgess/Cellibrium
static SyntaxTypeMatch CheckParseIntRange(const char *lval, const char *s, const char *range)
{
 Item *split, *ip, *rangep;
 int n;
 long long max = CF_LOWINIT, min = CF_HIGHINIT;

 // Numeric types are registered by range separated by comma str "min,max"
 if (*s == '[' || *s == '(')
    {
    return SYNTAX_TYPE_MATCH_ERROR_RANGE_BRACKETED;
    }

 split = SplitString(range, ',');

 if ((n = ListLen(split)) != 2)
    {
    ProgrammingError("Format specifier %s for irange rvalues is not ok for lval %s - got %d items", range, lval, n);
    }

 sscanf(split->name, "%lld", &min);

 if (strcmp(split->next->name, "inf") == 0)
    {
    max = CF_INFINITY;
    }
 else
    {
    sscanf(split->next->name, "%lld", &max);
    }

 DeleteItemList(split);

 if (min == CF_HIGHINIT || max == CF_LOWINIT)
    {
    ProgrammingError("Could not parse irange format specifier for int rvalues for lval %s", lval);
    }

 if (IsCf3VarString(s))
    {
    return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED;
    }

 rangep = SplitString(s, ',');

 if ((n = ListLen(rangep)) != 2)
    {
    return SYNTAX_TYPE_MATCH_ERROR_RANGE_MULTIPLE_ITEMS;
    }

 for (ip = rangep; ip != NULL; ip = ip->next)
    {
    long val = IntFromString(ip->name);

    if (val > max || val < min)
       {
       return SYNTAX_TYPE_MATCH_ERROR_INT_OUT_OF_RANGE;
       }
    }

 DeleteItemList(rangep);

 return SYNTAX_TYPE_MATCH_OK;
}
コード例 #25
0
ファイル: syntax.c プロジェクト: markburgess/Cellibrium
static SyntaxTypeMatch CheckParseRealRange(const char *lval, const char *s, const char *range)
{
 Item *split, *rangep, *ip;
 double max = (double) CF_LOWINIT, min = (double) CF_HIGHINIT, val;
 int n;

 if (*s == '[' || *s == '(')
    {
    return SYNTAX_TYPE_MATCH_ERROR_RANGE_BRACKETED;
    }

 if (strcmp(s, "inf") == 0)
    {
    return SYNTAX_TYPE_MATCH_ERROR_REAL_INF;
    }

 if (IsCf3VarString(s))
    {
    return SYNTAX_TYPE_MATCH_ERROR_UNEXPANDED;
    }

/* Numeric types are registered by range separated by comma str "min,max" */

 split = SplitString(range, ',');

 if ((n = ListLen(split)) != 2)
    {
    ProgrammingError("Format specifier for real rvalues is not ok for lval %s - %d items", lval, n);
    }

 sscanf(split->name, "%lf", &min);
 sscanf(split->next->name, "%lf", &max);
 DeleteItemList(split);

 if (min == CF_HIGHINIT || max == CF_LOWINIT)
    {
    ProgrammingError("Could not parse format specifier for int rvalues for lval %s", lval);
    }

 rangep = SplitString(s, ',');

 if ((n = ListLen(rangep)) != 2)
    {
    return SYNTAX_TYPE_MATCH_ERROR_RANGE_MULTIPLE_ITEMS;
    }

 for (ip = rangep; ip != NULL; ip = ip->next)
    {
    if (!DoubleFromString(ip->name, &val))
       {
       return SYNTAX_TYPE_MATCH_ERROR_REAL_OUT_OF_RANGE;
       }

    if (val > max || val < min)
       {
       return SYNTAX_TYPE_MATCH_ERROR_REAL_OUT_OF_RANGE;
       }
    }

 DeleteItemList(rangep);

 return SYNTAX_TYPE_MATCH_OK;
}
コード例 #26
0
ファイル: expand.c プロジェクト: kkaempf/core
/**
 * Expand a #string into Buffer #out, returning the pointer to the string
 * itself, inside the Buffer #out. If #out is NULL then the buffer will be
 * created and destroyed internally.
 *
 * @retval NULL something went wrong
 */
char *ExpandScalar(const EvalContext *ctx, const char *ns, const char *scope,
                   const char *string, Buffer *out)
{
    bool out_belongs_to_us = false;

    if (out == NULL)
    {
        out               = BufferNew();
        out_belongs_to_us = true;
    }

    assert(string != NULL);
    assert(out != NULL);
    Buffer *current_item = BufferNew();

    for (const char *sp = string; *sp != '\0'; sp++)
    {
        BufferClear(current_item);
        ExtractScalarPrefix(current_item, sp, strlen(sp));

        BufferAppend(out, BufferData(current_item), BufferSize(current_item));
        sp += BufferSize(current_item);
        if (*sp == '\0')
        {
            break;
        }

        BufferClear(current_item);
        char varstring = sp[1];
        ExtractScalarReference(current_item,  sp, strlen(sp), true);
        sp += BufferSize(current_item) + 2;

        if (IsCf3VarString(BufferData(current_item)))
        {
            Buffer *temp = BufferCopy(current_item);
            BufferClear(current_item);
            ExpandScalar(ctx, ns, scope, BufferData(temp), current_item);
            BufferDestroy(temp);
        }

        if (!IsExpandable(BufferData(current_item)))
        {
            VarRef *ref = VarRefParseFromNamespaceAndScope(
                BufferData(current_item),
                ns, scope, CF_NS, '.');
            DataType value_type;
            const void *value = EvalContextVariableGet(ctx, ref, &value_type);
            VarRefDestroy(ref);

            switch (DataTypeToRvalType(value_type))
            {
            case RVAL_TYPE_SCALAR:
                assert(value != NULL);
                BufferAppendString(out, value);
                continue;
                break;

            case RVAL_TYPE_CONTAINER:
            {
                assert(value != NULL);
                const JsonElement *jvalue = value;      /* instead of casts */
                if (JsonGetElementType(jvalue) == JSON_ELEMENT_TYPE_PRIMITIVE)
                {
                    BufferAppendString(out, JsonPrimitiveGetAsString(jvalue));
                    continue;
                }
                break;
            }
            default:
                /* TODO Log() */
                break;
            }
        }

        if (varstring == '{')
        {
            BufferAppendF(out, "${%s}", BufferData(current_item));
        }
        else
        {
            BufferAppendF(out, "$(%s)", BufferData(current_item));
        }
    }

    BufferDestroy(current_item);

    LogDebug(LOG_MOD_EXPAND, "ExpandScalar( %s : %s . %s )  =>  %s",
             SAFENULL(ns), SAFENULL(scope), string, BufferData(out));

    return out_belongs_to_us ? BufferClose(out) : BufferGet(out);
}
コード例 #27
0
ファイル: expand.c プロジェクト: tzz/core
Rval EvaluateFinalRval(EvalContext *ctx, const Policy *policy, const char *ns, const char *scope,
                       Rval rval, bool forcelist, const Promise *pp)
{
    assert(ctx);
    assert(policy);

    Rval returnval, newret;
    if ((rval.type == RVAL_TYPE_SCALAR) && IsNakedVar(rval.item, '@'))        /* Treat lists specially here */
    {
        char naked[CF_MAXVARSIZE];
        GetNaked(naked, rval.item);

        if (!IsExpandable(naked))
        {
            VarRef *ref = VarRefParseFromScope(naked, scope);
            DataType value_type = DATA_TYPE_NONE;
            const void *value = EvalContextVariableGet(ctx, ref, &value_type);

            if (!value || DataTypeToRvalType(value_type) != RVAL_TYPE_LIST)
            {
                returnval = ExpandPrivateRval(ctx, NULL, "this", rval.item, rval.type);
            }
            else
            {
                returnval.item = ExpandList(ctx, ns, scope, value, true);
                returnval.type = RVAL_TYPE_LIST;
            }

            VarRefDestroy(ref);
        }
        else
        {
            returnval = ExpandPrivateRval(ctx, NULL, "this", rval.item, rval.type);
        }
    }
    else
    {
        if (forcelist)          /* We are replacing scalar @(name) with list */
        {
            returnval = ExpandPrivateRval(ctx, ns, scope, rval.item, rval.type);
        }
        else
        {
            if (FnCallIsBuiltIn(rval))
            {
                returnval = RvalCopy(rval);
            }
            else
            {
                returnval = ExpandPrivateRval(ctx, NULL, "this", rval.item, rval.type);
            }
        }
    }

    switch (returnval.type)
    {
    case RVAL_TYPE_SCALAR:
    case RVAL_TYPE_CONTAINER:
        break;

    case RVAL_TYPE_LIST:
        for (Rlist *rp = RvalRlistValue(returnval); rp; rp = rp->next)
        {
            if (rp->val.type == RVAL_TYPE_FNCALL)
            {
                FnCall *fp = RlistFnCallValue(rp);
                FnCallResult res = FnCallEvaluate(ctx, policy, fp, pp);

                FnCallDestroy(fp);
                rp->val = res.rval;
            }
            else
            {
                if (EvalContextStackCurrentPromise(ctx))
                {
                    if (IsCf3VarString(RlistScalarValue(rp)))
                    {
                        newret = ExpandPrivateRval(ctx, NULL, "this", rp->val.item, rp->val.type);
                        free(rp->val.item);
                        rp->val.item = newret.item;
                    }
                }
            }

            /* returnval unchanged */
        }
        break;

    case RVAL_TYPE_FNCALL:
        if (FnCallIsBuiltIn(returnval))
        {
            FnCall *fp = RvalFnCallValue(returnval);
            returnval = FnCallEvaluate(ctx, policy, fp, pp).rval;
            FnCallDestroy(fp);
        }
        break;

    default:
        returnval.item = NULL;
        returnval.type = RVAL_TYPE_NOPROMISEE;
        break;
    }

    return returnval;
}
コード例 #28
0
ファイル: expand.c プロジェクト: tzz/core
bool ExpandScalar(const EvalContext *ctx, const char *ns, const char *scope, const char *string, Buffer *out)
{
    assert(string);

    bool returnval = true;

    if (strlen(string) == 0)
    {
        return false;
    }

    // TODO: cleanup, optimize this mess
    Buffer *var = BufferNew();
    Buffer *current_item = BufferNew();
    Buffer *temp = BufferNew();
    for (const char *sp = string; /* No exit */ ; sp++)     /* check for varitems */
    {
        char varstring = false;
        size_t increment = 0;

        if (*sp == '\0')
        {
            break;
        }

        BufferZero(current_item);
        ExtractScalarPrefix(current_item, sp, strlen(sp));

        BufferAppend(out, BufferData(current_item), BufferSize(current_item));
        sp += BufferSize(current_item);

        if (*sp == '\0')
        {
            break;
        }

        BufferZero(var);
        if (*sp == '$')
        {
            switch (*(sp + 1))
            {
            case '(':
                varstring = ')';
                ExtractScalarReference(var, sp, strlen(sp), false);
                if (BufferSize(var) == 0)
                {
                    BufferAppendChar(out, '$');
                    continue;
                }
                break;

            case '{':
                varstring = '}';
                ExtractScalarReference(var, sp, strlen(sp), false);
                if (BufferSize(var) == 0)
                {
                    BufferAppendChar(out, '$');
                    continue;
                }
                break;

            default:
                BufferAppendChar(out, '$');
                continue;
            }
        }

        BufferZero(current_item);
        {
            BufferZero(temp);
            ExtractScalarReference(temp, sp, strlen(sp), true);

            if (IsCf3VarString(BufferData(temp)))
            {
                ExpandScalar(ctx, ns, scope, BufferData(temp), current_item);
            }
            else
            {
                BufferAppend(current_item, BufferData(temp), BufferSize(temp));
            }
        }

        increment = BufferSize(var) - 1;

        char name[CF_MAXVARSIZE] = "";
        if (!IsExpandable(BufferData(current_item)))
        {
            DataType type = DATA_TYPE_NONE;
            const void *value = NULL;
            {
                VarRef *ref = VarRefParseFromNamespaceAndScope(BufferData(current_item), ns, scope, CF_NS, '.');
                value = EvalContextVariableGet(ctx, ref, &type);
                VarRefDestroy(ref);
            }

            if (value)
            {
                switch (type)
                {
                case DATA_TYPE_STRING:
                case DATA_TYPE_INT:
                case DATA_TYPE_REAL:
                    BufferAppend(out, value, strlen(value));
                    break;

                case DATA_TYPE_STRING_LIST:
                case DATA_TYPE_INT_LIST:
                case DATA_TYPE_REAL_LIST:
                case DATA_TYPE_NONE:
                    if (varstring == '}')
                    {
                        snprintf(name, CF_MAXVARSIZE, "${%s}", BufferData(current_item));
                    }
                    else
                    {
                        snprintf(name, CF_MAXVARSIZE, "$(%s)", BufferData(current_item));
                    }

                    BufferAppend(out, name, strlen(name));
                    returnval = false;
                    break;

                default:
                    Log(LOG_LEVEL_DEBUG, "Returning Unknown Scalar ('%s' => '%s')", string, BufferData(out));
                    BufferDestroy(var);
                    BufferDestroy(current_item);
                    BufferDestroy(temp);
                    return false;

                }
            }
            else
            {
                if (varstring == '}')
                {
                    snprintf(name, CF_MAXVARSIZE, "${%s}", BufferData(current_item));
                }
                else
                {
                    snprintf(name, CF_MAXVARSIZE, "$(%s)", BufferData(current_item));
                }

                BufferAppend(out, name, strlen(name));
                returnval = false;
            }
        }

        sp += increment;
        BufferZero(current_item);
    }

    BufferDestroy(var);
    BufferDestroy(current_item);
    BufferDestroy(temp);

    return returnval;
}
コード例 #29
0
ファイル: syntax.c プロジェクト: dnaeon/core
static void CheckParseInt(char *lval, char *s, const char *range)
{
    Item *split;
    int n;
    long max = CF_LOWINIT, min = CF_HIGHINIT, val;
    char output[CF_BUFSIZE];

/* Numeric types are registered by range separated by comma str "min,max" */
    CfDebug("\nCheckParseInt(%s => %s/%s)\n", lval, s, range);

    if (s == NULL)
    {
        return;
    }

    split = SplitString(range, ',');

    if ((n = ListLen(split)) != 2)
    {
        FatalError("INTERN: format specifier for int rvalues is not ok for lval %s - got %d items", lval, n);
    }

    sscanf(split->name, "%ld", &min);

    if (strcmp(split->next->name, "inf") == 0)
    {
        max = CF_INFINITY;
    }
    else
    {
        sscanf(split->next->name, "%ld", &max);
    }

    DeleteItemList(split);

    if (min == CF_HIGHINIT || max == CF_LOWINIT)
    {
        FatalError("INTERN: could not parse format specifier for int rvalues for lval %s", lval);
    }

    if (IsCf3VarString(s))
    {
        CfDebug("Validation: Unable to verify syntax of int \'%s\' due to variable expansion at this stage\n", s);
        return;
    }

    val = Str2Int(s);

    if (val == CF_NOINT)
    {
        snprintf(output, CF_BUFSIZE, "Int item on rhs of lval \'%s\' given as \'%s\' could not be parsed", lval, s);
        ReportError(output);
        return;
    }

    if (val > max || val < min)
    {
        snprintf(output, CF_BUFSIZE,
                 "Int item on rhs of lval \'%s\' given as {%s => %ld} is out of bounds (should be in [%s])", lval, s,
                 val, range);
        ReportError(output);
        return;
    }

    CfDebug("CheckParseInt - syntax verified\n\n");
}
コード例 #30
0
ファイル: expand.c プロジェクト: kkaempf/core
Rval EvaluateFinalRval(EvalContext *ctx, const Policy *policy,
                       const char *ns, const char *scope,
                       Rval rval, bool forcelist, const Promise *pp)
{
    assert(ctx);
    assert(policy);
    Rval returnval;

    /* Treat lists specially. */
    if (rval.type == RVAL_TYPE_SCALAR && IsNakedVar(rval.item, '@'))
    {
        char naked[CF_MAXVARSIZE];
        GetNaked(naked, rval.item);

        if (IsExpandable(naked))                /* example: @(blah_$(blue)) */
        {
            returnval = ExpandPrivateRval(ctx, NULL, "this", rval.item, rval.type);
        }
        else
        {
            VarRef *ref = VarRefParseFromScope(naked, scope);
            DataType value_type;
            const void *value = EvalContextVariableGet(ctx, ref, &value_type);
            VarRefDestroy(ref);

            if (DataTypeToRvalType(value_type) == RVAL_TYPE_LIST)
            {
                returnval.item = ExpandList(ctx, ns, scope, value, true);
                returnval.type = RVAL_TYPE_LIST;
            }
            else
            {
                returnval = ExpandPrivateRval(ctx, NULL, "this", rval.item, rval.type);
            }
        }
    }
    else if (forcelist) /* We are replacing scalar @(name) with list */
    {
        returnval = ExpandPrivateRval(ctx, ns, scope, rval.item, rval.type);
    }
    else if (FnCallIsBuiltIn(rval))
    {
        returnval = RvalCopy(rval);
    }
    else
    {
        returnval = ExpandPrivateRval(ctx, NULL, "this", rval.item, rval.type);
    }

    switch (returnval.type)
    {
    case RVAL_TYPE_SCALAR:
    case RVAL_TYPE_CONTAINER:
        break;

    case RVAL_TYPE_LIST:
        for (Rlist *rp = RvalRlistValue(returnval); rp; rp = rp->next)
        {
            switch (rp->val.type)
            {
            case RVAL_TYPE_FNCALL:
            {
                FnCall *fp = RlistFnCallValue(rp);
                rp->val = FnCallEvaluate(ctx, policy, fp, pp).rval;
                FnCallDestroy(fp);
                break;
            }
            case RVAL_TYPE_SCALAR:
                if (EvalContextStackCurrentPromise(ctx) &&
                    IsCf3VarString(RlistScalarValue(rp)))
                {
                    void *prior = rp->val.item;
                    rp->val = ExpandPrivateRval(ctx, NULL, "this",
                                                prior, RVAL_TYPE_SCALAR);
                    free(prior);
                }
                /* else: returnval unchanged. */
                break;
            default:
                assert(!"Bad type for entry in Rlist");
            }
        }
        break;

    case RVAL_TYPE_FNCALL:
        if (FnCallIsBuiltIn(returnval))
        {
            FnCall *fp = RvalFnCallValue(returnval);
            returnval = FnCallEvaluate(ctx, policy, fp, pp).rval;
            FnCallDestroy(fp);
        }
        break;

    default:
        assert(returnval.item == NULL); /* else we're leaking it */
        returnval.item = NULL;
        returnval.type = RVAL_TYPE_NOPROMISEE;
        break;
    }

    return returnval;
}