/*
* 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 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* eval.c -- constraint evaluation module
*
* this module evaluates constraints.
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#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);
extern fmd_hdl_t *Hdl; /* handle from eft.c */
/*
* 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->t == T_FUNC && np->u.func.s == L_fru)
nodep = eval_fru(np->u.func.arglist);
else if (np->t == T_FUNC && np->u.func.s == L_asru)
nodep = eval_asru(np->u.func.arglist);
else if (np->t == T_FUNC) {
if (eval_expr(np, ex, events, globals, croot, arrowp, try,
&val) == 0)
/*
* Can't evaluate yet. Return null so constraint is
* deferred.
*/
return (NULL);
if (val.t == NODEPTR)
return ((struct node *)(uintptr_t)val.v);
else
/*
* just return the T_FUNC - which the caller will
* reject.
*/
return (np);
} 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);
}
/*ARGSUSED*/
static int
eval_cat(struct node *np, struct lut *ex, struct node *events[],
struct lut **globals, struct config *croot, struct arrow *arrowp,
int try, struct evalue *valuep)
{
if (np->t == T_LIST) {
struct evalue lval;
struct evalue rval;
int len;
char *s;
if (!eval_cat(np->u.expr.left, ex, events, globals, croot,
arrowp, try, &lval))
return (0);
if (!eval_cat(np->u.expr.right, ex, events, globals, croot,
arrowp, try, &rval))
return (0);
len = snprintf(NULL, 0, "%s%s", (char *)(uintptr_t)lval.v,
(char *)(uintptr_t)rval.v);
s = MALLOC(len + 1);
(void) snprintf(s, len + 1, "%s%s", (char *)(uintptr_t)lval.v,
(char *)(uintptr_t)rval.v);
outfl(O_ALTFP|O_VERB2, np->file, np->line,
"eval_cat: %s %s returns %s", (char *)(uintptr_t)lval.v,
(char *)(uintptr_t)rval.v, s);
valuep->t = STRING;
valuep->v = (uintptr_t)stable(s);
FREE(s);
} else {
if (!eval_expr(np, ex, events, globals, croot,
arrowp, try, valuep))
return (0);
if (check_expr_args(valuep, NULL, STRING, np))
return (0);
}
return (1);
}
/*
* 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);
if (!rhs || !lhs)
return (0);
if (rhs->t != T_NAME || lhs->t != T_NAME) {
valuep->t = UNDEFINED;
return (1);
}
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)
return (0);
if (nodep->t != T_NAME) {
valuep->t = UNDEFINED;
return (1);
}
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);
if (!rhs || !lhs)
return (0);
if (rhs->t != T_NAME || lhs->t != T_NAME) {
valuep->t = UNDEFINED;
return (1);
}
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)
return (0);
if (nodep->t != T_NAME) {
valuep->t = UNDEFINED;
return (1);
}
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)
return (0);
if (nodep->t != T_NAME) {
valuep->t = UNDEFINED;
return (1);
}
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)
return (0);
if (nodep->t != T_NAME) {
valuep->t = UNDEFINED;
return (1);
}
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_has_fault) {
nvlist_t *asru = NULL, *fru = NULL, *rsrc = NULL;
nodep = eval_getname(funcnp, ex, events, np->u.expr.left,
globals, croot, arrowp, try, &duped);
if (!nodep)
return (0);
if (nodep->t != T_NAME) {
valuep->t = UNDEFINED;
return (1);
}
path = ipath2str(NULL, ipath(nodep));
platform_units_translate(0, croot, &asru, &fru, &rsrc, path);
outfl(O_ALTFP|O_VERB2|O_NONL, np->file, np->line, "has_fault(");
ptree_name_iter(O_ALTFP|O_VERB2|O_NONL, nodep);
out(O_ALTFP|O_VERB2|O_NONL, "(%s), \"%s\") ", path,
np->u.expr.right->u.quote.s);
FREE((void *)path);
if (duped)
tree_free(nodep);
if (rsrc == NULL) {
valuep->v = 0;
out(O_ALTFP|O_VERB2, "no path");
} else {
valuep->v = fmd_nvl_fmri_has_fault(Hdl, rsrc,
FMD_HAS_FAULT_RESOURCE,
strcmp(np->u.expr.right->u.quote.s, "") == 0 ?
NULL : (char *)np->u.expr.right->u.quote.s);
out(O_ALTFP|O_VERB2, "returned %lld", valuep->v);
nvlist_free(rsrc);
}
valuep->t = UINT64;
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 (arrowp->head->myevent->count == 0) {
/*
* Haven't seen this ereport yet, so must defer
*/
out(O_ALTFP|O_VERB2, "ereport not yet seen - defer.");
return (0);
} else if (platform_payloadprop(np, valuep)) {
/* platform_payloadprop() returned false */
out(O_ALTFP|O_VERB, "not found.");
valuep->t = UNDEFINED;
return (1);
} else {
switch (valuep->t) {
case NODEPTR:
if (((struct node *)(uintptr_t)
(valuep->v))->t == T_NAME) {
char *s = ipath2str(NULL,
ipath((struct node *)
(uintptr_t)valuep->v));
out(O_ALTFP|O_VERB2,
"found: \"%s\"", s);
FREE(s);
} else
out(O_ALTFP|O_VERB2, "found: %llu",
valuep->v);
break;
case UINT64:
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;
int alloced = 0;
ASSERTinfo(np->t == T_LIST, ptree_nodetype2str(np->t));
ASSERTinfo(np->u.expr.left->t == T_QUOTE,
ptree_nodetype2str(np->u.expr.left->t));
if (!(arrowp->head->myevent->cached_state & REQMNTS_CREDIBLE))
return (0);
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));
alloced = 1;
}
if (!eval_expr(np->u.expr.right, ex, events, globals, croot,
arrowp, try, payloadvalp)) {
out(O_ALTFP|O_VERB2, " (cannot eval)");
if (alloced)
FREE(payloadvalp);
return (0);
} else {
if (payloadvalp->t == UNDEFINED) {
/* function is always true */
out(O_ALTFP|O_VERB2, " (undefined)");
valuep->t = UINT64;
valuep->v = 1;
return (1);
}
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_cat) {
int retval = eval_cat(np, ex, events, globals, croot,
arrowp, try, valuep);
outfl(O_ALTFP|O_VERB2, np->file, np->line,
"cat: returns %s", (char *)(uintptr_t)valuep->v);
return (retval);
} else if (funcname == L_setserdn || funcname == L_setserdt ||
funcname == L_setserdsuffix || funcname == L_setserdincrement) {
struct evalue *serdvalp;
int alloced = 0;
char *str;
struct event *flt = arrowp->tail->myevent;
if (!(arrowp->head->myevent->cached_state & REQMNTS_CREDIBLE))
return (0);
if (funcname == L_setserdn)
str = "n";
else if (funcname == L_setserdt)
str = "t";
else if (funcname == L_setserdsuffix)
str = "suffix";
else if (funcname == L_setserdincrement)
str = "increment";
/*
* allocate a struct evalue to hold the serd 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 ((serdvalp = (struct evalue *)lut_lookup(flt->serdprops,
(void *)str, (lut_cmp)strcmp)) == NULL) {
serdvalp = MALLOC(sizeof (*serdvalp));
alloced = 1;
}
if (!eval_expr(np, ex, events, globals, croot, arrowp, try,
serdvalp)) {
outfl(O_ALTFP|O_VERB2|O_NONL, np->file, np->line,
"setserd%s: %s: ", str,
flt->enode->u.event.ename->u.name.s);
ptree_name_iter(O_ALTFP|O_VERB2|O_NONL, np);
out(O_ALTFP|O_VERB2, " (cannot eval)");
if (alloced)
FREE(serdvalp);
return (0);
} else if (serdvalp->t == UNDEFINED) {
outfl(O_ALTFP|O_VERB2|O_NONL, np->file, np->line,
"setserd%s: %s: ", str,
flt->enode->u.event.ename->u.name.s);
ptree_name_iter(O_ALTFP|O_VERB2|O_NONL, np);
out(O_ALTFP|O_VERB2, " (undefined)");
} else {
outfl(O_ALTFP|O_VERB2|O_NONL, np->file, np->line,
"setserd%s: %s: ", str,
flt->enode->u.event.ename->u.name.s);
ptree_name_iter(O_ALTFP|O_VERB2|O_NONL, np);
if ((funcname == L_setserdincrement ||
funcname == L_setserdn) && serdvalp->t == STRING) {
serdvalp->t = UINT64;
serdvalp->v = strtoull((char *)
(uintptr_t)serdvalp->v, NULL, 0);
}
if (funcname == L_setserdt && serdvalp->t == UINT64) {
int len = snprintf(NULL, 0, "%lldns",
serdvalp->v);
char *buf = MALLOC(len + 1);
(void) snprintf(buf, len + 1, "%lldns",
serdvalp->v);
serdvalp->t = STRING;
serdvalp->v = (uintptr_t)stable(buf);
FREE(buf);
}
if (funcname == L_setserdsuffix &&
serdvalp->t == UINT64) {
int len = snprintf(NULL, 0, "%lld",
serdvalp->v);
char *buf = MALLOC(len + 1);
(void) snprintf(buf, len + 1, "%lld",
serdvalp->v);
serdvalp->t = STRING;
serdvalp->v = (uintptr_t)stable(buf);
FREE(buf);
}
if (serdvalp->t == UINT64)
out(O_ALTFP|O_VERB2, " (%llu)", serdvalp->v);
else
out(O_ALTFP|O_VERB2, " (\"%s\")",
(char *)(uintptr_t)serdvalp->v);
flt->serdprops = lut_add(flt->serdprops, (void *)str,
(void *)serdvalp, (lut_cmp)strcmp);
}
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 (arrowp->head->myevent->count == 0) {
/*
* Haven't seen this ereport yet, so must defer
*/
out(O_ALTFP|O_VERB2, "ereport not yet seen - defer.");
return (0);
} else if (platform_payloadprop(np, NULL)) {
/* platform_payloadprop() returned false */
valuep->v = 0;
out(O_ALTFP|O_VERB2, "not found.");
} 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) ");
return (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 (arrowp->head->myevent->count == 0) {
/*
* Haven't seen this ereport yet, so must defer
*/
out(O_ALTFP|O_VERB2, "ereport not yet seen - defer.");
return (0);
} else if (nvals == 0) {
out(O_ALTFP|O_VERB2, "not found.");
return (1);
} 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);
case T_TIMEVAL:
newnp = newnode(T_TIMEVAL, 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 numbers to strings */
if (dtype == STRING) {
if (lp->t == UINT64) {
int len = snprintf(NULL, 0, "%llx", lp->v);
char *s = MALLOC(len + 1);
(void) snprintf(s, len + 1, "%llx", lp->v);
lp->t = STRING;
lp->v = (uintptr_t)stable(s);
FREE(s);
}
if (rp != NULL && rp->t == UINT64) {
int len = snprintf(NULL, 0, "%llx", rp->v);
char *s = MALLOC(len + 1);
(void) snprintf(s, len + 1, "%llx", rp->v);
rp->t = STRING;
rp->v = (uintptr_t)stable(s);
FREE(s);
}
}
/* 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))
return (0);
if (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 irrespective of lhs
*/
if (!try) {
np->u.expr.temp = EXPR_TEMP_BOTH_UNK;
return (0);
}
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 irrespective of lhs
*/
if (!try) {
np->u.expr.temp = EXPR_TEMP_BOTH_UNK;
return (0);
}
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:
case T_TIMEVAL:
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);
}