/*
* 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
* 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"
/*
* begins_with -- return true if rhs path begins with everything in lhs path
*/
static int
{
int lnum;
int rnum;
return (1); /* yep -- it all matched */
return (0); /* nope, ran out of rhs first */
return (0); /* nope, different component names */
NULL);
else
} else {
}
NULL);
else
} else {
}
return (0); /* nope, instance numbers were different */
}
/*
* 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 *
{
&val) == 0)
/*
* Can't evaluate yet. Return null so constraint is
* deferred.
*/
return (NULL);
else
/*
* just return the T_FUNC - which the caller will
* reject.
*/
return (np);
} else
if (try) {
else {
*dupedp = 1;
}
}
return (nodep);
}
/*ARGSUSED*/
static int
{
int len;
char *s;
return (0);
return (0);
FREE(s);
} else {
return (0);
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
{
char *path;
/* within()'s are not really constraints -- always true */
valuep->v = 1;
return (1);
} else if (funcname == L_is_under) {
return (0);
return (1);
}
if (duped_lhs)
if (duped_rhs)
return (1);
const char *s;
/* for now s will point to a quote [see addconfigprop()] */
if (!nodep)
return (0);
return (1);
}
} else {
}
if (funcname == L_confprop) {
if (duped)
return (1);
} else {
valuep->v = 0;
if (duped)
return (1);
}
}
"class-code") == 0)
if (s == NULL) {
if (funcname == L_confprop) {
"%s: \"%s\" not found for path ",
if (duped)
return (1);
} else {
valuep->v = 0;
if (duped)
return (1);
}
}
if (funcname == L_confprop) {
"\", \"%s\") = \"%s\" ",
} else {
valuep->v = 1;
}
if (duped)
return (1);
} else if (funcname == L_is_connected) {
char *nameslist, *w;
int i, j;
return (0);
return (1);
}
else
else
if (duped_lhs)
if (duped_rhs)
valuep->v = 0;
return (1);
/* to thine self always be connected */
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++) {
j++) {
s = config_getprop(cp[i],
stable(connstrings[j]));
if (s != NULL) {
while (w != NULL) {
valuep->v = 1;
break;
}
}
}
}
}
return (1);
const char *s;
int i;
if (!nodep)
return (0);
return (1);
}
} else {
}
if (duped)
return (1);
for (i = 0; typestrings[i] != NULL; i++) {
if (s != NULL) {
break;
}
}
return (1);
const char *s;
int i, j;
if (!nodep)
return (0);
return (1);
}
} else {
}
if (duped)
valuep->v = 0;
return (1);
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) {
if (!nodep)
return (0);
return (1);
}
} else {
}
if (duped)
valuep->v = 0;
valuep->v = 1;
return (1);
} else if (funcname == L_has_fault) {
if (!nodep)
return (0);
return (1);
}
if (duped)
valuep->v = 0;
} else {
}
return (1);
if (try) {
else {
duped = 1;
}
}
valuep->v = 0;
else
if (duped)
return (1);
"eval_func: %s not yet supported", funcname);
}
if (try)
return (0);
return (1);
return (1);
return (1);
} else if (funcname == L_payloadprop) {
/*
* Haven't seen this ereport yet, so must defer
*/
return (0);
/* platform_payloadprop() returned false */
return (1);
} else {
switch (valuep->t) {
case NODEPTR:
"found: \"%s\"", s);
FREE(s);
} else
valuep->v);
break;
case UINT64:
break;
case STRING:
break;
default:
break;
}
return (1);
}
} else if (funcname == L_setpayloadprop) {
int alloced = 0;
return (0);
"setpayloadprop: %s: %s=",
/*
* 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.
*/
alloced = 1;
}
if (alloced)
return (0);
} else {
if (payloadvalp->t == UNDEFINED) {
/* function is always true */
valuep->v = 1;
return (1);
}
if (payloadvalp->t == UINT64)
" (%llu)", payloadvalp->v);
else
(char *)(uintptr_t)payloadvalp->v);
}
/* add to table of payload properties for current problem */
(void *)payloadvalp, NULL);
/* function is always true */
valuep->v = 1;
return (1);
return (retval);
int alloced = 0;
char *str;
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.
*/
alloced = 1;
}
serdvalp)) {
"setserd%s: %s: ", str,
if (alloced)
return (0);
"setserd%s: %s: ", str,
} else {
"setserd%s: %s: ", str,
if ((funcname == L_setserdincrement ||
}
serdvalp->v);
serdvalp->v);
}
if (funcname == L_setserdsuffix &&
serdvalp->v);
serdvalp->v);
}
else
}
valuep->v = 1;
return (1);
} else if (funcname == L_payloadprop_defined) {
/*
* Haven't seen this ereport yet, so must defer
*/
return (0);
/* platform_payloadprop() returned false */
valuep->v = 0;
} else {
valuep->v = 1;
}
return (1);
} else if (funcname == L_payloadprop_contains) {
int nvals;
"payloadprop_contains(\"%s\", ",
/* evaluate the expression we're comparing against */
"(cannot eval) ");
return (0);
} else {
switch (cmpval.t) {
case UNDEFINED:
break;
case UINT64:
"(%llu) ", cmpval.v);
break;
case STRING:
break;
case NODEPTR:
break;
}
}
/* get the payload values and check for a match */
&nvals);
valuep->v = 0;
/*
* Haven't seen this ereport yet, so must defer
*/
return (0);
} else if (nvals == 0) {
return (1);
} else {
int i;
for (i = 0; i < nvals; i++) {
continue;
/*
* If we auto-converted the value to a
* string, we need to free the
* original tree value.
*/
T_NAME) {
preval.v);
}
valuep->v = 1;
break;
}
}
if (valuep->v)
else
for (i = 0; i < nvals; i++) {
vals[i].v);
break;
}
}
}
return (1);
} else if (funcname == L_confcall) {
} else
"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
static struct node *
{
return (NULL);
switch (np->t) {
case T_GLOBID:
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:
case T_LIST:
case T_AND:
case EXPR_TEMP_LHS_UNK:
case EXPR_TEMP_RHS_UNK:
default:
}
case T_OR:
case EXPR_TEMP_LHS_UNK:
case EXPR_TEMP_RHS_UNK:
default:
}
case T_NAME: {
int got_matchf = 0;
int got_matcht = 0;
/*
* Check if we already have a match of the nonwildcarded path
* in oldepname (check both to and from events).
*/
break;
break;
break;
break;
got_matchf++;
}
break;
break;
break;
break;
got_matcht++;
}
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) {
} else {
}
"eval_dup: could not resolve "
}
} else {
}
}
"eval_dup: could not resolve "
}
} else {
}
}
} else {
/*
* not wildcarding - check if explicit iterator
*/
/* explicit iterator; not part of pathname */
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).
*/
"eval_dup: could not resolve "
}
} else {
}
}
return (retp);
}
case T_EVENT:
return (tree_event(newnp,
case T_FUNC:
case T_QUOTE:
return (newnp);
case T_NUM:
return (newnp);
case T_TIMEVAL:
return (newnp);
default:
"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
{
/*
* couldn't eval expression because
* it contains deferred items. make
* a duplicate expression with all the
* non-deferred items expanded.
*/
/*
* constraint is potentially true if deferred
* expression in newnp is true. *newc was NULL
* so new constraint is just the one in 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.
*/
return (1);
}
/* constraint can never be true */
return (0);
/* constraint can never be true */
return (0);
} else {
/* constraint is always true (nothing deferred to eval) */
return (1);
}
}
static int
{
/* auto-convert T_NAMES to strings */
T_NAME) {
FREE(s);
}
T_NAME) {
FREE(s);
}
/* auto-convert numbers to strings */
FREE(s);
}
FREE(s);
}
}
/* auto-convert strings to numbers */
}
}
}
"invalid datatype of argument for operation %s",
ptree_nodetype2str(np->t));
/* NOTREACHED */
return (1);
}
"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
{
return (1);
}
switch (np->t) {
case T_GLOBID:
if (try)
return (0);
/*
* only handle case of getting (and not setting) the value
* of a global variable
*/
return (0);
} else {
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
*/
return (0);
}
"assign $%s=%llu",
} else {
"assign $%s=\"%s\"",
}
/*
* but always return true -- an assignment should not
* cause a constraint to be false.
*/
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 &&
return (0);
return (1);
}
#endif /* IMPLICIT_ASSIGN_IN_EQ */
return (0);
return (0);
return (0);
} else {
return (0);
}
return (1);
case T_LT:
return (0);
return (0);
return (0);
return (1);
case T_LE:
return (0);
return (0);
return (0);
return (1);
case T_GT:
return (0);
return (0);
return (0);
return (1);
case T_GE:
return (0);
return (0);
return (0);
return (1);
case T_BITAND:
return (0);
return (0);
return (0);
return (1);
case T_BITOR:
return (0);
return (0);
return (0);
return (1);
case T_BITXOR:
return (0);
return (0);
return (0);
return (1);
case T_BITNOT:
return (0);
return (0);
return (1);
case T_LSHIFT:
return (0);
return (0);
return (0);
return (1);
case T_RSHIFT:
return (0);
return (0);
return (0);
return (1);
case T_CONDIF: {
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.
*/
return (0);
dotrue = 1;
if (dotrue)
else
} else {
/* no ELSE clause */
if (dotrue)
else {
"eval_expr: missing condelse");
}
}
return (0);
return (1);
}
case T_CONDELSE:
/*
* shouldn't get here, since T_CONDELSE is supposed to be
* evaluated as part of T_CONDIF
*/
ptree_nodetype2str(np->t));
/*NOTREACHED*/
case T_NE:
return (0);
return (0);
return (0);
} else {
return (0);
}
return (1);
case T_LIST:
case T_AND:
/*
* if lhs is unknown, still check rhs. If that
* is false we can return false irrespective of lhs
*/
if (!try) {
return (0);
}
return (0);
}
if (valuep->v != 0) {
return (0);
}
}
if (valuep->v == 0) {
return (1);
}
return (0);
}
return (1);
case T_OR:
/*
* if lhs is unknown, still check rhs. If that
* is true we can return true irrespective of lhs
*/
if (!try) {
return (0);
}
return (0);
}
if (valuep->v == 0) {
return (0);
}
}
if (valuep->v != 0) {
valuep->v = 1;
return (1);
}
return (0);
}
return (1);
case T_NOT:
return (0);
return (1);
case T_ADD:
return (0);
return (0);
return (0);
return (1);
case T_SUB:
return (0);
return (0);
return (0);
/* since valuep is unsigned, return false if lval.v < rval.v */
"eval_expr: T_SUB result is out of range");
}
return (1);
case T_MUL:
return (0);
return (0);
return (0);
return (1);
case T_DIV:
return (0);
return (0);
return (0);
/* return false if dividing by zero */
if (rval.v == 0) {
"eval_expr: T_DIV division by zero");
}
return (1);
case T_MOD:
return (0);
return (0);
return (0);
/* return false if dividing by zero */
if (rval.v == 0) {
"eval_expr: T_MOD division by zero");
}
return (1);
case T_NAME:
if (try) {
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++) {
break;
break;
break;
break;
gotmatch++;
}
return (1);
}
}
if (!gotmatch) {
/*
* we're not wildcarding. However at
* itree_create() time, we can also expand
* simple iterators - so check for those.
*/
NULL);
valuep->v =
return (1);
}
}
/*
* For anything else we'll have to wait for eval_dup().
*/
return (0);
}
/* return address of struct node */
return (1);
case T_QUOTE:
return (1);
case T_FUNC:
case T_NUM:
case T_TIMEVAL:
return (1);
default:
"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 *
{
return (np);
}
static struct node *
{
return (np);
}