/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * eval.c -- constraint evaluation module * * this module evaluates constraints. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include "alloc.h" #include "out.h" #include "stable.h" #include "literals.h" #include "lut.h" #include "tree.h" #include "ptree.h" #include "itree.h" #include "ipath.h" #include "eval.h" #include "config.h" #include "platform.h" #include "fme.h" #include "stats.h" static struct node *eval_dup(struct node *np, struct lut *ex, struct node *events[]); static int check_expr_args(struct evalue *lp, struct evalue *rp, enum datatype dtype, struct node *np); static struct node *eval_fru(struct node *np); static struct node *eval_asru(struct node *np); /* * begins_with -- return true if rhs path begins with everything in lhs path */ static int begins_with(struct node *lhs, struct node *rhs, struct lut *ex) { int lnum; int rnum; struct iterinfo *iterinfop; if (lhs == NULL) return (1); /* yep -- it all matched */ if (rhs == NULL) return (0); /* nope, ran out of rhs first */ ASSERTeq(lhs->t, T_NAME, ptree_nodetype2str); ASSERTeq(rhs->t, T_NAME, ptree_nodetype2str); if (lhs->u.name.s != rhs->u.name.s) return (0); /* nope, different component names */ if (lhs->u.name.child && lhs->u.name.child->t == T_NUM) { lnum = (int)lhs->u.name.child->u.ull; } else if (lhs->u.name.child && lhs->u.name.child->t == T_NAME) { iterinfop = lut_lookup(ex, (void *)lhs->u.name.child->u.name.s, NULL); if (iterinfop != NULL) lnum = iterinfop->num; else out(O_DIE, "begins_with: unexpected lhs child"); } else { out(O_DIE, "begins_with: unexpected lhs child"); } if (rhs->u.name.child && rhs->u.name.child->t == T_NUM) { rnum = (int)rhs->u.name.child->u.ull; } else if (rhs->u.name.child && rhs->u.name.child->t == T_NAME) { iterinfop = lut_lookup(ex, (void *)rhs->u.name.child->u.name.s, NULL); if (iterinfop != NULL) rnum = iterinfop->num; else out(O_DIE, "begins_with: unexpected rhs child"); } else { out(O_DIE, "begins_with: unexpected rhs child"); } if (lnum != rnum) return (0); /* nope, instance numbers were different */ return (begins_with(lhs->u.name.next, rhs->u.name.next, ex)); } /* * eval_getname - used by eval_func to evaluate a name, preferably without using * eval_dup (but if it does have to use eval_dup then the *dupedp flag is set). */ static struct node * eval_getname(struct node *funcnp, struct lut *ex, struct node *events[], struct node *np, struct lut **globals, struct config *croot, struct arrow *arrowp, int try, int *dupedp) { struct node *nodep; const char *funcname = funcnp->u.func.s; struct evalue val; if (np->t == T_NAME) nodep = np; else if (np->u.func.s == L_fru) nodep = eval_fru(np->u.func.arglist); else if (np->u.func.s == L_asru) nodep = eval_asru(np->u.func.arglist); else out(O_DIE, "%s: unexpected type: %s", funcname, ptree_nodetype2str(np->t)); if (try) { if (eval_expr(nodep, ex, events, globals, croot, arrowp, try, &val) && val.t == NODEPTR) nodep = (struct node *)(uintptr_t)val.v; else { *dupedp = 1; nodep = eval_dup(nodep, ex, events); } } return (nodep); } /* * evaluate a variety of functions and place result in valuep. return 1 if * function evaluation was successful; 0 if otherwise (e.g., the case of an * invalid argument to the function) */ /*ARGSUSED*/ static int eval_func(struct node *funcnp, struct lut *ex, struct node *events[], struct node *np, struct lut **globals, struct config *croot, struct arrow *arrowp, int try, struct evalue *valuep) { const char *funcname = funcnp->u.func.s; int duped_lhs = 0, duped_rhs = 0, duped = 0; struct node *lhs; struct node *rhs; struct config *cp; struct node *nodep; char *path; struct evalue val; if (funcname == L_within) { /* within()'s are not really constraints -- always true */ valuep->t = UINT64; valuep->v = 1; return (1); } else if (funcname == L_is_under) { lhs = eval_getname(funcnp, ex, events, np->u.expr.left, globals, croot, arrowp, try, &duped_lhs); rhs = eval_getname(funcnp, ex, events, np->u.expr.right, globals, croot, arrowp, try, &duped_rhs); valuep->t = UINT64; valuep->v = begins_with(lhs, rhs, ex); out(O_ALTFP|O_VERB2|O_NONL, "eval_func:is_under("); ptree_name_iter(O_ALTFP|O_VERB2|O_NONL, lhs); out(O_ALTFP|O_VERB2|O_NONL, ","); ptree_name_iter(O_ALTFP|O_VERB2|O_NONL, rhs); out(O_ALTFP|O_VERB2|O_NONL, ") returned %d", (int)valuep->v); if (duped_lhs) tree_free(lhs); if (duped_rhs) tree_free(rhs); return (1); } else if (funcname == L_confprop || funcname == L_confprop_defined) { const char *s; /* for now s will point to a quote [see addconfigprop()] */ ASSERT(np->u.expr.right->t == T_QUOTE); nodep = eval_getname(funcnp, ex, events, np->u.expr.left, globals, croot, arrowp, try, &duped); if (nodep->u.name.last->u.name.cp != NULL) { cp = nodep->u.name.last->u.name.cp; } else { path = ipath2str(NULL, ipath(nodep)); cp = config_lookup(croot, path, 0); FREE((void *)path); } if (cp == NULL) { if (funcname == L_confprop) { out(O_ALTFP|O_VERB3, "%s: path ", funcname); ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, nodep); out(O_ALTFP|O_VERB3, " not found"); valuep->v = (uintptr_t)stable(""); valuep->t = STRING; if (duped) tree_free(nodep); return (1); } else { valuep->v = 0; valuep->t = UINT64; if (duped) tree_free(nodep); return (1); } } s = config_getprop(cp, np->u.expr.right->u.quote.s); if (s == NULL && strcmp(np->u.expr.right->u.quote.s, "class-code") == 0) s = config_getprop(cp, "CLASS-CODE"); if (s == NULL) { if (funcname == L_confprop) { out(O_ALTFP|O_VERB3|O_NONL, "%s: \"%s\" not found for path ", funcname, np->u.expr.right->u.quote.s); ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, nodep); valuep->v = (uintptr_t)stable(""); valuep->t = STRING; if (duped) tree_free(nodep); return (1); } else { valuep->v = 0; valuep->t = UINT64; if (duped) tree_free(nodep); return (1); } } if (funcname == L_confprop) { valuep->v = (uintptr_t)stable(s); valuep->t = STRING; out(O_ALTFP|O_VERB3|O_NONL, " %s(\"", funcname); ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, nodep); out(O_ALTFP|O_VERB3|O_NONL, "\", \"%s\") = \"%s\" ", np->u.expr.right->u.quote.s, (char *)(uintptr_t)valuep->v); } else { valuep->v = 1; valuep->t = UINT64; } if (duped) tree_free(nodep); return (1); } else if (funcname == L_is_connected) { const char *connstrings[] = { "connected", "CONNECTED", NULL }; struct config *cp[2]; const char *matchthis[2], *s; char *nameslist, *w; int i, j; lhs = eval_getname(funcnp, ex, events, np->u.expr.left, globals, croot, arrowp, try, &duped_lhs); rhs = eval_getname(funcnp, ex, events, np->u.expr.right, globals, croot, arrowp, try, &duped_rhs); path = ipath2str(NULL, ipath(lhs)); matchthis[1] = stable(path); if (lhs->u.name.last->u.name.cp != NULL) cp[0] = lhs->u.name.last->u.name.cp; else cp[0] = config_lookup(croot, path, 0); FREE((void *)path); path = ipath2str(NULL, ipath(rhs)); matchthis[0] = stable(path); if (rhs->u.name.last->u.name.cp != NULL) cp[1] = rhs->u.name.last->u.name.cp; else cp[1] = config_lookup(croot, path, 0); FREE((void *)path); if (duped_lhs) tree_free(lhs); if (duped_rhs) tree_free(rhs); valuep->t = UINT64; valuep->v = 0; if (cp[0] == NULL || cp[1] == NULL) return (1); /* to thine self always be connected */ if (cp[0] == cp[1]) { valuep->v = 1; return (1); } /* * Extract "connected" property from each cp. Search this * property for the name associated with the other cp[]. */ for (i = 0; i < 2 && valuep->v == 0; i++) { for (j = 0; connstrings[j] != NULL && valuep->v == 0; j++) { s = config_getprop(cp[i], stable(connstrings[j])); if (s != NULL) { nameslist = STRDUP(s); w = strtok(nameslist, " ,"); while (w != NULL) { if (stable(w) == matchthis[i]) { valuep->v = 1; break; } w = strtok(NULL, " ,"); } FREE(nameslist); } } } return (1); } else if (funcname == L_is_type) { const char *typestrings[] = { "type", "TYPE", NULL }; const char *s; int i; nodep = eval_getname(funcnp, ex, events, np, globals, croot, arrowp, try, &duped); if (nodep->u.name.last->u.name.cp != NULL) { cp = nodep->u.name.last->u.name.cp; } else { path = ipath2str(NULL, ipath(nodep)); cp = config_lookup(croot, path, 0); FREE((void *)path); } if (duped) tree_free(nodep); valuep->t = STRING; valuep->v = (uintptr_t)stable(""); if (cp == NULL) return (1); for (i = 0; typestrings[i] != NULL; i++) { s = config_getprop(cp, stable(typestrings[i])); if (s != NULL) { valuep->v = (uintptr_t)stable(s); break; } } return (1); } else if (funcname == L_is_on) { const char *onstrings[] = { "on", "ON", NULL }; const char *truestrings[] = { "yes", "YES", "y", "Y", "true", "TRUE", "t", "T", "1", NULL }; const char *s; int i, j; nodep = eval_getname(funcnp, ex, events, np, globals, croot, arrowp, try, &duped); if (nodep->u.name.last->u.name.cp != NULL) { cp = nodep->u.name.last->u.name.cp; } else { path = ipath2str(NULL, ipath(nodep)); cp = config_lookup(croot, path, 0); FREE((void *)path); } if (duped) tree_free(nodep); valuep->t = UINT64; valuep->v = 0; if (cp == NULL) return (1); for (i = 0; onstrings[i] != NULL; i++) { s = config_getprop(cp, stable(onstrings[i])); if (s != NULL) { s = stable(s); for (j = 0; truestrings[j] != NULL; j++) { if (s == stable(truestrings[j])) { valuep->v = 1; return (1); } } } } return (1); } else if (funcname == L_is_present) { nodep = eval_getname(funcnp, ex, events, np, globals, croot, arrowp, try, &duped); if (nodep->u.name.last->u.name.cp != NULL) { cp = nodep->u.name.last->u.name.cp; } else { path = ipath2str(NULL, ipath(nodep)); cp = config_lookup(croot, path, 0); FREE((void *)path); } if (duped) tree_free(nodep); valuep->t = UINT64; valuep->v = 0; if (cp != NULL) valuep->v = 1; return (1); } else if (funcname == L_count) { struct stats *statp; struct istat_entry ent; ASSERTinfo(np->t == T_EVENT, ptree_nodetype2str(np->t)); nodep = np->u.event.epname; if (try) { if (eval_expr(nodep, ex, events, globals, croot, arrowp, try, &val) && val.t == NODEPTR) nodep = (struct node *)(uintptr_t)val.v; else { duped = 1; nodep = eval_dup(nodep, ex, events); } } ent.ename = np->u.event.ename->u.name.s; ent.ipath = ipath(nodep); valuep->t = UINT64; if ((statp = (struct stats *) lut_lookup(Istats, &ent, (lut_cmp)istat_cmp)) == NULL) valuep->v = 0; else valuep->v = stats_counter_value(statp); if (duped) tree_free(nodep); return (1); } else if (funcname == L_envprop) { outfl(O_DIE, np->file, np->line, "eval_func: %s not yet supported", funcname); } if (try) return (0); if (funcname == L_fru) { valuep->t = NODEPTR; valuep->v = (uintptr_t)eval_fru(np); return (1); } else if (funcname == L_asru) { valuep->t = NODEPTR; valuep->v = (uintptr_t)eval_asru(np); return (1); } else if (funcname == L_defined) { ASSERTeq(np->t, T_GLOBID, ptree_nodetype2str); valuep->t = UINT64; valuep->v = (lut_lookup(*globals, (void *)np->u.globid.s, NULL) != NULL); return (1); } else if (funcname == L_call) { return (! platform_call(np, globals, croot, arrowp, valuep)); } else if (funcname == L_payloadprop) { outfl(O_ALTFP|O_VERB2|O_NONL, np->file, np->line, "payloadprop(\"%s\") ", np->u.quote.s); if (platform_payloadprop(np, valuep)) { /* platform_payloadprop() returned false */ out(O_ALTFP|O_VERB, "payloadprop \"%s\" not found.", np->u.quote.s); return (0); } else { switch (valuep->t) { case UINT64: case NODEPTR: out(O_ALTFP|O_VERB2, "found: %llu", valuep->v); break; case STRING: out(O_ALTFP|O_VERB2, "found: \"%s\"", (char *)(uintptr_t)valuep->v); break; default: out(O_ALTFP|O_VERB2, "found: undefined"); break; } return (1); } } else if (funcname == L_setpayloadprop) { struct evalue *payloadvalp; ASSERTinfo(np->t == T_LIST, ptree_nodetype2str(np->t)); ASSERTinfo(np->u.expr.left->t == T_QUOTE, ptree_nodetype2str(np->u.expr.left->t)); outfl(O_ALTFP|O_VERB2|O_NONL, np->file, np->line, "setpayloadprop: %s: %s=", arrowp->tail->myevent->enode->u.event.ename->u.name.s, np->u.expr.left->u.quote.s); ptree_name_iter(O_ALTFP|O_VERB2|O_NONL, np->u.expr.right); /* * allocate a struct evalue to hold the payload property's * value, unless we've been here already, in which case we * might calculate a different value, but we'll store it * in the already-allocated struct evalue. */ if ((payloadvalp = (struct evalue *)lut_lookup( arrowp->tail->myevent->payloadprops, (void *)np->u.expr.left->u.quote.s, NULL)) == NULL) { payloadvalp = MALLOC(sizeof (*payloadvalp)); } if (!eval_expr(np->u.expr.right, ex, events, globals, croot, arrowp, try, payloadvalp)) { out(O_ALTFP|O_VERB2, " (cannot eval, using zero)"); payloadvalp->t = UINT64; payloadvalp->v = 0; } else { if (payloadvalp->t == UINT64) out(O_ALTFP|O_VERB2, " (%llu)", payloadvalp->v); else out(O_ALTFP|O_VERB2, " (\"%s\")", (char *)(uintptr_t)payloadvalp->v); } /* add to table of payload properties for current problem */ arrowp->tail->myevent->payloadprops = lut_add(arrowp->tail->myevent->payloadprops, (void *)np->u.expr.left->u.quote.s, (void *)payloadvalp, NULL); /* function is always true */ valuep->t = UINT64; valuep->v = 1; return (1); } else if (funcname == L_payloadprop_defined) { outfl(O_ALTFP|O_VERB2|O_NONL, np->file, np->line, "payloadprop_defined(\"%s\") ", np->u.quote.s); if (platform_payloadprop(np, NULL)) { /* platform_payloadprop() returned false */ valuep->v = 0; out(O_ALTFP|O_VERB2, "payloadprop_defined: \"%s\" " "not defined.", np->u.quote.s); } else { valuep->v = 1; out(O_ALTFP|O_VERB2, "found."); } valuep->t = UINT64; return (1); } else if (funcname == L_payloadprop_contains) { int nvals; struct evalue *vals; struct evalue cmpval; ASSERTinfo(np->t == T_LIST, ptree_nodetype2str(np->t)); ASSERTinfo(np->u.expr.left->t == T_QUOTE, ptree_nodetype2str(np->u.expr.left->t)); outfl(O_ALTFP|O_VERB2|O_NONL, np->file, np->line, "payloadprop_contains(\"%s\", ", np->u.expr.left->u.quote.s); ptree_name_iter(O_ALTFP|O_VERB2|O_NONL, np->u.expr.right); out(O_ALTFP|O_VERB2|O_NONL, ") "); /* evaluate the expression we're comparing against */ if (!eval_expr(np->u.expr.right, ex, events, globals, croot, arrowp, try, &cmpval)) { out(O_ALTFP|O_VERB2|O_NONL, "(cannot eval, using zero) "); cmpval.t = UINT64; cmpval.v = 0; } else { switch (cmpval.t) { case UNDEFINED: out(O_ALTFP|O_VERB2, "(undefined type)"); break; case UINT64: out(O_ALTFP|O_VERB2, "(%llu) ", cmpval.v); break; case STRING: out(O_ALTFP|O_VERB2, "(\"%s\") ", (char *)(uintptr_t)cmpval.v); break; case NODEPTR: out(O_ALTFP|O_VERB2|O_NONL, "("); ptree_name_iter(O_ALTFP|O_VERB2|O_NONL, (struct node *)(uintptr_t)(cmpval.v)); out(O_ALTFP|O_VERB2, ") "); break; } } /* get the payload values and check for a match */ vals = platform_payloadprop_values(np->u.expr.left->u.quote.s, &nvals); valuep->t = UINT64; valuep->v = 0; if (nvals == 0) { out(O_ALTFP|O_VERB2, "not found."); return (0); } else { struct evalue preval; int i; out(O_ALTFP|O_VERB2|O_NONL, "found %d values ", nvals); for (i = 0; i < nvals; i++) { preval.t = vals[i].t; preval.v = vals[i].v; if (check_expr_args(&vals[i], &cmpval, UNDEFINED, np)) continue; /* * If we auto-converted the value to a * string, we need to free the * original tree value. */ if (preval.t == NODEPTR && ((struct node *)(uintptr_t)(preval.v))->t == T_NAME) { tree_free((struct node *)(uintptr_t) preval.v); } if (vals[i].v == cmpval.v) { valuep->v = 1; break; } } if (valuep->v) out(O_ALTFP|O_VERB2, "match."); else out(O_ALTFP|O_VERB2, "no match."); for (i = 0; i < nvals; i++) { if (vals[i].t == NODEPTR) { tree_free((struct node *)(uintptr_t) vals[i].v); break; } } FREE(vals); } return (1); } else if (funcname == L_confcall) { return (!platform_confcall(np, globals, croot, arrowp, valuep)); } else outfl(O_DIE, np->file, np->line, "eval_func: unexpected func: %s", funcname); /*NOTREACHED*/ return (0); } /* * defines for u.expr.temp - these are used for T_OR and T_AND so that if * we worked out that part of the expression was true or false during an * earlier eval_expr, then we don't need to dup that part. */ #define EXPR_TEMP_BOTH_UNK 0 #define EXPR_TEMP_LHS_UNK 1 #define EXPR_TEMP_RHS_UNK 2 static struct node * eval_dup(struct node *np, struct lut *ex, struct node *events[]) { struct node *newnp; if (np == NULL) return (NULL); switch (np->t) { case T_GLOBID: return (tree_globid(np->u.globid.s, np->file, np->line)); case T_ASSIGN: case T_CONDIF: case T_CONDELSE: case T_NE: case T_EQ: case T_LT: case T_LE: case T_GT: case T_GE: case T_BITAND: case T_BITOR: case T_BITXOR: case T_BITNOT: case T_LSHIFT: case T_RSHIFT: case T_NOT: case T_ADD: case T_SUB: case T_MUL: case T_DIV: case T_MOD: return (tree_expr(np->t, eval_dup(np->u.expr.left, ex, events), eval_dup(np->u.expr.right, ex, events))); case T_LIST: case T_AND: switch (np->u.expr.temp) { case EXPR_TEMP_LHS_UNK: return (eval_dup(np->u.expr.left, ex, events)); case EXPR_TEMP_RHS_UNK: return (eval_dup(np->u.expr.right, ex, events)); default: return (tree_expr(np->t, eval_dup(np->u.expr.left, ex, events), eval_dup(np->u.expr.right, ex, events))); } case T_OR: switch (np->u.expr.temp) { case EXPR_TEMP_LHS_UNK: return (eval_dup(np->u.expr.left, ex, events)); case EXPR_TEMP_RHS_UNK: return (eval_dup(np->u.expr.right, ex, events)); default: return (tree_expr(T_OR, eval_dup(np->u.expr.left, ex, events), eval_dup(np->u.expr.right, ex, events))); } case T_NAME: { struct iterinfo *iterinfop; int got_matchf = 0; int got_matcht = 0; struct evalue value; struct node *np1f, *np2f, *np1t, *np2t, *retp = NULL; struct node *npstart, *npcont, *npend, *npref, *newnp, *nprest; /* * Check if we already have a match of the nonwildcarded path * in oldepname (check both to and from events). */ for (np1f = np, np2f = events[0]->u.event.oldepname; np1f != NULL && np2f != NULL; np1f = np1f->u.name.next, np2f = np2f->u.name.next) { if (strcmp(np1f->u.name.s, np2f->u.name.s) != 0) break; if (np1f->u.name.child->t != np2f->u.name.child->t) break; if (np1f->u.name.child->t == T_NUM && np1f->u.name.child->u.ull != np2f->u.name.child->u.ull) break; if (np1f->u.name.child->t == T_NAME && strcmp(np1f->u.name.child->u.name.s, np2f->u.name.child->u.name.s) != 0) break; got_matchf++; } for (np1t = np, np2t = events[1]->u.event.oldepname; np1t != NULL && np2t != NULL; np1t = np1t->u.name.next, np2t = np2t->u.name.next) { if (strcmp(np1t->u.name.s, np2t->u.name.s) != 0) break; if (np1t->u.name.child->t != np2t->u.name.child->t) break; if (np1t->u.name.child->t == T_NUM && np1t->u.name.child->u.ull != np2t->u.name.child->u.ull) break; if (np1t->u.name.child->t == T_NAME && strcmp(np1t->u.name.child->u.name.s, np2t->u.name.child->u.name.s) != 0) break; got_matcht++; } nprest = np; if (got_matchf || got_matcht) { /* * so we are wildcarding. Copy ewname in full, plus * matching section of oldepname. Use whichever gives * the closest match. */ if (got_matchf > got_matcht) { npstart = events[0]->u.event.ewname; npcont = events[0]->u.event.oldepname; npend = np2f; nprest = np1f; } else { npstart = events[1]->u.event.ewname; npcont = events[1]->u.event.oldepname; npend = np2t; nprest = np1t; } for (npref = npstart; npref != NULL; npref = npref->u.name.next) { newnp = newnode(T_NAME, np->file, np->line); newnp->u.name.t = npref->u.name.t; newnp->u.name.s = npref->u.name.s; newnp->u.name.last = newnp; newnp->u.name.it = npref->u.name.it; newnp->u.name.cp = npref->u.name.cp; newnp->u.name.child = newnode(T_NUM, np->file, np->line); if (eval_expr(npref->u.name.child, ex, events, NULL, NULL, NULL, 1, &value) == 0 || value.t != UINT64) { outfl(O_DIE, np->file, np->line, "eval_dup: could not resolve " "iterator of %s", np->u.name.s); } newnp->u.name.child->u.ull = value.v; if (retp == NULL) { retp = newnp; } else { retp->u.name.last->u.name.next = newnp; retp->u.name.last = newnp; } } for (npref = npcont; npref != NULL && npref != npend; npref = npref->u.name.next) { newnp = newnode(T_NAME, np->file, np->line); newnp->u.name.t = npref->u.name.t; newnp->u.name.s = npref->u.name.s; newnp->u.name.last = newnp; newnp->u.name.it = npref->u.name.it; newnp->u.name.cp = npref->u.name.cp; newnp->u.name.child = newnode(T_NUM, np->file, np->line); if (eval_expr(npref->u.name.child, ex, events, NULL, NULL, NULL, 1, &value) == 0 || value.t != UINT64) { outfl(O_DIE, np->file, np->line, "eval_dup: could not resolve " "iterator of %s", np->u.name.s); } newnp->u.name.child->u.ull = value.v; if (retp == NULL) { retp = newnp; } else { retp->u.name.last->u.name.next = newnp; retp->u.name.last = newnp; } } } else { /* * not wildcarding - check if explicit iterator */ iterinfop = lut_lookup(ex, (void *)np->u.name.s, NULL); if (iterinfop != NULL) { /* explicit iterator; not part of pathname */ newnp = newnode(T_NUM, np->file, np->line); newnp->u.ull = iterinfop->num; return (newnp); } } /* * finally, whether wildcarding or not, we need to copy the * remaining part of the path (if any). This must be defined * absolutely (no more expansion/wildcarding). */ for (npref = nprest; npref != NULL; npref = npref->u.name.next) { newnp = newnode(T_NAME, np->file, np->line); newnp->u.name.t = npref->u.name.t; newnp->u.name.s = npref->u.name.s; newnp->u.name.last = newnp; newnp->u.name.it = npref->u.name.it; newnp->u.name.cp = npref->u.name.cp; newnp->u.name.child = newnode(T_NUM, np->file, np->line); if (eval_expr(npref->u.name.child, ex, events, NULL, NULL, NULL, 1, &value) == 0 || value.t != UINT64) { outfl(O_DIE, np->file, np->line, "eval_dup: could not resolve " "iterator of %s", np->u.name.s); } newnp->u.name.child->u.ull = value.v; if (retp == NULL) { retp = newnp; } else { retp->u.name.last->u.name.next = newnp; retp->u.name.last = newnp; } } return (retp); } case T_EVENT: newnp = newnode(T_NAME, np->file, np->line); newnp->u.name.t = np->u.event.ename->u.name.t; newnp->u.name.s = np->u.event.ename->u.name.s; newnp->u.name.it = np->u.event.ename->u.name.it; newnp->u.name.last = newnp; return (tree_event(newnp, eval_dup(np->u.event.epname, ex, events), eval_dup(np->u.event.eexprlist, ex, events))); case T_FUNC: return (tree_func(np->u.func.s, eval_dup(np->u.func.arglist, ex, events), np->file, np->line)); case T_QUOTE: newnp = newnode(T_QUOTE, np->file, np->line); newnp->u.quote.s = np->u.quote.s; return (newnp); case T_NUM: newnp = newnode(T_NUM, np->file, np->line); newnp->u.ull = np->u.ull; return (newnp); default: outfl(O_DIE, np->file, np->line, "eval_dup: unexpected node type: %s", ptree_nodetype2str(np->t)); } /*NOTREACHED*/ return (0); } /* * eval_potential -- see if constraint is potentially true * * this function is used at instance tree creation time to see if * any constraints are already known to be false. if this function * returns false, then the constraint will always be false and there's * no need to include the propagation arrow in the instance tree. * * if this routine returns true, either the constraint is known to * be always true (so there's no point in attaching the constraint * to the propagation arrow in the instance tree), or the constraint * contains "deferred" expressions like global variables or poller calls * and so it must be evaluated during calls to fme_eval(). in this last * case, where a constraint needs to be attached to the propagation arrow * in the instance tree, this routine returns a newly created constraint * in *newc where all the non-deferred things have been filled in. * * so in summary: * * return of false: constraint can never be true, *newc will be NULL. * * return of true with *newc unchanged: constraint will always be true. * * return of true with *newc changed: use new constraint in *newc. * * the lookup table for all explicit iterators, ex, is passed in. * * *newc can either be NULL on entry, or if can contain constraints from * previous calls to eval_potential() (i.e. for building up an instance * tree constraint from several potential constraints). if *newc already * contains constraints, anything added to it will be joined by adding * a T_AND node at the top of *newc. */ int eval_potential(struct node *np, struct lut *ex, struct node *events[], struct node **newc, struct config *croot) { struct node *newnp; struct evalue value; if (eval_expr(np, ex, events, NULL, croot, NULL, 1, &value) == 0) { /* * couldn't eval expression because * it contains deferred items. make * a duplicate expression with all the * non-deferred items expanded. */ newnp = eval_dup(np, ex, events); if (*newc == NULL) { /* * constraint is potentially true if deferred * expression in newnp is true. *newc was NULL * so new constraint is just the one in newnp. */ *newc = newnp; return (1); } else { /* * constraint is potentially true if deferred * expression in newnp is true. *newc already * contained a constraint so add an AND with the * constraint in newnp. */ *newc = tree_expr(T_AND, *newc, newnp); return (1); } } else if (value.t == UNDEFINED) { /* constraint can never be true */ return (0); } else if (value.t == UINT64 && value.v == 0) { /* constraint can never be true */ return (0); } else { /* constraint is always true (nothing deferred to eval) */ return (1); } } static int check_expr_args(struct evalue *lp, struct evalue *rp, enum datatype dtype, struct node *np) { /* auto-convert T_NAMES to strings */ if (lp->t == NODEPTR && ((struct node *)(uintptr_t)(lp->v))->t == T_NAME) { char *s = ipath2str(NULL, ipath((struct node *)(uintptr_t)lp->v)); lp->t = STRING; lp->v = (uintptr_t)stable(s); FREE(s); out(O_ALTFP|O_VERB2, "convert lhs path to \"%s\"", (char *)(uintptr_t)lp->v); } if (rp != NULL && rp->t == NODEPTR && ((struct node *)(uintptr_t)(rp->v))->t == T_NAME) { char *s = ipath2str(NULL, ipath((struct node *)(uintptr_t)rp->v)); rp->t = STRING; rp->v = (uintptr_t)stable(s); FREE(s); out(O_ALTFP|O_VERB2, "convert rhs path to \"%s\"", (char *)(uintptr_t)rp->v); } /* auto-convert strings to numbers */ if (dtype == UINT64) { if (lp->t == STRING) { lp->t = UINT64; lp->v = strtoull((char *)(uintptr_t)lp->v, NULL, 0); } if (rp != NULL && rp->t == STRING) { rp->t = UINT64; rp->v = strtoull((char *)(uintptr_t)rp->v, NULL, 0); } } if (dtype != UNDEFINED && lp->t != dtype) { outfl(O_DIE, np->file, np->line, "invalid datatype of argument for operation %s", ptree_nodetype2str(np->t)); /* NOTREACHED */ return (1); } if (rp != NULL && lp->t != rp->t) { outfl(O_DIE, np->file, np->line, "mismatch in datatype of arguments for operation %s", ptree_nodetype2str(np->t)); /* NOTREACHED */ return (1); } return (0); } /* * eval_expr -- evaluate expression into *valuep * * the meaning of the return value depends on the input value of try. * * for try == 1: if any deferred items are encounted, bail out and return * false. returns true if we made it through entire expression without * hitting any deferred items. * * for try == 0: return true if all operations were performed successfully. * return false if otherwise. for example, any of the following conditions * will result in a false return value: * - attempted use of an uninitialized global variable * - failure in function evaluation * - illegal arithmetic operation (argument out of range) */ int eval_expr(struct node *np, struct lut *ex, struct node *events[], struct lut **globals, struct config *croot, struct arrow *arrowp, int try, struct evalue *valuep) { struct evalue *gval; struct evalue lval; struct evalue rval; if (np == NULL) { valuep->t = UINT64; valuep->v = 1; /* no constraint means "true" */ return (1); } valuep->t = UNDEFINED; switch (np->t) { case T_GLOBID: if (try) return (0); /* * only handle case of getting (and not setting) the value * of a global variable */ gval = lut_lookup(*globals, (void *)np->u.globid.s, NULL); if (gval == NULL) { return (0); } else { valuep->t = gval->t; valuep->v = gval->v; return (1); } case T_ASSIGN: if (try) return (0); /* * first evaluate rhs, then try to store value in lhs which * should be a global variable */ if (!eval_expr(np->u.expr.right, ex, events, globals, croot, arrowp, try, &rval)) return (0); ASSERT(np->u.expr.left->t == T_GLOBID); gval = lut_lookup(*globals, (void *)np->u.expr.left->u.globid.s, NULL); if (gval == NULL) { gval = MALLOC(sizeof (*gval)); *globals = lut_add(*globals, (void *) np->u.expr.left->u.globid.s, gval, NULL); } gval->t = rval.t; gval->v = rval.v; if (gval->t == UINT64) { out(O_ALTFP|O_VERB2, "assign $%s=%llu", np->u.expr.left->u.globid.s, gval->v); } else { out(O_ALTFP|O_VERB2, "assign $%s=\"%s\"", np->u.expr.left->u.globid.s, (char *)(uintptr_t)gval->v); } /* * but always return true -- an assignment should not * cause a constraint to be false. */ valuep->t = UINT64; valuep->v = 1; return (1); case T_EQ: #define IMPLICIT_ASSIGN_IN_EQ #ifdef IMPLICIT_ASSIGN_IN_EQ /* * if lhs is an uninitialized global variable, perform * an assignment. * * one insidious side effect of implicit assignment is * that the "==" operator does not return a Boolean if * implicit assignment was performed. */ if (try == 0 && np->u.expr.left->t == T_GLOBID && (gval = lut_lookup(*globals, (void *)np->u.expr.left->u.globid.s, NULL)) == NULL) { if (!eval_expr(np->u.expr.right, ex, events, globals, croot, arrowp, try, &rval)) return (0); gval = MALLOC(sizeof (*gval)); *globals = lut_add(*globals, (void *) np->u.expr.left->u.globid.s, gval, NULL); gval->t = rval.t; gval->v = rval.v; valuep->t = rval.t; valuep->v = rval.v; return (1); } #endif /* IMPLICIT_ASSIGN_IN_EQ */ if (!eval_expr(np->u.expr.left, ex, events, globals, croot, arrowp, try, &lval)) return (0); if (!eval_expr(np->u.expr.right, ex, events, globals, croot, arrowp, try, &rval)) return (0); if (rval.t == UINT64 || lval.t == UINT64) { if (check_expr_args(&lval, &rval, UINT64, np)) return (0); } else { if (check_expr_args(&lval, &rval, UNDEFINED, np)) return (0); } valuep->t = UINT64; valuep->v = (lval.v == rval.v); return (1); case T_LT: if (!eval_expr(np->u.expr.left, ex, events, globals, croot, arrowp, try, &lval)) return (0); if (!eval_expr(np->u.expr.right, ex, events, globals, croot, arrowp, try, &rval)) return (0); if (check_expr_args(&lval, &rval, UINT64, np)) return (0); valuep->t = UINT64; valuep->v = (lval.v < rval.v); return (1); case T_LE: if (!eval_expr(np->u.expr.left, ex, events, globals, croot, arrowp, try, &lval)) return (0); if (!eval_expr(np->u.expr.right, ex, events, globals, croot, arrowp, try, &rval)) return (0); if (check_expr_args(&lval, &rval, UINT64, np)) return (0); valuep->t = UINT64; valuep->v = (lval.v <= rval.v); return (1); case T_GT: if (!eval_expr(np->u.expr.left, ex, events, globals, croot, arrowp, try, &lval)) return (0); if (!eval_expr(np->u.expr.right, ex, events, globals, croot, arrowp, try, &rval)) return (0); if (check_expr_args(&lval, &rval, UINT64, np)) return (0); valuep->t = UINT64; valuep->v = (lval.v > rval.v); return (1); case T_GE: if (!eval_expr(np->u.expr.left, ex, events, globals, croot, arrowp, try, &lval)) return (0); if (!eval_expr(np->u.expr.right, ex, events, globals, croot, arrowp, try, &rval)) return (0); if (check_expr_args(&lval, &rval, UINT64, np)) return (0); valuep->t = UINT64; valuep->v = (lval.v >= rval.v); return (1); case T_BITAND: if (!eval_expr(np->u.expr.left, ex, events, globals, croot, arrowp, try, &lval)) return (0); if (!eval_expr(np->u.expr.right, ex, events, globals, croot, arrowp, try, &rval)) return (0); if (check_expr_args(&lval, &rval, UINT64, np)) return (0); valuep->t = lval.t; valuep->v = (lval.v & rval.v); return (1); case T_BITOR: if (!eval_expr(np->u.expr.left, ex, events, globals, croot, arrowp, try, &lval)) return (0); if (!eval_expr(np->u.expr.right, ex, events, globals, croot, arrowp, try, &rval)) return (0); if (check_expr_args(&lval, &rval, UINT64, np)) return (0); valuep->t = lval.t; valuep->v = (lval.v | rval.v); return (1); case T_BITXOR: if (!eval_expr(np->u.expr.left, ex, events, globals, croot, arrowp, try, &lval)) return (0); if (!eval_expr(np->u.expr.right, ex, events, globals, croot, arrowp, try, &rval)) return (0); if (check_expr_args(&lval, &rval, UINT64, np)) return (0); valuep->t = lval.t; valuep->v = (lval.v ^ rval.v); return (1); case T_BITNOT: if (!eval_expr(np->u.expr.left, ex, events, globals, croot, arrowp, try, &lval)) return (0); ASSERT(np->u.expr.right == NULL); if (check_expr_args(&lval, NULL, UINT64, np)) return (0); valuep->t = UINT64; valuep->v = ~ lval.v; return (1); case T_LSHIFT: if (!eval_expr(np->u.expr.left, ex, events, globals, croot, arrowp, try, &lval)) return (0); if (!eval_expr(np->u.expr.right, ex, events, globals, croot, arrowp, try, &rval)) return (0); if (check_expr_args(&lval, &rval, UINT64, np)) return (0); valuep->t = UINT64; valuep->v = (lval.v << rval.v); return (1); case T_RSHIFT: if (!eval_expr(np->u.expr.left, ex, events, globals, croot, arrowp, try, &lval)) return (0); if (!eval_expr(np->u.expr.right, ex, events, globals, croot, arrowp, try, &rval)) return (0); if (check_expr_args(&lval, &rval, UINT64, np)) return (0); valuep->t = UINT64; valuep->v = (lval.v >> rval.v); return (1); case T_CONDIF: { struct node *retnp; int dotrue = 0; /* * evaluate * expression ? stmtA [ : stmtB ] * * first see if expression is true or false, then determine * if stmtA (or stmtB, if it exists) should be evaluated. * * "dotrue = 1" means stmtA should be evaluated. */ if (eval_expr(np->u.expr.left, ex, events, globals, croot, arrowp, try, &lval) && lval.t != UNDEFINED && lval.v != 0) dotrue = 1; ASSERT(np->u.expr.right != NULL); if (np->u.expr.right->t == T_CONDELSE) { if (dotrue) retnp = np->u.expr.right->u.expr.left; else retnp = np->u.expr.right->u.expr.right; } else { /* no ELSE clause */ if (dotrue) retnp = np->u.expr.right; else { outfl(O_DIE, np->file, np->line, "eval_expr: missing condelse"); } } if (!eval_expr(retnp, ex, events, globals, croot, arrowp, try, valuep)) return (0); return (1); } case T_CONDELSE: /* * shouldn't get here, since T_CONDELSE is supposed to be * evaluated as part of T_CONDIF */ out(O_ALTFP|O_DIE, "eval_expr: wrong context for operation %s", ptree_nodetype2str(np->t)); /*NOTREACHED*/ case T_NE: if (!eval_expr(np->u.expr.left, ex, events, globals, croot, arrowp, try, &lval)) return (0); if (!eval_expr(np->u.expr.right, ex, events, globals, croot, arrowp, try, &rval)) return (0); if (rval.t == UINT64 || lval.t == UINT64) { if (check_expr_args(&lval, &rval, UINT64, np)) return (0); } else { if (check_expr_args(&lval, &rval, UNDEFINED, np)) return (0); } valuep->t = UINT64; valuep->v = (lval.v != rval.v); return (1); case T_LIST: case T_AND: if (!eval_expr(np->u.expr.left, ex, events, globals, croot, arrowp, try, valuep)) { /* * if lhs is unknown, still check rhs. If that * is false we can return false irrespectice of lhs */ if (!eval_expr(np->u.expr.right, ex, events, globals, croot, arrowp, try, valuep)) { np->u.expr.temp = EXPR_TEMP_BOTH_UNK; return (0); } if (valuep->v != 0) { np->u.expr.temp = EXPR_TEMP_LHS_UNK; return (0); } } if (valuep->v == 0) { valuep->t = UINT64; return (1); } if (!eval_expr(np->u.expr.right, ex, events, globals, croot, arrowp, try, valuep)) { np->u.expr.temp = EXPR_TEMP_RHS_UNK; return (0); } valuep->t = UINT64; valuep->v = valuep->v == 0 ? 0 : 1; return (1); case T_OR: if (!eval_expr(np->u.expr.left, ex, events, globals, croot, arrowp, try, valuep)) { /* * if lhs is unknown, still check rhs. If that * is true we can return true irrespectice of lhs */ if (!eval_expr(np->u.expr.right, ex, events, globals, croot, arrowp, try, valuep)) { np->u.expr.temp = EXPR_TEMP_BOTH_UNK; return (0); } if (valuep->v == 0) { np->u.expr.temp = EXPR_TEMP_LHS_UNK; return (0); } } if (valuep->v != 0) { valuep->t = UINT64; valuep->v = 1; return (1); } if (!eval_expr(np->u.expr.right, ex, events, globals, croot, arrowp, try, valuep)) { np->u.expr.temp = EXPR_TEMP_RHS_UNK; return (0); } valuep->t = UINT64; valuep->v = valuep->v == 0 ? 0 : 1; return (1); case T_NOT: if (!eval_expr(np->u.expr.left, ex, events, globals, croot, arrowp, try, valuep)) return (0); valuep->t = UINT64; valuep->v = ! valuep->v; return (1); case T_ADD: if (!eval_expr(np->u.expr.left, ex, events, globals, croot, arrowp, try, &lval)) return (0); if (!eval_expr(np->u.expr.right, ex, events, globals, croot, arrowp, try, &rval)) return (0); if (check_expr_args(&lval, &rval, UINT64, np)) return (0); valuep->t = lval.t; valuep->v = lval.v + rval.v; return (1); case T_SUB: if (!eval_expr(np->u.expr.left, ex, events, globals, croot, arrowp, try, &lval)) return (0); if (!eval_expr(np->u.expr.right, ex, events, globals, croot, arrowp, try, &rval)) return (0); if (check_expr_args(&lval, &rval, UINT64, np)) return (0); /* since valuep is unsigned, return false if lval.v < rval.v */ if (lval.v < rval.v) { outfl(O_DIE, np->file, np->line, "eval_expr: T_SUB result is out of range"); } valuep->t = lval.t; valuep->v = lval.v - rval.v; return (1); case T_MUL: if (!eval_expr(np->u.expr.left, ex, events, globals, croot, arrowp, try, &lval)) return (0); if (!eval_expr(np->u.expr.right, ex, events, globals, croot, arrowp, try, &rval)) return (0); if (check_expr_args(&lval, &rval, UINT64, np)) return (0); valuep->t = lval.t; valuep->v = lval.v * rval.v; return (1); case T_DIV: if (!eval_expr(np->u.expr.left, ex, events, globals, croot, arrowp, try, &lval)) return (0); if (!eval_expr(np->u.expr.right, ex, events, globals, croot, arrowp, try, &rval)) return (0); if (check_expr_args(&lval, &rval, UINT64, np)) return (0); /* return false if dividing by zero */ if (rval.v == 0) { outfl(O_DIE, np->file, np->line, "eval_expr: T_DIV division by zero"); } valuep->t = lval.t; valuep->v = lval.v / rval.v; return (1); case T_MOD: if (!eval_expr(np->u.expr.left, ex, events, globals, croot, arrowp, try, &lval)) return (0); if (!eval_expr(np->u.expr.right, ex, events, globals, croot, arrowp, try, &rval)) return (0); if (check_expr_args(&lval, &rval, UINT64, np)) return (0); /* return false if dividing by zero */ if (rval.v == 0) { outfl(O_DIE, np->file, np->line, "eval_expr: T_MOD division by zero"); } valuep->t = lval.t; valuep->v = lval.v % rval.v; return (1); case T_NAME: if (try) { struct iterinfo *iterinfop; struct node *np1, *np2; int i, gotmatch = 0; /* * Check if we have an exact match of the nonwildcarded * path in oldepname - if so we can just use the * full wildcarded path in epname. */ for (i = 0; i < 1; i++) { for (np1 = np, np2 = events[i]->u.event.oldepname; np1 != NULL && np2 != NULL; np1 = np1->u.name.next, np2 = np2->u.name.next) { if (strcmp(np1->u.name.s, np2->u.name.s) != 0) break; if (np1->u.name.child->t != np2->u.name.child->t) break; if (np1->u.name.child->t == T_NUM && np1->u.name.child->u.ull != np2->u.name.child->u.ull) break; if (np1->u.name.child->t == T_NAME && strcmp(np1->u.name.child->u.name.s, np2->u.name.child->u.name.s) != 0) break; gotmatch++; } if (np1 == NULL && np2 == NULL) { valuep->t = NODEPTR; valuep->v = (uintptr_t) events[i]->u.event.epname; return (1); } } if (!gotmatch) { /* * we're not wildcarding. However at * itree_create() time, we can also expand * simple iterators - so check for those. */ iterinfop = lut_lookup(ex, (void *)np->u.name.s, NULL); if (iterinfop != NULL) { valuep->t = UINT64; valuep->v = (unsigned long long)iterinfop->num; return (1); } } /* * For anything else we'll have to wait for eval_dup(). */ return (0); } /* return address of struct node */ valuep->t = NODEPTR; valuep->v = (uintptr_t)np; return (1); case T_QUOTE: valuep->t = STRING; valuep->v = (uintptr_t)np->u.quote.s; return (1); case T_FUNC: return (eval_func(np, ex, events, np->u.func.arglist, globals, croot, arrowp, try, valuep)); case T_NUM: valuep->t = UINT64; valuep->v = np->u.ull; return (1); default: outfl(O_DIE, np->file, np->line, "eval_expr: unexpected node type: %s", ptree_nodetype2str(np->t)); } /*NOTREACHED*/ return (0); } /* * eval_fru() and eval_asru() don't do much, but are called from a number * of places. */ static struct node * eval_fru(struct node *np) { ASSERT(np->t == T_NAME); return (np); } static struct node * eval_asru(struct node *np) { ASSERT(np->t == T_NAME); return (np); }