void RvalDestroy(Rval rval) { if (rval.item == NULL) { return; } switch (rval.type) { case RVAL_TYPE_SCALAR: ThreadLock(cft_lock); free(RvalScalarValue(rval)); ThreadUnlock(cft_lock); return; case RVAL_TYPE_LIST: RlistDestroy(RvalRlistValue(rval)); return; case RVAL_TYPE_FNCALL: FnCallDestroy(RvalFnCallValue(rval)); break; case RVAL_TYPE_CONTAINER: JsonDestroy(RvalContainerValue(rval)); break; case RVAL_TYPE_NOPROMISEE: return; } }
void RvalDestroy(Rval rval) { Rlist *clist, *next = NULL; CfDebug("DeleteRvalItem(%c)", rval.type); if (DEBUG) { RvalShow(stdout, rval); } CfDebug("\n"); if (rval.item == NULL) { CfDebug("DeleteRval NULL\n"); return; } switch (rval.type) { case RVAL_TYPE_SCALAR: ThreadLock(cft_lock); free((char *) rval.item); ThreadUnlock(cft_lock); break; case RVAL_TYPE_LIST: /* rval is now a list whose first item is clist->item */ for (clist = (Rlist *) rval.item; clist != NULL; clist = next) { next = clist->next; if (clist->item) { RvalDestroy((Rval) {clist->item, clist->type}); } free(clist); } break; case RVAL_TYPE_FNCALL: FnCallDestroy((FnCall *) rval.item); break; default: CfDebug("Nothing to do\n"); return; } }
void RvalDestroy(Rval rval) { Rlist *clist, *next = NULL; if (rval.item == NULL) { return; } switch (rval.type) { case RVAL_TYPE_SCALAR: ThreadLock(cft_lock); free((char *) rval.item); ThreadUnlock(cft_lock); break; case RVAL_TYPE_LIST: /* rval is now a list whose first item is clist->item */ for (clist = (Rlist *) rval.item; clist != NULL; clist = next) { next = clist->next; if (clist->item) { RvalDestroy((Rval) {clist->item, clist->type}); } free(clist); } break; case RVAL_TYPE_FNCALL: FnCallDestroy((FnCall *) rval.item); break; default: return; } }
static int EvalClassExpression(EvalContext *ctx, Constraint *cp, Promise *pp) { int result_and = true; int result_or = false; int result_xor = 0; int result = 0, total = 0; char buffer[CF_MAXVARSIZE]; Rlist *rp; FnCall *fp; Rval rval; if (cp == NULL) { Log(LOG_LEVEL_ERR, "EvalClassExpression internal diagnostic discovered an ill-formed condition"); } if (!IsDefinedClass(ctx, pp->classes, PromiseGetNamespace(pp))) { return false; } if (EvalContextPromiseIsDone(ctx, pp)) { return false; } if (IsDefinedClass(ctx, pp->promiser, PromiseGetNamespace(pp))) { if (PromiseGetConstraintAsInt(ctx, "persistence", pp) == 0) { Log(LOG_LEVEL_VERBOSE, " ?> Cancelling cached persistent class %s", pp->promiser); EvalContextHeapPersistentRemove(pp->promiser); } return false; } switch (cp->rval.type) { case RVAL_TYPE_FNCALL: fp = (FnCall *) cp->rval.item; /* Special expansion of functions for control, best effort only */ FnCallResult res = FnCallEvaluate(ctx, fp, pp); FnCallDestroy(fp); cp->rval = res.rval; break; case RVAL_TYPE_LIST: for (rp = (Rlist *) cp->rval.item; rp != NULL; rp = rp->next) { rval = EvaluateFinalRval(ctx, "this", (Rval) {rp->item, rp->type}, true, pp); RvalDestroy((Rval) {rp->item, rp->type}); rp->item = rval.item; rp->type = rval.type; } break; default: rval = ExpandPrivateRval(ctx, "this", cp->rval); RvalDestroy(cp->rval); cp->rval = rval; break; } if (strcmp(cp->lval, "expression") == 0) { if (cp->rval.type != RVAL_TYPE_SCALAR) { return false; } if (IsDefinedClass(ctx, (char *) cp->rval.item, PromiseGetNamespace(pp))) { return true; } else { return false; } } if (strcmp(cp->lval, "not") == 0) { if (cp->rval.type != RVAL_TYPE_SCALAR) { return false; } if (IsDefinedClass(ctx, (char *) cp->rval.item, PromiseGetNamespace(pp))) { return false; } else { return true; } } // Class selection if (strcmp(cp->lval, "select_class") == 0) { char splay[CF_MAXVARSIZE]; int i, n; double hash; total = 0; for (rp = (Rlist *) cp->rval.item; rp != NULL; rp = rp->next) { total++; } if (total == 0) { Log(LOG_LEVEL_ERR, "No classes to select on RHS"); PromiseRef(LOG_LEVEL_ERR, pp); return false; } snprintf(splay, CF_MAXVARSIZE, "%s+%s+%ju", VFQNAME, VIPADDRESS, (uintmax_t)getuid()); hash = (double) OatHash(splay, CF_HASHTABLESIZE); n = (int) (total * hash / (double) CF_HASHTABLESIZE); for (rp = (Rlist *) cp->rval.item, i = 0; rp != NULL; rp = rp->next, i++) { if (i == n) { EvalContextHeapAddSoft(ctx, rp->item, PromiseGetNamespace(pp)); return true; } } } /* If we get here, anything remaining on the RHS must be a clist */ if (cp->rval.type != RVAL_TYPE_LIST) { Log(LOG_LEVEL_ERR, "RHS of promise body attribute '%s' is not a list", cp->lval); PromiseRef(LOG_LEVEL_ERR, pp); return true; } // Class distributions if (strcmp(cp->lval, "dist") == 0) { for (rp = (Rlist *) cp->rval.item; rp != NULL; rp = rp->next) { result = IntFromString(rp->item); if (result < 0) { Log(LOG_LEVEL_ERR, "Non-positive integer in class distribution"); PromiseRef(LOG_LEVEL_ERR, pp); return false; } total += result; } if (total == 0) { Log(LOG_LEVEL_ERR, "An empty distribution was specified on RHS"); PromiseRef(LOG_LEVEL_ERR, pp); return false; } double fluct = drand48(); double cum = 0.0; for (rp = (Rlist *) cp->rval.item; rp != NULL; rp = rp->next) { double prob = ((double) IntFromString(rp->item)) / ((double) total); cum += prob; if (fluct < cum) { break; } } snprintf(buffer, CF_MAXVARSIZE - 1, "%s_%s", pp->promiser, (char *) rp->item); /* FIXME: figure why explicit mark and get rid of it */ EvalContextMarkPromiseDone(ctx, pp); if (strcmp(PromiseGetBundle(pp)->type, "common") == 0) { EvalContextHeapAddSoft(ctx, buffer, PromiseGetNamespace(pp)); } else { EvalContextStackFrameAddSoft(ctx, buffer); } return true; } /* and/or/xor expressions */ for (rp = (Rlist *) cp->rval.item; rp != NULL; rp = rp->next) { if (rp->type != RVAL_TYPE_SCALAR) { return false; } result = IsDefinedClass(ctx, (char *) (rp->item), PromiseGetNamespace(pp)); result_and = result_and && result; result_or = result_or || result; result_xor ^= result; } // Class combinations if (strcmp(cp->lval, "or") == 0) { return result_or; } if (strcmp(cp->lval, "xor") == 0) { return (result_xor == 1) ? true : false; } if (strcmp(cp->lval, "and") == 0) { return result_and; } return false; }
int ScopeMapBodyArgs(EvalContext *ctx, const char *scopeid, Rlist *give, const Rlist *take) { Rlist *rpg = NULL; const Rlist *rpt = NULL; FnCall *fp; DataType dtg = DATA_TYPE_NONE, dtt = DATA_TYPE_NONE; char *lval; void *rval; int len1, len2; CfDebug("MapBodyArgs(begin)\n"); len1 = RlistLen(give); len2 = RlistLen(take); if (len1 != len2) { CfOut(OUTPUT_LEVEL_ERROR, "", " !! Argument mismatch in body template give[+args] = %d, take[-args] = %d", len1, len2); return false; } for (rpg = give, rpt = take; rpg != NULL && rpt != NULL; rpg = rpg->next, rpt = rpt->next) { dtg = StringDataType(ctx, scopeid, (char *) rpg->item); dtt = StringDataType(ctx, scopeid, (char *) rpt->item); if (dtg != dtt) { CfOut(OUTPUT_LEVEL_ERROR, "", "Type mismatch between logical/formal parameters %s/%s\n", (char *) rpg->item, (char *) rpt->item); CfOut(OUTPUT_LEVEL_ERROR, "", "%s is %s whereas %s is %s\n", (char *) rpg->item, CF_DATATYPES[dtg], (char *) rpt->item, CF_DATATYPES[dtt]); } switch (rpg->type) { case RVAL_TYPE_SCALAR: lval = (char *) rpt->item; rval = rpg->item; CfDebug("MapBodyArgs(SCALAR,%s,%s)\n", lval, (char *) rval); EvalContextVariablePut(ctx, (VarRef) { NULL, scopeid, lval }, (Rval) { rval, RVAL_TYPE_SCALAR }, dtg); break; case RVAL_TYPE_LIST: lval = (char *) rpt->item; rval = rpg->item; EvalContextVariablePut(ctx, (VarRef) { NULL, scopeid, lval }, (Rval) { rval, RVAL_TYPE_LIST }, dtg); break; case RVAL_TYPE_FNCALL: fp = (FnCall *) rpg->item; dtg = DATA_TYPE_NONE; { const FnCallType *fncall_type = FnCallTypeGet(fp->name); if (fncall_type) { dtg = fncall_type->dtype; } } FnCallResult res = FnCallEvaluate(ctx, fp, NULL); if (res.status == FNCALL_FAILURE && THIS_AGENT_TYPE != AGENT_TYPE_COMMON) { // Unresolved variables if (VERBOSE) { printf (" !! Embedded function argument does not resolve to a name - probably too many evaluation levels for "); FnCallShow(stdout, fp); printf(" (try simplifying)\n"); } } else { FnCallDestroy(fp); rpg->item = res.rval.item; rpg->type = res.rval.type; lval = (char *) rpt->item; rval = rpg->item; EvalContextVariablePut(ctx, (VarRef) { NULL, scopeid, lval }, (Rval) {rval, RVAL_TYPE_SCALAR }, dtg); } break; default: /* Nothing else should happen */ ProgrammingError("Software error: something not a scalar/function in argument literal"); } } CfDebug("MapBodyArgs(end)\n"); return true; }
int ScopeMapBodyArgs(EvalContext *ctx, const char *ns, const char *scope, Rlist *give, const Rlist *take) { Rlist *rpg = NULL; const Rlist *rpt = NULL; FnCall *fp; DataType dtg = DATA_TYPE_NONE, dtt = DATA_TYPE_NONE; char *lval; void *rval; int len1, len2; len1 = RlistLen(give); len2 = RlistLen(take); if (len1 != len2) { Log(LOG_LEVEL_ERR, "Argument mismatch in body template give[+args] = %d, take[-args] = %d", len1, len2); return false; } for (rpg = give, rpt = take; rpg != NULL && rpt != NULL; rpg = rpg->next, rpt = rpt->next) { dtg = StringDataType(ctx, (char *) rpg->item); dtt = StringDataType(ctx, (char *) rpt->item); if (dtg != dtt) { Log(LOG_LEVEL_ERR, "Type mismatch between logical/formal parameters %s/%s", (char *) rpg->item, (char *) rpt->item); Log(LOG_LEVEL_ERR, "%s is %s whereas %s is %s", (char *) rpg->item, DataTypeToString(dtg), (char *) rpt->item, DataTypeToString(dtt)); } switch (rpg->type) { case RVAL_TYPE_SCALAR: { lval = (char *) rpt->item; rval = rpg->item; VarRef *ref = VarRefParseFromNamespaceAndScope(lval, ns, scope, CF_NS, '.'); EvalContextVariablePut(ctx, ref, (Rval) { rval, RVAL_TYPE_SCALAR }, dtg); } break; case RVAL_TYPE_LIST: { lval = (char *) rpt->item; rval = rpg->item; VarRef *ref = VarRefParseFromNamespaceAndScope(lval, ns, scope, CF_NS, '.'); EvalContextVariablePut(ctx, ref, (Rval) { rval, RVAL_TYPE_LIST }, dtg); VarRefDestroy(ref); } break; case RVAL_TYPE_FNCALL: fp = (FnCall *) rpg->item; dtg = DATA_TYPE_NONE; { const FnCallType *fncall_type = FnCallTypeGet(fp->name); if (fncall_type) { dtg = fncall_type->dtype; } } FnCallResult res = FnCallEvaluate(ctx, fp, NULL); if (res.status == FNCALL_FAILURE && THIS_AGENT_TYPE != AGENT_TYPE_COMMON) { Log(LOG_LEVEL_VERBOSE, "Embedded function argument does not resolve to a name - probably too many evaluation levels for '%s'", fp->name); } else { FnCallDestroy(fp); rpg->item = res.rval.item; rpg->type = res.rval.type; lval = (char *) rpt->item; rval = rpg->item; VarRef *ref = VarRefParseFromNamespaceAndScope(lval, ns, scope, CF_NS, '.'); EvalContextVariablePut(ctx, ref, (Rval) {rval, RVAL_TYPE_SCALAR }, dtg); VarRefDestroy(ref); } break; default: /* Nothing else should happen */ ProgrammingError("Software error: something not a scalar/function in argument literal"); } } return true; }
static void ParserStateReset(ParserState *p, bool discard) { p->agent_type = AGENT_TYPE_COMMON; p->warnings = PARSER_WARNING_ALL; p->policy = NULL; int i = CF_MAX_NESTING; while (i-- > 0) /* Clear stacks from top down */ { if (discard) { free(p->currentfnid[i]); RlistDestroy(p->giveargs[i]); FnCallDestroy(p->currentfncall[i]); } else { assert(!p->currentfnid[i]); assert(!p->giveargs[i]); assert(!p->currentfncall[i]); } p->currentfnid[i] = NULL; p->giveargs[i] = NULL; p->currentfncall[i] = NULL; } free(p->current_line); p->current_line = NULL; p->line_no = 1; p->line_pos = 1; p->error_count = 0; p->warning_count = 0; p->list_nesting = 0; p->arg_nesting = 0; free(p->current_namespace); p->current_namespace = xstrdup("default"); p->currentid[0] = '\0'; if (p->currentstring) { free(p->currentstring); } p->currentstring = NULL; p->currenttype[0] = '\0'; if (p->currentclasses) { free(p->currentclasses); } p->currentclasses = NULL; p->currentRlist = NULL; p->currentpromise = NULL; p->currentbody = NULL; if (p->promiser) { free(p->promiser); } p->promiser = NULL; p->blockid[0] = '\0'; p->blocktype[0] = '\0'; p->rval = RvalNew(NULL, RVAL_TYPE_NOPROMISEE); }
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; }
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; }
static bool EvalClassExpression(EvalContext *ctx, Constraint *cp, const Promise *pp) { assert(pp); if (cp == NULL) // ProgrammingError ? We'll crash RSN anyway ... { Log(LOG_LEVEL_ERR, "EvalClassExpression internal diagnostic discovered an ill-formed condition"); } if (!IsDefinedClass(ctx, pp->classes)) { return false; } if (IsDefinedClass(ctx, pp->promiser)) { if (PromiseGetConstraintAsInt(ctx, "persistence", pp) == 0) { Log(LOG_LEVEL_VERBOSE, " ?> Cancelling cached persistent class %s", pp->promiser); EvalContextHeapPersistentRemove(pp->promiser); } return false; } switch (cp->rval.type) { Rval rval; FnCall *fp; case RVAL_TYPE_FNCALL: fp = RvalFnCallValue(cp->rval); /* Special expansion of functions for control, best effort only: */ FnCallResult res = FnCallEvaluate(ctx, PromiseGetPolicy(pp), fp, pp); FnCallDestroy(fp); cp->rval = res.rval; break; case RVAL_TYPE_LIST: for (Rlist *rp = cp->rval.item; rp != NULL; rp = rp->next) { rval = EvaluateFinalRval(ctx, PromiseGetPolicy(pp), NULL, "this", rp->val, true, pp); RvalDestroy(rp->val); rp->val = rval; } break; default: rval = ExpandPrivateRval(ctx, NULL, "this", cp->rval.item, cp->rval.type); RvalDestroy(cp->rval); cp->rval = rval; break; } if (strcmp(cp->lval, "expression") == 0) { return (cp->rval.type == RVAL_TYPE_SCALAR && IsDefinedClass(ctx, RvalScalarValue(cp->rval))); } if (strcmp(cp->lval, "not") == 0) { return (cp->rval.type == RVAL_TYPE_SCALAR && !IsDefinedClass(ctx, RvalScalarValue(cp->rval))); } /* If we get here, anything remaining on the RHS must be a clist */ if (cp->rval.type != RVAL_TYPE_LIST) { Log(LOG_LEVEL_ERR, "RHS of promise body attribute '%s' is not a list", cp->lval); PromiseRef(LOG_LEVEL_ERR, pp); return true; } // Class selection if (strcmp(cp->lval, "select_class") == 0) { return SelectClass(ctx, cp->rval.item, pp); } // Class distributions if (strcmp(cp->lval, "dist") == 0) { return DistributeClass(ctx, cp->rval.item, pp); } /* Combine with and/or/xor: */ if (strcmp(cp->lval, "or") == 0) { return EvalBoolCombination(ctx, cp->rval.item, c_or); } else if (strcmp(cp->lval, "and") == 0) { return EvalBoolCombination(ctx, cp->rval.item, c_and); } else if (strcmp(cp->lval, "xor") == 0) { return EvalBoolCombination(ctx, cp->rval.item, c_xor); } return false; }
void ScopeMapBodyArgs(EvalContext *ctx, const Body *body, const Rlist *args) { const Rlist *arg = NULL; const Rlist *param = NULL; for (arg = args, param = body->args; arg != NULL && param != NULL; arg = arg->next, param = param->next) { DataType arg_type = StringDataType(ctx, RlistScalarValue(arg)); DataType param_type = StringDataType(ctx, RlistScalarValue(param)); if (arg_type != param_type) { Log(LOG_LEVEL_ERR, "Type mismatch between logical/formal parameters %s/%s", (char *) arg->item, (char *) param->item); Log(LOG_LEVEL_ERR, "%s is %s whereas %s is %s", (char *) arg->item, DataTypeToString(arg_type), (char *) param->item, DataTypeToString(param_type)); } switch (arg->type) { case RVAL_TYPE_SCALAR: { const char *lval = RlistScalarValue(param); void *rval = arg->item; VarRef *ref = VarRefParseFromNamespaceAndScope(lval, NULL, "body", CF_NS, '.'); EvalContextVariablePut(ctx, ref, (Rval) { rval, RVAL_TYPE_SCALAR }, arg_type); } break; case RVAL_TYPE_LIST: { const char *lval = RlistScalarValue(param->item); void *rval = arg->item; VarRef *ref = VarRefParseFromNamespaceAndScope(lval, NULL, "body", CF_NS, '.'); EvalContextVariablePut(ctx, ref, (Rval) { rval, RVAL_TYPE_LIST }, arg_type); VarRefDestroy(ref); } break; case RVAL_TYPE_FNCALL: { FnCall *fp = arg->item; arg_type = DATA_TYPE_NONE; { const FnCallType *fncall_type = FnCallTypeGet(fp->name); if (fncall_type) { arg_type = fncall_type->dtype; } } FnCallResult res = FnCallEvaluate(ctx, fp, NULL); if (res.status == FNCALL_FAILURE && THIS_AGENT_TYPE != AGENT_TYPE_COMMON) { Log(LOG_LEVEL_VERBOSE, "Embedded function argument does not resolve to a name - probably too many evaluation levels for '%s'", fp->name); } else { FnCallDestroy(fp); const char *lval = RlistScalarValue(param); void *rval = res.rval.item; VarRef *ref = VarRefParseFromNamespaceAndScope(lval, NULL, "body", CF_NS, '.'); EvalContextVariablePut(ctx, ref, (Rval) {rval, RVAL_TYPE_SCALAR }, res.rval.type); VarRefDestroy(ref); } } break; default: /* Nothing else should happen */ ProgrammingError("Software error: something not a scalar/function in argument literal"); } } }
static int EvalClassExpression(EvalContext *ctx, Constraint *cp, const Promise *pp) { assert(pp); int result_and = true; int result_or = false; int result_xor = 0; int result = 0, total = 0; char buffer[CF_MAXVARSIZE]; Rlist *rp; if (cp == NULL) // ProgrammingError ? We'll crash RSN anyway ... { Log(LOG_LEVEL_ERR, "EvalClassExpression internal diagnostic discovered an ill-formed condition"); } if (!IsDefinedClass(ctx, pp->classes)) { return false; } if (IsDefinedClass(ctx, pp->promiser)) { if (PromiseGetConstraintAsInt(ctx, "persistence", pp) == 0) { Log(LOG_LEVEL_VERBOSE, " ?> Cancelling cached persistent class %s", pp->promiser); EvalContextHeapPersistentRemove(pp->promiser); } return false; } switch (cp->rval.type) { Rval rval; FnCall *fp; case RVAL_TYPE_FNCALL: fp = RvalFnCallValue(cp->rval); /* Special expansion of functions for control, best effort only: */ FnCallResult res = FnCallEvaluate(ctx, PromiseGetPolicy(pp), fp, pp); FnCallDestroy(fp); cp->rval = res.rval; break; case RVAL_TYPE_LIST: for (rp = (Rlist *) cp->rval.item; rp != NULL; rp = rp->next) { rval = EvaluateFinalRval(ctx, PromiseGetPolicy(pp), NULL, "this", rp->val, true, pp); RvalDestroy(rp->val); rp->val = rval; } break; default: rval = ExpandPrivateRval(ctx, NULL, "this", cp->rval.item, cp->rval.type); RvalDestroy(cp->rval); cp->rval = rval; break; } if (strcmp(cp->lval, "expression") == 0) { return (cp->rval.type == RVAL_TYPE_SCALAR && IsDefinedClass(ctx, RvalScalarValue(cp->rval))); } if (strcmp(cp->lval, "not") == 0) { return (cp->rval.type == RVAL_TYPE_SCALAR && !IsDefinedClass(ctx, RvalScalarValue(cp->rval))); } // Class selection if (strcmp(cp->lval, "select_class") == 0) { char splay[CF_MAXVARSIZE]; int i, n; double hash; total = 0; for (rp = (Rlist *) cp->rval.item; rp != NULL; rp = rp->next) { total++; } if (total == 0) { Log(LOG_LEVEL_ERR, "No classes to select on RHS"); PromiseRef(LOG_LEVEL_ERR, pp); return false; } snprintf(splay, CF_MAXVARSIZE, "%s+%s+%ju", VFQNAME, VIPADDRESS, (uintmax_t)getuid()); hash = (double) StringHash(splay, 0, CF_HASHTABLESIZE); n = (int) (total * hash / (double) CF_HASHTABLESIZE); for (rp = (Rlist *) cp->rval.item, i = 0; rp != NULL; rp = rp->next, i++) { if (i == n) { EvalContextClassPutSoft(ctx, RlistScalarValue(rp), CONTEXT_SCOPE_NAMESPACE, "source=promise"); return true; } } } /* If we get here, anything remaining on the RHS must be a clist */ if (cp->rval.type != RVAL_TYPE_LIST) { Log(LOG_LEVEL_ERR, "RHS of promise body attribute '%s' is not a list", cp->lval); PromiseRef(LOG_LEVEL_ERR, pp); return true; } // Class distributions if (strcmp(cp->lval, "dist") == 0) { for (rp = (Rlist *) cp->rval.item; rp != NULL; rp = rp->next) { result = IntFromString(RlistScalarValue(rp)); if (result < 0) { Log(LOG_LEVEL_ERR, "Non-positive integer in class distribution"); PromiseRef(LOG_LEVEL_ERR, pp); return false; } total += result; } if (total == 0) { Log(LOG_LEVEL_ERR, "An empty distribution was specified on RHS"); PromiseRef(LOG_LEVEL_ERR, pp); return false; } double fluct = drand48(); double cum = 0.0; for (rp = (Rlist *) cp->rval.item; rp != NULL; rp = rp->next) { double prob = ((double) IntFromString(RlistScalarValue(rp))) / ((double) total); cum += prob; if (fluct < cum) { break; } } snprintf(buffer, CF_MAXVARSIZE - 1, "%s_%s", pp->promiser, RlistScalarValue(rp)); if (strcmp(PromiseGetBundle(pp)->type, "common") == 0) { EvalContextClassPutSoft(ctx, buffer, CONTEXT_SCOPE_NAMESPACE, "source=promise"); } else { EvalContextClassPutSoft(ctx, buffer, CONTEXT_SCOPE_BUNDLE, "source=promise"); } return true; } /* and/or/xor expressions */ enum { c_or = 0, c_and, c_xor } logic; if (strcmp(cp->lval, "or") == 0) { logic = c_or; } else if (strcmp(cp->lval, "and") == 0) { logic = c_and; } else if (strcmp(cp->lval, "xor") == 0) { logic = c_xor; } for (rp = (Rlist *) cp->rval.item; rp != NULL; rp = rp->next) { // tolerate unexpanded entries here and interpret as "class not set" if (rp->val.type != RVAL_TYPE_SCALAR) { result = false; } else { result = IsDefinedClass(ctx, RlistScalarValue(rp)); } // shortcut and and or switch (logic) { case c_or: if (result) { return true; } break; case c_and: if (!result) { return false; } break; default: break; } result_and = result_and && result; result_or = result_or || result; result_xor ^= result; } // Class combinations switch (logic) { case c_or: return result_or; case c_xor: return result_xor == 1; case c_and: return result_and; } return false; }