# # 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 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # # eft.so (the eversholt DE) # # Copyright (c) 2018, Joyent, Inc. .KEEP_STATE: EVERSRCDIR=../../../eversholt/common MODULE = eft CLASS = common DMOD = $(MODULE).so YSRCS=escparse.y SRCS = alloc.c check.c config.c eft.c eftread.c esclex.c eval.c evnv.c \ fme.c iexpr.c io.c ipath.c itree.c lut.c literals.c out.c platform.c \ ptree.c stable.c stats.c tree.c DMOD_SRCS = eft_mdb.c include ../../Makefile.plugin CPPFLAGS += -DFMAPLUGIN -I$(EVERSRCDIR) -I. CERRWARN += $(CNOWARN_UNINIT) CERRWARN += -Wno-switch CERRWARN += -Wno-parentheses CERRWARN += -Wno-implicit-function-declaration # Hammerhead: Suppress pointer/int cast warnings in legacy code CERRWARN += -Wno-pointer-to-int-cast CERRWARN += -Wno-int-to-pointer-cast # because of labels from yacc escparse.o : CERRWARN += -Wno-unused-label # not linted SMATCH=off $(PROG) : LDFLAGS += -R/usr/lib/fm $(PROG) : LDLIBS += -L$(ROOTLIB)/fm -ltopo CLEANFILES += y.tab.h y.tab.c esclex.o: escparse.o %.o: $(EVERSRCDIR)/%.c $(COMPILE.c) $< $(CTFCONVERT_O) %.ln: $(EVERSRCDIR)/%.c $(LINT.c) -c $< escparse.o: $(EVERSRCDIR)/escparse.y $(YACC) -dt $(EVERSRCDIR)/escparse.y $(COMPILE.c) -DYYDEBUG -c -o $@ y.tab.c $(CTFCONVERT_O) # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License, Version 1.0 only # (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 2004 Sun Microsystems, Inc. All rights reserved. Use is subject to license terms. This file is usr/src/cmd/fma/modules/common/eversholt/README. The files in this directory build the "eft.so" plugin to the fault management daemon (fmd). This plugin is a diagnosis engine driven by one or more eversholt fault tree (.eft) files. eft.c implements the diagnosis engine entry points expected by the fmd. The diagnosis engine is considered platform-neutral code and is being used on other, non-Solaris platforms. The platform specific services it requires are either provided by the fmd interfaces, or by the routines in platform.c in this directory. Most of the files that go into building eft.so are shared with the eversholt compiler. Those files are pulled in by the Makefile in this directory. They live in: usr/src/cmd/fma/eversholt/common When running under Solaris, eft.so uses the hardware topology library (libtopo) to collect configuration information. /* * 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. * * alloc.c -- memory allocation wrapper functions, for eft.so FMD module * */ #include #include #include #include #include "alloc.h" #include "out.h" #include "stats.h" extern fmd_hdl_t *Hdl; /* handle from eft.c */ /* room to store size, possibly more to maintain alignment for long longs */ #define HDRSIZ sizeof (long long) static struct stats *Malloctotal; static struct stats *Freetotal; static struct stats *Malloccount; static struct stats *Freecount; static int totalcount; void alloc_init(void) { Malloctotal = stats_new_counter("alloc.total", "bytes allocated", 1); Freetotal = stats_new_counter("free.total", "bytes freed", 1); Malloccount = stats_new_counter("alloc.calls", "alloc calls", 1); Freecount = stats_new_counter("free.calls", "free calls", 1); } void alloc_fini(void) { struct stats *mt, *ft, *mc, *fc; mt = Malloctotal; ft = Freetotal; mc = Malloccount; fc = Freecount; Malloctotal = NULL; Freetotal = NULL; Malloccount = NULL; Freecount = NULL; stats_delete(mt); stats_delete(ft); stats_delete(mc); stats_delete(fc); } /* * alloc_malloc -- a malloc() with checks * * this routine is typically called via the MALLOC() macro in alloc.h */ /*ARGSUSED*/ void * alloc_malloc(size_t nbytes, const char *fname, int line) { char *retval; ASSERT(nbytes > 0); retval = fmd_hdl_alloc(Hdl, nbytes + HDRSIZ, FMD_SLEEP); /* retval can't be NULL since fmd_hdl_alloc() sleeps for memory */ bcopy((void *)&nbytes, (void *)retval, sizeof (nbytes)); retval += HDRSIZ; if (Malloctotal) stats_counter_add(Malloctotal, nbytes); if (Malloccount) stats_counter_bump(Malloccount); totalcount += nbytes + HDRSIZ; return ((void *)retval); } /* * alloc_realloc -- a realloc() with checks * * this routine is typically called via the REALLOC() macro in alloc.h */ void * alloc_realloc(void *ptr, size_t nbytes, const char *fname, int line) { void *retval = alloc_malloc(nbytes, fname, line); if (ptr != NULL) { size_t osize; bcopy((void *)((char *)ptr - HDRSIZ), (void *)&osize, sizeof (osize)); /* now we have the new memory, copy in the old contents */ bcopy(ptr, retval, (osize < nbytes) ? osize : nbytes); /* don't need the old memory anymore */ alloc_free((char *)ptr, fname, line); } return (retval); } /* * alloc_strdup -- a strdup() with checks * * this routine is typically called via the STRDUP() macro in alloc.h */ char * alloc_strdup(const char *ptr, const char *fname, int line) { char *retval = alloc_malloc(strlen(ptr) + 1, fname, line); (void) strcpy(retval, ptr); return (retval); } /* * alloc_free -- a free() with checks * * this routine is typically called via the FREE() macro in alloc.h */ /*ARGSUSED*/ void alloc_free(void *ptr, const char *fname, int line) { size_t osize; ASSERT(ptr != NULL); bcopy((void *)((char *)ptr - HDRSIZ), (void *)&osize, sizeof (osize)); /* nothing to check in this version */ fmd_hdl_free(Hdl, (char *)ptr - HDRSIZ, osize + HDRSIZ); if (Freetotal) stats_counter_add(Freetotal, osize); if (Freecount) stats_counter_bump(Freecount); totalcount -= osize + HDRSIZ; } int alloc_total() { return (totalcount); } /* * variants that don't maintain size in header - saves space */ void * alloc_xmalloc(size_t nbytes) { char *retval; ASSERT(nbytes > 0); retval = fmd_hdl_alloc(Hdl, nbytes, FMD_SLEEP); if (Malloctotal) stats_counter_add(Malloctotal, nbytes); if (Malloccount) stats_counter_bump(Malloccount); totalcount += nbytes; return ((void *)retval); } void alloc_xfree(void *ptr, size_t size) { ASSERT(ptr != NULL); fmd_hdl_free(Hdl, (char *)ptr, size); if (Freetotal) stats_counter_add(Freetotal, size); if (Freecount) stats_counter_bump(Freecount); totalcount -= size; } /* * 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 (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * config.c -- system configuration cache module * * this module caches the system configuration in a format useful * to eft. the information is loaded into this module by * config_snapshot() at the beginning of each FME. config_snapshot() * calls the platform-specific platform_config_snapshot() to get * the configuration information loaded up. */ #include #include #include #include #include #include #include "alloc.h" #include "out.h" #include "literals.h" #include "stable.h" #include "lut.h" #include "tree.h" #include "itree.h" #include "ipath.h" #include "ptree.h" #include "eval.h" #include "config.h" #include "config_impl.h" #include "fme.h" #include "platform.h" static const char *config_lastcomp; /* * newcnode -- local function to allocate new config node */ static struct config * newcnode(const char *s, int num) { struct config *retval; retval = MALLOC(sizeof (struct config)); retval->s = s; retval->num = num; retval->next = NULL; retval->props = NULL; retval->child = retval->parent = NULL; return (retval); } /* * If we need to cache certain types of nodes for reverse look-up or * somesuch, do it here. Currently we need to cache nodes representing * cpus. */ static void config_node_cache(struct cfgdata *cdata, struct config *n) { if (n->s != stable("cpu")) return; cdata->cpucache = lut_add(cdata->cpucache, (void *)n->num, (void *)n, NULL); } /* * config_lookup -- lookup/add components in configuration cache */ struct config * config_lookup(struct config *croot, char *path, int add) { char *pathbegin = path; struct config *parent = croot; struct config *cp; struct config *lastcp; struct config *newnode; char *thiscom; /* this component */ char *nextcom; /* next component */ char svdigit; int len; int num; const char *s; int exists; if (parent == NULL) out(O_DIE, "uninitialized configuration"); while (*path) { if ((nextcom = strchr(path, '/')) != NULL) *nextcom = '\0'; if ((len = strlen(path)) == 0) out(O_DIE, "config_lookup: zero length component"); /* start at end of string and work backwards */ thiscom = &path[len - 1]; if (!isdigit(*thiscom)) out(O_DIE, "config_lookup: " "component \"%s\" has no number following it", path); while (thiscom > path && isdigit(*thiscom)) thiscom--; if (thiscom == path && isdigit(*thiscom)) out(O_DIE, "config_lookup: " "component \"%s\" has no name part", path); thiscom++; /* move to first numeric character */ num = atoi(thiscom); svdigit = *thiscom; *thiscom = '\0'; s = stable(path); if (add) config_lastcomp = s; *thiscom = svdigit; if (nextcom != NULL) *nextcom++ = '/'; /* now we have s & num, figure out if it exists already */ exists = 0; lastcp = NULL; for (cp = parent->child; cp; lastcp = cp, cp = cp->next) if (cp->s == s && cp->num == num) { exists = 1; parent = cp; } if (!exists) { /* creating new node */ if (!add) { /* * indicate component not found by copying * it to path (allows better error messages * in the caller). */ (void) strcpy(pathbegin, s); return (NULL); } newnode = newcnode(s, num); if (lastcp) lastcp->next = newnode; else parent->child = newnode; newnode->parent = parent; parent = newnode; } if (nextcom == NULL) return (parent); /* all done */ /* move on to next component */ path = nextcom; } return (parent); } /* * addconfigprop -- add a config prop to a config cache entry */ static void addconfigprop(const char *lhs, struct node *rhs, void *arg) { struct config *cp = (struct config *)arg; ASSERT(cp != NULL); ASSERT(lhs != NULL); ASSERT(rhs != NULL); ASSERT(rhs->t == T_QUOTE); config_setprop(cp, lhs, STRDUP(rhs->u.quote.s)); } /* * addconfig -- add a config from parse tree to given configuration cache */ /*ARGSUSED*/ static void addconfig(struct node *lhs, struct node *rhs, void *arg) { struct config *parent = (struct config *)arg; struct config *cp; const char *s; int num; struct config *lastcp; struct config *newnode; int exists; struct lut *lutp; ASSERT(rhs->t == T_CONFIG); lutp = rhs->u.stmt.lutp; rhs = rhs->u.stmt.np; while (rhs != NULL) { ASSERT(rhs->t == T_NAME); ASSERT(rhs->u.name.child->t == T_NUM); s = rhs->u.name.s; num = rhs->u.name.child->u.ull; /* now we have s & num, figure out if it exists already */ exists = 0; lastcp = NULL; for (cp = parent->child; cp; lastcp = cp, cp = cp->next) if (cp->s == s && cp->num == num) { exists = 1; parent = cp; } if (!exists) { /* creating new node */ newnode = newcnode(s, num); if (lastcp) lastcp->next = newnode; else parent->child = newnode; newnode->parent = parent; parent = newnode; } /* move on to next component */ rhs = rhs->u.name.next; } /* add configuration properties */ lut_walk(lutp, (lut_cb)addconfigprop, (void *)parent); } /* * config_cook -- convert raw config strings to eft internal representation */ void config_cook(struct cfgdata *cdata) { struct config *newnode; char *cfgstr, *equals; const char *pn, *sv; char *pv; const char *ptr; extern struct lut *Usedprops; extern struct lut *Usednames; cdata->cooked = newcnode(NULL, 0); if ((cfgstr = cdata->begin) == cdata->nextfree) { out(O_ALTFP|O_VERB, "Platform provided no config data."); goto eftcfgs; } /* * add the following properties to the "usedprops" table as they * are used internally by eft */ ptr = stable("module"); Usedprops = lut_add(Usedprops, (void *)ptr, (void *)ptr, NULL); ptr = stable("resource"); Usedprops = lut_add(Usedprops, (void *)ptr, (void *)ptr, NULL); ptr = stable("serial"); Usedprops = lut_add(Usedprops, (void *)ptr, (void *)ptr, NULL); out(O_ALTFP|O_VERB3, "Raw config data follows:"); out(O_ALTFP|O_VERB3|O_NONL, "nextfree is %p\n%p ", (void *)cdata->nextfree, (void *)cfgstr); while (cfgstr < cdata->nextfree) { if (!*cfgstr) out(O_ALTFP|O_VERB3|O_NONL, "\n%p ", (void *)(cfgstr + 1)); else out(O_ALTFP|O_VERB3|O_NONL, "%c", *cfgstr); cfgstr++; } out(O_ALTFP|O_VERB3, NULL); cfgstr = cdata->begin; while (cfgstr < cdata->nextfree) { while (*cfgstr == '/' && cfgstr < cdata->nextfree) { out(O_ALTFP|O_VERB3, "next string (%p) is %s", (void *)cfgstr, cfgstr); /* skip the initial slash from libtopo */ newnode = config_lookup(cdata->cooked, cfgstr + 1, 1); /* * Note we'll only cache nodes that have * properties on them. Intermediate nodes * will have been added to the config tree, * but we don't have easy means of accessing * them except if we climb the tree from this * newnode to the root. * * Luckily, the nodes we care to cache * (currently just cpus) always have some * properties attached to them * so we don't bother climbing the tree. */ config_node_cache(cdata, newnode); cfgstr += strlen(cfgstr) + 1; } if (cfgstr >= cdata->nextfree) break; out(O_ALTFP|O_VERB3, "next string (%p) is %s", (void *)cfgstr, cfgstr); if ((equals = strchr(cfgstr, '=')) == NULL) { out(O_ALTFP|O_VERB3, "raw config data bad (%p); " "property missing equals.\n", (void *)cfgstr); break; } *equals = '\0'; pn = stable(cfgstr); /* * only actually add the props if the rules use them (saves * memory) */ if ((lut_lookup(Usedprops, (void *)pn, NULL) != NULL || strncmp(pn, "serd_", 5) == 0) && lut_lookup(Usednames, (void *)config_lastcomp, NULL) != NULL) { pv = STRDUP(equals + 1); out(O_ALTFP|O_VERB3, "add prop (%s) val %p", pn, (void *)pv); config_setprop(newnode, pn, pv); } /* * If this property is a device path, tp or devid, cache it * for quick lookup. */ if (config_lastcomp == stable(SCSI_DEVICE) || config_lastcomp == stable(SMP_DEVICE)) { /* * we can't get ereports on SCSI_DEVICE or SMP_DEVICE * nodes, so don't cache. */ out(O_ALTFP|O_VERB3, "not caching %s for %s", pn, config_lastcomp); } else if (pn == stable(TOPO_IO_DEV)) { sv = stable(equals + 1); out(O_ALTFP|O_VERB3, "caching dev %s", sv); cdata->devcache = lut_add(cdata->devcache, (void *)sv, (void *)newnode, NULL); } else if (pn == stable(TOPO_IO_DEVID) || pn == stable(TOPO_PROP_SES_DEVID) || pn == stable(TOPO_PROP_SMP_DEVID)) { sv = stable(equals + 1); out(O_ALTFP|O_VERB3, "caching devid %s", sv); cdata->devidcache = lut_add(cdata->devidcache, (void *)sv, (void *)newnode, NULL); } else if (pn == stable(TOPO_STORAGE_TARGET_PORT_L0IDS)) { /* * This was stored as a set of space-separated strings. * Find each string in turn and add to the lut. Then if * a ereport comes in with a target-path matching any * of the strings we will match it. */ char *x, *y = equals; while (y != NULL) { x = y + 1; y = strchr(x, ' '); if (y != NULL) *y = '\0'; sv = stable(x); out(O_ALTFP|O_VERB3, "caching tp %s", sv); cdata->tpcache = lut_add(cdata->tpcache, (void *)sv, (void *)newnode, NULL); if (y != NULL) *y = ' '; } } *equals = '='; cfgstr += strlen(cfgstr) + 1; } eftcfgs: /* now run through Configs table, adding to config cache */ lut_walk(Configs, (lut_cb)addconfig, (void *)cdata->cooked); } /* * config_snapshot -- gather a snapshot of the current configuration */ struct cfgdata * config_snapshot(void) { struct cfgdata *rawcfg; rawcfg = platform_config_snapshot(); config_cook(rawcfg); return (rawcfg); } /* * prop_destructor -- free a prop value */ /*ARGSUSED*/ static void prop_destructor(void *left, void *right, void *arg) { FREE(right); } /* * structconfig_free -- free a struct config pointer and all its relatives */ void structconfig_free(struct config *cp) { if (cp == NULL) return; structconfig_free(cp->child); structconfig_free(cp->next); lut_free(cp->props, prop_destructor, NULL); FREE(cp); } /* * config_free -- free a configuration snapshot */ void config_free(struct cfgdata *cp) { if (cp == NULL) return; if (--cp->raw_refcnt == 0) { if (cp->devcache != NULL) lut_free(cp->devcache, NULL, NULL); cp->devcache = NULL; if (cp->tpcache != NULL) lut_free(cp->tpcache, NULL, NULL); cp->tpcache = NULL; if (cp->devidcache != NULL) lut_free(cp->devidcache, NULL, NULL); cp->devidcache = NULL; if (cp->cpucache != NULL) lut_free(cp->cpucache, NULL, NULL); cp->cpucache = NULL; if (cp->begin != NULL) FREE(cp->begin); FREE(cp); } } /* * config_next -- get the "next" config node */ struct config * config_next(struct config *cp) { ASSERT(cp != NULL); return ((struct config *)((struct config *)cp)->next); } /* * config_child -- get the "child" of a config node */ struct config * config_child(struct config *cp) { ASSERT(cp != NULL); return ((struct config *)((struct config *)cp)->child); } /* * config_parent -- get the "parent" of a config node */ struct config * config_parent(struct config *cp) { ASSERT(cp != NULL); return ((struct config *)((struct config *)cp)->parent); } /* * config_setprop -- add a property to a config node */ void config_setprop(struct config *cp, const char *propname, const char *propvalue) { const char *pn = stable(propname); cp->props = lut_add(cp->props, (void *)pn, (void *)propvalue, NULL); } /* * config_getprop -- lookup a config property */ const char * config_getprop(struct config *cp, const char *propname) { return (lut_lookup(cp->props, (void *) stable(propname), NULL)); } /* * config_getcompname -- get the component name of a config node */ void config_getcompname(struct config *cp, char **name, int *inst) { ASSERT(cp != NULL); if (name != NULL) *name = (char *)cp->s; if (inst != NULL) *inst = cp->num; } /* * config_nodeize -- convert the config element represented by cp to struct * node format */ static struct node * config_nodeize(struct config *cp) { struct node *tmpn, *ptmpn; struct node *numn; const char *sname; if (cp == NULL || cp->s == NULL) return (NULL); sname = stable(cp->s); numn = newnode(T_NUM, NULL, 0); numn->u.ull = cp->num; tmpn = tree_name_iterator(tree_name(sname, IT_VERTICAL, NULL, 0), numn); if ((ptmpn = config_nodeize(cp->parent)) == NULL) return (tmpn); return (tree_name_append(ptmpn, tmpn)); } /*ARGSUSED*/ static void prtdevcache(void *lhs, void *rhs, void *arg) { out(O_ALTFP|O_VERB3, "%s -> %p", (char *)lhs, rhs); } /*ARGSUSED*/ static void prtdevidcache(void *lhs, void *rhs, void *arg) { out(O_ALTFP|O_VERB3, "%s -> %p", (char *)lhs, rhs); } /*ARGSUSED*/ static void prttpcache(void *lhs, void *rhs, void *arg) { out(O_ALTFP|O_VERB3, "%s -> %p", (char *)lhs, rhs); } /*ARGSUSED*/ static void prtcpucache(void *lhs, void *rhs, void *arg) { out(O_ALTFP|O_VERB, "%u -> %p", (uint32_t)lhs, rhs); } /* * config_bydev_lookup -- look up the path in our devcache lut. If we find * it return the config path, but as a struct node. */ struct node * config_bydev_lookup(struct cfgdata *fromcfg, const char *path) { struct config *find; struct node *np; out(O_ALTFP|O_VERB3, "Device path cache:"); lut_walk(fromcfg->devcache, (lut_cb)prtdevcache, NULL); if ((find = lut_lookup(fromcfg->devcache, (void *) stable(path), NULL)) == NULL) return (NULL); np = config_nodeize(find); if (np != NULL) { out(O_ALTFP|O_VERB, "Matching config entry:"); ptree_name_iter(O_ALTFP|O_VERB|O_NONL, np); out(O_ALTFP|O_VERB, NULL); } return (np); } /* * config_bydevid_lookup -- look up the path in our DEVIDcache lut. * If we find it return the config path, but as a struct node. */ struct node * config_bydevid_lookup(struct cfgdata *fromcfg, const char *devid) { struct config *find; struct node *np; out(O_ALTFP|O_VERB3, "Device id cache:"); lut_walk(fromcfg->devcache, (lut_cb)prtdevidcache, NULL); if ((find = lut_lookup(fromcfg->devidcache, (void *) stable(devid), NULL)) == NULL) return (NULL); np = config_nodeize(find); if (np != NULL) { out(O_ALTFP|O_VERB, "Matching config entry:"); ptree_name_iter(O_ALTFP|O_VERB|O_NONL, np); out(O_ALTFP|O_VERB, NULL); } return (np); } /* * config_bytp_lookup -- look up the path in our TPcache lut. * If we find it return the config path, but as a struct node. */ struct node * config_bytp_lookup(struct cfgdata *fromcfg, const char *tp) { struct config *find; struct node *np; out(O_ALTFP|O_VERB3, "Device id cache:"); lut_walk(fromcfg->devcache, (lut_cb)prttpcache, NULL); if ((find = lut_lookup(fromcfg->tpcache, (void *) stable(tp), NULL)) == NULL) return (NULL); np = config_nodeize(find); if (np != NULL) { out(O_ALTFP|O_VERB, "Matching config entry:"); ptree_name_iter(O_ALTFP|O_VERB|O_NONL, np); out(O_ALTFP|O_VERB, NULL); } return (np); } /* * config_bycpuid_lookup -- look up the cpu id in our CPUcache lut. * If we find it return the config path, but as a struct node. */ struct node * config_bycpuid_lookup(struct cfgdata *fromcfg, uint32_t id) { struct config *find; struct node *np; out(O_ALTFP|O_VERB, "Cpu cache:"); lut_walk(fromcfg->cpucache, (lut_cb)prtcpucache, NULL); if ((find = lut_lookup(fromcfg->cpucache, (void *)id, NULL)) == NULL) return (NULL); np = config_nodeize(find); if (np != NULL) { out(O_ALTFP|O_VERB3, "Matching config entry:"); ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, np); out(O_ALTFP|O_VERB3, NULL); } return (np); } /* * printprop -- print prop associated with config node */ static void printprop(const char *lhs, const char *rhs, void *arg) { int flags = (int)arg; out(flags, "\t%s=%s", lhs, rhs); } /* * pconf -- internal printing function to recurse through the tree */ static void pconf(int flags, struct config *cp, char *buf, int offset, int limit) { char *sep = "/"; if (offset) sep = "/"; else sep = ""; (void) snprintf(&buf[offset], limit - offset, "%s%s%d", sep, cp->s, cp->num); if (cp->child == NULL) { out(flags, "%s", buf); lut_walk(cp->props, (lut_cb)printprop, (void *)flags); } else pconf(flags, cp->child, buf, strlen(buf), limit); if (cp->next) pconf(flags, cp->next, buf, offset, limit); } /* * config_print -- spew the current configuration cache */ #define MAXCONFLINE 4096 void config_print(int flags, struct config *croot) { char buf[MAXCONFLINE]; if (croot == NULL) out(flags, "empty configuration"); else pconf(flags, croot->child, buf, 0, MAXCONFLINE); } /* * 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 (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. * * config.h -- public definitions for config module * * this module supports management of system configuration information * */ #ifndef _EFT_CONFIG_H #define _EFT_CONFIG_H #include #ifdef __cplusplus extern "C" { #endif #include "tree.h" #include "lut.h" struct cfgdata { int raw_refcnt; /* * The begin field points to the first byte of raw * configuration information and end to the byte past the last * byte where configuration information may be stored. * nextfree points to where the next string may be added. */ char *begin; char *end; char *nextfree; struct config *cooked; struct lut *devcache; struct lut *devidcache; struct lut *tpcache; struct lut *cpucache; }; void structconfig_free(struct config *cp); struct cfgdata *config_snapshot(void); void config_cook(struct cfgdata *cdata); void config_free(struct cfgdata *cdata); struct config *config_lookup(struct config *croot, char *path, int add); struct config *config_next(struct config *cp); struct config *config_child(struct config *cp); struct config *config_parent(struct config *cp); const char *config_getprop(struct config *cp, const char *name); void config_setprop(struct config *cp, const char *name, const char *val); void config_getcompname(struct config *cp, char **name, int *inst); int config_is_connected(struct node *np, struct config *croot, struct evalue *valuep); int config_is_type(struct node *np, struct config *croot, struct evalue *valuep); int config_is_on(struct node *np, struct config *croot, struct evalue *valuep); int config_is_present(struct node *np, struct config *croot, struct evalue *valuep); void config_print(int flags, struct config *croot); struct node *config_bydev_lookup(struct cfgdata *, const char *); struct node *config_bycpuid_lookup(struct cfgdata *, uint32_t); struct node *config_bydevid_lookup(struct cfgdata *, const char *); struct node *config_bytp_lookup(struct cfgdata *, const char *); #ifdef __cplusplus } #endif #endif /* _EFT_CONFIG_H */ /* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _EFT_CONFIG_IMPL_H #define _EFT_CONFIG_IMPL_H #ifdef __cplusplus extern "C" { #endif /* * private to config.c and mdb module. * * Data structure for storing config. all access to * to this information happens using the config.h interfaces. */ struct config { struct config *next; struct config *child; struct config *parent; const char *s; intptr_t num; struct lut *props; }; #ifdef __cplusplus } #endif #endif /* _EFT_CONFIG_IMPL_H */ /* * 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 (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include "out.h" #include "stats.h" #include "alloc.h" #include "stable.h" #include "literals.h" #include "lut.h" #include "esclex.h" #include "tree.h" #include "ipath.h" #include "itree.h" #include "iexpr.h" #include "ptree.h" #include "check.h" #include "version.h" #include "fme.h" #include "eval.h" #include "config.h" #include "platform.h" /* * eversholt diagnosis engine (eft.so) main entry points */ fmd_hdl_t *Hdl; /* handle in global for platform.c */ int Debug = 1; /* turn on here and let fmd_hdl_debug() decide if really on */ hrtime_t Hesitate; /* hesitation time in ns */ char *Serd_Override; /* override for Serd engines */ int Verbose; int Estats; int Warn; /* zero -- eft.so should not issue language warnings */ char **Efts; int Max_fme; /* Maximum number of open FMEs */ /* stuff exported by yacc-generated parsers */ extern void yyparse(void); extern int yydebug; extern struct lut *Dicts; extern void literals_init(void); extern void literals_fini(void); struct eftsubr { const char *prefix; void (*hdlr)(fmd_hdl_t *, fmd_event_t *, nvlist_t *, const char *); } eftsubrs[] = { { "ereport.", fme_receive_external_report }, { "list.repaired", fme_receive_repair_list }, { NULL, NULL } }; /*ARGSUSED*/ static void eft_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class) { struct eftsubr *sp = eftsubrs; while (sp->prefix != NULL) { if (strncmp(class, sp->prefix, strlen(sp->prefix)) == 0) break; sp++; } if (sp->prefix != NULL) { (sp->hdlr)(hdl, ep, nvl, class); } else { out(O_DIE, "eft_recv: event class \"%s\" does not match our " "subscriptions", class); } } /*ARGSUSED*/ static void eft_timeout(fmd_hdl_t *hdl, id_t tid, void *arg) { out(O_ALTFP|O_STAMP, "\neft.so timer %ld fired with arg %p", tid, arg); if (arg == NULL) return; fme_timer_fired(arg, tid); } static void eft_close(fmd_hdl_t *hdl, fmd_case_t *fmcase) { out(O_ALTFP, "eft_close called for case %s", fmd_case_uuid(hdl, fmcase)); fme_close_case(hdl, fmcase); } /* * The "serd_override" property allows the N and T parameters of specified serd * engines to be overridden. The property is a string consisting of one or more * space separated triplets. Each triplet is of the form "name,N,T" where "name" * is the name of the serd engine and N and T are the new paremeters to use. * For example "serd.io.device.nonfatal,5,3h" would set the parameters for the * serd.io.device.nonfatal engine to 5 in 3 hours. */ static const fmd_prop_t eft_props[] = { { "estats", FMD_TYPE_BOOL, "false" }, { "hesitate", FMD_TYPE_INT64, "10000000000" }, { "serd_override", FMD_TYPE_STRING, NULL }, { "verbose", FMD_TYPE_INT32, "0" }, { "warn", FMD_TYPE_BOOL, "false" }, { "status", FMD_TYPE_STRING, NULL }, { "maxfme", FMD_TYPE_INT32, "0" }, { NULL, 0, NULL } }; /*ARGSUSED*/ static void eft_topo_change(fmd_hdl_t *hdl, topo_hdl_t *thp) { fme_receive_topology_change(); } static const fmd_hdl_ops_t eft_ops = { eft_recv, /* fmdo_recv */ eft_timeout, /* fmdo_timeout */ eft_close, /* fmdo_close */ NULL, /* fmdo_stats */ NULL, /* fmdo_gc */ NULL, /* fmdo_send */ eft_topo_change /* fmdo_topo_change */ }; #define xstr(s) str(s) #define str(s) #s static const fmd_hdl_info_t fmd_info = { "eft diagnosis engine", xstr(VERSION_MAJOR) "." xstr(VERSION_MINOR), &eft_ops, eft_props }; /* * ename_strdup -- like strdup but ename comes in and class string goes out */ static char * ename_strdup(struct node *np) { struct node *mynp; int len; char *buf; /* calculate length of buffer required */ len = 0; for (mynp = np; mynp; mynp = mynp->u.name.next) len += strlen(mynp->u.name.s) + 1; /* +1 for dot or NULL */ buf = MALLOC(len); buf[0] = '\0'; /* now build the string */ while (np) { (void) strcat(buf, np->u.name.s); np = np->u.name.next; if (np) (void) strcat(buf, "."); } return (buf); } /*ARGSUSED*/ static void dosubscribe(struct node *lhs, struct node *rhs, void *arg) { char *ename = ename_strdup(lhs); fmd_hdl_subscribe(Hdl, ename); FREE(ename); } /*ARGSUSED*/ static void dodiscardprint(struct node *lhs, struct node *rhs, void *arg) { char *ename = (char *)lhs; out(O_VERB, "allow silent discard_if_config_unknown: \"%s\"", ename); } extern struct stats *Filecount; /* * Call all of the _fini() routines to clean up the exiting DE */ void call_finis(void) { platform_free_eft_files(Efts); Efts = NULL; platform_fini(); fme_fini(); itree_fini(); ipath_fini(); iexpr_fini(); istat_fini(); serd_fini(); lex_free(); check_fini(); tree_fini(); lut_fini(); literals_fini(); stable_fini(); stats_fini(); out_fini(); alloc_fini(); } /*ARGSUSED*/ static void doopendict(const char *lhs, void *rhs, void *arg) { out(O_VERB, "opendict: \"%s\"", lhs); fmd_hdl_opendict(Hdl, lhs); } void _fmd_init(fmd_hdl_t *hdl) { fmd_case_t *casep = NULL; int count; char *fname; (void) fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info); /* keep handle for routines like out() which need it */ Hdl = hdl; /* set up out(O_ALTFP) first things so it is available for debug */ alloc_init(); out_init("eft"); if ((fname = fmd_prop_get_string(hdl, "status")) != NULL) { FILE *fp; if ((fp = fopen(fname, "a")) == NULL) { fmd_prop_free_string(hdl, fname); out(O_DIE|O_SYS, "status property file: %s", fname); } (void) setlinebuf(fp); out_altfp(fp); out(O_DEBUG, "appending status changes to \"%s\"", fname); fmd_prop_free_string(hdl, fname); out(O_ALTFP|O_STAMP, "\neft.so startup"); } Estats = fmd_prop_get_int32(hdl, "estats"); stats_init(Estats); stable_init(0); literals_init(); platform_init(); lut_init(); tree_init(); ipath_init(); iexpr_init(); Efts = platform_get_eft_files(); lex_init(Efts, NULL, 0); check_init(); /* * If we read no .eft files, we can't do any * diagnosing, so we just unload ourselves */ if (stats_counter_value(Filecount) == 0) { (void) lex_fini(); call_finis(); fmd_hdl_debug(hdl, "no fault trees provided."); fmd_hdl_unregister(hdl); return; } yyparse(); (void) lex_fini(); tree_report(); if (count = out_errcount()) out(O_DIE, "%d language error%s encountered, exiting.", OUTS(count)); /* subscribe to events we expect to consume */ lut_walk(Ereportenames, (lut_cb)dosubscribe, NULL); lut_walk(Ereportenames_discard, (lut_cb)dodiscardprint, NULL); /* subscribe to repair events so we can clear state on repair */ fmd_hdl_subscribe(hdl, "list.repaired"); /* open dictionaries referenced by all .eft files */ lut_walk(Dicts, (lut_cb)doopendict, (void *)0); Verbose = fmd_prop_get_int32(hdl, "verbose"); Warn = fmd_prop_get_int32(hdl, "warn"); Hesitate = fmd_prop_get_int64(hdl, "hesitate"); Serd_Override = fmd_prop_get_string(hdl, "serd_override"); Max_fme = fmd_prop_get_int32(hdl, "maxfme"); out(O_DEBUG, "initialized, verbose %d warn %d maxfme %d", Verbose, Warn, Max_fme); fme_istat_load(hdl); fme_serd_load(hdl); out(O_VERB, "reconstituting any existing fmes"); while ((casep = fmd_case_next(hdl, casep)) != NULL) { fme_restart(hdl, casep); } } /*ARGSUSED*/ void _fmd_fini(fmd_hdl_t *hdl) { call_finis(); } # # 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 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" # # fmd configuration file for the eft.so diagnosis engine. # # The following configuration options are valid: # # setprop estats true # # Enables "extended stats" visible via "fmstat -m eft". # Useful only when doing performance work on eft.so. # # setprop status # # Tracks status of internal fault management exercises # to the given file each time the status changes. # Dumps an even larger amount of information if verbose # is also set. Note that every ereport received and # every timer that expires will cause the file to grow. # # setprop verbose # # Increase debugging verbosity to (1, 2, or 3). # # setprop warn true # # Turns on EFT file language warnings. Useful only # in very obscure debugging cases. # # setprop maxfme # # Sets the maximum number of open fault management exercises. # The value "0" (zero) specifies no limit. # dictionary SUNOS /* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2019 Joyent, Inc. */ #include #include #include #include "ipath_impl.h" #include "lut_impl.h" #include "config_impl.h" #include "stats_impl.h" #define LUT_SIZE_INIT 300 #define LUT_SIZE_INCR 100 struct lut_cp { uintptr_t lutcp_addr; struct lut lutcp_lut; }; #define LCPSZ sizeof (struct lut_cp) struct lut_dump_desc { struct lut_cp *ld_array; int ld_arraysz; int ld_nents; }; static void lut_dump_array_alloc(struct lut_dump_desc *lddp) { struct lut_cp *new; if (lddp->ld_array == NULL) { lddp->ld_arraysz = LUT_SIZE_INIT; lddp->ld_array = mdb_zalloc(LUT_SIZE_INIT * LCPSZ, UM_SLEEP); return; } new = mdb_zalloc((lddp->ld_arraysz + LUT_SIZE_INCR) * LCPSZ, UM_SLEEP); bcopy(lddp->ld_array, new, lddp->ld_arraysz * LCPSZ); mdb_free(lddp->ld_array, lddp->ld_arraysz * LCPSZ); lddp->ld_array = new; lddp->ld_arraysz += LUT_SIZE_INCR; } static void lut_dump_array_free(struct lut_dump_desc *lddp) { if (lddp->ld_array != NULL) { mdb_free(lddp->ld_array, lddp->ld_arraysz * LCPSZ); lddp->ld_array = NULL; } } static void lut_collect_addent(uintptr_t addr, struct lut *ent, struct lut_dump_desc *lddp) { struct lut_cp *lcp; if (lddp->ld_nents == lddp->ld_arraysz) lut_dump_array_alloc(lddp); lcp = &lddp->ld_array[lddp->ld_nents++]; lcp->lutcp_addr = addr; bcopy(ent, &lcp->lutcp_lut, sizeof (struct lut)); } static int eft_lut_walk(uintptr_t root, struct lut_dump_desc *lddp) { struct lut lutent; if (root) { if (mdb_vread(&lutent, sizeof (struct lut), root) != sizeof (struct lut)) { mdb_warn("failed to read struct lut at %p", root); return (WALK_ERR); } if (eft_lut_walk((uintptr_t)lutent.lut_left, lddp) != WALK_NEXT) return (WALK_ERR); lut_collect_addent(root, &lutent, lddp); if (eft_lut_walk((uintptr_t)lutent.lut_right, lddp) != WALK_NEXT) return (WALK_ERR); } return (WALK_NEXT); } static int lut_collect(uintptr_t addr, struct lut_dump_desc *lddp) { lut_dump_array_alloc(lddp); if (eft_lut_walk(addr, lddp) != WALK_NEXT) { lut_dump_array_free(lddp); return (WALK_ERR); } else { return (WALK_NEXT); /* caller must free dump array */ } } static int lut_walk_init(mdb_walk_state_t *wsp) { if (wsp->walk_addr == 0) { mdb_warn("lut walker requires a lut table address\n"); return (WALK_ERR); } wsp->walk_data = mdb_zalloc(sizeof (struct lut_dump_desc), UM_SLEEP); wsp->walk_arg = 0; if (lut_collect(wsp->walk_addr, wsp->walk_data) == WALK_NEXT) { return (WALK_NEXT); } else { mdb_warn("failed to suck in full lut\n"); mdb_free(wsp->walk_data, sizeof (struct lut_dump_desc)); return (WALK_ERR); } } static int lut_walk_step(mdb_walk_state_t *wsp) { struct lut_dump_desc *lddp = wsp->walk_data; int *ip = (int *)&wsp->walk_arg; struct lut_cp *lcp = &lddp->ld_array[*ip]; if (*ip == lddp->ld_nents) return (WALK_DONE); ++*ip; return (wsp->walk_callback(lcp->lutcp_addr, &lcp->lutcp_lut, wsp->walk_cbdata)); } static int ipath_walk_init(mdb_walk_state_t *wsp) { struct ipath *ipath; ipath = mdb_alloc(sizeof (struct ipath), UM_SLEEP); if (mdb_vread((void *)ipath, sizeof (struct ipath), wsp->walk_addr) != sizeof (struct ipath)) { mdb_warn("failed to read struct ipath at %p", wsp->walk_addr); return (WALK_ERR); } wsp->walk_data = (void *)ipath; if (ipath->s == NULL) return (WALK_DONE); else return (WALK_NEXT); } static void ipath_walk_fini(mdb_walk_state_t *wsp) { mdb_free(wsp->walk_data, sizeof (struct ipath)); } static int ipath_walk_step(mdb_walk_state_t *wsp) { int status; struct ipath *ipath = (struct ipath *)wsp->walk_data; struct ipath *ip = (struct ipath *)wsp->walk_addr; if (ip == NULL || ipath->s == NULL) return (WALK_DONE); status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data, wsp->walk_cbdata); wsp->walk_addr = (uintptr_t)(ip + 1); if (mdb_vread(wsp->walk_data, sizeof (struct ipath), wsp->walk_addr) != sizeof (struct ipath)) { mdb_warn("failed to read struct ipath at %p", wsp->walk_addr); return (WALK_ERR); } return (status); } static void lut_walk_fini(mdb_walk_state_t *wsp) { struct lut_dump_desc *lddp = wsp->walk_data; lut_dump_array_free(lddp); mdb_free(lddp, sizeof (struct lut_dump_desc)); } /*ARGSUSED*/ static int ipath_node(uintptr_t addr, const void *data, void *arg) { struct ipath *ipath = (struct ipath *)data; char buf[128]; if (mdb_readstr(buf, (size_t)sizeof (buf), (uintptr_t)ipath->s) < 0) (void) mdb_snprintf(buf, (size_t)sizeof (buf), "<%p>", ipath->s); mdb_printf("/%s=%d", buf, ipath->i); return (DCMD_OK); } /*ARGSUSED*/ static int ipath(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { if (argc) return (DCMD_USAGE); if (!(flags & DCMD_ADDRSPEC)) addr = mdb_get_dot(); if (mdb_pwalk("eft_ipath", ipath_node, NULL, addr) != 0) return (DCMD_ERR); return (DCMD_OK); } /*ARGSUSED*/ static int eft_count(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct lut lut; struct istat_entry istat_entry; struct stats count; GElf_Sym sym; char buf[128]; if (argc) return (DCMD_USAGE); if (!(flags & DCMD_ADDRSPEC)) { if (mdb_lookup_by_obj(MDB_OBJ_EVERY, "Istats", &sym) == -1 || sym.st_size != sizeof (addr)) return (DCMD_ERR); if (mdb_vread(&addr, sizeof (addr), (uintptr_t)sym.st_value) != sizeof (addr)) return (DCMD_ERR); if (addr == 0) return (DCMD_OK); if (mdb_pwalk_dcmd("lut", "eft_count", argc, argv, addr) != 0) return (DCMD_ERR); return (DCMD_OK); } if (mdb_vread(&lut, sizeof (struct lut), addr) != sizeof (struct lut)) { mdb_warn("failed to read struct lut at %p", addr); return (DCMD_ERR); } if (mdb_vread(&istat_entry, sizeof (struct istat_entry), (uintptr_t)lut.lut_lhs) != sizeof (struct istat_entry)) { mdb_warn("failed to read struct istat_entry at %p", addr); return (DCMD_ERR); } if (mdb_vread(&count, sizeof (struct stats), (uintptr_t)lut.lut_rhs) != sizeof (struct stats)) { mdb_warn("failed to read struct stats at %p", addr); return (DCMD_ERR); } if (mdb_readstr(buf, (size_t)sizeof (buf), (uintptr_t)istat_entry.ename) < 0) (void) mdb_snprintf(buf, (size_t)sizeof (buf), "<%p>", istat_entry.ename); mdb_printf("%s@", buf); (void) ipath((uintptr_t)istat_entry.ipath, DCMD_ADDRSPEC, 0, NULL); mdb_printf(" %d\n", count.fmd_stats.fmds_value.i32); return (DCMD_OK); } /*ARGSUSED*/ static int eft_time(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { unsigned long long val; unsigned long long ull; int opt_p = 0; if (!(flags & DCMD_ADDRSPEC)) addr = mdb_get_dot(); ull = addr; if (argc) { if (mdb_getopts(argc, argv, 'l', MDB_OPT_UINT64, &ull, 'p', MDB_OPT_SETBITS, TRUE, &opt_p, MDB_OPT_UINT64, NULL) != argc) { return (DCMD_USAGE); } } if (opt_p) { if (mdb_vread(&ull, sizeof (ull), addr) != sizeof (ull)) { mdb_warn("failed to read timeval at %p", addr); return (DCMD_ERR); } } #define NOREMAINDER(den, num, val) (((val) = ((den) / (num))) * (num) == (den)) if (ull == 0) mdb_printf("0us"); else if (ull >= TIMEVAL_EVENTUALLY) mdb_printf("infinity"); else if (NOREMAINDER(ull, 1000000000ULL*60*60*24*365, val)) mdb_printf("%lluyear%s", val, (val == 1) ? "" : "s"); else if (NOREMAINDER(ull, 1000000000ULL*60*60*24*30, val)) mdb_printf("%llumonth%s", val, (val == 1) ? "" : "s"); else if (NOREMAINDER(ull, 1000000000ULL*60*60*24*7, val)) mdb_printf("%lluweek%s", val, (val == 1) ? "" : "s"); else if (NOREMAINDER(ull, 1000000000ULL*60*60*24, val)) mdb_printf("%lluday%s", val, (val == 1) ? "" : "s"); else if (NOREMAINDER(ull, 1000000000ULL*60*60, val)) mdb_printf("%lluhour%s", val, (val == 1) ? "" : "s"); else if (NOREMAINDER(ull, 1000000000ULL*60, val)) mdb_printf("%lluminute%s", val, (val == 1) ? "" : "s"); else if (NOREMAINDER(ull, 1000000000ULL, val)) mdb_printf("%llusecond%s", val, (val == 1) ? "" : "s"); else if (NOREMAINDER(ull, 1000000ULL, val)) mdb_printf("%llums", val); else if (NOREMAINDER(ull, 1000ULL, val)) mdb_printf("%lluus", val); else mdb_printf("%lluns", ull); return (DCMD_OK); } /*ARGSUSED*/ static int eft_node(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct node node; int opt_v = 0; char buf[128]; if (!(flags & DCMD_ADDRSPEC)) addr = mdb_get_dot(); if (argc) { if (mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc) { return (DCMD_USAGE); } } if (addr == 0) return (DCMD_OK); if (mdb_vread(&node, sizeof (node), addr) != sizeof (node)) { mdb_warn("failed to read struct node at %p", addr); return (DCMD_ERR); } if (opt_v) { if (mdb_readstr(buf, (size_t)sizeof (buf), (uintptr_t)node.file) < 0) (void) mdb_snprintf(buf, (size_t)sizeof (buf), "<%p>", node.file); mdb_printf("%s len %d\n", buf, node.line); } switch (node.t) { case T_NOTHING: /* used to keep going on error cases */ mdb_printf("nothing"); break; case T_NAME: /* identifiers, sometimes chained */ if (mdb_readstr(buf, (size_t)sizeof (buf), (uintptr_t)node.u.name.s) < 0) (void) mdb_snprintf(buf, (size_t)sizeof (buf), "<%p>", node.u.name.s); mdb_printf("%s", buf); if (node.u.name.cp) { struct config cp; if (mdb_vread(&cp, sizeof (cp), (uintptr_t)node.u.name.cp) != sizeof (cp)) { mdb_warn("failed to read struct config at %p", node.u.name.cp); return (DCMD_ERR); } mdb_printf("%d", cp.num); } else if (node.u.name.it == IT_HORIZONTAL) { if (node.u.name.child && !node.u.name.childgen) { mdb_printf("<"); (void) eft_node((uintptr_t)node.u.name.child, DCMD_ADDRSPEC, 0, NULL); mdb_printf(">"); } else { mdb_printf("<> "); } } else if (node.u.name.child) { mdb_printf("["); (void) eft_node((uintptr_t)node.u.name.child, DCMD_ADDRSPEC, 0, NULL); mdb_printf("]"); } if (node.u.name.next) { if (node.u.name.it == IT_ENAME) mdb_printf("."); else mdb_printf("/"); (void) eft_node((uintptr_t)node.u.name.next, DCMD_ADDRSPEC, 0, NULL); } break; case T_GLOBID: /* globals (e.g. $a) */ if (mdb_readstr(buf, (size_t)sizeof (buf), (uintptr_t)node.u.globid.s) < 0) (void) mdb_snprintf(buf, (size_t)sizeof (buf), "<%p>", node.u.globid.s); mdb_printf("$%s", buf); break; case T_EVENT: /* class@path{expr} */ (void) eft_node((uintptr_t)node.u.event.ename, DCMD_ADDRSPEC, 0, NULL); mdb_printf("@"); (void) eft_node((uintptr_t)node.u.event.epname, DCMD_ADDRSPEC, 0, NULL); if (node.u.event.eexprlist) { mdb_printf(" { "); (void) eft_node((uintptr_t)node.u.event.eexprlist, DCMD_ADDRSPEC, 0, NULL); mdb_printf(" }"); } break; case T_ENGINE: /* upset threshold engine (e.g. SERD) */ mdb_printf("engine "); (void) eft_node((uintptr_t)node.u.event.ename, DCMD_ADDRSPEC, 0, NULL); break; case T_ASRU: /* ASRU declaration */ mdb_printf("asru "); (void) eft_node((uintptr_t)node.u.stmt.np, DCMD_ADDRSPEC, 0, NULL); if (node.u.stmt.nvpairs) { mdb_printf(" "); (void) eft_node((uintptr_t)node.u.stmt.nvpairs, DCMD_ADDRSPEC, 0, NULL); } break; case T_FRU: /* FRU declaration */ mdb_printf("fru "); (void) eft_node((uintptr_t)node.u.stmt.np, DCMD_ADDRSPEC, 0, NULL); if (node.u.stmt.nvpairs) { mdb_printf(" "); (void) eft_node((uintptr_t)node.u.stmt.nvpairs, DCMD_ADDRSPEC, 0, NULL); } break; case T_TIMEVAL: /* num w/time suffix (ns internally) */ { mdb_arg_t mdb_arg[2]; mdb_arg[0].a_type = MDB_TYPE_STRING; mdb_arg[0].a_un.a_str = "-l"; mdb_arg[1].a_type = MDB_TYPE_IMMEDIATE; mdb_arg[1].a_un.a_val = node.u.ull; (void) eft_time((uintptr_t)0, 0, 2, mdb_arg); break; } case T_NUM: /* num (ull internally) */ mdb_printf("%llu", node.u.ull); break; case T_QUOTE: /* quoted string */ if (mdb_readstr(buf, (size_t)sizeof (buf), (uintptr_t)node.u.quote.s) < 0) (void) mdb_snprintf(buf, (size_t)sizeof (buf), "<%p>", node.u.quote.s); mdb_printf("\"%s\"", buf); break; case T_FUNC: /* func(arglist) */ if (mdb_readstr(buf, (size_t)sizeof (buf), (uintptr_t)node.u.func.s) < 0) (void) mdb_snprintf(buf, (size_t)sizeof (buf), "<%p>", node.u.func.s); mdb_printf("%s(", buf); (void) eft_node((uintptr_t)node.u.func.arglist, DCMD_ADDRSPEC, 0, NULL); mdb_printf(")"); break; case T_NVPAIR: /* name=value pair in decl */ (void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0, NULL); mdb_printf(" = "); (void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0, NULL); break; case T_ASSIGN: /* assignment statement */ mdb_printf("("); (void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0, NULL); mdb_printf(" = "); (void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0, NULL); mdb_printf(")"); break; case T_CONDIF: /* a and T_CONDELSE in (a ? b : c ) */ mdb_printf("("); (void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0, NULL); mdb_printf(" ? "); (void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0, NULL); mdb_printf(")"); break; case T_CONDELSE: /* lists b and c in (a ? b : c ) */ (void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0, NULL); mdb_printf(" : "); (void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0, NULL); break; case T_NOT: /* boolean ! operator */ mdb_printf("!"); (void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0, NULL); break; case T_AND: /* boolean && operator */ (void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0, NULL); mdb_printf(" && "); (void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0, NULL); break; case T_OR: /* boolean || operator */ (void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0, NULL); mdb_printf(" || "); (void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0, NULL); break; case T_EQ: /* boolean == operator */ (void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0, NULL); mdb_printf(" == "); (void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0, NULL); break; case T_NE: /* boolean != operator */ (void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0, NULL); mdb_printf(" != "); (void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0, NULL); break; case T_SUB: /* integer - operator */ (void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0, NULL); mdb_printf(" - "); (void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0, NULL); break; case T_ADD: /* integer + operator */ (void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0, NULL); mdb_printf(" + "); (void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0, NULL); break; case T_MUL: /* integer * operator */ (void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0, NULL); mdb_printf(" * "); (void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0, NULL); break; case T_DIV: /* integer / operator */ (void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0, NULL); mdb_printf(" / "); (void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0, NULL); break; case T_MOD: /* integer % operator */ (void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0, NULL); mdb_printf(" % "); (void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0, NULL); break; case T_LT: /* boolean < operator */ (void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0, NULL); mdb_printf(" < "); (void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0, NULL); break; case T_LE: /* boolean <= operator */ (void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0, NULL); mdb_printf(" <= "); (void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0, NULL); break; case T_GT: /* boolean > operator */ (void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0, NULL); mdb_printf(" > "); (void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0, NULL); break; case T_GE: /* boolean >= operator */ (void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0, NULL); mdb_printf(" >= "); (void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0, NULL); break; case T_BITAND: /* bitwise & operator */ (void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0, NULL); mdb_printf(" & "); (void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0, NULL); break; case T_BITOR: /* bitwise | operator */ (void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0, NULL); mdb_printf(" | "); (void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0, NULL); break; case T_BITXOR: /* bitwise ^ operator */ (void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0, NULL); mdb_printf(" ^ "); (void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0, NULL); break; case T_BITNOT: /* bitwise ~ operator */ mdb_printf(" ~"); (void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0, NULL); break; case T_LSHIFT: /* bitwise << operator */ (void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0, NULL); mdb_printf(" << "); (void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0, NULL); break; case T_RSHIFT: /* bitwise >> operator */ (void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0, NULL); mdb_printf(" >> "); (void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0, NULL); break; case T_ARROW: /* lhs (N)->(K) rhs */ (void) eft_node((uintptr_t)node.u.arrow.lhs, DCMD_ADDRSPEC, 0, NULL); if (node.u.arrow.nnp) { mdb_printf("("); (void) eft_node((uintptr_t)node.u.arrow.nnp, DCMD_ADDRSPEC, 0, NULL); mdb_printf(")"); } mdb_printf("->"); if (node.u.arrow.knp) { mdb_printf("("); (void) eft_node((uintptr_t)node.u.arrow.knp, DCMD_ADDRSPEC, 0, NULL); mdb_printf(")"); } (void) eft_node((uintptr_t)node.u.arrow.rhs, DCMD_ADDRSPEC, 0, NULL); break; case T_LIST: /* comma-separated list */ (void) eft_node((uintptr_t)node.u.expr.left, DCMD_ADDRSPEC, 0, NULL); mdb_printf(", "); (void) eft_node((uintptr_t)node.u.expr.right, DCMD_ADDRSPEC, 0, NULL); break; case T_FAULT: /* fault declaration */ mdb_printf("fault."); (void) eft_node((uintptr_t)node.u.stmt.np, DCMD_ADDRSPEC, 0, NULL); if (node.u.stmt.nvpairs) { mdb_printf(" "); (void) eft_node((uintptr_t)node.u.stmt.nvpairs, DCMD_ADDRSPEC, 0, NULL); } break; case T_UPSET: /* upset declaration */ mdb_printf("upset."); (void) eft_node((uintptr_t)node.u.stmt.np, DCMD_ADDRSPEC, 0, NULL); if (node.u.stmt.nvpairs) { mdb_printf(" "); (void) eft_node((uintptr_t)node.u.stmt.nvpairs, DCMD_ADDRSPEC, 0, NULL); } break; case T_DEFECT: /* defect declaration */ mdb_printf("defect."); (void) eft_node((uintptr_t)node.u.stmt.np, DCMD_ADDRSPEC, 0, NULL); if (node.u.stmt.nvpairs) { mdb_printf(" "); (void) eft_node((uintptr_t)node.u.stmt.nvpairs, DCMD_ADDRSPEC, 0, NULL); } break; case T_ERROR: /* error declaration */ mdb_printf("error."); (void) eft_node((uintptr_t)node.u.stmt.np, DCMD_ADDRSPEC, 0, NULL); if (node.u.stmt.nvpairs) { mdb_printf(" "); (void) eft_node((uintptr_t)node.u.stmt.nvpairs, DCMD_ADDRSPEC, 0, NULL); } break; case T_EREPORT: /* ereport declaration */ mdb_printf("ereport."); (void) eft_node((uintptr_t)node.u.stmt.np, DCMD_ADDRSPEC, 0, NULL); if (node.u.stmt.nvpairs) { mdb_printf(" "); (void) eft_node((uintptr_t)node.u.stmt.nvpairs, DCMD_ADDRSPEC, 0, NULL); } break; case T_SERD: /* SERD engine declaration */ mdb_printf("serd."); (void) eft_node((uintptr_t)node.u.stmt.np, DCMD_ADDRSPEC, 0, NULL); if (node.u.stmt.nvpairs) { mdb_printf(" "); (void) eft_node((uintptr_t)node.u.stmt.nvpairs, DCMD_ADDRSPEC, 0, NULL); } else if (node.u.stmt.lutp) { if (mdb_pwalk_dcmd("lut", "eft_node", 0, NULL, (uintptr_t)node.u.stmt.lutp) != 0) return (DCMD_ERR); } break; case T_STAT: /* STAT engine declaration */ mdb_printf("stat."); (void) eft_node((uintptr_t)node.u.stmt.np, DCMD_ADDRSPEC, 0, NULL); if (node.u.stmt.nvpairs) { mdb_printf(" "); (void) eft_node((uintptr_t)node.u.stmt.nvpairs, DCMD_ADDRSPEC, 0, NULL); } else if (node.u.stmt.lutp) { if (mdb_pwalk_dcmd("lut", "eft_node", 0, NULL, (uintptr_t)node.u.stmt.lutp) != 0) return (DCMD_ERR); } break; case T_PROP: /* prop statement */ mdb_printf("prop "); (void) eft_node((uintptr_t)node.u.stmt.np, DCMD_ADDRSPEC, 0, NULL); break; case T_MASK: /* mask statement */ mdb_printf("mask "); (void) eft_node((uintptr_t)node.u.stmt.np, DCMD_ADDRSPEC, 0, NULL); break; case T_CONFIG: /* config statement */ mdb_printf("config "); (void) eft_node((uintptr_t)node.u.stmt.np, DCMD_ADDRSPEC, 0, NULL); if (node.u.stmt.nvpairs) { mdb_printf(" "); (void) eft_node((uintptr_t)node.u.stmt.nvpairs, DCMD_ADDRSPEC, 0, NULL); } break; default: mdb_printf("not a eversholt node\n"); break; } return (DCMD_OK); } static const mdb_walker_t walkers[] = { { "lut", "walk a lookup table", lut_walk_init, lut_walk_step, lut_walk_fini, NULL }, { "eft_ipath", "walk ipath", ipath_walk_init, ipath_walk_step, ipath_walk_fini, NULL }, { NULL, NULL, NULL, NULL, NULL, NULL } }; static const mdb_dcmd_t dcmds[] = { { "eft_ipath", "?", "print an ipath", ipath }, { "eft_count", "?", "print eversholt stats", eft_count }, { "eft_node", "?[-v]", "print eversholt node", eft_node }, { "eft_time", "?[-p][-l time]", "print eversholt timeval", eft_time }, { NULL } }; static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers }; const mdb_modinfo_t * _mdb_init(void) { return (&modinfo); } /* * 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 #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); 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 *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_unit_translate(0, croot, TOPO_PROP_RESOURCE, &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*/ break; 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); } /* * 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.h -- public definitions for eval module * */ #ifndef _EFT_EVAL_H #define _EFT_EVAL_H #ifdef __cplusplus extern "C" { #endif struct evalue { enum datatype { UNDEFINED = 0, UINT64, /* use for Boolean as well */ STRING, /* usually pointer from stable() */ NODEPTR /* (struct node *) */ } t; /* * using v to handle all values eliminates the need for switch() * blocks during assignments, comparisons and other operations */ unsigned long long v; }; int eval_potential(struct node *np, struct lut *ex, struct node *events[], struct node **newc, struct config *croot); 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); #ifdef __cplusplus } #endif #endif /* _EFT_EVAL_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * evnv.c -- eversholt specific nvpair manipulation functions * * this module provides the simulated fault management exercise. */ #include #include #include #include "evnv.h" #include "out.h" #define min(a, b) (((a) <= (b)) ? (a) : (b)) extern nv_alloc_t Eft_nv_hdl; static void outindent(int depth) { while (depth-- > 0) out(O_ALTFP|O_VERB3|O_NONL, " "); } /* * evnv_cmpnvl -- compare two asrus in their nvlist form */ int evnv_cmpnvl(nvlist_t *nvl1, nvlist_t *nvl2, int depth) { /* * an assumption here is that each list was constructed in the * same order, which is a safe assumption since we built the * list of ourself (well, libtopo did at any rate) */ data_type_t t1, t2; nvlist_t **la1 = NULL; nvlist_t **la2 = NULL; nvlist_t *l1 = NULL; nvlist_t *l2 = NULL; nvpair_t *p1 = NULL; nvpair_t *p2 = NULL; uint64_t lv1, lv2; uint_t m, na1, na2; char *s1, *s2; int ret, i; for (;;) { p1 = nvlist_next_nvpair(nvl1, p1); p2 = nvlist_next_nvpair(nvl2, p2); if (p1 == NULL && p2 == NULL) { outindent(depth); out(O_ALTFP|O_VERB3, "equal nvls\n"); return (0); } if (p1 == NULL) return (-1); if (p2 == NULL) return (1); s1 = nvpair_name(p1); s2 = nvpair_name(p2); outindent(depth); out(O_ALTFP|O_VERB3, "cmpnvl: pair %s vs %s", s1, s2); if ((ret = strcmp(s1, s2)) != 0) return (ret); t1 = nvpair_type(p1); t2 = nvpair_type(p2); if (t1 != t2) return (t1 - t2); /* * We don't compare all possible types, just the * ones we know are likely to actually be present * in nvlists we've generated. */ switch (t1) { case DATA_TYPE_NVLIST: (void) nvpair_value_nvlist(p1, &l1); (void) nvpair_value_nvlist(p2, &l2); if ((ret = evnv_cmpnvl(l1, l2, depth + 1)) != 0) return (ret); break; case DATA_TYPE_NVLIST_ARRAY: (void) nvpair_value_nvlist_array(p1, &la1, &na1); (void) nvpair_value_nvlist_array(p2, &la2, &na2); m = min(na1, na2); for (i = 0; i < m; i++) { if ((ret = evnv_cmpnvl(*la1, *la2, depth + 1)) != 0) return (ret); la1++; la2++; } if (na1 < na2) return (-1); else if (na2 < na1) return (1); break; case DATA_TYPE_STRING: (void) nvpair_value_string(p1, &s1); (void) nvpair_value_string(p2, &s2); if ((ret = strcmp(s1, s2)) != 0) { outindent(depth); if (ret < 0) out(O_ALTFP|O_VERB3, "cmpnvl: %s < %s", s1, s2); else out(O_ALTFP|O_VERB3, "cmpnvl: %s > %s", s1, s2); return (ret); } break; case DATA_TYPE_UINT64: lv1 = lv2 = 0; (void) nvpair_value_uint64(p1, &lv1); (void) nvpair_value_uint64(p2, &lv2); outindent(depth); out(O_ALTFP|O_VERB3, "cmpnvl: %llu vs %llu", lv1, lv2); if (lv1 > lv2) return (1); else if (lv2 > lv1) return (-1); break; case DATA_TYPE_INT64: lv1 = lv2 = 0; (void) nvpair_value_int64(p1, (int64_t *)&lv1); (void) nvpair_value_int64(p2, (int64_t *)&lv2); outindent(depth); out(O_ALTFP|O_VERB3, "cmpnvl: %lld vs %lld", lv1, lv2); if (lv1 > lv2) return (1); else if (lv2 > lv1) return (-1); break; case DATA_TYPE_UINT32: lv1 = lv2 = 0; (void) nvpair_value_uint32(p1, (uint32_t *)&lv1); (void) nvpair_value_uint32(p2, (uint32_t *)&lv2); outindent(depth); out(O_ALTFP|O_VERB3, "cmpnvl: %u vs %u", *(uint32_t *)&lv1, *(uint32_t *)&lv2); if (lv1 > lv2) return (1); else if (lv2 > lv1) return (-1); break; case DATA_TYPE_INT32: lv1 = lv2 = 0; (void) nvpair_value_int32(p1, (int32_t *)&lv1); (void) nvpair_value_int32(p2, (int32_t *)&lv2); outindent(depth); out(O_ALTFP|O_VERB3, "cmpnvl: %d vs %d", *(int32_t *)&lv1, *(int32_t *)&lv2); if (lv1 > lv2) return (1); else if (lv2 > lv1) return (-1); break; case DATA_TYPE_UINT16: lv1 = lv2 = 0; (void) nvpair_value_uint16(p1, (uint16_t *)&lv1); (void) nvpair_value_uint16(p2, (uint16_t *)&lv2); outindent(depth); out(O_ALTFP|O_VERB3, "cmpnvl: %u vs %u", *(uint16_t *)&lv1, *(uint16_t *)&lv2); if (lv1 > lv2) return (1); else if (lv2 > lv1) return (-1); break; case DATA_TYPE_INT16: lv1 = lv2 = 0; (void) nvpair_value_int16(p1, (int16_t *)&lv1); (void) nvpair_value_int16(p2, (int16_t *)&lv2); outindent(depth); out(O_ALTFP|O_VERB3, "cmpnvl: %d vs %d", *(int16_t *)&lv1, *(int16_t *)&lv2); if (lv1 > lv2) return (1); else if (lv2 > lv1) return (-1); break; case DATA_TYPE_UINT8: lv1 = lv2 = 0; (void) nvpair_value_uint8(p1, (uint8_t *)&lv1); (void) nvpair_value_uint8(p2, (uint8_t *)&lv2); outindent(depth); out(O_ALTFP|O_VERB3, "cmpnvl: %u vs %u", *(uint8_t *)&lv1, *(uint8_t *)&lv2); if (lv1 > lv2) return (1); else if (lv2 > lv1) return (-1); break; case DATA_TYPE_INT8: lv1 = lv2 = 0; (void) nvpair_value_int8(p1, (int8_t *)&lv1); (void) nvpair_value_int8(p2, (int8_t *)&lv2); outindent(depth); out(O_ALTFP|O_VERB3, "cmpnvl: %d vs %d", *(int8_t *)&lv1, *(int8_t *)&lv2); if (lv1 > lv2) return (1); else if (lv2 > lv1) return (-1); break; } } } /* * evnv_dupnvl -- duplicate a payload nvlist, keeping only the interesting stuff */ nvlist_t * evnv_dupnvl(nvlist_t *nvp) { nvlist_t *retval = NULL; int nvret; if (nvp == NULL) return (NULL); if ((nvret = nvlist_xdup(nvp, &retval, &Eft_nv_hdl)) != 0) out(O_DIE, "dupnvl: dup failed: %d", nvret); return (retval); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * evnv.h -- public definitions for evnv module * */ #ifndef _EFT_EVNV_H #define _EFT_EVNV_H #ifdef __cplusplus extern "C" { #endif int evnv_cmpnvl(nvlist_t *nvl1, nvlist_t *nvl2, int depth); nvlist_t *evnv_dupnvl(nvlist_t *nvp); #ifdef __cplusplus } #endif #endif /* _EFT_EVNV_H */ /* * 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 (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2012 Milan Jurik. All rights reserved. * Copyright (c) 2018, Joyent, Inc. * * fme.c -- fault management exercise module * * this module provides the simulated fault management exercise. */ #include #include #include #include #include #include #include #include #include #include #include "alloc.h" #include "out.h" #include "stats.h" #include "stable.h" #include "literals.h" #include "lut.h" #include "tree.h" #include "ptree.h" #include "itree.h" #include "ipath.h" #include "fme.h" #include "evnv.h" #include "eval.h" #include "config.h" #include "platform.h" #include "esclex.h" struct lut *Istats; struct lut *SerdEngines; nvlist_t *Action_nvl; /* imported from eft.c... */ extern hrtime_t Hesitate; extern char *Serd_Override; extern nv_alloc_t Eft_nv_hdl; extern int Max_fme; extern fmd_hdl_t *Hdl; static int Istat_need_save; static int Serd_need_save; void istat_save(void); void serd_save(void); /* fme under construction is global so we can free it on module abort */ static struct fme *Nfmep; static int Undiag_reason = UD_VAL_UNKNOWN; static int Nextid = 0; static int Open_fme_count = 0; /* Count of open FMEs */ /* list of fault management exercises underway */ static struct fme { struct fme *next; /* next exercise */ unsigned long long ull; /* time when fme was created */ int id; /* FME id */ struct config *config; /* cooked configuration data */ struct lut *eventtree; /* propagation tree for this FME */ /* * The initial error report that created this FME is kept in * two forms. e0 points to the instance tree node and is used * by fme_eval() as the starting point for the inference * algorithm. e0r is the event handle FMD passed to us when * the ereport first arrived and is used when setting timers, * which are always relative to the time of this initial * report. */ struct event *e0; fmd_event_t *e0r; id_t timer; /* for setting an fmd time-out */ struct event *ecurrent; /* ereport under consideration */ struct event *suspects; /* current suspect list */ struct event *psuspects; /* previous suspect list */ int nsuspects; /* count of suspects */ int posted_suspects; /* true if we've posted a diagnosis */ int uniqobs; /* number of unique events observed */ int peek; /* just peeking, don't track suspects */ int overflow; /* true if overflow FME */ enum fme_state { FME_NOTHING = 5000, /* not evaluated yet */ FME_WAIT, /* need to wait for more info */ FME_CREDIBLE, /* suspect list is credible */ FME_DISPROVED, /* no valid suspects found */ FME_DEFERRED /* don't know yet (k-count not met) */ } state; unsigned long long pull; /* time passed since created */ unsigned long long wull; /* wait until this time for re-eval */ struct event *observations; /* observation list */ struct lut *globals; /* values of global variables */ /* fmd interfacing */ fmd_hdl_t *hdl; /* handle for talking with fmd */ fmd_case_t *fmcase; /* what fmd 'case' we associate with */ /* stats */ struct stats *Rcount; struct stats *Hcallcount; struct stats *Rcallcount; struct stats *Ccallcount; struct stats *Ecallcount; struct stats *Tcallcount; struct stats *Marrowcount; struct stats *diags; } *FMElist, *EFMElist, *ClosedFMEs; static struct case_list { fmd_case_t *fmcase; struct case_list *next; } *Undiagablecaselist; static void fme_eval(struct fme *fmep, fmd_event_t *ffep); static enum fme_state hypothesise(struct fme *fmep, struct event *ep, unsigned long long at_latest_by, unsigned long long *pdelay); static struct node *eventprop_lookup(struct event *ep, const char *propname); static struct node *pathstring2epnamenp(char *path); static void publish_undiagnosable(fmd_hdl_t *hdl, fmd_event_t *ffep, fmd_case_t *fmcase, nvlist_t *detector, char *arg); static char *undiag_2reason_str(int ud, char *arg); static const char *undiag_2defect_str(int ud); static void restore_suspects(struct fme *fmep); static void save_suspects(struct fme *fmep); static void destroy_fme(struct fme *f); static void fme_receive_report(fmd_hdl_t *hdl, fmd_event_t *ffep, const char *eventstring, const struct ipath *ipp, nvlist_t *nvl); static void istat_counter_reset_cb(struct istat_entry *entp, struct stats *statp, const struct ipath *ipp); static void istat_counter_topo_chg_cb(struct istat_entry *entp, struct stats *statp, void *unused); static void serd_reset_cb(struct serd_entry *entp, void *unused, const struct ipath *ipp); static void serd_topo_chg_cb(struct serd_entry *entp, void *unused, void *unused2); static void destroy_fme_bufs(struct fme *fp); static struct fme * alloc_fme(void) { struct fme *fmep; fmep = MALLOC(sizeof (*fmep)); bzero(fmep, sizeof (*fmep)); return (fmep); } /* * fme_ready -- called when all initialization of the FME (except for * stats) has completed successfully. Adds the fme to global lists * and establishes its stats. */ static struct fme * fme_ready(struct fme *fmep) { char nbuf[100]; Nfmep = NULL; /* don't need to free this on module abort now */ if (EFMElist) { EFMElist->next = fmep; EFMElist = fmep; } else FMElist = EFMElist = fmep; (void) sprintf(nbuf, "fme%d.Rcount", fmep->id); fmep->Rcount = stats_new_counter(nbuf, "ereports received", 0); (void) sprintf(nbuf, "fme%d.Hcall", fmep->id); fmep->Hcallcount = stats_new_counter(nbuf, "calls to hypothesise()", 1); (void) sprintf(nbuf, "fme%d.Rcall", fmep->id); fmep->Rcallcount = stats_new_counter(nbuf, "calls to requirements_test()", 1); (void) sprintf(nbuf, "fme%d.Ccall", fmep->id); fmep->Ccallcount = stats_new_counter(nbuf, "calls to causes_test()", 1); (void) sprintf(nbuf, "fme%d.Ecall", fmep->id); fmep->Ecallcount = stats_new_counter(nbuf, "calls to effects_test()", 1); (void) sprintf(nbuf, "fme%d.Tcall", fmep->id); fmep->Tcallcount = stats_new_counter(nbuf, "calls to triggered()", 1); (void) sprintf(nbuf, "fme%d.Marrow", fmep->id); fmep->Marrowcount = stats_new_counter(nbuf, "arrows marked by mark_arrows()", 1); (void) sprintf(nbuf, "fme%d.diags", fmep->id); fmep->diags = stats_new_counter(nbuf, "suspect lists diagnosed", 0); out(O_ALTFP|O_VERB2, "newfme: config snapshot contains..."); config_print(O_ALTFP|O_VERB2, fmep->config); return (fmep); } extern void ipath_dummy_lut(struct arrow *); extern struct lut *itree_create_dummy(const char *, const struct ipath *); /* ARGSUSED */ static void set_needed_arrows(struct event *ep, struct event *ep2, struct fme *fmep) { struct bubble *bp; struct arrowlist *ap; for (bp = itree_next_bubble(ep, NULL); bp; bp = itree_next_bubble(ep, bp)) { if (bp->t != B_FROM) continue; for (ap = itree_next_arrow(bp, NULL); ap; ap = itree_next_arrow(bp, ap)) { ap->arrowp->pnode->u.arrow.needed = 1; ipath_dummy_lut(ap->arrowp); } } } /* ARGSUSED */ static void unset_needed_arrows(struct event *ep, struct event *ep2, struct fme *fmep) { struct bubble *bp; struct arrowlist *ap; for (bp = itree_next_bubble(ep, NULL); bp; bp = itree_next_bubble(ep, bp)) { if (bp->t != B_FROM) continue; for (ap = itree_next_arrow(bp, NULL); ap; ap = itree_next_arrow(bp, ap)) ap->arrowp->pnode->u.arrow.needed = 0; } } static void globals_destructor(void *left, void *right, void *arg); static void clear_arrows(struct event *ep, struct event *ep2, struct fme *fmep); static boolean_t prune_propagations(const char *e0class, const struct ipath *e0ipp) { char nbuf[100]; unsigned long long my_delay = TIMEVAL_EVENTUALLY; extern struct lut *Usednames; Nfmep = alloc_fme(); Nfmep->id = Nextid; Nfmep->state = FME_NOTHING; Nfmep->eventtree = itree_create_dummy(e0class, e0ipp); if ((Nfmep->e0 = itree_lookup(Nfmep->eventtree, e0class, e0ipp)) == NULL) { itree_free(Nfmep->eventtree); FREE(Nfmep); Nfmep = NULL; return (B_FALSE); } Nfmep->ecurrent = Nfmep->observations = Nfmep->e0; Nfmep->e0->count++; (void) sprintf(nbuf, "fme%d.Rcount", Nfmep->id); Nfmep->Rcount = stats_new_counter(nbuf, "ereports received", 0); (void) sprintf(nbuf, "fme%d.Hcall", Nfmep->id); Nfmep->Hcallcount = stats_new_counter(nbuf, "calls to hypothesise()", 1); (void) sprintf(nbuf, "fme%d.Rcall", Nfmep->id); Nfmep->Rcallcount = stats_new_counter(nbuf, "calls to requirements_test()", 1); (void) sprintf(nbuf, "fme%d.Ccall", Nfmep->id); Nfmep->Ccallcount = stats_new_counter(nbuf, "calls to causes_test()", 1); (void) sprintf(nbuf, "fme%d.Ecall", Nfmep->id); Nfmep->Ecallcount = stats_new_counter(nbuf, "calls to effects_test()", 1); (void) sprintf(nbuf, "fme%d.Tcall", Nfmep->id); Nfmep->Tcallcount = stats_new_counter(nbuf, "calls to triggered()", 1); (void) sprintf(nbuf, "fme%d.Marrow", Nfmep->id); Nfmep->Marrowcount = stats_new_counter(nbuf, "arrows marked by mark_arrows()", 1); (void) sprintf(nbuf, "fme%d.diags", Nfmep->id); Nfmep->diags = stats_new_counter(nbuf, "suspect lists diagnosed", 0); Nfmep->peek = 1; lut_walk(Nfmep->eventtree, (lut_cb)unset_needed_arrows, (void *)Nfmep); lut_free(Usednames, NULL, NULL); Usednames = NULL; lut_walk(Nfmep->eventtree, (lut_cb)clear_arrows, (void *)Nfmep); (void) hypothesise(Nfmep, Nfmep->e0, Nfmep->ull, &my_delay); itree_prune(Nfmep->eventtree); lut_walk(Nfmep->eventtree, (lut_cb)set_needed_arrows, (void *)Nfmep); stats_delete(Nfmep->Rcount); stats_delete(Nfmep->Hcallcount); stats_delete(Nfmep->Rcallcount); stats_delete(Nfmep->Ccallcount); stats_delete(Nfmep->Ecallcount); stats_delete(Nfmep->Tcallcount); stats_delete(Nfmep->Marrowcount); stats_delete(Nfmep->diags); itree_free(Nfmep->eventtree); lut_free(Nfmep->globals, globals_destructor, NULL); FREE(Nfmep); return (B_TRUE); } static struct fme * newfme(const char *e0class, const struct ipath *e0ipp, fmd_hdl_t *hdl, fmd_case_t *fmcase, fmd_event_t *ffep, nvlist_t *nvl) { struct cfgdata *cfgdata; int init_size; extern int alloc_total(); nvlist_t *detector = NULL; char *pathstr; char *arg; /* * First check if e0ipp is actually in the topology so we can give a * more useful error message. */ ipathlastcomp(e0ipp); pathstr = ipath2str(NULL, e0ipp); cfgdata = config_snapshot(); platform_unit_translate(0, cfgdata->cooked, TOPO_PROP_RESOURCE, &detector, pathstr); FREE(pathstr); structconfig_free(cfgdata->cooked); config_free(cfgdata); if (detector == NULL) { /* See if class permits silent discard on unknown component. */ if (lut_lookup(Ereportenames_discard, (void *)e0class, NULL)) { out(O_ALTFP|O_VERB2, "Unable to map \"%s\" ereport " "to component path, but silent discard allowed.", e0class); fmd_case_close(hdl, fmcase); } else { Undiag_reason = UD_VAL_BADEVENTPATH; (void) nvlist_lookup_nvlist(nvl, FM_EREPORT_DETECTOR, &detector); arg = ipath2str(e0class, e0ipp); publish_undiagnosable(hdl, ffep, fmcase, detector, arg); FREE(arg); } return (NULL); } /* * Next run a quick first pass of the rules with a dummy config. This * allows us to prune those rules which can't possibly cause this * ereport. */ if (!prune_propagations(e0class, e0ipp)) { /* * The fault class must have been in the rules or we would * not have registered for it (and got a "nosub"), and the * pathname must be in the topology or we would have failed the * previous test. So to get here means the combination of * class and pathname in the ereport must be invalid. */ Undiag_reason = UD_VAL_BADEVENTCLASS; arg = ipath2str(e0class, e0ipp); publish_undiagnosable(hdl, ffep, fmcase, detector, arg); nvlist_free(detector); FREE(arg); return (NULL); } /* * Now go ahead and create the real fme using the pruned rules. */ init_size = alloc_total(); out(O_ALTFP|O_STAMP, "start config_snapshot using %d bytes", init_size); nvlist_free(detector); pathstr = ipath2str(NULL, e0ipp); cfgdata = config_snapshot(); platform_unit_translate(0, cfgdata->cooked, TOPO_PROP_RESOURCE, &detector, pathstr); FREE(pathstr); platform_save_config(hdl, fmcase); out(O_ALTFP|O_STAMP, "config_snapshot added %d bytes", alloc_total() - init_size); Nfmep = alloc_fme(); Nfmep->id = Nextid++; Nfmep->config = cfgdata->cooked; config_free(cfgdata); Nfmep->posted_suspects = 0; Nfmep->uniqobs = 0; Nfmep->state = FME_NOTHING; Nfmep->pull = 0ULL; Nfmep->overflow = 0; Nfmep->fmcase = fmcase; Nfmep->hdl = hdl; if ((Nfmep->eventtree = itree_create(Nfmep->config)) == NULL) { Undiag_reason = UD_VAL_INSTFAIL; arg = ipath2str(e0class, e0ipp); publish_undiagnosable(hdl, ffep, fmcase, detector, arg); nvlist_free(detector); FREE(arg); structconfig_free(Nfmep->config); destroy_fme_bufs(Nfmep); FREE(Nfmep); Nfmep = NULL; return (NULL); } itree_ptree(O_ALTFP|O_VERB2, Nfmep->eventtree); if ((Nfmep->e0 = itree_lookup(Nfmep->eventtree, e0class, e0ipp)) == NULL) { Undiag_reason = UD_VAL_BADEVENTI; arg = ipath2str(e0class, e0ipp); publish_undiagnosable(hdl, ffep, fmcase, detector, arg); nvlist_free(detector); FREE(arg); itree_free(Nfmep->eventtree); structconfig_free(Nfmep->config); destroy_fme_bufs(Nfmep); FREE(Nfmep); Nfmep = NULL; return (NULL); } nvlist_free(detector); return (fme_ready(Nfmep)); } void fme_fini(void) { struct fme *sfp, *fp; struct case_list *ucasep, *nextcasep; ucasep = Undiagablecaselist; while (ucasep != NULL) { nextcasep = ucasep->next; FREE(ucasep); ucasep = nextcasep; } Undiagablecaselist = NULL; /* clean up closed fmes */ fp = ClosedFMEs; while (fp != NULL) { sfp = fp->next; destroy_fme(fp); fp = sfp; } ClosedFMEs = NULL; fp = FMElist; while (fp != NULL) { sfp = fp->next; destroy_fme(fp); fp = sfp; } FMElist = EFMElist = NULL; /* if we were in the middle of creating an fme, free it now */ if (Nfmep) { destroy_fme(Nfmep); Nfmep = NULL; } } /* * Allocated space for a buffer name. 20 bytes allows for * a ridiculous 9,999,999 unique observations. */ #define OBBUFNMSZ 20 /* * serialize_observation * * Create a recoverable version of the current observation * (f->ecurrent). We keep a serialized version of each unique * observation in order that we may resume correctly the fme in the * correct state if eft or fmd crashes and we're restarted. */ static void serialize_observation(struct fme *fp, const char *cls, const struct ipath *ipp) { size_t pkdlen; char tmpbuf[OBBUFNMSZ]; char *pkd = NULL; char *estr; (void) snprintf(tmpbuf, OBBUFNMSZ, "observed%d", fp->uniqobs); estr = ipath2str(cls, ipp); fmd_buf_create(fp->hdl, fp->fmcase, tmpbuf, strlen(estr) + 1); fmd_buf_write(fp->hdl, fp->fmcase, tmpbuf, (void *)estr, strlen(estr) + 1); FREE(estr); if (fp->ecurrent != NULL && fp->ecurrent->nvp != NULL) { (void) snprintf(tmpbuf, OBBUFNMSZ, "observed%d.nvp", fp->uniqobs); if (nvlist_xpack(fp->ecurrent->nvp, &pkd, &pkdlen, NV_ENCODE_XDR, &Eft_nv_hdl) != 0) out(O_DIE|O_SYS, "pack of observed nvl failed"); fmd_buf_create(fp->hdl, fp->fmcase, tmpbuf, pkdlen); fmd_buf_write(fp->hdl, fp->fmcase, tmpbuf, (void *)pkd, pkdlen); FREE(pkd); } fp->uniqobs++; fmd_buf_write(fp->hdl, fp->fmcase, WOBUF_NOBS, (void *)&fp->uniqobs, sizeof (fp->uniqobs)); } /* * init_fme_bufs -- We keep several bits of state about an fme for * use if eft or fmd crashes and we're restarted. */ static void init_fme_bufs(struct fme *fp) { fmd_buf_create(fp->hdl, fp->fmcase, WOBUF_PULL, sizeof (fp->pull)); fmd_buf_write(fp->hdl, fp->fmcase, WOBUF_PULL, (void *)&fp->pull, sizeof (fp->pull)); fmd_buf_create(fp->hdl, fp->fmcase, WOBUF_ID, sizeof (fp->id)); fmd_buf_write(fp->hdl, fp->fmcase, WOBUF_ID, (void *)&fp->id, sizeof (fp->id)); fmd_buf_create(fp->hdl, fp->fmcase, WOBUF_NOBS, sizeof (fp->uniqobs)); fmd_buf_write(fp->hdl, fp->fmcase, WOBUF_NOBS, (void *)&fp->uniqobs, sizeof (fp->uniqobs)); fmd_buf_create(fp->hdl, fp->fmcase, WOBUF_POSTD, sizeof (fp->posted_suspects)); fmd_buf_write(fp->hdl, fp->fmcase, WOBUF_POSTD, (void *)&fp->posted_suspects, sizeof (fp->posted_suspects)); } static void destroy_fme_bufs(struct fme *fp) { char tmpbuf[OBBUFNMSZ]; int o; platform_restore_config(fp->hdl, fp->fmcase); fmd_buf_destroy(fp->hdl, fp->fmcase, WOBUF_CFGLEN); fmd_buf_destroy(fp->hdl, fp->fmcase, WOBUF_CFG); fmd_buf_destroy(fp->hdl, fp->fmcase, WOBUF_PULL); fmd_buf_destroy(fp->hdl, fp->fmcase, WOBUF_ID); fmd_buf_destroy(fp->hdl, fp->fmcase, WOBUF_POSTD); fmd_buf_destroy(fp->hdl, fp->fmcase, WOBUF_NOBS); for (o = 0; o < fp->uniqobs; o++) { (void) snprintf(tmpbuf, OBBUFNMSZ, "observed%d", o); fmd_buf_destroy(fp->hdl, fp->fmcase, tmpbuf); (void) snprintf(tmpbuf, OBBUFNMSZ, "observed%d.nvp", o); fmd_buf_destroy(fp->hdl, fp->fmcase, tmpbuf); } } /* * reconstitute_observations -- convert a case's serialized observations * back into struct events. Returns zero if all observations are * successfully reconstituted. */ static int reconstitute_observations(struct fme *fmep) { struct event *ep; struct node *epnamenp = NULL; size_t pkdlen; char *pkd = NULL; char *tmpbuf = alloca(OBBUFNMSZ); char *sepptr; char *estr; int ocnt; int elen; for (ocnt = 0; ocnt < fmep->uniqobs; ocnt++) { (void) snprintf(tmpbuf, OBBUFNMSZ, "observed%d", ocnt); elen = fmd_buf_size(fmep->hdl, fmep->fmcase, tmpbuf); if (elen == 0) { out(O_ALTFP, "reconstitute_observation: no %s buffer found.", tmpbuf); Undiag_reason = UD_VAL_MISSINGOBS; break; } estr = MALLOC(elen); fmd_buf_read(fmep->hdl, fmep->fmcase, tmpbuf, estr, elen); sepptr = strchr(estr, '@'); if (sepptr == NULL) { out(O_ALTFP, "reconstitute_observation: %s: " "missing @ separator in %s.", tmpbuf, estr); Undiag_reason = UD_VAL_MISSINGPATH; FREE(estr); break; } *sepptr = '\0'; if ((epnamenp = pathstring2epnamenp(sepptr + 1)) == NULL) { out(O_ALTFP, "reconstitute_observation: %s: " "trouble converting path string \"%s\" " "to internal representation.", tmpbuf, sepptr + 1); Undiag_reason = UD_VAL_MISSINGPATH; FREE(estr); break; } /* construct the event */ ep = itree_lookup(fmep->eventtree, stable(estr), ipath(epnamenp)); if (ep == NULL) { out(O_ALTFP, "reconstitute_observation: %s: " "lookup of \"%s\" in itree failed.", tmpbuf, ipath2str(estr, ipath(epnamenp))); Undiag_reason = UD_VAL_BADOBS; tree_free(epnamenp); FREE(estr); break; } tree_free(epnamenp); /* * We may or may not have a saved nvlist for the observation */ (void) snprintf(tmpbuf, OBBUFNMSZ, "observed%d.nvp", ocnt); pkdlen = fmd_buf_size(fmep->hdl, fmep->fmcase, tmpbuf); if (pkdlen != 0) { pkd = MALLOC(pkdlen); fmd_buf_read(fmep->hdl, fmep->fmcase, tmpbuf, pkd, pkdlen); ASSERT(ep->nvp == NULL); if (nvlist_xunpack(pkd, pkdlen, &ep->nvp, &Eft_nv_hdl) != 0) out(O_DIE|O_SYS, "pack of observed nvl failed"); FREE(pkd); } if (ocnt == 0) fmep->e0 = ep; FREE(estr); fmep->ecurrent = ep; ep->count++; /* link it into list of observations seen */ ep->observations = fmep->observations; fmep->observations = ep; } if (ocnt == fmep->uniqobs) { (void) fme_ready(fmep); return (0); } return (1); } /* * restart_fme -- called during eft initialization. Reconstitutes * an in-progress fme. */ void fme_restart(fmd_hdl_t *hdl, fmd_case_t *inprogress) { nvlist_t *defect; struct case_list *bad; struct fme *fmep; struct cfgdata *cfgdata; size_t rawsz; struct event *ep; char *tmpbuf = alloca(OBBUFNMSZ); char *sepptr; char *estr; int elen; struct node *epnamenp = NULL; int init_size; extern int alloc_total(); char *reason; /* * ignore solved or closed cases */ if (fmd_case_solved(hdl, inprogress) || fmd_case_closed(hdl, inprogress)) return; fmep = alloc_fme(); fmep->fmcase = inprogress; fmep->hdl = hdl; if (fmd_buf_size(hdl, inprogress, WOBUF_POSTD) == 0) { out(O_ALTFP, "restart_fme: no saved posted status"); Undiag_reason = UD_VAL_MISSINGINFO; goto badcase; } else { fmd_buf_read(hdl, inprogress, WOBUF_POSTD, (void *)&fmep->posted_suspects, sizeof (fmep->posted_suspects)); } if (fmd_buf_size(hdl, inprogress, WOBUF_ID) == 0) { out(O_ALTFP, "restart_fme: no saved id"); Undiag_reason = UD_VAL_MISSINGINFO; goto badcase; } else { fmd_buf_read(hdl, inprogress, WOBUF_ID, (void *)&fmep->id, sizeof (fmep->id)); } if (Nextid <= fmep->id) Nextid = fmep->id + 1; out(O_ALTFP, "Replay FME %d", fmep->id); if (fmd_buf_size(hdl, inprogress, WOBUF_CFGLEN) != sizeof (size_t)) { out(O_ALTFP, "restart_fme: No config data"); Undiag_reason = UD_VAL_MISSINGINFO; goto badcase; } fmd_buf_read(hdl, inprogress, WOBUF_CFGLEN, (void *)&rawsz, sizeof (size_t)); if ((fmep->e0r = fmd_case_getprincipal(hdl, inprogress)) == NULL) { out(O_ALTFP, "restart_fme: No event zero"); Undiag_reason = UD_VAL_MISSINGZERO; goto badcase; } if (fmd_buf_size(hdl, inprogress, WOBUF_PULL) == 0) { out(O_ALTFP, "restart_fme: no saved wait time"); Undiag_reason = UD_VAL_MISSINGINFO; goto badcase; } else { fmd_buf_read(hdl, inprogress, WOBUF_PULL, (void *)&fmep->pull, sizeof (fmep->pull)); } if (fmd_buf_size(hdl, inprogress, WOBUF_NOBS) == 0) { out(O_ALTFP, "restart_fme: no count of observations"); Undiag_reason = UD_VAL_MISSINGINFO; goto badcase; } else { fmd_buf_read(hdl, inprogress, WOBUF_NOBS, (void *)&fmep->uniqobs, sizeof (fmep->uniqobs)); } (void) snprintf(tmpbuf, OBBUFNMSZ, "observed0"); elen = fmd_buf_size(fmep->hdl, fmep->fmcase, tmpbuf); if (elen == 0) { out(O_ALTFP, "reconstitute_observation: no %s buffer found.", tmpbuf); Undiag_reason = UD_VAL_MISSINGOBS; goto badcase; } estr = MALLOC(elen); fmd_buf_read(fmep->hdl, fmep->fmcase, tmpbuf, estr, elen); sepptr = strchr(estr, '@'); if (sepptr == NULL) { out(O_ALTFP, "reconstitute_observation: %s: " "missing @ separator in %s.", tmpbuf, estr); Undiag_reason = UD_VAL_MISSINGPATH; FREE(estr); goto badcase; } *sepptr = '\0'; if ((epnamenp = pathstring2epnamenp(sepptr + 1)) == NULL) { out(O_ALTFP, "reconstitute_observation: %s: " "trouble converting path string \"%s\" " "to internal representation.", tmpbuf, sepptr + 1); Undiag_reason = UD_VAL_MISSINGPATH; FREE(estr); goto badcase; } (void) prune_propagations(stable(estr), ipath(epnamenp)); tree_free(epnamenp); FREE(estr); init_size = alloc_total(); out(O_ALTFP|O_STAMP, "start config_restore using %d bytes", init_size); cfgdata = MALLOC(sizeof (struct cfgdata)); cfgdata->cooked = NULL; cfgdata->devcache = NULL; cfgdata->devidcache = NULL; cfgdata->tpcache = NULL; cfgdata->cpucache = NULL; cfgdata->raw_refcnt = 1; if (rawsz > 0) { if (fmd_buf_size(hdl, inprogress, WOBUF_CFG) != rawsz) { out(O_ALTFP, "restart_fme: Config data size mismatch"); Undiag_reason = UD_VAL_CFGMISMATCH; goto badcase; } cfgdata->begin = MALLOC(rawsz); cfgdata->end = cfgdata->nextfree = cfgdata->begin + rawsz; fmd_buf_read(hdl, inprogress, WOBUF_CFG, cfgdata->begin, rawsz); } else { cfgdata->begin = cfgdata->end = cfgdata->nextfree = NULL; } config_cook(cfgdata); fmep->config = cfgdata->cooked; config_free(cfgdata); out(O_ALTFP|O_STAMP, "config_restore added %d bytes", alloc_total() - init_size); if ((fmep->eventtree = itree_create(fmep->config)) == NULL) { /* case not properly saved or irretrievable */ out(O_ALTFP, "restart_fme: NULL instance tree"); Undiag_reason = UD_VAL_INSTFAIL; goto badcase; } itree_ptree(O_ALTFP|O_VERB2, fmep->eventtree); if (reconstitute_observations(fmep) != 0) goto badcase; out(O_ALTFP|O_NONL, "FME %d replay observations: ", fmep->id); for (ep = fmep->observations; ep; ep = ep->observations) { out(O_ALTFP|O_NONL, " "); itree_pevent_brief(O_ALTFP|O_NONL, ep); } out(O_ALTFP, NULL); Open_fme_count++; /* give the diagnosis algorithm a shot at the new FME state */ fme_eval(fmep, fmep->e0r); return; badcase: if (fmep->eventtree != NULL) itree_free(fmep->eventtree); if (fmep->config) structconfig_free(fmep->config); destroy_fme_bufs(fmep); FREE(fmep); /* * Since we're unable to restart the case, add it to the undiagable * list and solve and close it as appropriate. */ bad = MALLOC(sizeof (struct case_list)); bad->next = NULL; if (Undiagablecaselist != NULL) bad->next = Undiagablecaselist; Undiagablecaselist = bad; bad->fmcase = inprogress; out(O_ALTFP|O_NONL, "[case %s (unable to restart), ", fmd_case_uuid(hdl, bad->fmcase)); if (fmd_case_solved(hdl, bad->fmcase)) { out(O_ALTFP|O_NONL, "already solved, "); } else { out(O_ALTFP|O_NONL, "solving, "); defect = fmd_nvl_create_fault(hdl, undiag_2defect_str(Undiag_reason), 100, NULL, NULL, NULL); reason = undiag_2reason_str(Undiag_reason, NULL); (void) nvlist_add_string(defect, UNDIAG_REASON, reason); FREE(reason); fmd_case_add_suspect(hdl, bad->fmcase, defect); fmd_case_solve(hdl, bad->fmcase); Undiag_reason = UD_VAL_UNKNOWN; } if (fmd_case_closed(hdl, bad->fmcase)) { out(O_ALTFP, "already closed ]"); } else { out(O_ALTFP, "closing ]"); fmd_case_close(hdl, bad->fmcase); } } /*ARGSUSED*/ static void globals_destructor(void *left, void *right, void *arg) { struct evalue *evp = (struct evalue *)right; if (evp->t == NODEPTR) tree_free((struct node *)(uintptr_t)evp->v); evp->v = (uintptr_t)NULL; FREE(evp); } void destroy_fme(struct fme *f) { stats_delete(f->Rcount); stats_delete(f->Hcallcount); stats_delete(f->Rcallcount); stats_delete(f->Ccallcount); stats_delete(f->Ecallcount); stats_delete(f->Tcallcount); stats_delete(f->Marrowcount); stats_delete(f->diags); if (f->eventtree != NULL) itree_free(f->eventtree); if (f->config) structconfig_free(f->config); lut_free(f->globals, globals_destructor, NULL); FREE(f); } static const char * fme_state2str(enum fme_state s) { switch (s) { case FME_NOTHING: return ("NOTHING"); case FME_WAIT: return ("WAIT"); case FME_CREDIBLE: return ("CREDIBLE"); case FME_DISPROVED: return ("DISPROVED"); case FME_DEFERRED: return ("DEFERRED"); default: return ("UNKNOWN"); } } static int is_problem(enum nametype t) { return (t == N_FAULT || t == N_DEFECT || t == N_UPSET); } static int is_defect(enum nametype t) { return (t == N_DEFECT); } static int is_upset(enum nametype t) { return (t == N_UPSET); } static void fme_print(int flags, struct fme *fmep) { struct event *ep; out(flags, "Fault Management Exercise %d", fmep->id); out(flags, "\t State: %s", fme_state2str(fmep->state)); out(flags|O_NONL, "\t Start time: "); ptree_timeval(flags|O_NONL, &fmep->ull); out(flags, NULL); if (fmep->wull) { out(flags|O_NONL, "\t Wait time: "); ptree_timeval(flags|O_NONL, &fmep->wull); out(flags, NULL); } out(flags|O_NONL, "\t E0: "); if (fmep->e0) itree_pevent_brief(flags|O_NONL, fmep->e0); else out(flags|O_NONL, "NULL"); out(flags, NULL); out(flags|O_NONL, "\tObservations:"); for (ep = fmep->observations; ep; ep = ep->observations) { out(flags|O_NONL, " "); itree_pevent_brief(flags|O_NONL, ep); } out(flags, NULL); out(flags|O_NONL, "\tSuspect list:"); for (ep = fmep->suspects; ep; ep = ep->suspects) { out(flags|O_NONL, " "); itree_pevent_brief(flags|O_NONL, ep); } out(flags, NULL); if (fmep->eventtree != NULL) { out(flags|O_VERB2, "\t Tree:"); itree_ptree(flags|O_VERB2, fmep->eventtree); } } static struct node * pathstring2epnamenp(char *path) { char *sep = "/"; struct node *ret; char *ptr; if ((ptr = strtok(path, sep)) == NULL) out(O_DIE, "pathstring2epnamenp: invalid empty class"); ret = tree_iname(stable(ptr), NULL, 0); while ((ptr = strtok(NULL, sep)) != NULL) ret = tree_name_append(ret, tree_iname(stable(ptr), NULL, 0)); return (ret); } /* * for a given upset sp, increment the corresponding SERD engine. if the * SERD engine trips, return the ename and ipp of the resulting ereport. * returns true if engine tripped and *enamep and *ippp were filled in. */ static int serd_eval(struct fme *fmep, fmd_hdl_t *hdl, fmd_event_t *ffep, fmd_case_t *fmcase, struct event *sp, const char **enamep, const struct ipath **ippp) { struct node *serdinst; char *serdname; char *serdresource; char *serdclass; struct node *nid; struct serd_entry *newentp; int i, serdn = -1, serdincrement = 1, len = 0; char *serdsuffix = NULL, *serdt = NULL; struct evalue *ep; ASSERT(sp->t == N_UPSET); ASSERT(ffep != NULL); if ((ep = (struct evalue *)lut_lookup(sp->serdprops, (void *)"n", (lut_cmp)strcmp)) != NULL) { ASSERT(ep->t == UINT64); serdn = (int)ep->v; } if ((ep = (struct evalue *)lut_lookup(sp->serdprops, (void *)"t", (lut_cmp)strcmp)) != NULL) { ASSERT(ep->t == STRING); serdt = (char *)(uintptr_t)ep->v; } if ((ep = (struct evalue *)lut_lookup(sp->serdprops, (void *)"suffix", (lut_cmp)strcmp)) != NULL) { ASSERT(ep->t == STRING); serdsuffix = (char *)(uintptr_t)ep->v; } if ((ep = (struct evalue *)lut_lookup(sp->serdprops, (void *)"increment", (lut_cmp)strcmp)) != NULL) { ASSERT(ep->t == UINT64); serdincrement = (int)ep->v; } /* * obtain instanced SERD engine from the upset sp. from this * derive serdname, the string used to identify the SERD engine. */ serdinst = eventprop_lookup(sp, L_engine); if (serdinst == NULL) return (-1); len = strlen(serdinst->u.stmt.np->u.event.ename->u.name.s) + 1; if (serdsuffix != NULL) len += strlen(serdsuffix); serdclass = MALLOC(len); if (serdsuffix != NULL) (void) snprintf(serdclass, len, "%s%s", serdinst->u.stmt.np->u.event.ename->u.name.s, serdsuffix); else (void) snprintf(serdclass, len, "%s", serdinst->u.stmt.np->u.event.ename->u.name.s); serdresource = ipath2str(NULL, ipath(serdinst->u.stmt.np->u.event.epname)); len += strlen(serdresource) + 1; serdname = MALLOC(len); (void) snprintf(serdname, len, "%s@%s", serdclass, serdresource); FREE(serdresource); /* handle serd engine "id" property, if there is one */ if ((nid = lut_lookup(serdinst->u.stmt.lutp, (void *)L_id, NULL)) != NULL) { struct evalue *gval; char suffixbuf[200]; char *suffix; char *nserdname; size_t nname; out(O_ALTFP|O_NONL, "serd \"%s\" id: ", serdname); ptree_name_iter(O_ALTFP|O_NONL, nid); ASSERTinfo(nid->t == T_GLOBID, ptree_nodetype2str(nid->t)); if ((gval = lut_lookup(fmep->globals, (void *)nid->u.globid.s, NULL)) == NULL) { out(O_ALTFP, " undefined"); } else if (gval->t == UINT64) { out(O_ALTFP, " %llu", gval->v); (void) sprintf(suffixbuf, "%llu", gval->v); suffix = suffixbuf; } else { out(O_ALTFP, " \"%s\"", (char *)(uintptr_t)gval->v); suffix = (char *)(uintptr_t)gval->v; } nname = strlen(serdname) + strlen(suffix) + 2; nserdname = MALLOC(nname); (void) snprintf(nserdname, nname, "%s:%s", serdname, suffix); FREE(serdname); serdname = nserdname; } /* * if the engine is empty, and we have an override for n/t then * destroy and recreate it. */ if ((serdn != -1 || serdt != NULL) && fmd_serd_exists(hdl, serdname) && fmd_serd_empty(hdl, serdname)) fmd_serd_destroy(hdl, serdname); if (!fmd_serd_exists(hdl, serdname)) { struct node *nN, *nT; const char *s; struct node *nodep; struct config *cp; char *path; uint_t nval; hrtime_t tval; int i; char *ptr; int got_n_override = 0, got_t_override = 0; /* no SERD engine yet, so create it */ nodep = serdinst->u.stmt.np->u.event.epname; path = ipath2str(NULL, ipath(nodep)); cp = config_lookup(fmep->config, path, 0); FREE((void *)path); /* * We allow serd paramaters to be overridden, either from * eft.conf file values (if Serd_Override is set) or from * driver properties (for "serd.io.device" engines). */ if (Serd_Override != NULL) { char *save_ptr, *ptr1, *ptr2, *ptr3; ptr3 = save_ptr = STRDUP(Serd_Override); while (*ptr3 != '\0') { ptr1 = strchr(ptr3, ','); *ptr1 = '\0'; if (strcmp(ptr3, serdclass) == 0) { ptr2 = strchr(ptr1 + 1, ','); *ptr2 = '\0'; nval = atoi(ptr1 + 1); out(O_ALTFP, "serd override %s_n %d", serdclass, nval); ptr3 = strchr(ptr2 + 1, ' '); if (ptr3) *ptr3 = '\0'; ptr = STRDUP(ptr2 + 1); out(O_ALTFP, "serd override %s_t %s", serdclass, ptr); got_n_override = 1; got_t_override = 1; break; } else { ptr2 = strchr(ptr1 + 1, ','); ptr3 = strchr(ptr2 + 1, ' '); if (ptr3 == NULL) break; } ptr3++; } FREE(save_ptr); } if (cp && got_n_override == 0) { /* * convert serd engine class into property name */ char *prop_name = MALLOC(strlen(serdclass) + 3); for (i = 0; i < strlen(serdclass); i++) { if (serdclass[i] == '.') prop_name[i] = '_'; else prop_name[i] = serdclass[i]; } prop_name[i++] = '_'; prop_name[i++] = 'n'; prop_name[i] = '\0'; if (s = config_getprop(cp, prop_name)) { nval = atoi(s); out(O_ALTFP, "serd override %s_n %s", serdclass, s); got_n_override = 1; } prop_name[i - 1] = 't'; if (s = config_getprop(cp, prop_name)) { ptr = STRDUP(s); out(O_ALTFP, "serd override %s_t %s", serdclass, s); got_t_override = 1; } FREE(prop_name); } if (serdn != -1 && got_n_override == 0) { nval = serdn; out(O_ALTFP, "serd override %s_n %d", serdclass, serdn); got_n_override = 1; } if (serdt != NULL && got_t_override == 0) { ptr = STRDUP(serdt); out(O_ALTFP, "serd override %s_t %s", serdclass, serdt); got_t_override = 1; } if (!got_n_override) { nN = lut_lookup(serdinst->u.stmt.lutp, (void *)L_N, NULL); ASSERT(nN->t == T_NUM); nval = (uint_t)nN->u.ull; } if (!got_t_override) { nT = lut_lookup(serdinst->u.stmt.lutp, (void *)L_T, NULL); ASSERT(nT->t == T_TIMEVAL); tval = (hrtime_t)nT->u.ull; } else { const unsigned long long *ullp; const char *suffix; int len; len = strspn(ptr, "0123456789"); suffix = stable(&ptr[len]); ullp = (unsigned long long *)lut_lookup(Timesuffixlut, (void *)suffix, NULL); ptr[len] = '\0'; tval = strtoull(ptr, NULL, 0) * (ullp ? *ullp : 1ll); FREE(ptr); } fmd_serd_create(hdl, serdname, nval, tval); } newentp = MALLOC(sizeof (*newentp)); newentp->ename = stable(serdclass); FREE(serdclass); newentp->ipath = ipath(serdinst->u.stmt.np->u.event.epname); newentp->hdl = hdl; if (lut_lookup(SerdEngines, newentp, (lut_cmp)serd_cmp) == NULL) { SerdEngines = lut_add(SerdEngines, (void *)newentp, (void *)newentp, (lut_cmp)serd_cmp); Serd_need_save = 1; serd_save(); } else { FREE(newentp); } /* * increment SERD engine. if engine fires, reset serd * engine and return trip_strcode if required. */ for (i = 0; i < serdincrement; i++) { if (fmd_serd_record(hdl, serdname, ffep)) { fmd_case_add_serd(hdl, fmcase, serdname); fmd_serd_reset(hdl, serdname); if (ippp) { struct node *tripinst = lut_lookup(serdinst->u.stmt.lutp, (void *)L_trip, NULL); ASSERT(tripinst != NULL); *enamep = tripinst->u.event.ename->u.name.s; *ippp = ipath(tripinst->u.event.epname); out(O_ALTFP|O_NONL, "[engine fired: %s, sending: ", serdname); ipath_print(O_ALTFP|O_NONL, *enamep, *ippp); out(O_ALTFP, "]"); } else { out(O_ALTFP, "[engine fired: %s, no trip]", serdname); } FREE(serdname); return (1); } } FREE(serdname); return (0); } /* * search a suspect list for upsets. feed each upset to serd_eval() and * build up tripped[], an array of ereports produced by the firing of * any SERD engines. then feed each ereport back into * fme_receive_report(). * * returns ntrip, the number of these ereports produced. */ static int upsets_eval(struct fme *fmep, fmd_event_t *ffep) { /* we build an array of tripped ereports that we send ourselves */ struct { const char *ename; const struct ipath *ipp; } *tripped; struct event *sp; int ntrip, nupset, i; /* * count the number of upsets to determine the upper limit on * expected trip ereport strings. remember that one upset can * lead to at most one ereport. */ nupset = 0; for (sp = fmep->suspects; sp; sp = sp->suspects) { if (sp->t == N_UPSET) nupset++; } if (nupset == 0) return (0); /* * get to this point if we have upsets and expect some trip * ereports */ tripped = alloca(sizeof (*tripped) * nupset); bzero((void *)tripped, sizeof (*tripped) * nupset); ntrip = 0; for (sp = fmep->suspects; sp; sp = sp->suspects) if (sp->t == N_UPSET && serd_eval(fmep, fmep->hdl, ffep, fmep->fmcase, sp, &tripped[ntrip].ename, &tripped[ntrip].ipp) == 1) ntrip++; for (i = 0; i < ntrip; i++) { struct event *ep, *nep; struct fme *nfmep; fmd_case_t *fmcase; const struct ipath *ipp; const char *eventstring; int prev_verbose; unsigned long long my_delay = TIMEVAL_EVENTUALLY; enum fme_state state; /* * First try and evaluate a case with the trip ereport plus * all the other ereports that cause the trip. If that fails * to evaluate then try again with just this ereport on its own. */ out(O_ALTFP|O_NONL, "fme_receive_report_serd: "); ipath_print(O_ALTFP|O_NONL, tripped[i].ename, tripped[i].ipp); out(O_ALTFP|O_STAMP, NULL); ep = fmep->e0; eventstring = ep->enode->u.event.ename->u.name.s; ipp = ep->ipp; /* * create a duplicate fme and case */ fmcase = fmd_case_open(fmep->hdl, NULL); out(O_ALTFP|O_NONL, "duplicate fme for event ["); ipath_print(O_ALTFP|O_NONL, eventstring, ipp); out(O_ALTFP, " ]"); if ((nfmep = newfme(eventstring, ipp, fmep->hdl, fmcase, ffep, ep->nvp)) == NULL) { out(O_ALTFP|O_NONL, "["); ipath_print(O_ALTFP|O_NONL, eventstring, ipp); out(O_ALTFP, " CANNOT DIAGNOSE]"); continue; } Open_fme_count++; nfmep->pull = fmep->pull; init_fme_bufs(nfmep); out(O_ALTFP|O_NONL, "["); ipath_print(O_ALTFP|O_NONL, eventstring, ipp); out(O_ALTFP, " created FME%d, case %s]", nfmep->id, fmd_case_uuid(nfmep->hdl, nfmep->fmcase)); if (ffep) { fmd_case_setprincipal(nfmep->hdl, nfmep->fmcase, ffep); fmd_case_add_ereport(nfmep->hdl, nfmep->fmcase, ffep); nfmep->e0r = ffep; } /* * add the original ereports */ for (ep = fmep->observations; ep; ep = ep->observations) { eventstring = ep->enode->u.event.ename->u.name.s; ipp = ep->ipp; out(O_ALTFP|O_NONL, "adding event ["); ipath_print(O_ALTFP|O_NONL, eventstring, ipp); out(O_ALTFP, " ]"); nep = itree_lookup(nfmep->eventtree, eventstring, ipp); if (nep->count++ == 0) { nep->observations = nfmep->observations; nfmep->observations = nep; serialize_observation(nfmep, eventstring, ipp); nep->nvp = evnv_dupnvl(ep->nvp); } if (ep->ffep && ep->ffep != ffep) fmd_case_add_ereport(nfmep->hdl, nfmep->fmcase, ep->ffep); stats_counter_bump(nfmep->Rcount); } /* * add the serd trigger ereport */ if ((ep = itree_lookup(nfmep->eventtree, tripped[i].ename, tripped[i].ipp)) == NULL) { /* * The trigger ereport is not in the instance tree. It * was presumably removed by prune_propagations() as * this combination of events is not present in the * rules. */ out(O_ALTFP, "upsets_eval: e0 not in instance tree"); Undiag_reason = UD_VAL_BADEVENTI; goto retry_lone_ereport; } out(O_ALTFP|O_NONL, "adding event ["); ipath_print(O_ALTFP|O_NONL, tripped[i].ename, tripped[i].ipp); out(O_ALTFP, " ]"); nfmep->ecurrent = ep; ep->nvp = NULL; ep->count = 1; ep->observations = nfmep->observations; nfmep->observations = ep; /* * just peek first. */ nfmep->peek = 1; prev_verbose = Verbose; if (Debug == 0) Verbose = 0; lut_walk(nfmep->eventtree, (lut_cb)clear_arrows, (void *)nfmep); state = hypothesise(nfmep, nfmep->e0, nfmep->ull, &my_delay); nfmep->peek = 0; Verbose = prev_verbose; if (state == FME_DISPROVED) { out(O_ALTFP, "upsets_eval: hypothesis disproved"); Undiag_reason = UD_VAL_UNSOLVD; retry_lone_ereport: /* * However the trigger ereport on its own might be * diagnosable, so check for that. Undo the new fme * and case we just created and call fme_receive_report. */ out(O_ALTFP|O_NONL, "["); ipath_print(O_ALTFP|O_NONL, tripped[i].ename, tripped[i].ipp); out(O_ALTFP, " retrying with just trigger ereport]"); itree_free(nfmep->eventtree); nfmep->eventtree = NULL; structconfig_free(nfmep->config); nfmep->config = NULL; destroy_fme_bufs(nfmep); fmd_case_close(nfmep->hdl, nfmep->fmcase); fme_receive_report(fmep->hdl, ffep, tripped[i].ename, tripped[i].ipp, NULL); continue; } /* * and evaluate */ serialize_observation(nfmep, tripped[i].ename, tripped[i].ipp); fme_eval(nfmep, ffep); } return (ntrip); } /* * fme_receive_external_report -- call when an external ereport comes in * * this routine just converts the relevant information from the ereport * into a format used internally and passes it on to fme_receive_report(). */ void fme_receive_external_report(fmd_hdl_t *hdl, fmd_event_t *ffep, nvlist_t *nvl, const char *class) { struct node *epnamenp; fmd_case_t *fmcase; const struct ipath *ipp; nvlist_t *detector = NULL; class = stable(class); /* Get the component path from the ereport */ epnamenp = platform_getpath(nvl); /* See if we ended up without a path. */ if (epnamenp == NULL) { /* See if class permits silent discard on unknown component. */ if (lut_lookup(Ereportenames_discard, (void *)class, NULL)) { out(O_ALTFP|O_VERB2, "Unable to map \"%s\" ereport " "to component path, but silent discard allowed.", class); } else { /* * XFILE: Failure to find a component is bad unless * 'discard_if_config_unknown=1' was specified in the * ereport definition. Indicate undiagnosable. */ Undiag_reason = UD_VAL_NOPATH; fmcase = fmd_case_open(hdl, NULL); /* * We don't have a component path here (which means that * the detector was not in hc-scheme and couldn't be * converted to hc-scheme. Report the raw detector as * the suspect resource if there is one. */ (void) nvlist_lookup_nvlist(nvl, FM_EREPORT_DETECTOR, &detector); publish_undiagnosable(hdl, ffep, fmcase, detector, (char *)class); } return; } ipp = ipath(epnamenp); tree_free(epnamenp); fme_receive_report(hdl, ffep, class, ipp, nvl); } /*ARGSUSED*/ void fme_receive_repair_list(fmd_hdl_t *hdl, fmd_event_t *ffep, nvlist_t *nvl, const char *eventstring) { char *uuid; nvlist_t **nva; uint_t nvc; const struct ipath *ipp; if (nvlist_lookup_string(nvl, FM_SUSPECT_UUID, &uuid) != 0 || nvlist_lookup_nvlist_array(nvl, FM_SUSPECT_FAULT_LIST, &nva, &nvc) != 0) { out(O_ALTFP, "No uuid or fault list for list.repaired event"); return; } out(O_ALTFP, "Processing list.repaired from case %s", uuid); while (nvc-- != 0) { /* * Reset any istat or serd engine associated with this path. */ char *path; if ((ipp = platform_fault2ipath(*nva++)) == NULL) continue; path = ipath2str(NULL, ipp); out(O_ALTFP, "fme_receive_repair_list: resetting state for %s", path); FREE(path); lut_walk(Istats, (lut_cb)istat_counter_reset_cb, (void *)ipp); istat_save(); lut_walk(SerdEngines, (lut_cb)serd_reset_cb, (void *)ipp); serd_save(); } } /*ARGSUSED*/ void fme_receive_topology_change(void) { lut_walk(Istats, (lut_cb)istat_counter_topo_chg_cb, NULL); istat_save(); lut_walk(SerdEngines, (lut_cb)serd_topo_chg_cb, NULL); serd_save(); } static int mark_arrows(struct fme *fmep, struct event *ep, int mark, unsigned long long at_latest_by, unsigned long long *pdelay, int keep); /* ARGSUSED */ static void clear_arrows(struct event *ep, struct event *ep2, struct fme *fmep) { struct bubble *bp; struct arrowlist *ap; ep->cached_state = 0; ep->keep_in_tree = 0; for (bp = itree_next_bubble(ep, NULL); bp; bp = itree_next_bubble(ep, bp)) { if (bp->t != B_FROM) continue; bp->mark = 0; for (ap = itree_next_arrow(bp, NULL); ap; ap = itree_next_arrow(bp, ap)) ap->arrowp->mark = 0; } } static void fme_receive_report(fmd_hdl_t *hdl, fmd_event_t *ffep, const char *eventstring, const struct ipath *ipp, nvlist_t *nvl) { struct event *ep; struct fme *fmep = NULL; struct fme *ofmep = NULL; struct fme *cfmep, *svfmep; int matched = 0; nvlist_t *defect; fmd_case_t *fmcase; char *reason; out(O_ALTFP|O_NONL, "fme_receive_report: "); ipath_print(O_ALTFP|O_NONL, eventstring, ipp); out(O_ALTFP|O_STAMP, NULL); /* decide which FME it goes to */ for (fmep = FMElist; fmep; fmep = fmep->next) { int prev_verbose; unsigned long long my_delay = TIMEVAL_EVENTUALLY; enum fme_state state; nvlist_t *pre_peek_nvp = NULL; if (fmep->overflow) { if (!(fmd_case_closed(fmep->hdl, fmep->fmcase))) ofmep = fmep; continue; } /* * ignore solved or closed cases */ if (fmep->posted_suspects || fmd_case_solved(fmep->hdl, fmep->fmcase) || fmd_case_closed(fmep->hdl, fmep->fmcase)) continue; /* look up event in event tree for this FME */ if ((ep = itree_lookup(fmep->eventtree, eventstring, ipp)) == NULL) continue; /* note observation */ fmep->ecurrent = ep; if (ep->count++ == 0) { /* link it into list of observations seen */ ep->observations = fmep->observations; fmep->observations = ep; ep->nvp = evnv_dupnvl(nvl); } else { /* use new payload values for peek */ pre_peek_nvp = ep->nvp; ep->nvp = evnv_dupnvl(nvl); } /* tell hypothesise() not to mess with suspect list */ fmep->peek = 1; /* don't want this to be verbose (unless Debug is set) */ prev_verbose = Verbose; if (Debug == 0) Verbose = 0; lut_walk(fmep->eventtree, (lut_cb)clear_arrows, (void *)fmep); state = hypothesise(fmep, fmep->e0, fmep->ull, &my_delay); fmep->peek = 0; /* put verbose flag back */ Verbose = prev_verbose; if (state != FME_DISPROVED) { /* found an FME that explains the ereport */ matched++; out(O_ALTFP|O_NONL, "["); ipath_print(O_ALTFP|O_NONL, eventstring, ipp); out(O_ALTFP, " explained by FME%d]", fmep->id); nvlist_free(pre_peek_nvp); if (ep->count == 1) serialize_observation(fmep, eventstring, ipp); if (ffep) { fmd_case_add_ereport(hdl, fmep->fmcase, ffep); ep->ffep = ffep; } stats_counter_bump(fmep->Rcount); /* re-eval FME */ fme_eval(fmep, ffep); } else { /* not a match, undo noting of observation */ fmep->ecurrent = NULL; if (--ep->count == 0) { /* unlink it from observations */ fmep->observations = ep->observations; ep->observations = NULL; nvlist_free(ep->nvp); ep->nvp = NULL; } else { nvlist_free(ep->nvp); ep->nvp = pre_peek_nvp; } } } if (matched) return; /* explained by at least one existing FME */ /* clean up closed fmes */ cfmep = ClosedFMEs; while (cfmep != NULL) { svfmep = cfmep->next; destroy_fme(cfmep); cfmep = svfmep; } ClosedFMEs = NULL; if (ofmep) { out(O_ALTFP|O_NONL, "["); ipath_print(O_ALTFP|O_NONL, eventstring, ipp); out(O_ALTFP, " ADDING TO OVERFLOW FME]"); if (ffep) fmd_case_add_ereport(hdl, ofmep->fmcase, ffep); return; } else if (Max_fme && (Open_fme_count >= Max_fme)) { out(O_ALTFP|O_NONL, "["); ipath_print(O_ALTFP|O_NONL, eventstring, ipp); out(O_ALTFP, " MAX OPEN FME REACHED]"); fmcase = fmd_case_open(hdl, NULL); /* Create overflow fme */ if ((fmep = newfme(eventstring, ipp, hdl, fmcase, ffep, nvl)) == NULL) { out(O_ALTFP|O_NONL, "["); ipath_print(O_ALTFP|O_NONL, eventstring, ipp); out(O_ALTFP, " CANNOT OPEN OVERFLOW FME]"); return; } Open_fme_count++; init_fme_bufs(fmep); fmep->overflow = B_TRUE; if (ffep) fmd_case_add_ereport(hdl, fmep->fmcase, ffep); Undiag_reason = UD_VAL_MAXFME; defect = fmd_nvl_create_fault(hdl, undiag_2defect_str(Undiag_reason), 100, NULL, NULL, NULL); reason = undiag_2reason_str(Undiag_reason, NULL); (void) nvlist_add_string(defect, UNDIAG_REASON, reason); FREE(reason); fmd_case_add_suspect(hdl, fmep->fmcase, defect); fmd_case_solve(hdl, fmep->fmcase); Undiag_reason = UD_VAL_UNKNOWN; return; } /* open a case */ fmcase = fmd_case_open(hdl, NULL); /* start a new FME */ if ((fmep = newfme(eventstring, ipp, hdl, fmcase, ffep, nvl)) == NULL) { out(O_ALTFP|O_NONL, "["); ipath_print(O_ALTFP|O_NONL, eventstring, ipp); out(O_ALTFP, " CANNOT DIAGNOSE]"); return; } Open_fme_count++; init_fme_bufs(fmep); out(O_ALTFP|O_NONL, "["); ipath_print(O_ALTFP|O_NONL, eventstring, ipp); out(O_ALTFP, " created FME%d, case %s]", fmep->id, fmd_case_uuid(hdl, fmep->fmcase)); ep = fmep->e0; ASSERT(ep != NULL); /* note observation */ fmep->ecurrent = ep; if (ep->count++ == 0) { /* link it into list of observations seen */ ep->observations = fmep->observations; fmep->observations = ep; ep->nvp = evnv_dupnvl(nvl); serialize_observation(fmep, eventstring, ipp); } else { /* new payload overrides any previous */ nvlist_free(ep->nvp); ep->nvp = evnv_dupnvl(nvl); } stats_counter_bump(fmep->Rcount); if (ffep) { fmd_case_add_ereport(hdl, fmep->fmcase, ffep); fmd_case_setprincipal(hdl, fmep->fmcase, ffep); fmep->e0r = ffep; ep->ffep = ffep; } /* give the diagnosis algorithm a shot at the new FME state */ fme_eval(fmep, ffep); } void fme_status(int flags) { struct fme *fmep; if (FMElist == NULL) { out(flags, "No fault management exercises underway."); return; } for (fmep = FMElist; fmep; fmep = fmep->next) fme_print(flags, fmep); } /* * "indent" routines used mostly for nicely formatted debug output, but also * for sanity checking for infinite recursion bugs. */ #define MAX_INDENT 1024 static const char *indent_s[MAX_INDENT]; static int current_indent; static void indent_push(const char *s) { if (current_indent < MAX_INDENT) indent_s[current_indent++] = s; else out(O_DIE, "unexpected recursion depth (%d)", current_indent); } static void indent_set(const char *s) { current_indent = 0; indent_push(s); } static void indent_pop(void) { if (current_indent > 0) current_indent--; else out(O_DIE, "recursion underflow"); } static void indent(void) { int i; if (!Verbose) return; for (i = 0; i < current_indent; i++) out(O_ALTFP|O_VERB|O_NONL, indent_s[i]); } #define SLNEW 1 #define SLCHANGED 2 #define SLWAIT 3 #define SLDISPROVED 4 static void print_suspects(int circumstance, struct fme *fmep) { struct event *ep; out(O_ALTFP|O_NONL, "["); if (circumstance == SLCHANGED) { out(O_ALTFP|O_NONL, "FME%d diagnosis changed. state: %s, " "suspect list:", fmep->id, fme_state2str(fmep->state)); } else if (circumstance == SLWAIT) { out(O_ALTFP|O_NONL, "FME%d set wait timer %ld ", fmep->id, fmep->timer); ptree_timeval(O_ALTFP|O_NONL, &fmep->wull); } else if (circumstance == SLDISPROVED) { out(O_ALTFP|O_NONL, "FME%d DIAGNOSIS UNKNOWN", fmep->id); } else { out(O_ALTFP|O_NONL, "FME%d DIAGNOSIS PRODUCED:", fmep->id); } if (circumstance == SLWAIT || circumstance == SLDISPROVED) { out(O_ALTFP, "]"); return; } for (ep = fmep->suspects; ep; ep = ep->suspects) { out(O_ALTFP|O_NONL, " "); itree_pevent_brief(O_ALTFP|O_NONL, ep); } out(O_ALTFP, "]"); } static struct node * eventprop_lookup(struct event *ep, const char *propname) { return (lut_lookup(ep->props, (void *)propname, NULL)); } #define MAXDIGITIDX 23 static char numbuf[MAXDIGITIDX + 1]; static int node2uint(struct node *n, uint_t *valp) { struct evalue value; struct lut *globals = NULL; if (n == NULL) return (1); /* * check value.v since we are being asked to convert an unsigned * long long int to an unsigned int */ if (! eval_expr(n, NULL, NULL, &globals, NULL, NULL, 0, &value) || value.t != UINT64 || value.v > (1ULL << 32)) return (1); *valp = (uint_t)value.v; return (0); } static nvlist_t * node2fmri(struct node *n) { nvlist_t **pa, *f, *p; struct node *nc; uint_t depth = 0; char *numstr, *nullbyte; char *failure; int err, i; /* XXX do we need to be able to handle a non-T_NAME node? */ if (n == NULL || n->t != T_NAME) return (NULL); for (nc = n; nc != NULL; nc = nc->u.name.next) { if (nc->u.name.child == NULL || nc->u.name.child->t != T_NUM) break; depth++; } if (nc != NULL) { /* We bailed early, something went wrong */ return (NULL); } if ((err = nvlist_xalloc(&f, NV_UNIQUE_NAME, &Eft_nv_hdl)) != 0) out(O_DIE|O_SYS, "alloc of fmri nvl failed"); pa = alloca(depth * sizeof (nvlist_t *)); for (i = 0; i < depth; i++) pa[i] = NULL; err = nvlist_add_string(f, FM_FMRI_SCHEME, FM_FMRI_SCHEME_HC); err |= nvlist_add_uint8(f, FM_VERSION, FM_HC_SCHEME_VERSION); err |= nvlist_add_string(f, FM_FMRI_HC_ROOT, ""); err |= nvlist_add_uint32(f, FM_FMRI_HC_LIST_SZ, depth); if (err != 0) { failure = "basic construction of FMRI failed"; goto boom; } numbuf[MAXDIGITIDX] = '\0'; nullbyte = &numbuf[MAXDIGITIDX]; i = 0; for (nc = n; nc != NULL; nc = nc->u.name.next) { err = nvlist_xalloc(&p, NV_UNIQUE_NAME, &Eft_nv_hdl); if (err != 0) { failure = "alloc of an hc-pair failed"; goto boom; } err = nvlist_add_string(p, FM_FMRI_HC_NAME, nc->u.name.s); numstr = ulltostr(nc->u.name.child->u.ull, nullbyte); err |= nvlist_add_string(p, FM_FMRI_HC_ID, numstr); if (err != 0) { failure = "construction of an hc-pair failed"; goto boom; } pa[i++] = p; } err = nvlist_add_nvlist_array(f, FM_FMRI_HC_LIST, pa, depth); if (err == 0) { for (i = 0; i < depth; i++) nvlist_free(pa[i]); return (f); } failure = "addition of hc-pair array to FMRI failed"; boom: for (i = 0; i < depth; i++) nvlist_free(pa[i]); nvlist_free(f); out(O_DIE, "%s", failure); /*NOTREACHED*/ return (NULL); } /* an ipath cache entry is an array of these, with s==NULL at the end */ struct ipath { const char *s; /* component name (in stable) */ int i; /* instance number */ }; static nvlist_t * ipath2fmri(struct ipath *ipath) { nvlist_t **pa, *f, *p; uint_t depth = 0; char *numstr, *nullbyte; char *failure; int err, i; struct ipath *ipp; for (ipp = ipath; ipp->s != NULL; ipp++) depth++; if ((err = nvlist_xalloc(&f, NV_UNIQUE_NAME, &Eft_nv_hdl)) != 0) out(O_DIE|O_SYS, "alloc of fmri nvl failed"); pa = alloca(depth * sizeof (nvlist_t *)); for (i = 0; i < depth; i++) pa[i] = NULL; err = nvlist_add_string(f, FM_FMRI_SCHEME, FM_FMRI_SCHEME_HC); err |= nvlist_add_uint8(f, FM_VERSION, FM_HC_SCHEME_VERSION); err |= nvlist_add_string(f, FM_FMRI_HC_ROOT, ""); err |= nvlist_add_uint32(f, FM_FMRI_HC_LIST_SZ, depth); if (err != 0) { failure = "basic construction of FMRI failed"; goto boom; } numbuf[MAXDIGITIDX] = '\0'; nullbyte = &numbuf[MAXDIGITIDX]; i = 0; for (ipp = ipath; ipp->s != NULL; ipp++) { err = nvlist_xalloc(&p, NV_UNIQUE_NAME, &Eft_nv_hdl); if (err != 0) { failure = "alloc of an hc-pair failed"; goto boom; } err = nvlist_add_string(p, FM_FMRI_HC_NAME, ipp->s); numstr = ulltostr(ipp->i, nullbyte); err |= nvlist_add_string(p, FM_FMRI_HC_ID, numstr); if (err != 0) { failure = "construction of an hc-pair failed"; goto boom; } pa[i++] = p; } err = nvlist_add_nvlist_array(f, FM_FMRI_HC_LIST, pa, depth); if (err == 0) { for (i = 0; i < depth; i++) nvlist_free(pa[i]); return (f); } failure = "addition of hc-pair array to FMRI failed"; boom: for (i = 0; i < depth; i++) nvlist_free(pa[i]); nvlist_free(f); out(O_DIE, "%s", failure); /*NOTREACHED*/ return (NULL); } static uint8_t percentof(uint_t part, uint_t whole) { unsigned long long p = part * 1000; return ((p / whole / 10) + (((p / whole % 10) >= 5) ? 1 : 0)); } struct rsl { struct event *suspect; nvlist_t *asru; nvlist_t *fru; nvlist_t *rsrc; }; static void publish_suspects(struct fme *fmep, struct rsl *srl); /* * rslfree -- free internal members of struct rsl not expected to be * freed elsewhere. */ static void rslfree(struct rsl *freeme) { nvlist_free(freeme->asru); nvlist_free(freeme->fru); if (freeme->rsrc != freeme->asru) nvlist_free(freeme->rsrc); } /* * rslcmp -- compare two rsl structures. Use the following * comparisons to establish cardinality: * * 1. Name of the suspect's class. (simple strcmp) * 2. Name of the suspect's ASRU. (trickier, since nvlist) * */ static int rslcmp(const void *a, const void *b) { struct rsl *r1 = (struct rsl *)a; struct rsl *r2 = (struct rsl *)b; int rv; rv = strcmp(r1->suspect->enode->u.event.ename->u.name.s, r2->suspect->enode->u.event.ename->u.name.s); if (rv != 0) return (rv); if (r1->rsrc == NULL && r2->rsrc == NULL) return (0); if (r1->rsrc == NULL) return (-1); if (r2->rsrc == NULL) return (1); return (evnv_cmpnvl(r1->rsrc, r2->rsrc, 0)); } /* * get_resources -- for a given suspect, determine what ASRU, FRU and * RSRC nvlists should be advertised in the final suspect list. */ void get_resources(struct event *sp, struct rsl *rsrcs, struct config *croot) { struct node *asrudef, *frudef; const struct ipath *asrupath, *frupath; nvlist_t *asru = NULL, *fru = NULL; nvlist_t *rsrc = NULL; char *pathstr; /* * First find any ASRU and/or FRU defined in the * initial fault tree. */ asrudef = eventprop_lookup(sp, L_ASRU); frudef = eventprop_lookup(sp, L_FRU); /* * Create ipaths based on those definitions */ asrupath = ipath(asrudef); frupath = ipath(frudef); /* * Allow for platform translations of the FMRIs */ pathstr = ipath2str(NULL, sp->ipp); platform_unit_translate(is_defect(sp->t), croot, TOPO_PROP_RESOURCE, &rsrc, pathstr); FREE(pathstr); pathstr = ipath2str(NULL, asrupath); platform_unit_translate(is_defect(sp->t), croot, TOPO_PROP_ASRU, &asru, pathstr); FREE(pathstr); pathstr = ipath2str(NULL, frupath); platform_unit_translate(is_defect(sp->t), croot, TOPO_PROP_FRU, &fru, pathstr); FREE(pathstr); rsrcs->suspect = sp; rsrcs->asru = asru; rsrcs->fru = fru; rsrcs->rsrc = rsrc; } /* * trim_suspects -- prior to publishing, we may need to remove some * suspects from the list. If we're auto-closing upsets, we don't * want any of those in the published list. If the ASRUs for multiple * defects resolve to the same ASRU (driver) we only want to publish * that as a single suspect. */ static int trim_suspects(struct fme *fmep, struct rsl *begin, struct rsl *begin2, fmd_event_t *ffep) { struct event *ep; struct rsl *rp = begin; struct rsl *rp2 = begin2; int mess_zero_count = 0; int serd_rval; uint_t messval; /* remove any unwanted upsets and populate our array */ for (ep = fmep->psuspects; ep; ep = ep->psuspects) { if (is_upset(ep->t)) continue; serd_rval = serd_eval(fmep, fmep->hdl, ffep, fmep->fmcase, ep, NULL, NULL); if (serd_rval == 0) continue; if (node2uint(eventprop_lookup(ep, L_message), &messval) == 0 && messval == 0) { get_resources(ep, rp2, fmep->config); rp2++; mess_zero_count++; } else { get_resources(ep, rp, fmep->config); rp++; fmep->nsuspects++; } } return (mess_zero_count); } /* * addpayloadprop -- add a payload prop to a problem */ static void addpayloadprop(const char *lhs, struct evalue *rhs, nvlist_t *fault) { nvlist_t *rsrc, *hcs; ASSERT(fault != NULL); ASSERT(lhs != NULL); ASSERT(rhs != NULL); if (nvlist_lookup_nvlist(fault, FM_FAULT_RESOURCE, &rsrc) != 0) out(O_DIE, "cannot add payloadprop \"%s\" to fault", lhs); if (nvlist_lookup_nvlist(rsrc, FM_FMRI_HC_SPECIFIC, &hcs) != 0) { out(O_ALTFP|O_VERB2, "addpayloadprop: create hc_specific"); if (nvlist_xalloc(&hcs, NV_UNIQUE_NAME, &Eft_nv_hdl) != 0) out(O_DIE, "cannot add payloadprop \"%s\" to fault", lhs); if (nvlist_add_nvlist(rsrc, FM_FMRI_HC_SPECIFIC, hcs) != 0) out(O_DIE, "cannot add payloadprop \"%s\" to fault", lhs); nvlist_free(hcs); if (nvlist_lookup_nvlist(rsrc, FM_FMRI_HC_SPECIFIC, &hcs) != 0) out(O_DIE, "cannot add payloadprop \"%s\" to fault", lhs); } else out(O_ALTFP|O_VERB2, "addpayloadprop: reuse hc_specific"); if (rhs->t == UINT64) { out(O_ALTFP|O_VERB2, "addpayloadprop: %s=%llu", lhs, rhs->v); if (nvlist_add_uint64(hcs, lhs, rhs->v) != 0) out(O_DIE, "cannot add payloadprop \"%s\" to fault", lhs); } else { out(O_ALTFP|O_VERB2, "addpayloadprop: %s=\"%s\"", lhs, (char *)(uintptr_t)rhs->v); if (nvlist_add_string(hcs, lhs, (char *)(uintptr_t)rhs->v) != 0) out(O_DIE, "cannot add payloadprop \"%s\" to fault", lhs); } } static char *Istatbuf; static char *Istatbufptr; static int Istatsz; /* * istataddsize -- calculate size of istat and add it to Istatsz */ /*ARGSUSED2*/ static void istataddsize(const struct istat_entry *lhs, struct stats *rhs, void *arg) { int val; ASSERT(lhs != NULL); ASSERT(rhs != NULL); if ((val = stats_counter_value(rhs)) == 0) return; /* skip zero-valued stats */ /* count up the size of the stat name */ Istatsz += ipath2strlen(lhs->ename, lhs->ipath); Istatsz++; /* for the trailing NULL byte */ /* count up the size of the stat value */ Istatsz += snprintf(NULL, 0, "%d", val); Istatsz++; /* for the trailing NULL byte */ } /* * istat2str -- serialize an istat, writing result to *Istatbufptr */ /*ARGSUSED2*/ static void istat2str(const struct istat_entry *lhs, struct stats *rhs, void *arg) { char *str; int len; int val; ASSERT(lhs != NULL); ASSERT(rhs != NULL); if ((val = stats_counter_value(rhs)) == 0) return; /* skip zero-valued stats */ /* serialize the stat name */ str = ipath2str(lhs->ename, lhs->ipath); len = strlen(str); ASSERT(Istatbufptr + len + 1 < &Istatbuf[Istatsz]); (void) strlcpy(Istatbufptr, str, &Istatbuf[Istatsz] - Istatbufptr); Istatbufptr += len; FREE(str); *Istatbufptr++ = '\0'; /* serialize the stat value */ Istatbufptr += snprintf(Istatbufptr, &Istatbuf[Istatsz] - Istatbufptr, "%d", val); *Istatbufptr++ = '\0'; ASSERT(Istatbufptr <= &Istatbuf[Istatsz]); } void istat_save() { if (Istat_need_save == 0) return; /* figure out how big the serialzed info is */ Istatsz = 0; lut_walk(Istats, (lut_cb)istataddsize, NULL); if (Istatsz == 0) { /* no stats to save */ fmd_buf_destroy(Hdl, NULL, WOBUF_ISTATS); return; } /* create the serialized buffer */ Istatbufptr = Istatbuf = MALLOC(Istatsz); lut_walk(Istats, (lut_cb)istat2str, NULL); /* clear out current saved stats */ fmd_buf_destroy(Hdl, NULL, WOBUF_ISTATS); /* write out the new version */ fmd_buf_write(Hdl, NULL, WOBUF_ISTATS, Istatbuf, Istatsz); FREE(Istatbuf); Istat_need_save = 0; } int istat_cmp(struct istat_entry *ent1, struct istat_entry *ent2) { if (ent1->ename != ent2->ename) return (ent2->ename - ent1->ename); if (ent1->ipath != ent2->ipath) return ((char *)ent2->ipath - (char *)ent1->ipath); return (0); } /* * istat-verify -- verify the component associated with a stat still exists * * if the component no longer exists, this routine resets the stat and * returns 0. if the component still exists, it returns 1. */ static int istat_verify(struct node *snp, struct istat_entry *entp) { struct stats *statp; nvlist_t *fmri; fmri = node2fmri(snp->u.event.epname); if (platform_path_exists(fmri)) { nvlist_free(fmri); return (1); } nvlist_free(fmri); /* component no longer in system. zero out the associated stats */ if ((statp = (struct stats *) lut_lookup(Istats, entp, (lut_cmp)istat_cmp)) == NULL || stats_counter_value(statp) == 0) return (0); /* stat is already reset */ Istat_need_save = 1; stats_counter_reset(statp); return (0); } static void istat_bump(struct node *snp, int n) { struct stats *statp; struct istat_entry ent; ASSERT(snp != NULL); ASSERTinfo(snp->t == T_EVENT, ptree_nodetype2str(snp->t)); ASSERT(snp->u.event.epname != NULL); /* class name should be hoisted into a single stable entry */ ASSERT(snp->u.event.ename->u.name.next == NULL); ent.ename = snp->u.event.ename->u.name.s; ent.ipath = ipath(snp->u.event.epname); if (!istat_verify(snp, &ent)) { /* component no longer exists in system, nothing to do */ return; } if ((statp = (struct stats *) lut_lookup(Istats, &ent, (lut_cmp)istat_cmp)) == NULL) { /* need to create the counter */ int cnt = 0; struct node *np; char *sname; char *snamep; struct istat_entry *newentp; /* count up the size of the stat name */ np = snp->u.event.ename; while (np != NULL) { cnt += strlen(np->u.name.s); cnt++; /* for the '.' or '@' */ np = np->u.name.next; } np = snp->u.event.epname; while (np != NULL) { cnt += snprintf(NULL, 0, "%s%llu", np->u.name.s, np->u.name.child->u.ull); cnt++; /* for the '/' or trailing NULL byte */ np = np->u.name.next; } /* build the stat name */ snamep = sname = alloca(cnt); np = snp->u.event.ename; while (np != NULL) { snamep += snprintf(snamep, &sname[cnt] - snamep, "%s", np->u.name.s); np = np->u.name.next; if (np) *snamep++ = '.'; } *snamep++ = '@'; np = snp->u.event.epname; while (np != NULL) { snamep += snprintf(snamep, &sname[cnt] - snamep, "%s%llu", np->u.name.s, np->u.name.child->u.ull); np = np->u.name.next; if (np) *snamep++ = '/'; } *snamep++ = '\0'; /* create the new stat & add it to our list */ newentp = MALLOC(sizeof (*newentp)); *newentp = ent; statp = stats_new_counter(NULL, sname, 0); Istats = lut_add(Istats, (void *)newentp, (void *)statp, (lut_cmp)istat_cmp); } /* if n is non-zero, set that value instead of bumping */ if (n) { stats_counter_reset(statp); stats_counter_add(statp, n); } else stats_counter_bump(statp); Istat_need_save = 1; ipath_print(O_ALTFP|O_VERB2, ent.ename, ent.ipath); out(O_ALTFP|O_VERB2, " %s to value %d", n ? "set" : "incremented", stats_counter_value(statp)); } /*ARGSUSED*/ static void istat_destructor(void *left, void *right, void *arg) { struct istat_entry *entp = (struct istat_entry *)left; struct stats *statp = (struct stats *)right; FREE(entp); stats_delete(statp); } /* * Callback used in a walk of the Istats to reset matching stat counters. */ static void istat_counter_reset_cb(struct istat_entry *entp, struct stats *statp, const struct ipath *ipp) { char *path; if (entp->ipath == ipp) { path = ipath2str(entp->ename, ipp); out(O_ALTFP, "istat_counter_reset_cb: resetting %s", path); FREE(path); stats_counter_reset(statp); Istat_need_save = 1; } } /*ARGSUSED*/ static void istat_counter_topo_chg_cb(struct istat_entry *entp, struct stats *statp, void *unused) { char *path; nvlist_t *fmri; fmri = ipath2fmri((struct ipath *)(entp->ipath)); if (!platform_path_exists(fmri)) { path = ipath2str(entp->ename, entp->ipath); out(O_ALTFP, "istat_counter_topo_chg_cb: not present %s", path); FREE(path); stats_counter_reset(statp); Istat_need_save = 1; } nvlist_free(fmri); } void istat_fini(void) { lut_free(Istats, istat_destructor, NULL); } static char *Serdbuf; static char *Serdbufptr; static int Serdsz; /* * serdaddsize -- calculate size of serd and add it to Serdsz */ /*ARGSUSED*/ static void serdaddsize(const struct serd_entry *lhs, struct stats *rhs, void *arg) { ASSERT(lhs != NULL); /* count up the size of the stat name */ Serdsz += ipath2strlen(lhs->ename, lhs->ipath); Serdsz++; /* for the trailing NULL byte */ } /* * serd2str -- serialize a serd engine, writing result to *Serdbufptr */ /*ARGSUSED*/ static void serd2str(const struct serd_entry *lhs, struct stats *rhs, void *arg) { char *str; int len; ASSERT(lhs != NULL); /* serialize the serd engine name */ str = ipath2str(lhs->ename, lhs->ipath); len = strlen(str); ASSERT(Serdbufptr + len + 1 <= &Serdbuf[Serdsz]); (void) strlcpy(Serdbufptr, str, &Serdbuf[Serdsz] - Serdbufptr); Serdbufptr += len; FREE(str); *Serdbufptr++ = '\0'; ASSERT(Serdbufptr <= &Serdbuf[Serdsz]); } void serd_save() { if (Serd_need_save == 0) return; /* figure out how big the serialzed info is */ Serdsz = 0; lut_walk(SerdEngines, (lut_cb)serdaddsize, NULL); if (Serdsz == 0) { /* no serd engines to save */ fmd_buf_destroy(Hdl, NULL, WOBUF_SERDS); return; } /* create the serialized buffer */ Serdbufptr = Serdbuf = MALLOC(Serdsz); lut_walk(SerdEngines, (lut_cb)serd2str, NULL); /* clear out current saved stats */ fmd_buf_destroy(Hdl, NULL, WOBUF_SERDS); /* write out the new version */ fmd_buf_write(Hdl, NULL, WOBUF_SERDS, Serdbuf, Serdsz); FREE(Serdbuf); Serd_need_save = 0; } int serd_cmp(struct serd_entry *ent1, struct serd_entry *ent2) { if (ent1->ename != ent2->ename) return (ent2->ename - ent1->ename); if (ent1->ipath != ent2->ipath) return ((char *)ent2->ipath - (char *)ent1->ipath); return (0); } void fme_serd_load(fmd_hdl_t *hdl) { int sz; char *sbuf; char *sepptr; char *ptr; struct serd_entry *newentp; struct node *epname; nvlist_t *fmri; char *namestring; if ((sz = fmd_buf_size(hdl, NULL, WOBUF_SERDS)) == 0) return; sbuf = alloca(sz); fmd_buf_read(hdl, NULL, WOBUF_SERDS, sbuf, sz); ptr = sbuf; while (ptr < &sbuf[sz]) { sepptr = strchr(ptr, '@'); *sepptr = '\0'; namestring = ptr; sepptr++; ptr = sepptr; ptr += strlen(ptr); ptr++; /* move past the '\0' separating paths */ epname = pathstring2epnamenp(sepptr); fmri = node2fmri(epname); if (platform_path_exists(fmri)) { newentp = MALLOC(sizeof (*newentp)); newentp->hdl = hdl; newentp->ipath = ipath(epname); newentp->ename = stable(namestring); SerdEngines = lut_add(SerdEngines, (void *)newentp, (void *)newentp, (lut_cmp)serd_cmp); } else Serd_need_save = 1; tree_free(epname); nvlist_free(fmri); } /* save it back again in case some of the paths no longer exist */ serd_save(); } /*ARGSUSED*/ static void serd_destructor(void *left, void *right, void *arg) { struct serd_entry *entp = (struct serd_entry *)left; FREE(entp); } /* * Callback used in a walk of the SerdEngines to reset matching serd engines. */ /*ARGSUSED*/ static void serd_reset_cb(struct serd_entry *entp, void *unused, const struct ipath *ipp) { char *path; if (entp->ipath == ipp) { path = ipath2str(entp->ename, ipp); out(O_ALTFP, "serd_reset_cb: resetting %s", path); fmd_serd_reset(entp->hdl, path); FREE(path); Serd_need_save = 1; } } /*ARGSUSED*/ static void serd_topo_chg_cb(struct serd_entry *entp, void *unused, void *unused2) { char *path; nvlist_t *fmri; fmri = ipath2fmri((struct ipath *)(entp->ipath)); if (!platform_path_exists(fmri)) { path = ipath2str(entp->ename, entp->ipath); out(O_ALTFP, "serd_topo_chg_cb: not present %s", path); fmd_serd_reset(entp->hdl, path); FREE(path); Serd_need_save = 1; } nvlist_free(fmri); } void serd_fini(void) { lut_free(SerdEngines, serd_destructor, NULL); } static void publish_suspects(struct fme *fmep, struct rsl *srl) { struct rsl *rp; nvlist_t *fault; uint8_t cert; uint_t *frs; uint_t frsum, fr; uint_t messval; uint_t retireval; uint_t responseval; struct node *snp; int frcnt, fridx; boolean_t allfaulty = B_TRUE; struct rsl *erl = srl + fmep->nsuspects - 1; /* * sort the array */ qsort(srl, fmep->nsuspects, sizeof (struct rsl), rslcmp); /* sum the fitrates */ frs = alloca(fmep->nsuspects * sizeof (uint_t)); fridx = frcnt = frsum = 0; for (rp = srl; rp <= erl; rp++) { struct node *n; n = eventprop_lookup(rp->suspect, L_FITrate); if (node2uint(n, &fr) != 0) { out(O_DEBUG|O_NONL, "event "); ipath_print(O_DEBUG|O_NONL, rp->suspect->enode->u.event.ename->u.name.s, rp->suspect->ipp); out(O_VERB, " has no FITrate (using 1)"); fr = 1; } else if (fr == 0) { out(O_DEBUG|O_NONL, "event "); ipath_print(O_DEBUG|O_NONL, rp->suspect->enode->u.event.ename->u.name.s, rp->suspect->ipp); out(O_VERB, " has zero FITrate (using 1)"); fr = 1; } frs[fridx++] = fr; frsum += fr; frcnt++; } /* Add them in reverse order of our sort, as fmd reverses order */ for (rp = erl; rp >= srl; rp--) { cert = percentof(frs[--fridx], frsum); fault = fmd_nvl_create_fault(fmep->hdl, rp->suspect->enode->u.event.ename->u.name.s, cert, rp->asru, rp->fru, rp->rsrc); if (fault == NULL) out(O_DIE, "fault creation failed"); /* if "message" property exists, add it to the fault */ if (node2uint(eventprop_lookup(rp->suspect, L_message), &messval) == 0) { out(O_ALTFP, "[FME%d, %s adds message=%d to suspect list]", fmep->id, rp->suspect->enode->u.event.ename->u.name.s, messval); if (nvlist_add_boolean_value(fault, FM_SUSPECT_MESSAGE, (messval) ? B_TRUE : B_FALSE) != 0) { out(O_DIE, "cannot add no-message to fault"); } } /* if "retire" property exists, add it to the fault */ if (node2uint(eventprop_lookup(rp->suspect, L_retire), &retireval) == 0) { out(O_ALTFP, "[FME%d, %s adds retire=%d to suspect list]", fmep->id, rp->suspect->enode->u.event.ename->u.name.s, retireval); if (nvlist_add_boolean_value(fault, FM_SUSPECT_RETIRE, (retireval) ? B_TRUE : B_FALSE) != 0) { out(O_DIE, "cannot add no-retire to fault"); } } /* if "response" property exists, add it to the fault */ if (node2uint(eventprop_lookup(rp->suspect, L_response), &responseval) == 0) { out(O_ALTFP, "[FME%d, %s adds response=%d to suspect list]", fmep->id, rp->suspect->enode->u.event.ename->u.name.s, responseval); if (nvlist_add_boolean_value(fault, FM_SUSPECT_RESPONSE, (responseval) ? B_TRUE : B_FALSE) != 0) { out(O_DIE, "cannot add no-response to fault"); } } /* add any payload properties */ lut_walk(rp->suspect->payloadprops, (lut_cb)addpayloadprop, (void *)fault); rslfree(rp); /* * If "action" property exists, evaluate it; this must be done * before the allfaulty check below since some actions may * modify the asru to be used in fmd_nvl_fmri_has_fault. This * needs to be restructured if any new actions are introduced * that have effects that we do not want to be visible if * we decide not to publish in the dupclose check below. */ if ((snp = eventprop_lookup(rp->suspect, L_action)) != NULL) { struct evalue evalue; out(O_ALTFP|O_NONL, "[FME%d, %s action ", fmep->id, rp->suspect->enode->u.event.ename->u.name.s); ptree_name_iter(O_ALTFP|O_NONL, snp); out(O_ALTFP, "]"); Action_nvl = fault; (void) eval_expr(snp, NULL, NULL, NULL, NULL, NULL, 0, &evalue); } fmd_case_add_suspect(fmep->hdl, fmep->fmcase, fault); /* * check if the asru is already marked as "faulty". */ if (allfaulty) { nvlist_t *asru; out(O_ALTFP|O_VERB, "FME%d dup check ", fmep->id); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, rp->suspect); out(O_ALTFP|O_VERB|O_NONL, " "); if (nvlist_lookup_nvlist(fault, FM_FAULT_ASRU, &asru) != 0) { out(O_ALTFP|O_VERB, "NULL asru"); allfaulty = B_FALSE; } else if (fmd_nvl_fmri_has_fault(fmep->hdl, asru, FMD_HAS_FAULT_ASRU, NULL)) { out(O_ALTFP|O_VERB, "faulty"); } else { out(O_ALTFP|O_VERB, "not faulty"); allfaulty = B_FALSE; } } } if (!allfaulty) { /* * don't update the count stat if all asrus are already * present and unrepaired in the asru cache */ for (rp = erl; rp >= srl; rp--) { struct event *suspect = rp->suspect; if (suspect == NULL) continue; /* if "count" exists, increment the appropriate stat */ if ((snp = eventprop_lookup(suspect, L_count)) != NULL) { out(O_ALTFP|O_NONL, "[FME%d, %s count ", fmep->id, suspect->enode->u.event.ename->u.name.s); ptree_name_iter(O_ALTFP|O_NONL, snp); out(O_ALTFP, "]"); istat_bump(snp, 0); } } istat_save(); /* write out any istat changes */ } } static const char * undiag_2defect_str(int ud) { switch (ud) { case UD_VAL_MISSINGINFO: case UD_VAL_MISSINGOBS: case UD_VAL_MISSINGPATH: case UD_VAL_MISSINGZERO: case UD_VAL_BADOBS: case UD_VAL_CFGMISMATCH: return (UNDIAG_DEFECT_CHKPT); case UD_VAL_BADEVENTI: case UD_VAL_BADEVENTPATH: case UD_VAL_BADEVENTCLASS: case UD_VAL_INSTFAIL: case UD_VAL_NOPATH: case UD_VAL_UNSOLVD: return (UNDIAG_DEFECT_FME); case UD_VAL_MAXFME: return (UNDIAG_DEFECT_LIMIT); case UD_VAL_UNKNOWN: default: return (UNDIAG_DEFECT_UNKNOWN); } } static const char * undiag_2fault_str(int ud) { switch (ud) { case UD_VAL_BADEVENTI: case UD_VAL_BADEVENTPATH: case UD_VAL_BADEVENTCLASS: case UD_VAL_INSTFAIL: case UD_VAL_NOPATH: case UD_VAL_UNSOLVD: return (UNDIAG_FAULT_FME); default: return (NULL); } } static char * undiag_2reason_str(int ud, char *arg) { const char *ptr; char *buf; int with_arg = 0; switch (ud) { case UD_VAL_BADEVENTPATH: ptr = UD_STR_BADEVENTPATH; with_arg = 1; break; case UD_VAL_BADEVENTCLASS: ptr = UD_STR_BADEVENTCLASS; with_arg = 1; break; case UD_VAL_BADEVENTI: ptr = UD_STR_BADEVENTI; with_arg = 1; break; case UD_VAL_BADOBS: ptr = UD_STR_BADOBS; break; case UD_VAL_CFGMISMATCH: ptr = UD_STR_CFGMISMATCH; break; case UD_VAL_INSTFAIL: ptr = UD_STR_INSTFAIL; with_arg = 1; break; case UD_VAL_MAXFME: ptr = UD_STR_MAXFME; break; case UD_VAL_MISSINGINFO: ptr = UD_STR_MISSINGINFO; break; case UD_VAL_MISSINGOBS: ptr = UD_STR_MISSINGOBS; break; case UD_VAL_MISSINGPATH: ptr = UD_STR_MISSINGPATH; break; case UD_VAL_MISSINGZERO: ptr = UD_STR_MISSINGZERO; break; case UD_VAL_NOPATH: ptr = UD_STR_NOPATH; with_arg = 1; break; case UD_VAL_UNSOLVD: ptr = UD_STR_UNSOLVD; break; case UD_VAL_UNKNOWN: default: ptr = UD_STR_UNKNOWN; break; } if (with_arg) { buf = MALLOC(strlen(ptr) + strlen(arg) - 1); (void) sprintf(buf, ptr, arg); } else { buf = MALLOC(strlen(ptr) + 1); (void) sprintf(buf, ptr); } return (buf); } static void publish_undiagnosable(fmd_hdl_t *hdl, fmd_event_t *ffep, fmd_case_t *fmcase, nvlist_t *detector, char *arg) { struct case_list *newcase; nvlist_t *defect, *fault; const char *faultstr; char *reason = undiag_2reason_str(Undiag_reason, arg); out(O_ALTFP, "[undiagnosable ereport received, " "creating and closing a new case (%s)]", reason); newcase = MALLOC(sizeof (struct case_list)); newcase->next = NULL; newcase->fmcase = fmcase; if (Undiagablecaselist != NULL) newcase->next = Undiagablecaselist; Undiagablecaselist = newcase; if (ffep != NULL) fmd_case_add_ereport(hdl, newcase->fmcase, ffep); /* add defect */ defect = fmd_nvl_create_fault(hdl, undiag_2defect_str(Undiag_reason), 50, NULL, NULL, detector); (void) nvlist_add_string(defect, UNDIAG_REASON, reason); (void) nvlist_add_boolean_value(defect, FM_SUSPECT_RETIRE, B_FALSE); (void) nvlist_add_boolean_value(defect, FM_SUSPECT_RESPONSE, B_FALSE); fmd_case_add_suspect(hdl, newcase->fmcase, defect); /* add fault if appropriate */ faultstr = undiag_2fault_str(Undiag_reason); if (faultstr != NULL) { fault = fmd_nvl_create_fault(hdl, faultstr, 50, NULL, NULL, detector); (void) nvlist_add_string(fault, UNDIAG_REASON, reason); (void) nvlist_add_boolean_value(fault, FM_SUSPECT_RETIRE, B_FALSE); (void) nvlist_add_boolean_value(fault, FM_SUSPECT_RESPONSE, B_FALSE); fmd_case_add_suspect(hdl, newcase->fmcase, fault); } FREE(reason); /* solve and close case */ fmd_case_solve(hdl, newcase->fmcase); fmd_case_close(hdl, newcase->fmcase); Undiag_reason = UD_VAL_UNKNOWN; } static void fme_undiagnosable(struct fme *f) { nvlist_t *defect, *fault, *detector = NULL; struct event *ep; char *pathstr; const char *faultstr; char *reason = undiag_2reason_str(Undiag_reason, NULL); out(O_ALTFP, "[solving/closing FME%d, case %s (%s)]", f->id, fmd_case_uuid(f->hdl, f->fmcase), reason); for (ep = f->observations; ep; ep = ep->observations) { if (ep->ffep != f->e0r) fmd_case_add_ereport(f->hdl, f->fmcase, ep->ffep); pathstr = ipath2str(NULL, ipath(platform_getpath(ep->nvp))); platform_unit_translate(0, f->config, TOPO_PROP_RESOURCE, &detector, pathstr); FREE(pathstr); /* add defect */ defect = fmd_nvl_create_fault(f->hdl, undiag_2defect_str(Undiag_reason), 50 / f->uniqobs, NULL, NULL, detector); (void) nvlist_add_string(defect, UNDIAG_REASON, reason); (void) nvlist_add_boolean_value(defect, FM_SUSPECT_RETIRE, B_FALSE); (void) nvlist_add_boolean_value(defect, FM_SUSPECT_RESPONSE, B_FALSE); fmd_case_add_suspect(f->hdl, f->fmcase, defect); /* add fault if appropriate */ faultstr = undiag_2fault_str(Undiag_reason); if (faultstr == NULL) continue; fault = fmd_nvl_create_fault(f->hdl, faultstr, 50 / f->uniqobs, NULL, NULL, detector); (void) nvlist_add_string(fault, UNDIAG_REASON, reason); (void) nvlist_add_boolean_value(fault, FM_SUSPECT_RETIRE, B_FALSE); (void) nvlist_add_boolean_value(fault, FM_SUSPECT_RESPONSE, B_FALSE); fmd_case_add_suspect(f->hdl, f->fmcase, fault); nvlist_free(detector); } FREE(reason); fmd_case_solve(f->hdl, f->fmcase); fmd_case_close(f->hdl, f->fmcase); Undiag_reason = UD_VAL_UNKNOWN; } /* * fme_close_case * * Find the requested case amongst our fmes and close it. Free up * the related fme. */ void fme_close_case(fmd_hdl_t *hdl, fmd_case_t *fmcase) { struct case_list *ucasep, *prevcasep = NULL; struct fme *prev = NULL; struct fme *fmep; for (ucasep = Undiagablecaselist; ucasep; ucasep = ucasep->next) { if (fmcase != ucasep->fmcase) { prevcasep = ucasep; continue; } if (prevcasep == NULL) Undiagablecaselist = Undiagablecaselist->next; else prevcasep->next = ucasep->next; FREE(ucasep); return; } for (fmep = FMElist; fmep; fmep = fmep->next) { if (fmep->hdl == hdl && fmep->fmcase == fmcase) break; prev = fmep; } if (fmep == NULL) { out(O_WARN, "Eft asked to close unrecognized case [%s].", fmd_case_uuid(hdl, fmcase)); return; } if (EFMElist == fmep) EFMElist = prev; if (prev == NULL) FMElist = FMElist->next; else prev->next = fmep->next; fmep->next = NULL; /* Get rid of any timer this fme has set */ if (fmep->wull != 0) fmd_timer_remove(fmep->hdl, fmep->timer); if (ClosedFMEs == NULL) { ClosedFMEs = fmep; } else { fmep->next = ClosedFMEs; ClosedFMEs = fmep; } Open_fme_count--; /* See if we can close the overflow FME */ if (Open_fme_count <= Max_fme) { for (fmep = FMElist; fmep; fmep = fmep->next) { if (fmep->overflow && !(fmd_case_closed(fmep->hdl, fmep->fmcase))) break; } if (fmep != NULL) fmd_case_close(fmep->hdl, fmep->fmcase); } } /* * fme_set_timer() * If the time we need to wait for the given FME is less than the * current timer, kick that old timer out and establish a new one. */ static int fme_set_timer(struct fme *fmep, unsigned long long wull) { out(O_ALTFP|O_VERB|O_NONL, " fme_set_timer: request to wait "); ptree_timeval(O_ALTFP|O_VERB, &wull); if (wull <= fmep->pull) { out(O_ALTFP|O_VERB|O_NONL, "already have waited at least "); ptree_timeval(O_ALTFP|O_VERB, &fmep->pull); out(O_ALTFP|O_VERB, NULL); /* we've waited at least wull already, don't need timer */ return (0); } out(O_ALTFP|O_VERB|O_NONL, " currently "); if (fmep->wull != 0) { out(O_ALTFP|O_VERB|O_NONL, "waiting "); ptree_timeval(O_ALTFP|O_VERB, &fmep->wull); out(O_ALTFP|O_VERB, NULL); } else { out(O_ALTFP|O_VERB|O_NONL, "not waiting"); out(O_ALTFP|O_VERB, NULL); } if (fmep->wull != 0) if (wull >= fmep->wull) /* New timer would fire later than established timer */ return (0); if (fmep->wull != 0) { fmd_timer_remove(fmep->hdl, fmep->timer); } fmep->timer = fmd_timer_install(fmep->hdl, (void *)fmep, fmep->e0r, wull); out(O_ALTFP|O_VERB, "timer set, id is %ld", fmep->timer); fmep->wull = wull; return (1); } void fme_timer_fired(struct fme *fmep, id_t tid) { struct fme *ffmep = NULL; for (ffmep = FMElist; ffmep; ffmep = ffmep->next) if (ffmep == fmep) break; if (ffmep == NULL) { out(O_WARN, "Timer fired for an FME (%p) not in FMEs list.", (void *)fmep); return; } out(O_ALTFP|O_VERB, "Timer fired %lx", tid); fmep->pull = fmep->wull; fmep->wull = 0; fmd_buf_write(fmep->hdl, fmep->fmcase, WOBUF_PULL, (void *)&fmep->pull, sizeof (fmep->pull)); fme_eval(fmep, fmep->e0r); } /* * Preserve the fme's suspect list in its psuspects list, NULLing the * suspects list in the meantime. */ static void save_suspects(struct fme *fmep) { struct event *ep; struct event *nextep; /* zero out the previous suspect list */ for (ep = fmep->psuspects; ep; ep = nextep) { nextep = ep->psuspects; ep->psuspects = NULL; } fmep->psuspects = NULL; /* zero out the suspect list, copying it to previous suspect list */ fmep->psuspects = fmep->suspects; for (ep = fmep->suspects; ep; ep = nextep) { nextep = ep->suspects; ep->psuspects = ep->suspects; ep->suspects = NULL; ep->is_suspect = 0; } fmep->suspects = NULL; fmep->nsuspects = 0; } /* * Retrieve the fme's suspect list from its psuspects list. */ static void restore_suspects(struct fme *fmep) { struct event *ep; struct event *nextep; fmep->nsuspects = 0; fmep->suspects = fmep->psuspects; for (ep = fmep->psuspects; ep; ep = nextep) { fmep->nsuspects++; nextep = ep->psuspects; ep->suspects = ep->psuspects; } } /* * this is what we use to call the Emrys prototype code instead of main() */ static void fme_eval(struct fme *fmep, fmd_event_t *ffep) { struct event *ep; unsigned long long my_delay = TIMEVAL_EVENTUALLY; struct rsl *srl = NULL; struct rsl *srl2 = NULL; int mess_zero_count; int rpcnt; save_suspects(fmep); out(O_ALTFP, "Evaluate FME %d", fmep->id); indent_set(" "); lut_walk(fmep->eventtree, (lut_cb)clear_arrows, (void *)fmep); fmep->state = hypothesise(fmep, fmep->e0, fmep->ull, &my_delay); out(O_ALTFP|O_NONL, "FME%d state: %s, suspect list:", fmep->id, fme_state2str(fmep->state)); for (ep = fmep->suspects; ep; ep = ep->suspects) { out(O_ALTFP|O_NONL, " "); itree_pevent_brief(O_ALTFP|O_NONL, ep); } out(O_ALTFP, NULL); switch (fmep->state) { case FME_CREDIBLE: print_suspects(SLNEW, fmep); (void) upsets_eval(fmep, ffep); /* * we may have already posted suspects in upsets_eval() which * can recurse into fme_eval() again. If so then just return. */ if (fmep->posted_suspects) return; stats_counter_bump(fmep->diags); rpcnt = fmep->nsuspects; save_suspects(fmep); /* * create two lists, one for "message=1" faults and one for * "message=0" faults. If we have a mixture we will generate * two separate suspect lists. */ srl = MALLOC(rpcnt * sizeof (struct rsl)); bzero(srl, rpcnt * sizeof (struct rsl)); srl2 = MALLOC(rpcnt * sizeof (struct rsl)); bzero(srl2, rpcnt * sizeof (struct rsl)); mess_zero_count = trim_suspects(fmep, srl, srl2, ffep); /* * If the resulting suspect list has no members, we're * done so simply close the case. Otherwise sort and publish. */ if (fmep->nsuspects == 0 && mess_zero_count == 0) { out(O_ALTFP, "[FME%d, case %s (all suspects are upsets)]", fmep->id, fmd_case_uuid(fmep->hdl, fmep->fmcase)); fmd_case_close(fmep->hdl, fmep->fmcase); } else if (fmep->nsuspects != 0 && mess_zero_count == 0) { publish_suspects(fmep, srl); out(O_ALTFP, "[solving FME%d, case %s]", fmep->id, fmd_case_uuid(fmep->hdl, fmep->fmcase)); fmd_case_solve(fmep->hdl, fmep->fmcase); } else if (fmep->nsuspects == 0 && mess_zero_count != 0) { fmep->nsuspects = mess_zero_count; publish_suspects(fmep, srl2); out(O_ALTFP, "[solving FME%d, case %s]", fmep->id, fmd_case_uuid(fmep->hdl, fmep->fmcase)); fmd_case_solve(fmep->hdl, fmep->fmcase); } else { struct event *obsp; struct fme *nfmep; publish_suspects(fmep, srl); out(O_ALTFP, "[solving FME%d, case %s]", fmep->id, fmd_case_uuid(fmep->hdl, fmep->fmcase)); fmd_case_solve(fmep->hdl, fmep->fmcase); /* * Got both message=0 and message=1 so create a * duplicate case. Also need a temporary duplicate fme * structure for use by publish_suspects(). */ nfmep = alloc_fme(); nfmep->id = Nextid++; nfmep->hdl = fmep->hdl; nfmep->nsuspects = mess_zero_count; nfmep->fmcase = fmd_case_open(fmep->hdl, NULL); out(O_ALTFP|O_STAMP, "[creating parallel FME%d, case %s]", nfmep->id, fmd_case_uuid(nfmep->hdl, nfmep->fmcase)); Open_fme_count++; if (ffep) { fmd_case_setprincipal(nfmep->hdl, nfmep->fmcase, ffep); fmd_case_add_ereport(nfmep->hdl, nfmep->fmcase, ffep); } for (obsp = fmep->observations; obsp; obsp = obsp->observations) if (obsp->ffep && obsp->ffep != ffep) fmd_case_add_ereport(nfmep->hdl, nfmep->fmcase, obsp->ffep); publish_suspects(nfmep, srl2); out(O_ALTFP, "[solving FME%d, case %s]", nfmep->id, fmd_case_uuid(nfmep->hdl, nfmep->fmcase)); fmd_case_solve(nfmep->hdl, nfmep->fmcase); FREE(nfmep); } FREE(srl); FREE(srl2); restore_suspects(fmep); fmep->posted_suspects = 1; fmd_buf_write(fmep->hdl, fmep->fmcase, WOBUF_POSTD, (void *)&fmep->posted_suspects, sizeof (fmep->posted_suspects)); /* * Now the suspects have been posted, we can clear up * the instance tree as we won't be looking at it again. * Also cancel the timer as the case is now solved. */ if (fmep->wull != 0) { fmd_timer_remove(fmep->hdl, fmep->timer); fmep->wull = 0; } break; case FME_WAIT: ASSERT(my_delay > fmep->ull); (void) fme_set_timer(fmep, my_delay); print_suspects(SLWAIT, fmep); itree_prune(fmep->eventtree); return; case FME_DISPROVED: print_suspects(SLDISPROVED, fmep); Undiag_reason = UD_VAL_UNSOLVD; fme_undiagnosable(fmep); break; } itree_free(fmep->eventtree); fmep->eventtree = NULL; structconfig_free(fmep->config); fmep->config = NULL; destroy_fme_bufs(fmep); } static void indent(void); static int triggered(struct fme *fmep, struct event *ep, int mark); static enum fme_state effects_test(struct fme *fmep, struct event *fault_event, unsigned long long at_latest_by, unsigned long long *pdelay); static enum fme_state requirements_test(struct fme *fmep, struct event *ep, unsigned long long at_latest_by, unsigned long long *pdelay); static enum fme_state causes_test(struct fme *fmep, struct event *ep, unsigned long long at_latest_by, unsigned long long *pdelay); static int checkconstraints(struct fme *fmep, struct arrow *arrowp) { struct constraintlist *ctp; struct evalue value; char *sep = ""; if (arrowp->forever_false) { indent(); out(O_ALTFP|O_VERB|O_NONL, " Forever false constraint: "); for (ctp = arrowp->constraints; ctp != NULL; ctp = ctp->next) { out(O_ALTFP|O_VERB|O_NONL, sep); ptree(O_ALTFP|O_VERB|O_NONL, ctp->cnode, 1, 0); sep = ", "; } out(O_ALTFP|O_VERB, NULL); return (0); } if (arrowp->forever_true) { indent(); out(O_ALTFP|O_VERB|O_NONL, " Forever true constraint: "); for (ctp = arrowp->constraints; ctp != NULL; ctp = ctp->next) { out(O_ALTFP|O_VERB|O_NONL, sep); ptree(O_ALTFP|O_VERB|O_NONL, ctp->cnode, 1, 0); sep = ", "; } out(O_ALTFP|O_VERB, NULL); return (1); } for (ctp = arrowp->constraints; ctp != NULL; ctp = ctp->next) { if (eval_expr(ctp->cnode, NULL, NULL, &fmep->globals, fmep->config, arrowp, 0, &value)) { /* evaluation successful */ if (value.t == UNDEFINED || value.v == 0) { /* known false */ arrowp->forever_false = 1; indent(); out(O_ALTFP|O_VERB|O_NONL, " False constraint: "); ptree(O_ALTFP|O_VERB|O_NONL, ctp->cnode, 1, 0); out(O_ALTFP|O_VERB, NULL); return (0); } } else { /* evaluation unsuccessful -- unknown value */ indent(); out(O_ALTFP|O_VERB|O_NONL, " Deferred constraint: "); ptree(O_ALTFP|O_VERB|O_NONL, ctp->cnode, 1, 0); out(O_ALTFP|O_VERB, NULL); return (1); } } /* known true */ arrowp->forever_true = 1; indent(); out(O_ALTFP|O_VERB|O_NONL, " True constraint: "); for (ctp = arrowp->constraints; ctp != NULL; ctp = ctp->next) { out(O_ALTFP|O_VERB|O_NONL, sep); ptree(O_ALTFP|O_VERB|O_NONL, ctp->cnode, 1, 0); sep = ", "; } out(O_ALTFP|O_VERB, NULL); return (1); } static int triggered(struct fme *fmep, struct event *ep, int mark) { struct bubble *bp; struct arrowlist *ap; int count = 0; stats_counter_bump(fmep->Tcallcount); for (bp = itree_next_bubble(ep, NULL); bp; bp = itree_next_bubble(ep, bp)) { if (bp->t != B_TO) continue; for (ap = itree_next_arrow(bp, NULL); ap; ap = itree_next_arrow(bp, ap)) { /* check count of marks against K in the bubble */ if ((ap->arrowp->mark & mark) && ++count >= bp->nork) return (1); } } return (0); } static int mark_arrows(struct fme *fmep, struct event *ep, int mark, unsigned long long at_latest_by, unsigned long long *pdelay, int keep) { struct bubble *bp; struct arrowlist *ap; unsigned long long overall_delay = TIMEVAL_EVENTUALLY; unsigned long long my_delay; enum fme_state result; int retval = 0; for (bp = itree_next_bubble(ep, NULL); bp; bp = itree_next_bubble(ep, bp)) { if (bp->t != B_FROM) continue; stats_counter_bump(fmep->Marrowcount); for (ap = itree_next_arrow(bp, NULL); ap; ap = itree_next_arrow(bp, ap)) { struct event *ep2 = ap->arrowp->head->myevent; /* * if we're clearing marks, we can avoid doing * all that work evaluating constraints. */ if (mark == 0) { if (ap->arrowp->arrow_marked == 0) continue; ap->arrowp->arrow_marked = 0; ap->arrowp->mark &= ~EFFECTS_COUNTER; if (keep && (ep2->cached_state & (WAIT_EFFECT|CREDIBLE_EFFECT|PARENT_WAIT))) ep2->keep_in_tree = 1; ep2->cached_state &= ~(WAIT_EFFECT|CREDIBLE_EFFECT|PARENT_WAIT); (void) mark_arrows(fmep, ep2, mark, 0, NULL, keep); continue; } ap->arrowp->arrow_marked = 1; if (ep2->cached_state & REQMNTS_DISPROVED) { indent(); out(O_ALTFP|O_VERB|O_NONL, " ALREADY DISPROVED "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep2); out(O_ALTFP|O_VERB, NULL); continue; } if (ep2->cached_state & WAIT_EFFECT) { indent(); out(O_ALTFP|O_VERB|O_NONL, " ALREADY EFFECTS WAIT "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep2); out(O_ALTFP|O_VERB, NULL); continue; } if (ep2->cached_state & CREDIBLE_EFFECT) { indent(); out(O_ALTFP|O_VERB|O_NONL, " ALREADY EFFECTS CREDIBLE "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep2); out(O_ALTFP|O_VERB, NULL); continue; } if ((ep2->cached_state & PARENT_WAIT) && (mark & PARENT_WAIT)) { indent(); out(O_ALTFP|O_VERB|O_NONL, " ALREADY PARENT EFFECTS WAIT "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep2); out(O_ALTFP|O_VERB, NULL); continue; } platform_set_payloadnvp(ep2->nvp); if (checkconstraints(fmep, ap->arrowp) == 0) { platform_set_payloadnvp(NULL); indent(); out(O_ALTFP|O_VERB|O_NONL, " CONSTRAINTS FAIL "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep2); out(O_ALTFP|O_VERB, NULL); continue; } platform_set_payloadnvp(NULL); ap->arrowp->mark |= EFFECTS_COUNTER; if (!triggered(fmep, ep2, EFFECTS_COUNTER)) { indent(); out(O_ALTFP|O_VERB|O_NONL, " K-COUNT NOT YET MET "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep2); out(O_ALTFP|O_VERB, NULL); continue; } ep2->cached_state &= ~PARENT_WAIT; /* * if we've reached an ereport and no propagation time * is specified, use the Hesitate value */ if (ep2->t == N_EREPORT && at_latest_by == 0ULL && ap->arrowp->maxdelay == 0ULL) { out(O_ALTFP|O_VERB|O_NONL, " default wait "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep2); out(O_ALTFP|O_VERB, NULL); result = requirements_test(fmep, ep2, Hesitate, &my_delay); } else { result = requirements_test(fmep, ep2, at_latest_by + ap->arrowp->maxdelay, &my_delay); } if (result == FME_WAIT) { retval = WAIT_EFFECT; if (overall_delay > my_delay) overall_delay = my_delay; ep2->cached_state |= WAIT_EFFECT; indent(); out(O_ALTFP|O_VERB|O_NONL, " EFFECTS WAIT "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep2); out(O_ALTFP|O_VERB, NULL); indent_push(" E"); if (mark_arrows(fmep, ep2, PARENT_WAIT, at_latest_by, &my_delay, 0) == WAIT_EFFECT) { retval = WAIT_EFFECT; if (overall_delay > my_delay) overall_delay = my_delay; } indent_pop(); } else if (result == FME_DISPROVED) { indent(); out(O_ALTFP|O_VERB|O_NONL, " EFFECTS DISPROVED "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep2); out(O_ALTFP|O_VERB, NULL); } else { ep2->cached_state |= mark; indent(); if (mark == CREDIBLE_EFFECT) out(O_ALTFP|O_VERB|O_NONL, " EFFECTS CREDIBLE "); else out(O_ALTFP|O_VERB|O_NONL, " PARENT EFFECTS WAIT "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep2); out(O_ALTFP|O_VERB, NULL); indent_push(" E"); if (mark_arrows(fmep, ep2, mark, at_latest_by, &my_delay, 0) == WAIT_EFFECT) { retval = WAIT_EFFECT; if (overall_delay > my_delay) overall_delay = my_delay; } indent_pop(); } } } if (retval == WAIT_EFFECT) *pdelay = overall_delay; return (retval); } static enum fme_state effects_test(struct fme *fmep, struct event *fault_event, unsigned long long at_latest_by, unsigned long long *pdelay) { struct event *error_event; enum fme_state return_value = FME_CREDIBLE; unsigned long long overall_delay = TIMEVAL_EVENTUALLY; unsigned long long my_delay; stats_counter_bump(fmep->Ecallcount); indent_push(" E"); indent(); out(O_ALTFP|O_VERB|O_NONL, "->"); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, fault_event); out(O_ALTFP|O_VERB, NULL); if (mark_arrows(fmep, fault_event, CREDIBLE_EFFECT, at_latest_by, &my_delay, 0) == WAIT_EFFECT) { return_value = FME_WAIT; if (overall_delay > my_delay) overall_delay = my_delay; } for (error_event = fmep->observations; error_event; error_event = error_event->observations) { indent(); out(O_ALTFP|O_VERB|O_NONL, " "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, error_event); if (!(error_event->cached_state & CREDIBLE_EFFECT)) { if (error_event->cached_state & (PARENT_WAIT|WAIT_EFFECT)) { out(O_ALTFP|O_VERB, " NOT YET triggered"); continue; } return_value = FME_DISPROVED; out(O_ALTFP|O_VERB, " NOT triggered"); break; } else { out(O_ALTFP|O_VERB, " triggered"); } } if (return_value == FME_DISPROVED) { (void) mark_arrows(fmep, fault_event, 0, 0, NULL, 0); } else { fault_event->keep_in_tree = 1; (void) mark_arrows(fmep, fault_event, 0, 0, NULL, 1); } indent(); out(O_ALTFP|O_VERB|O_NONL, "<-EFFECTS %s ", fme_state2str(return_value)); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, fault_event); out(O_ALTFP|O_VERB, NULL); indent_pop(); if (return_value == FME_WAIT) *pdelay = overall_delay; return (return_value); } static enum fme_state requirements_test(struct fme *fmep, struct event *ep, unsigned long long at_latest_by, unsigned long long *pdelay) { int waiting_events; int credible_events; int deferred_events; enum fme_state return_value = FME_CREDIBLE; unsigned long long overall_delay = TIMEVAL_EVENTUALLY; unsigned long long arrow_delay; unsigned long long my_delay; struct event *ep2; struct bubble *bp; struct arrowlist *ap; if (ep->cached_state & REQMNTS_CREDIBLE) { indent(); out(O_ALTFP|O_VERB|O_NONL, " REQMNTS ALREADY CREDIBLE "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep); out(O_ALTFP|O_VERB, NULL); return (FME_CREDIBLE); } if (ep->cached_state & REQMNTS_DISPROVED) { indent(); out(O_ALTFP|O_VERB|O_NONL, " REQMNTS ALREADY DISPROVED "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep); out(O_ALTFP|O_VERB, NULL); return (FME_DISPROVED); } if (ep->cached_state & REQMNTS_WAIT) { indent(); *pdelay = ep->cached_delay; out(O_ALTFP|O_VERB|O_NONL, " REQMNTS ALREADY WAIT "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep); out(O_ALTFP|O_VERB|O_NONL, ", wait for: "); ptree_timeval(O_ALTFP|O_VERB|O_NONL, &at_latest_by); out(O_ALTFP|O_VERB, NULL); return (FME_WAIT); } stats_counter_bump(fmep->Rcallcount); indent_push(" R"); indent(); out(O_ALTFP|O_VERB|O_NONL, "->"); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep); out(O_ALTFP|O_VERB|O_NONL, ", at latest by: "); ptree_timeval(O_ALTFP|O_VERB|O_NONL, &at_latest_by); out(O_ALTFP|O_VERB, NULL); if (ep->t == N_EREPORT) { if (ep->count == 0) { if (fmep->pull >= at_latest_by) { return_value = FME_DISPROVED; } else { ep->cached_delay = *pdelay = at_latest_by; return_value = FME_WAIT; } } indent(); switch (return_value) { case FME_CREDIBLE: ep->cached_state |= REQMNTS_CREDIBLE; out(O_ALTFP|O_VERB|O_NONL, "<-REQMNTS CREDIBLE "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep); break; case FME_DISPROVED: ep->cached_state |= REQMNTS_DISPROVED; out(O_ALTFP|O_VERB|O_NONL, "<-REQMNTS DISPROVED "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep); break; case FME_WAIT: ep->cached_state |= REQMNTS_WAIT; out(O_ALTFP|O_VERB|O_NONL, "<-REQMNTS WAIT "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep); out(O_ALTFP|O_VERB|O_NONL, " to "); ptree_timeval(O_ALTFP|O_VERB|O_NONL, &at_latest_by); break; default: out(O_DIE, "requirements_test: unexpected fme_state"); break; } out(O_ALTFP|O_VERB, NULL); indent_pop(); return (return_value); } /* this event is not a report, descend the tree */ for (bp = itree_next_bubble(ep, NULL); bp; bp = itree_next_bubble(ep, bp)) { int n; if (bp->t != B_FROM) continue; n = bp->nork; credible_events = 0; waiting_events = 0; deferred_events = 0; arrow_delay = TIMEVAL_EVENTUALLY; /* * n is -1 for 'A' so adjust it. * XXX just count up the arrows for now. */ if (n < 0) { n = 0; for (ap = itree_next_arrow(bp, NULL); ap; ap = itree_next_arrow(bp, ap)) n++; indent(); out(O_ALTFP|O_VERB, " Bubble Counted N=%d", n); } else { indent(); out(O_ALTFP|O_VERB, " Bubble N=%d", n); } if (n == 0) continue; if (!(bp->mark & (BUBBLE_ELIDED|BUBBLE_OK))) { for (ap = itree_next_arrow(bp, NULL); ap; ap = itree_next_arrow(bp, ap)) { ep2 = ap->arrowp->head->myevent; platform_set_payloadnvp(ep2->nvp); (void) checkconstraints(fmep, ap->arrowp); if (!ap->arrowp->forever_false) { /* * if all arrows are invalidated by the * constraints, then we should elide the * whole bubble to be consistant with * the tree creation time behaviour */ bp->mark |= BUBBLE_OK; platform_set_payloadnvp(NULL); break; } platform_set_payloadnvp(NULL); } } for (ap = itree_next_arrow(bp, NULL); ap; ap = itree_next_arrow(bp, ap)) { ep2 = ap->arrowp->head->myevent; if (n <= credible_events) break; ap->arrowp->mark |= REQMNTS_COUNTER; if (triggered(fmep, ep2, REQMNTS_COUNTER)) /* XXX adding max timevals! */ switch (requirements_test(fmep, ep2, at_latest_by + ap->arrowp->maxdelay, &my_delay)) { case FME_DEFERRED: deferred_events++; break; case FME_CREDIBLE: credible_events++; break; case FME_DISPROVED: break; case FME_WAIT: if (my_delay < arrow_delay) arrow_delay = my_delay; waiting_events++; break; default: out(O_DIE, "Bug in requirements_test."); } else deferred_events++; } if (!(bp->mark & BUBBLE_OK) && waiting_events == 0) { bp->mark |= BUBBLE_ELIDED; continue; } indent(); out(O_ALTFP|O_VERB, " Credible: %d Waiting %d", credible_events + deferred_events, waiting_events); if (credible_events + deferred_events + waiting_events < n) { /* Can never meet requirements */ ep->cached_state |= REQMNTS_DISPROVED; indent(); out(O_ALTFP|O_VERB|O_NONL, "<-REQMNTS DISPROVED "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep); out(O_ALTFP|O_VERB, NULL); indent_pop(); return (FME_DISPROVED); } if (credible_events + deferred_events < n) { /* will have to wait */ /* wait time is shortest known */ if (arrow_delay < overall_delay) overall_delay = arrow_delay; return_value = FME_WAIT; } else if (credible_events < n) { if (return_value != FME_WAIT) return_value = FME_DEFERRED; } } /* * don't mark as FME_DEFERRED. If this event isn't reached by another * path, then this will be considered FME_CREDIBLE. But if it is * reached by a different path so the K-count is met, then might * get overridden by FME_WAIT or FME_DISPROVED. */ if (return_value == FME_WAIT) { ep->cached_state |= REQMNTS_WAIT; ep->cached_delay = *pdelay = overall_delay; } else if (return_value == FME_CREDIBLE) { ep->cached_state |= REQMNTS_CREDIBLE; } indent(); out(O_ALTFP|O_VERB|O_NONL, "<-REQMNTS %s ", fme_state2str(return_value)); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep); out(O_ALTFP|O_VERB, NULL); indent_pop(); return (return_value); } static enum fme_state causes_test(struct fme *fmep, struct event *ep, unsigned long long at_latest_by, unsigned long long *pdelay) { unsigned long long overall_delay = TIMEVAL_EVENTUALLY; unsigned long long my_delay; int credible_results = 0; int waiting_results = 0; enum fme_state fstate; struct event *tail_event; struct bubble *bp; struct arrowlist *ap; int k = 1; stats_counter_bump(fmep->Ccallcount); indent_push(" C"); indent(); out(O_ALTFP|O_VERB|O_NONL, "->"); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep); out(O_ALTFP|O_VERB, NULL); for (bp = itree_next_bubble(ep, NULL); bp; bp = itree_next_bubble(ep, bp)) { if (bp->t != B_TO) continue; k = bp->nork; /* remember the K value */ for (ap = itree_next_arrow(bp, NULL); ap; ap = itree_next_arrow(bp, ap)) { int do_not_follow = 0; /* * if we get to the same event multiple times * only worry about the first one. */ if (ap->arrowp->tail->myevent->cached_state & CAUSES_TESTED) { indent(); out(O_ALTFP|O_VERB|O_NONL, " causes test already run for "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ap->arrowp->tail->myevent); out(O_ALTFP|O_VERB, NULL); continue; } /* * see if false constraint prevents us * from traversing this arrow */ platform_set_payloadnvp(ep->nvp); if (checkconstraints(fmep, ap->arrowp) == 0) do_not_follow = 1; platform_set_payloadnvp(NULL); if (do_not_follow) { indent(); out(O_ALTFP|O_VERB|O_NONL, " False arrow from "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ap->arrowp->tail->myevent); out(O_ALTFP|O_VERB, NULL); continue; } ap->arrowp->tail->myevent->cached_state |= CAUSES_TESTED; tail_event = ap->arrowp->tail->myevent; fstate = hypothesise(fmep, tail_event, at_latest_by, &my_delay); switch (fstate) { case FME_WAIT: if (my_delay < overall_delay) overall_delay = my_delay; waiting_results++; break; case FME_CREDIBLE: credible_results++; break; case FME_DISPROVED: break; default: out(O_DIE, "Bug in causes_test"); } } } /* compare against K */ if (credible_results + waiting_results < k) { indent(); out(O_ALTFP|O_VERB|O_NONL, "<-CAUSES DISPROVED "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep); out(O_ALTFP|O_VERB, NULL); indent_pop(); return (FME_DISPROVED); } if (waiting_results != 0) { *pdelay = overall_delay; indent(); out(O_ALTFP|O_VERB|O_NONL, "<-CAUSES WAIT "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep); out(O_ALTFP|O_VERB|O_NONL, " to "); ptree_timeval(O_ALTFP|O_VERB|O_NONL, &at_latest_by); out(O_ALTFP|O_VERB, NULL); indent_pop(); return (FME_WAIT); } indent(); out(O_ALTFP|O_VERB|O_NONL, "<-CAUSES CREDIBLE "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep); out(O_ALTFP|O_VERB, NULL); indent_pop(); return (FME_CREDIBLE); } static enum fme_state hypothesise(struct fme *fmep, struct event *ep, unsigned long long at_latest_by, unsigned long long *pdelay) { enum fme_state rtr, otr; unsigned long long my_delay; unsigned long long overall_delay = TIMEVAL_EVENTUALLY; stats_counter_bump(fmep->Hcallcount); indent_push(" H"); indent(); out(O_ALTFP|O_VERB|O_NONL, "->"); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep); out(O_ALTFP|O_VERB|O_NONL, ", at latest by: "); ptree_timeval(O_ALTFP|O_VERB|O_NONL, &at_latest_by); out(O_ALTFP|O_VERB, NULL); rtr = requirements_test(fmep, ep, at_latest_by, &my_delay); if ((rtr == FME_WAIT) && (my_delay < overall_delay)) overall_delay = my_delay; if (rtr != FME_DISPROVED) { if (is_problem(ep->t)) { otr = effects_test(fmep, ep, at_latest_by, &my_delay); if (otr != FME_DISPROVED) { if (fmep->peek == 0 && ep->is_suspect == 0) { ep->suspects = fmep->suspects; ep->is_suspect = 1; fmep->suspects = ep; fmep->nsuspects++; } } } else otr = causes_test(fmep, ep, at_latest_by, &my_delay); if ((otr == FME_WAIT) && (my_delay < overall_delay)) overall_delay = my_delay; if ((otr != FME_DISPROVED) && ((rtr == FME_WAIT) || (otr == FME_WAIT))) *pdelay = overall_delay; } if (rtr == FME_DISPROVED) { indent(); out(O_ALTFP|O_VERB|O_NONL, "<-DISPROVED "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep); out(O_ALTFP|O_VERB, " (doesn't meet requirements)"); indent_pop(); return (FME_DISPROVED); } if ((otr == FME_DISPROVED) && is_problem(ep->t)) { indent(); out(O_ALTFP|O_VERB|O_NONL, "<-DISPROVED "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep); out(O_ALTFP|O_VERB, " (doesn't explain all reports)"); indent_pop(); return (FME_DISPROVED); } if (otr == FME_DISPROVED) { indent(); out(O_ALTFP|O_VERB|O_NONL, "<-DISPROVED "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep); out(O_ALTFP|O_VERB, " (causes are not credible)"); indent_pop(); return (FME_DISPROVED); } if ((rtr == FME_WAIT) || (otr == FME_WAIT)) { indent(); out(O_ALTFP|O_VERB|O_NONL, "<-WAIT "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep); out(O_ALTFP|O_VERB|O_NONL, " to "); ptree_timeval(O_ALTFP|O_VERB|O_NONL, &overall_delay); out(O_ALTFP|O_VERB, NULL); indent_pop(); return (FME_WAIT); } indent(); out(O_ALTFP|O_VERB|O_NONL, "<-CREDIBLE "); itree_pevent_brief(O_ALTFP|O_VERB|O_NONL, ep); out(O_ALTFP|O_VERB, NULL); indent_pop(); return (FME_CREDIBLE); } /* * fme_istat_load -- reconstitute any persistent istats */ void fme_istat_load(fmd_hdl_t *hdl) { int sz; char *sbuf; char *ptr; if ((sz = fmd_buf_size(hdl, NULL, WOBUF_ISTATS)) == 0) { out(O_ALTFP, "fme_istat_load: No stats"); return; } sbuf = alloca(sz); fmd_buf_read(hdl, NULL, WOBUF_ISTATS, sbuf, sz); /* * pick apart the serialized stats * * format is: * , '@', , '\0', , '\0' * for example: * "stat.first@stat0/path0\02\0stat.second@stat0/path1\023\0" * * since this is parsing our own serialized data, any parsing issues * are fatal, so we check for them all with ASSERT() below. */ ptr = sbuf; while (ptr < &sbuf[sz]) { char *sepptr; struct node *np; int val; sepptr = strchr(ptr, '@'); ASSERT(sepptr != NULL); *sepptr = '\0'; /* construct the event */ np = newnode(T_EVENT, NULL, 0); np->u.event.ename = newnode(T_NAME, NULL, 0); np->u.event.ename->u.name.t = N_STAT; np->u.event.ename->u.name.s = stable(ptr); np->u.event.ename->u.name.it = IT_ENAME; np->u.event.ename->u.name.last = np->u.event.ename; ptr = sepptr + 1; ASSERT(ptr < &sbuf[sz]); ptr += strlen(ptr); ptr++; /* move past the '\0' separating path from value */ ASSERT(ptr < &sbuf[sz]); ASSERT(isdigit(*ptr)); val = atoi(ptr); ASSERT(val > 0); ptr += strlen(ptr); ptr++; /* move past the final '\0' for this entry */ np->u.event.epname = pathstring2epnamenp(sepptr + 1); ASSERT(np->u.event.epname != NULL); istat_bump(np, val); tree_free(np); } istat_save(); } /* * 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. * * fme.h -- public definitions for fme module * * this module supports the management of a "fault management exercise". */ #ifndef _EFT_FME_H #define _EFT_FME_H #include #ifdef __cplusplus extern "C" { #endif #define UNDIAG_DEFECT_CHKPT "defect.sunos.eft.undiag.checkpoint" #define UNDIAG_DEFECT_FME "defect.sunos.eft.unexpected_telemetry" #define UNDIAG_DEFECT_LIMIT "defect.sunos.eft.undiag.limit" #define UNDIAG_DEFECT_UNKNOWN "defect.sunos.eft.undiag.unknown" #define UNDIAG_FAULT_FME "fault.sunos.eft.unexpected_telemetry" #define UNDIAG_REASON "reason" /* Undiagnosable reason values */ #define UD_VAL_UNKNOWN 0 #define UD_VAL_BADEVENTI 1 #define UD_VAL_BADOBS 2 #define UD_VAL_CFGMISMATCH 3 #define UD_VAL_INSTFAIL 4 #define UD_VAL_MAXFME 5 #define UD_VAL_MISSINGINFO 6 #define UD_VAL_MISSINGOBS 7 #define UD_VAL_MISSINGPATH 8 #define UD_VAL_MISSINGZERO 9 #define UD_VAL_NOPATH 10 #define UD_VAL_UNSOLVD 11 #define UD_VAL_BADEVENTPATH 12 #define UD_VAL_BADEVENTCLASS 13 /* Undiagnosable reason strings */ #define UD_STR_UNKNOWN "undiagnosable reason unknown" #define UD_STR_MISSINGPATH "bad or missing path in persisted observation" #define UD_STR_MISSINGINFO "buffer persisting case info is AWOL" #define UD_STR_MISSINGZERO "buffer persisting principal ereport is AWOL" #define UD_STR_CFGMISMATCH "persisted config buffer size != actual size" #define UD_STR_MISSINGOBS "buffer persisting an observation is AWOL" #define UD_STR_BADEVENTI "%s not found in pruned instance tree" #define UD_STR_INSTFAIL "%s pruned instance tree is empty" #define UD_STR_UNSOLVD "all hypotheses were disproved" #define UD_STR_BADOBS "persisted observation not found in instance tree" #define UD_STR_NOPATH "no valid path to component was found in %s" #define UD_STR_MAXFME "reached the maximum number of open FMEs (maxfme)" #define UD_STR_BADEVENTPATH "%s path was not in topology" #define UD_STR_BADEVENTCLASS "%s class and path are incompatible" #define WOBUF_CFGLEN "rawcfglen" #define WOBUF_POSTD "posted" #define WOBUF_NOBS "observations" #define WOBUF_PULL "timewaited" #define WOBUF_CFG "rawcfgdata" #define WOBUF_ID "fmeid" #define WOBUF_ISTATS "istats" #define WOBUF_SERDS "serds" extern struct lut *Istats; /* instanced stats a la "count=" */ extern struct lut *SerdEngines; struct fme; void fme_receive_external_report(fmd_hdl_t *hdl, fmd_event_t *ffep, nvlist_t *nvl, const char *eventstring); void fme_receive_topology_change(void); void fme_receive_repair_list(fmd_hdl_t *hdl, fmd_event_t *ffep, nvlist_t *nvl, const char *eventstring); void fme_restart(fmd_hdl_t *hdl, fmd_case_t *inprogress); void fme_istat_load(fmd_hdl_t *hdl); void fme_serd_load(fmd_hdl_t *hdl); void fme_close_case(fmd_hdl_t *hdl, fmd_case_t *fmcase); void fme_timer_fired(struct fme *, id_t); void fme_status(int flags); void fme_fini(void); void istat_fini(void); struct istat_entry { const char *ename; const struct ipath *ipath; }; int istat_cmp(struct istat_entry *ent1, struct istat_entry *ent2); void serd_fini(void); struct serd_entry { const char *ename; const struct ipath *ipath; fmd_hdl_t *hdl; }; int serd_cmp(struct serd_entry *ent1, struct serd_entry *ent2); #ifdef __cplusplus } #endif #endif /* _EFT_FME_H */ /* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * iexpr.c -- instanced expression cache module * * this module provides a cache of fully instantized expressions. */ #include #include #include "alloc.h" #include "out.h" #include "lut.h" #include "tree.h" #include "ptree.h" #include "itree.h" #include "ipath.h" #include "iexpr.h" #include "stats.h" #include "eval.h" #include "config.h" #define IEXPRSZ 1024 /* hash table size */ static struct stats *Niexpr; /* the cache is a hash table of these structs */ static struct iexpr { struct node *np; struct iexpr *next; /* next entry in hash bucket */ int count; } *Cache[IEXPRSZ]; /* * iexpr_init -- initialize the iexpr module */ void iexpr_init(void) { Niexpr = stats_new_counter("iexpr.niexpr", "iexpr cache entries", 1); } /* * iexpr_hash -- produce a simple hash from an instanced expression tree */ static unsigned iexpr_hash(struct node *np) { if (np == NULL) return (1); switch (np->t) { case T_GLOBID: return ((uintptr_t)np->u.globid.s); 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_LIST: case T_AND: case T_OR: case T_NOT: case T_ADD: case T_SUB: case T_MUL: case T_DIV: case T_MOD: return ((int)np->t * (iexpr_hash(np->u.expr.left) + iexpr_hash(np->u.expr.right))); case T_NAME: return ((uintptr_t)np->u.name.s); case T_EVENT: return (iexpr_hash(np->u.event.ename) + iexpr_hash(np->u.event.epname)); case T_FUNC: return ((uintptr_t)np->u.func.s + iexpr_hash(np->u.func.arglist)); case T_QUOTE: return ((uintptr_t)np->u.quote.s); case T_NUM: case T_TIMEVAL: return ((int)np->u.ull); default: outfl(O_DIE, np->file, np->line, "iexpr_hash: unexpected node type: %s", ptree_nodetype2str(np->t)); } /*NOTREACHED*/ return (1); } /* * iexpr_cmp -- compare two instanced expression trees */ static int iexpr_cmp(struct node *np1, struct node *np2) { int diff; if (np1 == np2) return (0); if (np1 == NULL) return (1); if (np2 == NULL) return (-1); if (np1->t != np2->t) return (np2->t - np1->t); /* types match, need to see additional info matches */ switch (np1->t) { case T_GLOBID: return (np2->u.globid.s - np1->u.globid.s); 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_LIST: case T_AND: case T_OR: case T_NOT: case T_ADD: case T_SUB: case T_MUL: case T_DIV: case T_MOD: diff = iexpr_cmp(np1->u.expr.left, np2->u.expr.left); if (diff != 0) return (diff); return (iexpr_cmp(np1->u.expr.right, np2->u.expr.right)); case T_NAME: if (np2->u.name.s != np1->u.name.s) return (np2->u.name.s - np1->u.name.s); diff = iexpr_cmp(np1->u.name.child, np2->u.name.child); if (diff != 0) return (diff); return (iexpr_cmp(np1->u.name.next, np2->u.name.next)); case T_EVENT: diff = iexpr_cmp(np1->u.event.ename, np2->u.event.ename); if (diff != 0) return (diff); return (iexpr_cmp(np1->u.event.epname, np2->u.event.epname)); case T_FUNC: if (np1->u.func.s != np2->u.func.s) return (np2->u.func.s - np1->u.func.s); return (iexpr_cmp(np1->u.func.arglist, np2->u.func.arglist)); case T_QUOTE: return (np2->u.quote.s - np1->u.quote.s); case T_NUM: case T_TIMEVAL: if (np2->u.ull > np1->u.ull) return (1); else if (np1->u.ull > np2->u.ull) return (-1); else return (0); default: outfl(O_DIE, np1->file, np1->line, "iexpr_cmp: unexpected node type: %s", ptree_nodetype2str(np1->t)); } /*NOTREACHED*/ return (0); } /* * iexpr -- find instanced expr in cache, or add it if necessary */ struct node * iexpr(struct node *np) { unsigned idx = iexpr_hash(np) % IEXPRSZ; struct iexpr *bucketp = Cache[idx]; struct iexpr *cp; /* search cache */ for (cp = bucketp; cp != NULL; cp = cp->next) if (iexpr_cmp(cp->np, np) == 0) { /* found it */ tree_free(np); cp->count++; return (cp->np); } /* allocate new cache entry */ cp = MALLOC(sizeof (*cp)); cp->np = np; cp->next = bucketp; cp->count = 1; Cache[idx] = cp; stats_counter_bump(Niexpr); return (np); } void iexpr_free(struct node *np) { unsigned idx = iexpr_hash(np) % IEXPRSZ; struct iexpr *cp; struct iexpr *prevcp = NULL; /* search cache */ for (cp = Cache[idx]; cp != NULL; cp = cp->next) { if (iexpr_cmp(cp->np, np) == 0) { /* found it */ cp->count--; if (cp->count == 0) { tree_free(cp->np); if (prevcp == NULL) Cache[idx] = cp->next; else prevcp->next = cp->next; FREE(cp); } return; } prevcp = cp; } } /* * iexpr_cached -- return true if np is in the iexpr cache */ int iexpr_cached(struct node *np) { struct iexpr *cp = Cache[iexpr_hash(np) % IEXPRSZ]; /* search cache */ for (; cp != NULL; cp = cp->next) if (iexpr_cmp(cp->np, np) == 0) { /* found it */ return (1); } return (0); } /* * iexpr_fini -- free the iexpr cache */ void iexpr_fini(void) { int i; for (i = 0; i < IEXPRSZ; i++) { struct iexpr *cp; struct iexpr *ncp; for (cp = Cache[i]; cp != NULL; cp = ncp) { tree_free(cp->np); ncp = cp->next; FREE(cp); } Cache[i] = NULL; } } /* * 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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * iexpr.h -- public definitions for iexpr module * */ #ifndef _EFT_IEXPR_H #define _EFT_IEXPR_H #ifdef __cplusplus extern "C" { #endif void iexpr_init(void); struct node *iexpr(struct node *np); int iexpr_cached(struct node *np); void iexpr_free(struct node *np); void iexpr_fini(void); #ifdef __cplusplus } #endif #endif /* _EFT_IEXPR_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * io.c -- input/output routines for the eft DE */ #include #include "io.h" extern fmd_hdl_t *Hdl; /* handle in global for platform.c */ void io_abort(const char *buf) { fmd_hdl_abort(Hdl, "%s\n", buf); } void io_die(const char *buf) { fmd_hdl_abort(Hdl, "%s\n", buf); } void io_err(const char *buf) { fmd_hdl_abort(Hdl, "%s\n", buf); } void io_out(const char *buf) { fmd_hdl_debug(Hdl, "%s\n", buf); } void io_exit(int code) { fmd_hdl_abort(Hdl, "eft: exitcode %d\n", code); } /* * 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. * * ipath.c -- instanced pathname module * * this module provides a cache of fully instantized component paths, * stored in a fairly compact format. */ #include #include #include "alloc.h" #include "out.h" #include "lut.h" #include "tree.h" #include "ptree.h" #include "itree.h" #include "ipath.h" #include "ipath_impl.h" #include "stats.h" #include "eval.h" #include "config.h" static struct stats *Nipath; static struct stats *Nbytes; static struct lut *Ipaths; /* the ipath cache itself */ /* * ipath_init -- initialize the ipath module */ void ipath_init(void) { Nipath = stats_new_counter("ievent.nipath", "ipath cache entries", 1); Nbytes = stats_new_counter("ievent.nbytes", "total cache size", 1); } /* * ipath_cmp -- compare two ipath entries * * since two ipaths containing the same components and instance * numbers always point to the same cache entry, they are equal * if their pointers are equal, so this function is not necessary * to test if two ipaths are same. but when inserting a new ipath * into the cache, we must use the same lut comparison logic as when * we're searching for it, so this function must always match the * itree_epnamecmp() function's logic (see below) for searching the lut. */ static int ipath_cmp(struct ipath *ipp1, struct ipath *ipp2) { int i; ASSERT(ipp1 != NULL); ASSERT(ipp2 != NULL); for (i = 0; ipp1[i].s != NULL && ipp2[i].s != NULL; i++) if (ipp1[i].s != ipp2[i].s) return (ipp2[i].s - ipp1[i].s); else if (ipp1[i].i != ipp2[i].i) return (ipp2[i].i - ipp1[i].i); if (ipp1[i].s == NULL && ipp2[i].s == NULL) return (0); else if (ipp1[i].s == NULL) return (1); else return (-1); } /* * ipath_epnamecmp -- compare an ipath with a struct node *epname list * * this function is used when searching the cache, allowing us to search * a lut full of ipaths by looking directly at a struct node *epname * (without having to convert it first). the comparison logic here must * exactly match itree_cmp()'s logic (see above) so lut lookups use find * the same node as lut inserts. */ static int ipath_epnamecmp(struct ipath *ipp, struct node *np) { int i; ASSERT(np != NULL); ASSERT(ipp != NULL); for (i = 0; ipp[i].s != NULL && np != NULL; i++, np = np->u.name.next) { ASSERTinfo(np->t == T_NAME, ptree_nodetype2str(np->t)); if (ipp[i].s != np->u.name.s) return (np->u.name.s - ipp[i].s); else { int inum; if (np->u.name.child != NULL && np->u.name.child->t == T_NUM) inum = (int)np->u.name.child->u.ull; else config_getcompname(np->u.name.cp, NULL, &inum); if (ipp[i].i != inum) return (inum - ipp[i].i); } } if (ipp[i].s == NULL && np == NULL) return (0); else if (ipp[i].s == NULL) return (1); else return (-1); } /* * The following functions are only used in the "itree_create_dummy()" first * pass at itree creation. ipath_dummy() creates paths used in the itree (see * comment above add_event_dummy() for details). ipath_for_usednames() creates * a different set of paths using the full names from the propagations. These * are only used by ipath_dummy_lut() in order to set up the Usednames lut * correctly, which in turn allows conf propteries on any alement in those * names to be used in constraints. */ struct lut *Usednames; void ipath_dummy_lut(struct arrow *arrowp) { const struct ipath *ipp; ipp = arrowp->head->myevent->ipp_un; while (ipp->s != NULL) { Usednames = lut_add(Usednames, (void *)ipp->s, (void *)ipp->s, NULL); ipp++; } ipp = arrowp->tail->myevent->ipp_un; while (ipp->s != NULL) { Usednames = lut_add(Usednames, (void *)ipp->s, (void *)ipp->s, NULL); ipp++; } } struct ipath * ipath_dummy(struct node *np, struct ipath *ipp) { struct ipath *ret; ret = ipp; while (ipp[1].s != NULL) ipp++; if (strcmp(ipp[0].s, np->u.name.last->u.name.s) == 0) return (ret); ret = MALLOC(sizeof (*ret) * 2); ret[0].s = np->u.name.last->u.name.s; ret[0].i = 0; ret[1].s = NULL; if ((ipp = lut_lookup(Ipaths, (void *)ret, (lut_cmp)ipath_cmp)) != NULL) { FREE(ret); return (ipp); } Ipaths = lut_add(Ipaths, (void *)ret, (void *)ret, (lut_cmp)ipath_cmp); stats_counter_bump(Nipath); stats_counter_add(Nbytes, 2 * sizeof (struct ipath)); return (ret); } struct ipath * ipath_for_usednames(struct node *np) { struct ipath *ret, *ipp; int i = 0; struct node *np2; for (np2 = np; np2 != NULL; np2 = np2->u.name.next) i++; ret = MALLOC(sizeof (*ret) * (i + 1)); for (i = 0, np2 = np; np2 != NULL; np2 = np2->u.name.next) { ret[i].s = np2->u.name.s; ret[i++].i = 0; } ret[i].s = NULL; if ((ipp = lut_lookup(Ipaths, (void *)ret, (lut_cmp)ipath_cmp)) != NULL) { FREE(ret); return (ipp); } Ipaths = lut_add(Ipaths, (void *)ret, (void *)ret, (lut_cmp)ipath_cmp); stats_counter_bump(Nipath); stats_counter_add(Nbytes, (i + 1) * sizeof (struct ipath)); return (ret); } /* * ipath -- find instanced path in cache, or add it if necessary */ const struct ipath * ipath(struct node *np) { struct ipath *ret; int count; struct node *namep; int i; if ((ret = lut_lookup(Ipaths, (void *)np, (lut_cmp)ipath_epnamecmp)) != NULL) return (ret); /* already in cache */ /* * not in cache, make new cache entry. * start by counting the length of the name. */ count = 0; namep = np; while (namep != NULL) { ASSERTinfo(namep->t == T_NAME, ptree_nodetype2str(namep->t)); count++; namep = namep->u.name.next; } ASSERT(count > 0); /* allocate array for name and last NULL entry */ ret = MALLOC(sizeof (*ret) * (count + 1)); ret[count].s = NULL; /* fill in ipath entry */ namep = np; i = 0; while (namep != NULL) { ASSERT(i < count); ret[i].s = namep->u.name.s; if (namep->u.name.child != NULL && namep->u.name.child->t == T_NUM) ret[i].i = (int)namep->u.name.child->u.ull; else config_getcompname(namep->u.name.cp, NULL, &ret[i].i); i++; namep = namep->u.name.next; } /* add it to the cache */ Ipaths = lut_add(Ipaths, (void *)ret, (void *)ret, (lut_cmp)ipath_cmp); stats_counter_bump(Nipath); stats_counter_add(Nbytes, (count + 1) * sizeof (struct ipath)); return (ret); } /* * ipath2str -- convert ename and ipath to class@path string * * if both ename and ipp are provided (non-NULL), the resulting string * will be "class@path". otherwise, the string will just contain the * event class name (e.g. "ereport.io.pci.device") or just the path * name (e.g. "mothboard0/hostbridge0/pcibus1/pcidev0/pcifn1"), depending * on which argument is non-NULL. */ char * ipath2str(const char *ename, const struct ipath *ipp) { int i; size_t len = 0; char *ret; char *cp; /* count up length of class string */ if (ename != NULL) len += strlen(ename); /* count up length of path string, including slash separators */ if (ipp != NULL) { for (i = 0; ipp[i].s != NULL; i++) { /* add slash separator, but no leading slash */ if (i != 0) len++; len += snprintf(NULL, 0, "%s%d", ipp[i].s, ipp[i].i); } } if (ename != NULL && ipp != NULL) len++; /* room for '@' */ len++; /* room for final '\0' */ cp = ret = MALLOC(len); if (ename != NULL) { /* construct class string */ (void) strcpy(cp, ename); cp += strlen(cp); } /* if doing both strings, put '@' between them */ if (ename != NULL && ipp != NULL) *cp++ = '@'; if (ipp != NULL) { /* construct path string */ for (i = 0; ipp[i].s != NULL; i++) { if (i != 0) *cp++ = '/'; (void) snprintf(cp, &ret[len] - cp, "%s%d", ipp[i].s, ipp[i].i); cp += strlen(cp); } } *cp++ = '\0'; return (ret); } void ipathlastcomp(const struct ipath *ipp) { int i; for (i = 0; ipp[i].s != NULL; i++) ; out(O_ALTFP, "newfme: add %s to Usednames", ipp[i - 1].s); Usednames = lut_add(Usednames, (void *)ipp[i - 1].s, (void *)ipp[i - 1].s, NULL); } /* * ipath2strlen -- calculate the len of what ipath2str() would return */ size_t ipath2strlen(const char *ename, const struct ipath *ipp) { int i; size_t len = 0; /* count up length of class string */ if (ename != NULL) len += strlen(ename); /* count up length of path string, including slash separators */ if (ipp != NULL) { for (i = 0; ipp[i].s != NULL; i++) { /* add slash separator, but no leading slash */ if (i != 0) len++; len += snprintf(NULL, 0, "%s%d", ipp[i].s, ipp[i].i); } } if (ename != NULL && ipp != NULL) len++; /* room for '@' */ return (len); } /* * ipath_print -- print out an ename, ipath, or both with '@' between them */ void ipath_print(int flags, const char *ename, const struct ipath *ipp) { if (ename != NULL) { out(flags|O_NONL, ename); if (ipp != NULL) out(flags|O_NONL, "@"); } if (ipp != NULL) { char *sep = ""; while (ipp->s != NULL) { out(flags|O_NONL, "%s%s%d", sep, ipp->s, ipp->i); ipp++; sep = "/"; } } } /*ARGSUSED*/ static void ipath_destructor(void *left, void *right, void *arg) { struct ipath *ipp = (struct ipath *)right; FREE(ipp); } /* * ipath_fini -- free the ipath cache */ void ipath_fini(void) { lut_free(Ipaths, ipath_destructor, NULL); Ipaths = NULL; lut_free(Usednames, NULL, NULL); Usednames = NULL; if (Nipath) { stats_delete(Nipath); Nipath = NULL; } if (Nbytes) { stats_delete(Nbytes); Nbytes = NULL; } } /* * 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. * * ipath.h -- public definitions for ipath module * */ #ifndef _EFT_IPATH_H #define _EFT_IPATH_H #ifdef __cplusplus extern "C" { #endif void ipath_init(void); const struct ipath *ipath(struct node *np); void ipathlastcomp(const struct ipath *ipp); char *ipath2str(const char *ename, const struct ipath *ipp); size_t ipath2strlen(const char *ename, const struct ipath *ipp); void ipath_print(int flags, const char *ename, const struct ipath *ipp); void ipath_fini(void); #ifdef __cplusplus } #endif #endif /* _EFT_IPATH_H */ /* * 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. */ #ifndef _EFT_IPATH_IMPL_H #define _EFT_IPATH_IMPL_H #ifdef __cplusplus extern "C" { #endif /* * ipath_impl.h -- ipath private data shared with mdb module */ /* an ipath cache entry is an array of these, with s==NULL at the end */ struct ipath { const char *s; /* component name (in stable) */ int i; /* instance number */ }; #ifdef __cplusplus } #endif #endif /* _EFT_IPATH_IMPL_H */ /* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * itree.c -- instance tree creation and manipulation * * this module provides the instance tree */ #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 "iexpr.h" #include "eval.h" #include "config.h" /* * struct info contains the state we keep when expanding a prop statement * as part of constructing the instance tree. state kept in struct info * is the non-recursive stuff -- the stuff that doesn't need to be on * the stack. the rest of the state that is passed between all the * mutually recursive functions, is required to be on the stack since * we need to backtrack and recurse as we do the instance tree construction. */ struct info { struct lut *lut; struct node *anp; /* arrow np */ struct lut *ex; /* dictionary of explicit iterators */ struct config *croot; } Ninfo; /* * struct wildcardinfo is used to track wildcarded portions of paths. * * for example, if the epname of an event is "c/d" and the path "a/b/c/d" * exists, the wildcard path ewname is filled in with the path "a/b". when * matching is done, epname is temporarily replaced with the concatenation * of ewname and epname. * * a linked list of these structs is used to track the expansion of each * event node as it is processed in vmatch() --> vmatch_event() calls. */ struct wildcardinfo { struct node *nptop; /* event node fed to vmatch */ struct node *oldepname; /* epname without the wildcard part */ struct node *ewname; /* wildcard path */ int got_wc_hz; struct wildcardinfo *next; }; static struct wildcardinfo *wcproot = NULL; static void vmatch(struct info *infop, struct node *np, struct node *lnp, struct node *anp); static void hmatch(struct info *infop, struct node *np, struct node *nextnp); static void hmatch_event(struct info *infop, struct node *eventnp, struct node *epname, struct config *ncp, struct node *nextnp, int rematch); static void itree_pbubble(int flags, struct bubble *bp); static void itree_pruner(void *left, void *right, void *arg); static void itree_destructor(void *left, void *right, void *arg); static int itree_set_arrow_traits(struct arrow *ap, struct node *fromev, struct node *toev, struct lut *ex); static void itree_free_arrowlists(struct bubble *bubp, int arrows_too); static void itree_prune_arrowlists(struct bubble *bubp); static void arrow_add_within(struct arrow *ap, struct node *xpr); static struct arrow *itree_add_arrow(struct node *apnode, struct node *fromevent, struct node *toevent, struct lut *ex); static struct event *find_or_add_event(struct info *infop, struct node *np); static void add_arrow(struct bubble *bp, struct arrow *ap); static struct constraintlist *itree_add_constraint(struct arrow *arrowp, struct node *c); static struct bubble *itree_add_bubble(struct event *eventp, enum bubbletype btype, int nork, int gen); static void itree_free_bubble(struct bubble *freeme); static void itree_free_constraints(struct arrow *ap); /* * the following struct contains the state we build up during * vertical and horizontal expansion so that generate() * has everything it needs to construct the appropriate arrows. * after setting up the state by calling: * generate_arrownp() * generate_nork() * generate_new() * generate_from() * generate_to() * the actual arrow generation is done by calling: * generate() */ static struct { int generation; /* generation number of arrow set */ int matched; /* number of matches */ struct node *arrownp; /* top-level parse tree for arrow */ int n; /* n value associated with arrow */ int k; /* k value associated with arrow */ struct node *fromnp; /* left-hand-side event in parse tree */ struct node *tonp; /* right-hand-side event in parse tree */ struct event *frome; /* left-hand-side event in instance tree */ struct event *toe; /* right-hand-side event in instance tree */ struct bubble *frombp; /* bubble arrow comes from */ struct bubble *tobp; /* bubble arrow goes to */ } G; static void generate_arrownp(struct node *arrownp) { G.arrownp = arrownp; } static void generate_nork(int n, int k) { G.n = n; G.k = k; } static void generate_new(void) { G.generation++; } static void generate_from(struct node *fromeventnp) { G.frombp = NULL; G.fromnp = fromeventnp; } static void generate_to(struct node *toeventnp) { G.tonp = toeventnp; } static void generate(struct info *infop) { struct arrow *arrowp; ASSERT(G.arrownp != NULL); ASSERT(G.fromnp != NULL); ASSERT(G.tonp != NULL); out(O_ALTFP|O_VERB3|O_NONL, " Arrow \""); ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, G.fromnp); out(O_ALTFP|O_VERB3|O_NONL, "\" -> \""); ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, G.tonp); out(O_ALTFP|O_VERB3|O_NONL, "\" "); arrowp = itree_add_arrow(G.arrownp, G.fromnp, G.tonp, infop->ex); if (arrowp == NULL) { out(O_ALTFP|O_VERB3, "(prevented by constraints)"); } else { out(O_ALTFP|O_VERB3, ""); if (!G.frombp) { G.frome = find_or_add_event(infop, G.fromnp); G.frombp = itree_add_bubble(G.frome, B_FROM, G.n, 0); } G.toe = find_or_add_event(infop, G.tonp); G.tobp = itree_add_bubble(G.toe, B_TO, G.k, G.generation); arrowp->tail = G.frombp; arrowp->head = G.tobp; add_arrow(G.frombp, arrowp); add_arrow(G.tobp, arrowp); } } enum childnode_action { CN_NONE, CN_DUP }; static struct node * tname_dup(struct node *namep, enum childnode_action act) { struct node *retp = NULL; const char *file; int line; if (namep == NULL) return (NULL); file = namep->file; line = namep->line; for (; namep != NULL; namep = namep->u.name.next) { struct node *newnp = newnode(T_NAME, file, line); newnp->u.name.t = namep->u.name.t; newnp->u.name.s = namep->u.name.s; newnp->u.name.last = newnp; newnp->u.name.it = namep->u.name.it; newnp->u.name.cp = namep->u.name.cp; if (act == CN_DUP) { struct node *npc; npc = namep->u.name.child; if (npc != NULL) { switch (npc->t) { case T_NUM: newnp->u.name.child = newnode(T_NUM, file, line); newnp->u.name.child->u.ull = npc->u.ull; break; case T_NAME: newnp->u.name.child = tree_name(npc->u.name.s, npc->u.name.it, file, line); break; default: out(O_DIE, "tname_dup: " "invalid child type %s", ptree_nodetype2str(npc->t)); } } } if (retp == NULL) { retp = newnp; } else { retp->u.name.last->u.name.next = newnp; retp->u.name.last = newnp; } } return (retp); } struct prop_wlk_data { struct lut *props; struct node *epname; }; static struct lut *props2instance(struct node *, struct node *); /* * let oldepname be a subset of epname. return the subsection of epname * that ends with oldepname. make each component in the path explicitly * instanced (i.e., with a T_NUM child). */ static struct node * tname_dup_to_epname(struct node *oldepname, struct node *epname) { struct node *npref, *npend, *np1, *np2; struct node *ret = NULL; int foundmatch = 0; if (epname == NULL) return (NULL); /* * search for the longest path in epname which contains * oldnode->u.event.epname. set npend to point to just past the * end of this path. */ npend = NULL; for (npref = epname; npref; npref = npref->u.name.next) { if (npref->u.name.s == oldepname->u.name.s) { for (np1 = npref, np2 = oldepname; np1 != NULL && np2 != NULL; np1 = np1->u.name.next, np2 = np2->u.name.next) { if (np1->u.name.s != np2->u.name.s) break; } if (np2 == NULL) { foundmatch = 1; npend = np1; if (np1 == NULL) { /* oldepname matched npref up to end */ break; } } } } if (foundmatch == 0) { /* * if oldepname could not be found in epname, return a * duplicate of the former. do not try to instantize * oldepname since it might not be a path. */ return (tname_dup(oldepname, CN_DUP)); } /* * dup (epname -- npend). all children should be T_NUMs. */ for (npref = epname; ! (npref == NULL || npref == npend); npref = npref->u.name.next) { struct node *newnp = newnode(T_NAME, oldepname->file, oldepname->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, oldepname->file, oldepname->line); if (npref->u.name.child == NULL || npref->u.name.child->t != T_NUM) { int childnum; ASSERT(npref->u.name.cp != NULL); config_getcompname(npref->u.name.cp, NULL, &childnum); newnp->u.name.child->u.ull = childnum; } else { newnp->u.name.child->u.ull = npref->u.name.child->u.ull; } if (ret == NULL) { ret = newnp; } else { ret->u.name.last->u.name.next = newnp; ret->u.name.last = newnp; } } return (ret); } /* * restriction: oldnode->u.event.epname has to be equivalent to or a subset * of epname */ static struct node * tevent_dup_to_epname(struct node *oldnode, struct node *epname) { struct node *ret; ret = newnode(T_EVENT, oldnode->file, oldnode->line); ret->u.event.ename = tname_dup(oldnode->u.event.ename, CN_NONE); ret->u.event.epname = tname_dup_to_epname(oldnode->u.event.epname, epname); return (ret); } static void nv_instantiate(void *name, void *val, void *arg) { struct prop_wlk_data *pd = (struct prop_wlk_data *)arg; struct node *orhs = (struct node *)val; struct node *nrhs; /* handle engines by instantizing the entire engine */ if (name == L_engine) { ASSERT(orhs->t == T_EVENT); ASSERT(orhs->u.event.ename->u.name.t == N_SERD); /* there are only SERD engines for now */ nrhs = newnode(T_SERD, orhs->file, orhs->line); nrhs->u.stmt.np = tevent_dup_to_epname(orhs, pd->epname); nrhs->u.stmt.lutp = props2instance(orhs, pd->epname); pd->props = lut_add(pd->props, name, nrhs, NULL); return; } switch (orhs->t) { case T_NUM: nrhs = newnode(T_NUM, orhs->file, orhs->line); nrhs->u.ull = orhs->u.ull; pd->props = lut_add(pd->props, name, nrhs, NULL); break; case T_TIMEVAL: nrhs = newnode(T_TIMEVAL, orhs->file, orhs->line); nrhs->u.ull = orhs->u.ull; pd->props = lut_add(pd->props, name, nrhs, NULL); break; case T_NAME: nrhs = tname_dup_to_epname(orhs, pd->epname); pd->props = lut_add(pd->props, name, nrhs, NULL); break; case T_EVENT: nrhs = tevent_dup_to_epname(orhs, pd->epname); pd->props = lut_add(pd->props, name, nrhs, NULL); break; case T_GLOBID: nrhs = newnode(T_GLOBID, orhs->file, orhs->line); nrhs->u.globid.s = orhs->u.globid.s; pd->props = lut_add(pd->props, name, nrhs, NULL); break; case T_FUNC: /* for T_FUNC, we don't duplicate it, just point to node */ pd->props = lut_add(pd->props, name, orhs, NULL); break; default: out(O_DIE, "unexpected nvpair value type %s", ptree_nodetype2str(((struct node *)val)->t)); } } static struct lut * props2instance(struct node *eventnp, struct node *epname) { struct prop_wlk_data pd; pd.props = NULL; pd.epname = epname; ASSERT(eventnp->u.event.declp != NULL); lut_walk(eventnp->u.event.declp->u.stmt.lutp, nv_instantiate, &pd); return (pd.props); } /*ARGSUSED*/ static void instances_destructor(void *left, void *right, void *arg) { struct node *dn = (struct node *)right; if (dn->t == T_SERD) { /* we allocated the lut during itree_create(), so free it */ lut_free(dn->u.stmt.lutp, instances_destructor, NULL); dn->u.stmt.lutp = NULL; } if (dn->t != T_FUNC) /* T_FUNC pointed to original node */ tree_free(dn); } /*ARGSUSED*/ static void payloadprops_destructor(void *left, void *right, void *arg) { FREE(right); } /*ARGSUSED*/ static void serdprops_destructor(void *left, void *right, void *arg) { FREE(right); } /* * event_cmp -- used via lut_lookup/lut_add on instance tree lut */ static int event_cmp(struct event *ep1, struct event *ep2) { int diff; if ((diff = ep2->enode->u.event.ename->u.name.s - ep1->enode->u.event.ename->u.name.s) != 0) return (diff); if ((diff = (char *)ep2->ipp - (char *)ep1->ipp) != 0) return (diff); return (0); } struct event * itree_lookup(struct lut *itp, const char *ename, const struct ipath *ipp) { struct event searchevent; /* just used for searching */ struct node searcheventnode; struct node searchenamenode; searchevent.enode = &searcheventnode; searcheventnode.t = T_EVENT; searcheventnode.u.event.ename = &searchenamenode; searchenamenode.t = T_NAME; searchenamenode.u.name.s = ename; searchevent.ipp = ipp; return (lut_lookup(itp, (void *)&searchevent, (lut_cmp)event_cmp)); } static struct event * find_or_add_event(struct info *infop, struct node *np) { struct event *ret; struct event searchevent; /* just used for searching */ ASSERTeq(np->t, T_EVENT, ptree_nodetype2str); searchevent.enode = np; searchevent.ipp = ipath(np->u.event.epname); if ((ret = lut_lookup(infop->lut, (void *)&searchevent, (lut_cmp)event_cmp)) != NULL) return (ret); /* wasn't already in tree, allocate it */ ret = alloc_xmalloc(sizeof (*ret)); bzero(ret, sizeof (*ret)); ret->t = np->u.event.ename->u.name.t; ret->enode = np; ret->ipp = searchevent.ipp; ret->props = props2instance(np, np->u.event.epname); infop->lut = lut_add(infop->lut, (void *)ret, (void *)ret, (lut_cmp)event_cmp); return (ret); } /* * Used for handling expansions where first part of oldepname is a horizontal * expansion. Recurses through entire tree. oldepname argument is always the * full path as in the rules. Once we find a match we go back to using * hmatch_event to handle the rest. */ static void hmatch_full_config(struct info *infop, struct node *eventnp, struct node *oldepname, struct config *ncp, struct node *nextnp, struct iterinfo *iterinfop) { char *cp_s; int cp_num; struct config *cp; struct node *saved_ewname; struct node *saved_epname; struct config *pcp, *ocp; struct node *cpnode; struct node *ewlp, *ewfp; for (cp = ncp; cp; cp = config_next(cp)) { config_getcompname(cp, &cp_s, &cp_num); if (cp_s == oldepname->u.name.s) { /* * Found one. */ iterinfop->num = cp_num; /* * Need to set ewname, epname for correct node as is * needed by constraint path matching. This code is * similar to that in vmatch_event. */ saved_ewname = eventnp->u.event.ewname; saved_epname = eventnp->u.event.epname; ocp = oldepname->u.name.cp; /* * Find correct ewname by walking back up the config * tree adding each name portion as we go. */ pcp = config_parent(cp); eventnp->u.event.ewname = NULL; for (; pcp != infop->croot; pcp = config_parent(pcp)) { config_getcompname(pcp, &cp_s, &cp_num); cpnode = tree_name(cp_s, IT_NONE, NULL, 0); cpnode->u.name.child = newnode(T_NUM, NULL, 0); cpnode->u.name.child->u.ull = cp_num; cpnode->u.name.cp = pcp; if (eventnp->u.event.ewname != NULL) { cpnode->u.name.next = eventnp->u.event.ewname; cpnode->u.name.last = eventnp->u.event.ewname-> u.name.last; } eventnp->u.event.ewname = cpnode; } /* * Now create correct epname by duping new ewname * and appending oldepname. */ ewfp = tname_dup(eventnp->u.event.ewname, CN_DUP); ewlp = ewfp->u.name.last; ewfp->u.name.last = oldepname->u.name.last; ewlp->u.name.next = oldepname; oldepname->u.name.cp = cp; eventnp->u.event.epname = ewfp; outfl(O_ALTFP|O_VERB3|O_NONL, infop->anp->file, infop->anp->line, "hmatch_full_config: "); ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, eventnp->u.event.epname); out(O_ALTFP|O_VERB3, NULL); /* * Now complete hmatch. */ hmatch_event(infop, eventnp, oldepname->u.name.next, config_child(cp), nextnp, 1); /* * set everything back again */ oldepname->u.name.cp = ocp; iterinfop->num = -1; ewlp->u.name.next = NULL; ewfp->u.name.last = ewlp; tree_free(ewfp); tree_free(eventnp->u.event.ewname); eventnp->u.event.ewname = saved_ewname; eventnp->u.event.epname = saved_epname; } /* * Try the next level down. */ hmatch_full_config(infop, eventnp, oldepname, config_child(cp), nextnp, iterinfop); } } /* * hmatch_event -- perform any appropriate horizontal expansion on an event * * this routine is used to perform horizontal expansion on both the * left-hand-side events in a prop, and the right-hand-side events. * when called to handle a left-side event, nextnp point to the right * side of the prop that should be passed to hmatch() for each match * found by horizontal expansion. when no horizontal expansion exists, * we will still "match" one event for every event found in the list on * the left-hand-side of the prop because vmatch() already found that * there's at least one match during vertical expansion. */ static void hmatch_event(struct info *infop, struct node *eventnp, struct node *epname, struct config *ncp, struct node *nextnp, int rematch) { if (epname == NULL) { /* * end of pathname recursion, either we just located * a left-hand-side event and we're ready to move on * to the expanding the right-hand-side events, or * we're further down the recursion and we just located * a right-hand-side event. the passed-in parameter * "nextnp" tells us whether we're working on the left * side and need to move on to nextnp, or if nextnp is * NULL, we're working on the right side. */ if (nextnp) { /* * finished a left side expansion, move on to right. * tell generate() what event we just matched so * it can be used at the source of any arrows * we generate as we match events on the right side. */ generate_from(eventnp); hmatch(infop, nextnp, NULL); } else { /* * finished a right side expansion. tell generate * the information about the destination and let * it construct the arrows as appropriate. */ generate_to(eventnp); generate(infop); } return; } ASSERTeq(epname->t, T_NAME, ptree_nodetype2str); if (epname->u.name.cp == NULL) return; /* * we only get here when eventnp already has a completely * instanced epname in it already. so we first recurse * down to the end of the name and as the recursion pops * up, we look for opportunities to advance horizontal * expansions on to the next match. */ if (epname->u.name.it == IT_HORIZONTAL || rematch) { struct config *cp; struct config *ocp = epname->u.name.cp; char *cp_s; int cp_num; int ocp_num; struct iterinfo *iterinfop = NULL; const char *iters; int hexpand = 0; if (epname->u.name.it != IT_HORIZONTAL) { /* * Ancestor was horizontal though, so must rematch * against the name/num found in vmatch. */ config_getcompname(ocp, NULL, &ocp_num); } else { iters = epname->u.name.child->u.name.s; if ((iterinfop = lut_lookup(infop->ex, (void *)iters, NULL)) == NULL) { /* * do horizontal expansion on this node */ hexpand = 1; iterinfop = alloc_xmalloc( sizeof (struct iterinfo)); iterinfop->num = -1; iterinfop->np = epname; infop->ex = lut_add(infop->ex, (void *)iters, iterinfop, NULL); } else if (iterinfop->num == -1) { hexpand = 1; } else { /* * This name has already been used in a * horizontal expansion. This time just match it */ ocp_num = iterinfop->num; } } /* * handle case where this is the first section of oldepname * and it is horizontally expanded. Instead of just looking for * siblings, we want to scan the entire config tree for further * matches. */ if (epname == eventnp->u.event.oldepname && epname->u.name.it == IT_HORIZONTAL) { /* * Run through config looking for any that match the * name. */ hmatch_full_config(infop, eventnp, epname, infop->croot, nextnp, iterinfop); return; } /* * Run through siblings looking for any that match the name. * If hexpand not set then num must also match ocp_num. */ for (cp = rematch ? ncp : ocp; cp; cp = config_next(cp)) { config_getcompname(cp, &cp_s, &cp_num); if (cp_s == epname->u.name.s) { if (hexpand) iterinfop->num = cp_num; else if (ocp_num != cp_num) continue; epname->u.name.cp = cp; hmatch_event(infop, eventnp, epname->u.name.next, config_child(cp), nextnp, 1); } } epname->u.name.cp = ocp; if (hexpand) iterinfop->num = -1; } else { hmatch_event(infop, eventnp, epname->u.name.next, NULL, nextnp, 0); } } /* * hmatch -- check for horizontal expansion matches * * np points to the things we're matching (like a T_LIST or a T_EVENT) * and if we're working on a left-side of a prop, nextnp points to * the other side of the prop that we'll tackle next when this recursion * bottoms out. when all the events in the entire prop arrow have been * horizontally expanded, generate() will be called to generate the * actualy arrow. */ static void hmatch(struct info *infop, struct node *np, struct node *nextnp) { if (np == NULL) return; /* all done */ /* * for each item in the list of events (which could just * be a single event, or it could get larger in the loop * below due to horizontal expansion), call hmatch on * the right side and create arrows to each element. */ switch (np->t) { case T_LIST: /* loop through the list */ if (np->u.expr.left) hmatch(infop, np->u.expr.left, nextnp); if (np->u.expr.right) hmatch(infop, np->u.expr.right, nextnp); break; case T_EVENT: hmatch_event(infop, np, np->u.event.epname, NULL, nextnp, 0); break; default: outfl(O_DIE, np->file, np->line, "hmatch: unexpected type: %s", ptree_nodetype2str(np->t)); } } static int itree_np2nork(struct node *norknp) { if (norknp == NULL) return (1); else if (norknp->t == T_NAME && norknp->u.name.s == L_A) return (-1); /* our internal version of "all" */ else if (norknp->t == T_NUM) return ((int)norknp->u.ull); else outfl(O_DIE, norknp->file, norknp->line, "itree_np2nork: internal error type %s", ptree_nodetype2str(norknp->t)); /*NOTREACHED*/ return (1); } static struct iterinfo * newiterinfo(int num, struct node *np) { struct iterinfo *ret = alloc_xmalloc(sizeof (*ret)); ret->num = num; ret->np = np; return (ret); } /*ARGSUSED*/ static void iterinfo_destructor(void *left, void *right, void *arg) { struct iterinfo *iterinfop = (struct iterinfo *)right; alloc_xfree(iterinfop, sizeof (*iterinfop)); } static void vmatch_event(struct info *infop, struct config *cp, struct node *np, struct node *lnp, struct node *anp, struct wildcardinfo *wcp) { char *cp_s; int cp_num; struct node *ewlp, *ewfp; struct config *pcp; struct node *cpnode; int newewname = 0; /* * handle case where the first section of the path name is horizontally * expanded. The whole expansion is handled by hmatch on the first * match here - so we just skip any subsequent matches here. */ if (wcp->got_wc_hz == 1) return; if (np == NULL) { /* * Reached the end of the name. u.name.cp pointers should be set * up for each part of name. From this we can use config tree * to build up the wildcard part of the name (if any). */ pcp = config_parent(wcp->nptop->u.event.epname->u.name.cp); if (pcp == infop->croot) { /* * no wildcarding done - move on to next entry */ wcp->nptop->u.event.ewname = wcp->ewname; wcp->nptop->u.event.oldepname = wcp->oldepname; vmatch(infop, np, lnp, anp); wcp->got_wc_hz = 0; return; } if (wcp->ewname == NULL) { /* * ewname not yet set up - do it now */ newewname = 1; for (; pcp != infop->croot; pcp = config_parent(pcp)) { config_getcompname(pcp, &cp_s, &cp_num); cpnode = tree_name(cp_s, IT_NONE, NULL, 0); cpnode->u.name.child = newnode(T_NUM, NULL, 0); cpnode->u.name.child->u.ull = cp_num; cpnode->u.name.cp = pcp; if (wcp->ewname != NULL) { cpnode->u.name.next = wcp->ewname; cpnode->u.name.last = wcp->ewname->u.name.last; } wcp->ewname = cpnode; } } /* * dup ewname and append oldepname */ ewfp = tname_dup(wcp->ewname, CN_DUP); ewlp = ewfp->u.name.last; ewfp->u.name.last = wcp->oldepname->u.name.last; ewlp->u.name.next = wcp->oldepname; wcp->nptop->u.event.epname = ewfp; wcp->nptop->u.event.ewname = wcp->ewname; wcp->nptop->u.event.oldepname = wcp->oldepname; vmatch(infop, np, lnp, anp); wcp->got_wc_hz = 0; wcp->nptop->u.event.epname = wcp->oldepname; /* * reduce duped ewname to original then free */ ewlp->u.name.next = NULL; ewfp->u.name.last = ewlp; tree_free(ewfp); if (newewname) { /* * free ewname if allocated above */ tree_free(wcp->ewname); wcp->ewname = NULL; } return; } /* * We have an np. See if we can match it in this section of * the config tree. */ if (cp == NULL) return; /* no more config to match against */ for (; cp; cp = config_next(cp)) { config_getcompname(cp, &cp_s, &cp_num); if (cp_s == np->u.name.s) { /* found a matching component name */ if (np->u.name.child && np->u.name.child->t == T_NUM) { /* * an explicit instance number was given * in the source. so only consider this * a configuration match if the number * also matches. */ if (cp_num != np->u.name.child->u.ull) continue; } else if (np->u.name.it != IT_HORIZONTAL) { struct iterinfo *iterinfop; const char *iters; /* * vertical iterator. look it up in * the appropriate lut and if we get * back a value it is either one that we * set earlier, in which case we record * the new value for this iteration and * keep matching, or it is one that was * set by an earlier reference to the * iterator, in which case we only consider * this a configuration match if the number * matches cp_num. */ ASSERT(np->u.name.child != NULL); ASSERT(np->u.name.child->t == T_NAME); iters = np->u.name.child->u.name.s; if ((iterinfop = lut_lookup(infop->ex, (void *)iters, NULL)) == NULL) { /* we're the first use, record our np */ infop->ex = lut_add(infop->ex, (void *)iters, newiterinfo(cp_num, np), NULL); } else if (np == iterinfop->np) { /* * we're the first use, back again * for another iteration. so update * the num bound to this iterator in * the lut. */ iterinfop->num = cp_num; } else if (cp_num != iterinfop->num) { /* * an earlier reference to this * iterator bound it to a different * instance number, so there's no * match here after all. * * however, it's possible that this * component should really be part of * the wildcard. we explore this by * forcing this component into the * wildcarded section. * * for an more details of what's * going to happen now, see * comments block below entitled * "forcing components into * wildcard path". */ if (np == wcp->nptop->u.event.epname) vmatch_event(infop, config_child(cp), np, lnp, anp, wcp); continue; } } /* * if this was an IT_HORIZONTAL name, hmatch() will * expand all matches horizontally into a list. * we know the list will contain at least * one element (the one we just matched), * so we just let hmatch_event() do the rest. * * recurse on to next component. Note that * wildcarding is now turned off. */ np->u.name.cp = cp; vmatch_event(infop, config_child(cp), np->u.name.next, lnp, anp, wcp); np->u.name.cp = NULL; /* * handle case where this is the first section of the * path name and it is horizontally expanded. * In this case we want all matching nodes in the config * to be expanded horizontally - so set got_wc_hz and * leave all further processing to hmatch. */ if (G.matched && np == wcp->nptop->u.event.epname && np->u.name.it == IT_HORIZONTAL) wcp->got_wc_hz = 1; /* * forcing components into wildcard path: * * if this component is the first match, force it * to be part of the wildcarded path and see if we * can get additional matches. * * here's an example. suppose we have the * definition * event foo@x/y * and configuration * a0/x0/y0/a1/x1/y1 * * the code up to this point will treat "a0" as the * wildcarded part of the path and "x0/y0" as the * nonwildcarded part, resulting in the instanced * event * foo@a0/x0/y0 * * in order to discover the next match (.../x1/y1) * in the configuration we have to force "x0" into * the wildcarded part of the path. * by doing so, we discover the wildcarded part * "a0/x0/y0/a1" and the nonwildcarded part "x1/y1" * * the following call to vmatch_event() is also * needed to properly handle the configuration * b0/x0/b1/x1/y1 * * the recursions into vmatch_event() will start * off uncovering "b0" as the wildcarded part and * "x0" as the start of the nonwildcarded path. * however, the next recursion will not result in a * match since there is no "y" following "x0". the * subsequent match of (wildcard = "b0/x0/b1" and * nonwildcard = "x1/y1") will be discovered only * if "x0" is forced to be a part of the wildcarded * path. */ if (np == wcp->nptop->u.event.epname) vmatch_event(infop, config_child(cp), np, lnp, anp, wcp); if (np->u.name.it == IT_HORIZONTAL) { /* * hmatch() finished iterating through * the configuration as described above, so * don't continue iterating here. */ return; } } else if (np == wcp->nptop->u.event.epname) { /* * no match - carry on down the tree looking for * wildcarding */ vmatch_event(infop, config_child(cp), np, lnp, anp, wcp); } } } /* * vmatch -- find the next vertical expansion match in the config database * * this routine is called with three node pointers: * np -- the parse we're matching * lnp -- the rest of the list we're currently working on * anp -- the rest of the arrow we're currently working on * * the expansion matching happens via three types of recursion: * * - when given an arrow, handle the left-side and then recursively * handle the right side (which might be another cascaded arrow). * * - when handling one side of an arrow, recurse through the T_LIST * to get to each event (or just move on to the event if there * is a single event instead of a list) since the arrow parse * trees recurse left, we actually start with the right-most * event list in the prop statement and work our way towards * the left-most event list. * * - when handling an event, recurse down each component of the * pathname, matching in the config database and recording the * matches in the explicit iterator dictionary as we go. * * when the bottom of this matching recursion is met, meaning we have * set the "cp" pointers on all the names in the entire statement, * we call hmatch() which does it's own recursion to handle horizontal * expandsion and then call generate() to generate nodes, bubbles, and * arrows in the instance tree. generate() looks at the cp pointers to * see what instance numbers were matched in the configuration database. * * when horizontal expansion appears, vmatch() finds only the first match * and hmatch() then takes the horizontal expansion through all the other * matches when generating the arrows in the instance tree. * * the "infop" passed down through the recursion contains a dictionary * of the explicit iterators (all the implicit iterators have been converted * to explicit iterators when the parse tree was created by tree.c), which * allows things like this to work correctly: * * prop error.a@x[n]/y/z -> error.b@x/y[n]/z -> error.c@x/y/z[n]; * * during the top level call, the explicit iterator "n" will match an * instance number in the config database, and the result will be recorded * in the explicit iterator dictionary and passed down via "infop". so * when the recursive call tries to match y[n] in the config database, it * will only match the same instance number as x[n] did since the dictionary * is consulted to see if "n" took on a value already. * * at any point during the recursion, match*() can return to indicate * a match was not found in the config database and that the caller should * move on to the next potential match, if any. * * constraints are completely ignored by match(), so the statement: * * prop error.a@x[n] -> error.b@x[n] {n != 0}; * * might very well match x[0] if it appears in the config database. it * is the generate() routine that takes that match and then decides what * arrow, if any, should be generated in the instance tree. generate() * looks at the explicit iterator dictionary to get values like "n" in * the above example so that it can evaluate constraints. * */ static void vmatch(struct info *infop, struct node *np, struct node *lnp, struct node *anp) { struct node *np1, *np2, *oldepname, *oldnptop; int epmatches; struct config *cp; struct wildcardinfo *wcp; if (np == NULL) { G.matched = 1; if (lnp) vmatch(infop, lnp, NULL, anp); else if (anp) vmatch(infop, anp, NULL, NULL); else { struct node *src; struct node *dst; /* end of vertical match recursion */ outfl(O_ALTFP|O_VERB3|O_NONL, infop->anp->file, infop->anp->line, "vmatch: "); ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, infop->anp); out(O_ALTFP|O_VERB3, NULL); generate_nork( itree_np2nork(infop->anp->u.arrow.nnp), itree_np2nork(infop->anp->u.arrow.knp)); dst = infop->anp->u.arrow.rhs; src = infop->anp->u.arrow.lhs; for (;;) { generate_new(); /* new set of arrows */ if (src->t == T_ARROW) { hmatch(infop, src->u.arrow.rhs, dst); generate_nork( itree_np2nork(src->u.arrow.nnp), itree_np2nork(src->u.arrow.knp)); dst = src->u.arrow.rhs; src = src->u.arrow.lhs; } else { hmatch(infop, src, dst); break; } } } return; } switch (np->t) { case T_EVENT: { epmatches = 0; /* * see if we already have a match in the wcps */ for (wcp = wcproot; wcp; wcp = wcp->next) { oldepname = wcp->oldepname; oldnptop = wcp->nptop; for (np1 = oldepname, np2 = np->u.event.epname; 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; epmatches++; } if (epmatches) break; } if (epmatches && np1 == NULL && np2 == NULL) { /* * complete names match, can just borrow the fields */ oldepname = np->u.event.epname; np->u.event.epname = oldnptop->u.event.epname; np->u.event.oldepname = wcp->oldepname; np->u.event.ewname = wcp->ewname; vmatch(infop, NULL, lnp, anp); np->u.event.epname = oldepname; return; } G.matched = 0; if (epmatches) { /* * just first part of names match - do wildcarding * by using existing wcp including ewname and also * copying as much of pwname as is valid, then start * vmatch_event() at start of non-matching section */ for (np1 = oldepname, np2 = np->u.event.epname; epmatches != 0; epmatches--) { cp = np1->u.name.cp; np2->u.name.cp = cp; np1 = np1->u.name.next; np2 = np2->u.name.next; } wcp->oldepname = np->u.event.epname; wcp->nptop = np; vmatch_event(infop, config_child(cp), np2, lnp, anp, wcp); wcp->oldepname = oldepname; wcp->nptop = oldnptop; if (G.matched == 0) { /* * This list entry is NULL. Move on to next item * in the list. */ vmatch(infop, NULL, lnp, anp); } return; } /* * names do not match - allocate a new wcp */ wcp = MALLOC(sizeof (struct wildcardinfo)); wcp->next = wcproot; wcproot = wcp; wcp->nptop = np; wcp->oldepname = np->u.event.epname; wcp->ewname = NULL; wcp->got_wc_hz = 0; vmatch_event(infop, config_child(infop->croot), np->u.event.epname, lnp, anp, wcp); wcproot = wcp->next; FREE(wcp); if (G.matched == 0) { /* * This list entry is NULL. Move on to next item in the * list. */ vmatch(infop, NULL, lnp, anp); } break; } case T_LIST: ASSERT(lnp == NULL); vmatch(infop, np->u.expr.right, np->u.expr.left, anp); break; case T_ARROW: ASSERT(lnp == NULL && anp == NULL); vmatch(infop, np->u.arrow.rhs, NULL, np->u.arrow.parent); break; default: outfl(O_DIE, np->file, np->line, "vmatch: unexpected type: %s", ptree_nodetype2str(np->t)); } } static void find_first_arrow(struct info *infop, struct node *anp) { if (anp->u.arrow.lhs->t == T_ARROW) { anp->u.arrow.lhs->u.arrow.parent = anp; find_first_arrow(infop, anp->u.arrow.lhs); } else vmatch(infop, anp->u.arrow.lhs, NULL, anp); } static void cp_reset(struct node *np) { if (np == NULL) return; switch (np->t) { case T_NAME: np->u.name.cp = NULL; cp_reset(np->u.name.next); break; case T_LIST: cp_reset(np->u.expr.left); cp_reset(np->u.expr.right); break; case T_ARROW: cp_reset(np->u.arrow.lhs); cp_reset(np->u.arrow.rhs); break; case T_EVENT: cp_reset(np->u.event.epname); break; } } /* * itree_create -- apply the current config to the current parse tree * * returns a lut mapping fully-instance-qualified names to struct events. * */ struct lut * itree_create(struct config *croot) { struct lut *retval; struct node *propnp; extern int alloc_total(); int init_size; Ninfo.lut = NULL; Ninfo.croot = croot; init_size = alloc_total(); out(O_ALTFP|O_STAMP, "start itree_create using %d bytes", init_size); for (propnp = Props; propnp; propnp = propnp->u.stmt.next) { struct node *anp = propnp->u.stmt.np; ASSERTeq(anp->t, T_ARROW, ptree_nodetype2str); if (!anp->u.arrow.needed) continue; Ninfo.anp = anp; Ninfo.ex = NULL; generate_arrownp(anp); anp->u.arrow.parent = NULL; find_first_arrow(&Ninfo, anp); if (Ninfo.ex) { lut_free(Ninfo.ex, iterinfo_destructor, NULL); Ninfo.ex = NULL; } cp_reset(anp); } out(O_ALTFP|O_STAMP, "itree_create added %d bytes", alloc_total() - init_size); retval = Ninfo.lut; Ninfo.lut = NULL; return (retval); } /* * initial first pass of the rules. * We don't use the config at all. Just check the last part of the pathname * in the rules. If this matches the last part of the pathname in the first * ereport, then set pathname to the pathname in the ereport. If not then * set pathname to just the last part of pathname with instance number 0. * Constraints are ignored and all nork values are set to 0. If after all that * any rules can still not be associated with the ereport, then they are set * to not needed in prune_propagations() and ignored in the real itree_create() * which follows. */ static struct event * add_event_dummy(struct node *np, const struct ipath *ipp) { struct event *ret; struct event searchevent; /* just used for searching */ extern struct ipath *ipath_dummy(struct node *, struct ipath *); struct ipath *ipp_un; extern struct ipath *ipath_for_usednames(struct node *); searchevent.enode = np; searchevent.ipp = ipath_dummy(np->u.event.epname, (struct ipath *)ipp); ipp_un = ipath_for_usednames(np->u.event.epname); if ((ret = lut_lookup(Ninfo.lut, (void *)&searchevent, (lut_cmp)event_cmp)) != NULL) return (ret); ret = alloc_xmalloc(sizeof (*ret)); bzero(ret, sizeof (*ret)); ret->t = np->u.event.ename->u.name.t; ret->enode = np; ret->ipp = searchevent.ipp; ret->ipp_un = ipp_un; Ninfo.lut = lut_add(Ninfo.lut, (void *)ret, (void *)ret, (lut_cmp)event_cmp); return (ret); } /*ARGSUSED*/ struct lut * itree_create_dummy(const char *e0class, const struct ipath *e0ipp) { struct node *propnp; struct event *frome, *toe; struct bubble *frombp, *tobp; struct arrow *arrowp; struct node *src, *dst, *slst, *dlst, *arrownp, *oldarrownp; int gen = 0; extern int alloc_total(); int init_size; Ninfo.lut = NULL; init_size = alloc_total(); out(O_ALTFP|O_STAMP, "start itree_create using %d bytes", init_size); for (propnp = Props; propnp; propnp = propnp->u.stmt.next) { arrownp = propnp->u.stmt.np; while (arrownp) { gen++; dlst = arrownp->u.arrow.rhs; slst = arrownp->u.arrow.lhs; oldarrownp = arrownp; if (slst->t == T_ARROW) { arrownp = slst; slst = slst->u.arrow.rhs; } else { arrownp = NULL; } while (slst) { if (slst->t == T_LIST) { src = slst->u.expr.right; slst = slst->u.expr.left; } else { src = slst; slst = NULL; } frome = add_event_dummy(src, e0ipp); frombp = itree_add_bubble(frome, B_FROM, 0, 0); while (dlst) { if (dlst->t == T_LIST) { dst = dlst->u.expr.right; dlst = dlst->u.expr.left; } else { dst = dlst; dlst = NULL; } arrowp = alloc_xmalloc( sizeof (struct arrow)); bzero(arrowp, sizeof (struct arrow)); arrowp->pnode = oldarrownp; toe = add_event_dummy(dst, e0ipp); tobp = itree_add_bubble(toe, B_TO, 0, gen); arrowp->tail = frombp; arrowp->head = tobp; add_arrow(frombp, arrowp); add_arrow(tobp, arrowp); arrow_add_within(arrowp, dst->u.event.declp->u.stmt.np-> u.event.eexprlist); arrow_add_within(arrowp, dst->u.event.eexprlist); } } } } out(O_ALTFP|O_STAMP, "itree_create added %d bytes", alloc_total() - init_size); return (Ninfo.lut); } void itree_free(struct lut *lutp) { int init_size; init_size = alloc_total(); out(O_ALTFP|O_STAMP, "start itree_free"); lut_free(lutp, itree_destructor, NULL); out(O_ALTFP|O_STAMP, "itree_free freed %d bytes", init_size - alloc_total()); } void itree_prune(struct lut *lutp) { int init_size; init_size = alloc_total(); out(O_ALTFP|O_STAMP, "start itree_prune"); lut_walk(lutp, itree_pruner, NULL); out(O_ALTFP|O_STAMP, "itree_prune freed %d bytes", init_size - alloc_total()); } void itree_pevent_brief(int flags, struct event *ep) { ASSERT(ep != NULL); ASSERT(ep->enode != NULL); ASSERT(ep->ipp != NULL); ipath_print(flags, ep->enode->u.event.ename->u.name.s, ep->ipp); } /*ARGSUSED*/ static void itree_pevent(struct event *lhs, struct event *ep, void *arg) { struct plut_wlk_data propd; struct bubble *bp; int flags = (int)(intptr_t)arg; itree_pevent_brief(flags, ep); if (ep->t == N_EREPORT) out(flags, " (count %d)", ep->count); else out(flags, NULL); if (ep->props) { propd.flags = flags; propd.first = 1; out(flags, "Properties:"); lut_walk(ep->props, ptree_plut, (void *)&propd); } for (bp = itree_next_bubble(ep, NULL); bp; bp = itree_next_bubble(ep, bp)) { /* Print only TO bubbles in this loop */ if (bp->t != B_TO) continue; itree_pbubble(flags, bp); } for (bp = itree_next_bubble(ep, NULL); bp; bp = itree_next_bubble(ep, bp)) { /* Print only INHIBIT bubbles in this loop */ if (bp->t != B_INHIBIT) continue; itree_pbubble(flags, bp); } for (bp = itree_next_bubble(ep, NULL); bp; bp = itree_next_bubble(ep, bp)) { /* Print only FROM bubbles in this loop */ if (bp->t != B_FROM) continue; itree_pbubble(flags, bp); } } static void itree_pbubble(int flags, struct bubble *bp) { struct constraintlist *cp; struct arrowlist *ap; ASSERT(bp != NULL); out(flags|O_NONL, " "); if (bp->mark) out(flags|O_NONL, "*"); else out(flags|O_NONL, " "); if (bp->t == B_FROM) out(flags|O_NONL, "N=%d to:", bp->nork); else if (bp->t == B_TO) out(flags|O_NONL, "K=%d from:", bp->nork); else out(flags|O_NONL, "K=%d masked from:", bp->nork); if (bp->t == B_TO || bp->t == B_INHIBIT) { for (ap = itree_next_arrow(bp, NULL); ap; ap = itree_next_arrow(bp, ap)) { ASSERT(ap->arrowp->head == bp); ASSERT(ap->arrowp->tail != NULL); ASSERT(ap->arrowp->tail->myevent != NULL); out(flags|O_NONL, " "); itree_pevent_brief(flags, ap->arrowp->tail->myevent); } out(flags, NULL); return; } for (ap = itree_next_arrow(bp, NULL); ap; ap = itree_next_arrow(bp, ap)) { ASSERT(ap->arrowp->tail == bp); ASSERT(ap->arrowp->head != NULL); ASSERT(ap->arrowp->head->myevent != NULL); out(flags|O_NONL, " "); itree_pevent_brief(flags, ap->arrowp->head->myevent); out(flags|O_NONL, " "); ptree_timeval(flags, &ap->arrowp->mindelay); out(flags|O_NONL, ","); ptree_timeval(flags, &ap->arrowp->maxdelay); /* Display anything from the propogation node? */ out(O_VERB3|O_NONL, " <%s:%d>", ap->arrowp->pnode->file, ap->arrowp->pnode->line); if (itree_next_constraint(ap->arrowp, NULL)) out(flags|O_NONL, " {"); for (cp = itree_next_constraint(ap->arrowp, NULL); cp; cp = itree_next_constraint(ap->arrowp, cp)) { ptree(flags, cp->cnode, 1, 0); if (itree_next_constraint(ap->arrowp, cp)) out(flags|O_NONL, ", "); } if (itree_next_constraint(ap->arrowp, NULL)) out(flags|O_NONL, "}"); } out(flags, NULL); } void itree_ptree(int flags, struct lut *itp) { lut_walk(itp, (lut_cb)itree_pevent, (void *)(intptr_t)flags); } /*ARGSUSED*/ static void itree_destructor(void *left, void *right, void *arg) { struct event *ep = (struct event *)right; struct bubble *nextbub, *bub; /* Free the properties */ if (ep->props) lut_free(ep->props, instances_destructor, NULL); /* Free the payload properties */ if (ep->payloadprops) lut_free(ep->payloadprops, payloadprops_destructor, NULL); /* Free the serd properties */ if (ep->serdprops) lut_free(ep->serdprops, serdprops_destructor, NULL); /* Free my bubbles */ for (bub = ep->bubbles; bub != NULL; ) { nextbub = bub->next; /* * Free arrows if they are FROM me. Free arrowlists on * other types of bubbles (but not the attached arrows, * which will be freed when we free the originating * bubble. */ if (bub->t == B_FROM) itree_free_arrowlists(bub, 1); else itree_free_arrowlists(bub, 0); itree_free_bubble(bub); bub = nextbub; } nvlist_free(ep->nvp); alloc_xfree(ep, sizeof (*ep)); } /*ARGSUSED*/ static void itree_pruner(void *left, void *right, void *arg) { struct event *ep = (struct event *)right; struct bubble *nextbub, *bub; if (ep->keep_in_tree) return; /* Free the properties */ lut_free(ep->props, instances_destructor, NULL); /* Free the payload properties */ lut_free(ep->payloadprops, payloadprops_destructor, NULL); /* Free the serd properties */ lut_free(ep->serdprops, serdprops_destructor, NULL); /* Free my bubbles */ for (bub = ep->bubbles; bub != NULL; ) { nextbub = bub->next; itree_prune_arrowlists(bub); itree_free_bubble(bub); bub = nextbub; } nvlist_free(ep->nvp); ep->props = NULL; ep->payloadprops = NULL; ep->serdprops = NULL; ep->bubbles = NULL; ep->nvp = NULL; } static void itree_free_bubble(struct bubble *freeme) { alloc_xfree(freeme, sizeof (*freeme)); } static struct bubble * itree_add_bubble(struct event *eventp, enum bubbletype btype, int nork, int gen) { struct bubble *prev = NULL; struct bubble *curr; struct bubble *newb; /* Use existing bubbles as appropriate when possible */ for (curr = eventp->bubbles; curr != NULL; prev = curr, curr = curr->next) { if (btype == B_TO && curr->t == B_TO) { /* see if an existing "to" bubble works for us */ if (gen == curr->gen) return (curr); /* matched gen number */ else if (nork == 1 && curr->nork == 1) { curr->gen = gen; return (curr); /* coalesce K==1 bubbles */ } } else if (btype == B_FROM && curr->t == B_FROM) { /* see if an existing "from" bubble works for us */ if ((nork == N_IS_ALL && curr->nork == N_IS_ALL) || (nork == 0 && curr->nork == 0)) return (curr); } } newb = alloc_xmalloc(sizeof (struct bubble)); newb->next = NULL; newb->t = btype; newb->myevent = eventp; newb->nork = nork; newb->mark = 0; newb->gen = gen; newb->arrows = NULL; if (prev == NULL) eventp->bubbles = newb; else prev->next = newb; return (newb); } struct bubble * itree_next_bubble(struct event *eventp, struct bubble *last) { struct bubble *next; for (;;) { if (last != NULL) next = last->next; else next = eventp->bubbles; if (next == NULL || next->arrows != NULL) return (next); /* bubble was empty, skip it */ last = next; } } static void add_arrow(struct bubble *bp, struct arrow *ap) { struct arrowlist *prev = NULL; struct arrowlist *curr; struct arrowlist *newal; newal = alloc_xmalloc(sizeof (struct arrowlist)); bzero(newal, sizeof (struct arrowlist)); newal->arrowp = ap; curr = itree_next_arrow(bp, NULL); while (curr != NULL) { prev = curr; curr = itree_next_arrow(bp, curr); } if (prev == NULL) bp->arrows = newal; else prev->next = newal; } static struct arrow * itree_add_arrow(struct node *apnode, struct node *fromevent, struct node *toevent, struct lut *ex) { struct arrow *newa; newa = alloc_xmalloc(sizeof (struct arrow)); bzero(newa, sizeof (struct arrow)); newa->pnode = apnode; newa->constraints = NULL; /* * Set default delays, then try to re-set them from * any within() constraints. */ newa->mindelay = newa->maxdelay = 0ULL; if (itree_set_arrow_traits(newa, fromevent, toevent, ex) == 0) { alloc_xfree(newa, sizeof (struct arrow)); return (NULL); } return (newa); } /* returns false if traits show that arrow should not be added after all */ static int itree_set_arrow_traits(struct arrow *ap, struct node *fromev, struct node *toev, struct lut *ex) { struct node *events[] = { NULL, NULL, NULL }; struct node *newc = NULL; ASSERTeq(fromev->t, T_EVENT, ptree_nodetype2str); ASSERTeq(toev->t, T_EVENT, ptree_nodetype2str); /* * search for the within values first on the declaration of * the destination event, and then on the prop. this allows * one to specify a "default" within by putting it on the * declaration, but then allow overriding on the prop statement. */ arrow_add_within(ap, toev->u.event.declp->u.stmt.np->u.event.eexprlist); arrow_add_within(ap, toev->u.event.eexprlist); /* * handle any global constraints inherited from the * "fromev" event's declaration */ ASSERT(fromev->u.event.declp != NULL); ASSERT(fromev->u.event.declp->u.stmt.np != NULL); #ifdef notdef /* XXX not quite ready to evaluate constraints from decls yet */ if (fromev->u.event.declp->u.stmt.np->u.event.eexprlist) (void) itree_add_constraint(ap, fromev->u.event.declp->u.stmt.np->u.event.eexprlist); #endif /* notdef */ /* handle constraints on the from event in the prop statement */ events[0] = fromev; events[1] = toev; if (eval_potential(fromev->u.event.eexprlist, ex, events, &newc, Ninfo.croot) == 0) return (0); /* constraint disallows arrow */ /* * handle any global constraints inherited from the * "toev" event's declaration */ ASSERT(toev->u.event.declp != NULL); ASSERT(toev->u.event.declp->u.stmt.np != NULL); #ifdef notdef /* XXX not quite ready to evaluate constraints from decls yet */ if (toev->u.event.declp->u.stmt.np->u.event.eexprlist) (void) itree_add_constraint(ap, toev->u.event.declp->u.stmt.np->u.event.eexprlist); #endif /* notdef */ /* handle constraints on the to event in the prop statement */ events[0] = toev; events[1] = fromev; if (eval_potential(toev->u.event.eexprlist, ex, events, &newc, Ninfo.croot) == 0) { if (newc != NULL) tree_free(newc); return (0); /* constraint disallows arrow */ } /* if we came up with any deferred constraints, add them to arrow */ if (newc != NULL) { out(O_ALTFP|O_VERB3, "(deferred constraints)"); (void) itree_add_constraint(ap, iexpr(newc)); } return (1); /* constraints allow arrow */ } /* * Set within() constraint. If the constraint were set multiple times, * the last one would "win". */ static void arrow_add_within(struct arrow *ap, struct node *xpr) { struct node *arglist; /* end of expressions list */ if (xpr == NULL) return; switch (xpr->t) { case T_LIST: arrow_add_within(ap, xpr->u.expr.left); arrow_add_within(ap, xpr->u.expr.right); return; case T_FUNC: if (xpr->u.func.s != L_within) return; arglist = xpr->u.func.arglist; switch (arglist->t) { case T_TIMEVAL: ap->mindelay = 0; ap->maxdelay = arglist->u.ull; break; case T_NAME: ASSERT(arglist->u.name.s == L_infinity); ap->mindelay = 0; ap->maxdelay = TIMEVAL_EVENTUALLY; break; case T_LIST: ASSERT(arglist->u.expr.left->t == T_TIMEVAL); ap->mindelay = arglist->u.expr.left->u.ull; switch (arglist->u.expr.right->t) { case T_TIMEVAL: ap->maxdelay = arglist->u.ull; break; case T_NAME: ASSERT(arglist->u.expr.right->u.name.s == L_infinity); ap->maxdelay = TIMEVAL_EVENTUALLY; break; default: out(O_DIE, "within: unexpected 2nd arg type"); } break; default: out(O_DIE, "within: unexpected 1st arg type"); } break; default: return; } } static void itree_free_arrowlists(struct bubble *bubp, int arrows_too) { struct arrowlist *al, *nal; al = bubp->arrows; while (al != NULL) { nal = al->next; if (arrows_too) { itree_free_constraints(al->arrowp); alloc_xfree(al->arrowp, sizeof (struct arrow)); } alloc_xfree(al, sizeof (*al)); al = nal; } } static void itree_delete_arrow(struct bubble *bubp, struct arrow *arrow) { struct arrowlist *al, *oal; al = bubp->arrows; if (al->arrowp == arrow) { bubp->arrows = al->next; alloc_xfree(al, sizeof (*al)); return; } while (al != NULL) { oal = al; al = al->next; ASSERT(al != NULL); if (al->arrowp == arrow) { oal->next = al->next; alloc_xfree(al, sizeof (*al)); return; } } } static void itree_prune_arrowlists(struct bubble *bubp) { struct arrowlist *al, *nal; al = bubp->arrows; while (al != NULL) { nal = al->next; if (bubp->t == B_FROM) itree_delete_arrow(al->arrowp->head, al->arrowp); else itree_delete_arrow(al->arrowp->tail, al->arrowp); itree_free_constraints(al->arrowp); alloc_xfree(al->arrowp, sizeof (struct arrow)); alloc_xfree(al, sizeof (*al)); al = nal; } } struct arrowlist * itree_next_arrow(struct bubble *bubble, struct arrowlist *last) { struct arrowlist *next; if (last != NULL) next = last->next; else next = bubble->arrows; return (next); } static struct constraintlist * itree_add_constraint(struct arrow *arrowp, struct node *c) { struct constraintlist *prev = NULL; struct constraintlist *curr; struct constraintlist *newc; for (curr = arrowp->constraints; curr != NULL; prev = curr, curr = curr->next) ; newc = alloc_xmalloc(sizeof (struct constraintlist)); newc->next = NULL; newc->cnode = c; if (prev == NULL) arrowp->constraints = newc; else prev->next = newc; return (newc); } struct constraintlist * itree_next_constraint(struct arrow *arrowp, struct constraintlist *last) { struct constraintlist *next; if (last != NULL) next = last->next; else next = arrowp->constraints; return (next); } static void itree_free_constraints(struct arrow *ap) { struct constraintlist *cl, *ncl; cl = ap->constraints; while (cl != NULL) { ncl = cl->next; ASSERT(cl->cnode != NULL); if (!iexpr_cached(cl->cnode)) tree_free(cl->cnode); else iexpr_free(cl->cnode); alloc_xfree(cl, sizeof (*cl)); cl = ncl; } } const char * itree_bubbletype2str(enum bubbletype t) { static char buf[100]; switch (t) { case B_FROM: return L_from; case B_TO: return L_to; case B_INHIBIT: return L_inhibit; default: (void) sprintf(buf, "[unexpected bubbletype: %d]", t); return (buf); } } /* * itree_fini -- clean up any half-built itrees */ void itree_fini(void) { if (Ninfo.lut != NULL) { itree_free(Ninfo.lut); Ninfo.lut = NULL; } if (Ninfo.ex) { lut_free(Ninfo.ex, iterinfo_destructor, NULL); Ninfo.ex = NULL; } } /* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * itree.h -- public definitions for itree module * */ #ifndef _EFT_ITREE_H #define _EFT_ITREE_H #ifdef __cplusplus extern "C" { #endif /* the "fault" field in the event struct requires the definition of nvlist_t */ #include #include /* Numerical representation of propagation N value (A), short for All */ #define N_IS_ALL -1 /* * effects_test event cached_state bits * - reset on each call to effects_test() */ #define CREDIBLE_EFFECT 1 #define WAIT_EFFECT 2 #define PARENT_WAIT 4 /* * arrow mark bits (for K-count) */ #define EFFECTS_COUNTER 8 #define REQMNTS_COUNTER 16 /* * requirements_test event cached_state bits */ #define REQMNTS_CREDIBLE 32 #define REQMNTS_DISPROVED 64 #define REQMNTS_WAIT 128 /* * requirements_test bubble mark bits */ #define BUBBLE_ELIDED 256 #define BUBBLE_OK 512 /* * causes_test event cached_state bits */ #define CAUSES_TESTED 1024 struct event { struct event *suspects; struct event *psuspects; struct event *observations; /* for lists like suspect list */ fmd_event_t *ffep; nvlist_t *nvp; /* payload nvp for ereports */ struct node *enode; /* event node in parse tree */ const struct ipath *ipp; /* instanced version of event */ const struct ipath *ipp_un; /* full version for Usednames */ struct lut *props; /* instanced version of nvpairs */ struct lut *payloadprops; /* nvpairs for problem payload */ struct lut *serdprops; /* nvpairs for dynamic serd args */ int count; /* for reports, number seen */ enum nametype t:3; /* defined in tree.h */ int is_suspect:1; /* true if on suspect list */ int keep_in_tree:1; int cached_state:11; unsigned long long cached_delay; struct bubble { struct bubble *next; struct event *myevent; int gen; /* generation # */ int nork; int mark:11; enum bubbletype { B_FROM, B_TO, B_INHIBIT } t:2; struct arrowlist { struct arrowlist *next; struct arrow { struct bubble *head; struct bubble *tail; /* prop node in parse tree */ struct node *pnode; struct constraintlist { struct constraintlist *next; /* deferred constraints */ struct node *cnode; } *constraints; int forever_false:1; int forever_true:1; int arrow_marked:1; int mark:11; unsigned long long mindelay; unsigned long long maxdelay; } *arrowp; } *arrows; } *bubbles; }; /* * struct iterinfo is the stuff we store in the dictionary of iterators * when we assign a value to an iterator. it not only contains the value * we assigned to the iterator, it contains a node pointer which we use to * determine if we're the one that defined the value when popping [vh]match() * recursion. */ struct iterinfo { int num; struct node *np; }; struct lut *itree_create(struct config *croot); void itree_free(struct lut *itp); void itree_prune(struct lut *itp); struct event *itree_lookup(struct lut *itp, const char *ename, const struct ipath *ipp); struct arrowlist *itree_next_arrow(struct bubble *bubblep, struct arrowlist *last); struct bubble *itree_next_bubble(struct event *eventp, struct bubble *last); struct constraintlist *itree_next_constraint(struct arrow *arrowp, struct constraintlist *last); void itree_pevent_brief(int flags, struct event *eventp); void itree_ptree(int flags, struct lut *itp); const char *itree_bubbletype2str(enum bubbletype t); void itree_fini(void); #ifdef __cplusplus } #endif #endif /* _EFT_ITREE_H */ /* * 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 (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. * * platform.c -- interfaces to the platform's configuration information * * this platform.c allows eft to run on Solaris systems. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "alloc.h" #include "out.h" #include "tree.h" #include "itree.h" #include "ipath.h" #include "ptree.h" #include "fme.h" #include "stable.h" #include "eval.h" #include "config.h" #include "platform.h" extern fmd_hdl_t *Hdl; /* handle from eft.c */ /* * Lastcfg points to the last configuration snapshot we made. */ static struct cfgdata *Lastcfg; static fmd_hdl_t *Lasthdl; static fmd_case_t *Lastfmcase; static const char *lastcomp; static int in_getpath; extern struct lut *Usednames; int prune_raw_config = 0; static topo_hdl_t *Eft_topo_hdl; void * topo_use_alloc(size_t bytes) { void *p = alloc_malloc(bytes, NULL, 0); bzero(p, bytes); return (p); } void topo_use_free(void *p) { alloc_free(p, NULL, 0); } /*ARGSUSED*/ static void * alloc_nv_alloc(nv_alloc_t *nva, size_t size) { return (alloc_malloc(size, NULL, 0)); } /*ARGSUSED*/ static void alloc_nv_free(nv_alloc_t *nva, void *p, size_t sz) { alloc_free(p, NULL, 0); } const nv_alloc_ops_t Eft_nv_alloc_ops = { NULL, /* nv_ao_init() */ NULL, /* nv_ao_fini() */ alloc_nv_alloc, /* nv_ao_alloc() */ alloc_nv_free, /* nv_ao_free() */ NULL /* nv_ao_reset() */ }; nv_alloc_t Eft_nv_hdl; static char *Root; static char *Mach; static char *Plat; static char tmpbuf[MAXPATHLEN]; static char numbuf[MAXPATHLEN]; /* * platform_globals -- set global variables based on sysinfo() calls */ static void platform_globals() { Root = fmd_prop_get_string(Hdl, "fmd.rootdir"); Mach = fmd_prop_get_string(Hdl, "fmd.machine"); Plat = fmd_prop_get_string(Hdl, "fmd.platform"); } static void platform_free_globals() { fmd_prop_free_string(Hdl, Root); fmd_prop_free_string(Hdl, Mach); fmd_prop_free_string(Hdl, Plat); } /* * platform_init -- perform any platform-specific initialization */ void platform_init(void) { (void) nv_alloc_init(&Eft_nv_hdl, &Eft_nv_alloc_ops); Eft_topo_hdl = fmd_hdl_topo_hold(Hdl, TOPO_VERSION); platform_globals(); out(O_ALTFP, "platform_init() sucessful"); } void platform_fini(void) { if (Lastcfg != NULL) { config_free(Lastcfg); Lastcfg = NULL; } fmd_hdl_topo_rele(Hdl, Eft_topo_hdl); platform_free_globals(); (void) nv_alloc_fini(&Eft_nv_hdl); out(O_ALTFP, "platform_fini() sucessful"); } /* * hc_fmri_nodeize -- convert hc-scheme FMRI to eft compatible format * * this is an internal platform.c helper routine */ static struct node * hc_fmri_nodeize(nvlist_t *hcfmri) { struct node *pathtree = NULL; struct node *tmpn; nvlist_t **hc_prs; uint_t hc_nprs; const char *sname; char *ename; char *eid; int e, r; /* * What to do with/about hc-root? Would we have any clue what * to do with it if it weren't /? For now, we don't bother * even looking it up. */ /* * Get the hc-list of elements in the FMRI */ if (nvlist_lookup_nvlist_array(hcfmri, FM_FMRI_HC_LIST, &hc_prs, &hc_nprs) != 0) { out(O_ALTFP, "XFILE: hc FMRI missing %s", FM_FMRI_HC_LIST); return (NULL); } for (e = 0; e < hc_nprs; e++) { ename = NULL; eid = NULL; r = nvlist_lookup_string(hc_prs[e], FM_FMRI_HC_NAME, &ename); r |= nvlist_lookup_string(hc_prs[e], FM_FMRI_HC_ID, &eid); if (r != 0) { /* probably should bail */ continue; } sname = stable(ename); tmpn = tree_name_iterator( tree_name(sname, IT_VERTICAL, NULL, 0), tree_num(eid, NULL, 0)); if (pathtree == NULL) pathtree = tmpn; else (void) tree_name_append(pathtree, tmpn); } return (pathtree); } /* * platform_getpath -- extract eft-compatible path from ereport */ struct node * platform_getpath(nvlist_t *nvl) { struct node *ret; nvlist_t *dfmri, *real_fmri, *resource; char *scheme; char *path; char *devid; char *tp; uint32_t cpuid; int err; enum {DT_HC, DT_DEVID, DT_TP, DT_DEV, DT_CPU, DT_UNKNOWN} type = DT_UNKNOWN; /* Find the detector */ if (nvlist_lookup_nvlist(nvl, FM_EREPORT_DETECTOR, &dfmri) != 0) { out(O_ALTFP, "XFILE: ereport has no detector FMRI"); return (NULL); } /* get the scheme from the detector */ if (nvlist_lookup_string(dfmri, FM_FMRI_SCHEME, &scheme) != 0) { out(O_ALTFP, "XFILE: detector FMRI missing scheme"); return (NULL); } /* based on scheme, determine type */ if (strcmp(scheme, FM_FMRI_SCHEME_HC) == 0) { /* already in hc scheme */ type = DT_HC; } else if (strcmp(scheme, FM_FMRI_SCHEME_DEV) == 0) { /* * devid takes precedence over tp which takes precedence over * path */ if (nvlist_lookup_string(dfmri, FM_FMRI_DEV_ID, &devid) == 0) type = DT_DEVID; else if (nvlist_lookup_string(dfmri, TOPO_STORAGE_TARGET_PORT_L0ID, &tp) == 0) type = DT_TP; else if (nvlist_lookup_string(dfmri, FM_FMRI_DEV_PATH, &path) == 0) type = DT_DEV; else { out(O_ALTFP, "XFILE: detector FMRI missing %s or %s", FM_FMRI_DEV_ID, FM_FMRI_DEV_PATH); return (NULL); } } else if (strcmp(scheme, FM_FMRI_SCHEME_CPU) == 0) { if (nvlist_lookup_uint32(dfmri, FM_FMRI_CPU_ID, &cpuid) == 0) type = DT_CPU; else { out(O_ALTFP, "XFILE: detector FMRI missing %s", FM_FMRI_CPU_ID); return (NULL); } } else { out(O_ALTFP, "XFILE: detector FMRI not recognized " "(scheme is %s, expect %s or %s or %s)", scheme, FM_FMRI_SCHEME_HC, FM_FMRI_SCHEME_DEV, FM_FMRI_SCHEME_CPU); return (NULL); } out(O_ALTFP|O_VERB, "Received ereport in scheme %s", scheme); /* take a config snapshot */ lut_free(Usednames, NULL, NULL); Usednames = NULL; in_getpath = 1; if (config_snapshot() == NULL) { if (type == DT_HC) { /* * If hc-scheme use the fmri that was passed in. */ in_getpath = 0; return (hc_fmri_nodeize(dfmri)); } out(O_ALTFP, "XFILE: cannot snapshot configuration"); in_getpath = 0; return (NULL); } /* * For hc scheme, if we can find the resource from the tolopogy, use * that - otherwise use the fmri that was passed in. For other schemes * look up the path, cpuid, tp or devid in the topology. */ switch (type) { case DT_HC: if (topo_fmri_getprop(Eft_topo_hdl, dfmri, TOPO_PGROUP_PROTOCOL, TOPO_PROP_RESOURCE, NULL, &resource, &err) == -1) { ret = hc_fmri_nodeize(dfmri); break; } else if (nvlist_lookup_nvlist(resource, TOPO_PROP_VAL_VAL, &real_fmri) != 0) ret = hc_fmri_nodeize(dfmri); else ret = hc_fmri_nodeize(real_fmri); nvlist_free(resource); break; case DT_DEV: if ((ret = config_bydev_lookup(Lastcfg, path)) == NULL) out(O_ALTFP, "platform_getpath: no configuration node " "has device path matching \"%s\".", path); break; case DT_TP: if ((ret = config_bytp_lookup(Lastcfg, tp)) == NULL) out(O_ALTFP, "platform_getpath: no configuration node " "has tp matching \"%s\".", tp); break; case DT_DEVID: if ((ret = config_bydevid_lookup(Lastcfg, devid)) == NULL) out(O_ALTFP, "platform_getpath: no configuration node " "has devid matching \"%s\".", devid); break; case DT_CPU: if ((ret = config_bycpuid_lookup(Lastcfg, cpuid)) == NULL) out(O_ALTFP, "platform_getpath: no configuration node " "has cpu-id matching %u.", cpuid); break; } /* free the snapshot */ structconfig_free(Lastcfg->cooked); config_free(Lastcfg); in_getpath = 0; return (ret); } /* Allocate space for raw config strings in chunks of this size */ #define STRSBUFLEN 512 /* * cfgadjust -- Make sure the amount we want to add to the raw config string * buffer will fit, and if not, increase the size of the buffer. */ static void cfgadjust(struct cfgdata *rawdata, int addlen) { int curnext, newlen; if (rawdata->nextfree + addlen >= rawdata->end) { newlen = (((rawdata->nextfree - rawdata->begin + 1 + addlen) / STRSBUFLEN) + 1) * STRSBUFLEN; curnext = rawdata->nextfree - rawdata->begin; rawdata->begin = REALLOC(rawdata->begin, newlen); rawdata->nextfree = rawdata->begin + curnext; rawdata->end = rawdata->begin + newlen; } } static char * hc_path(tnode_t *node) { int i, err; char *name, *instance, *estr; nvlist_t *fmri, **hcl; ulong_t ul; uint_t nhc; if (topo_prop_get_fmri(node, TOPO_PGROUP_PROTOCOL, TOPO_PROP_RESOURCE, &fmri, &err) < 0) return (NULL); if (nvlist_lookup_nvlist_array(fmri, FM_FMRI_HC_LIST, &hcl, &nhc) != 0) { nvlist_free(fmri); return (NULL); } tmpbuf[0] = '\0'; for (i = 0; i < nhc; ++i) { err = nvlist_lookup_string(hcl[i], FM_FMRI_HC_NAME, &name); err |= nvlist_lookup_string(hcl[i], FM_FMRI_HC_ID, &instance); if (err) { nvlist_free(fmri); return (NULL); } ul = strtoul(instance, &estr, 10); /* conversion to number failed? */ if (estr == instance) { nvlist_free(fmri); return (NULL); } (void) strlcat(tmpbuf, "/", MAXPATHLEN); (void) strlcat(tmpbuf, name, MAXPATHLEN); (void) snprintf(numbuf, MAXPATHLEN, "%lu", ul); (void) strlcat(tmpbuf, numbuf, MAXPATHLEN); lastcomp = stable(name); } nvlist_free(fmri); return (tmpbuf); } static void add_prop_val(topo_hdl_t *thp, struct cfgdata *rawdata, char *propn, nvpair_t *pv_nvp) { int addlen, err; char *propv, *fmristr = NULL; nvlist_t *fmri; uint32_t ui32; int64_t i64; int32_t i32; boolean_t bool; uint64_t ui64; char buf[32]; /* big enough for any 64-bit int */ uint_t nelem; int i, j, sz; char **propvv; /* * malformed prop nvpair */ if (propn == NULL) return; switch (nvpair_type(pv_nvp)) { case DATA_TYPE_STRING_ARRAY: /* * Convert string array into single space-separated string */ (void) nvpair_value_string_array(pv_nvp, &propvv, &nelem); for (sz = 0, i = 0; i < nelem; i++) sz += strlen(propvv[i]) + 1; propv = MALLOC(sz); for (j = 0, i = 0; i < nelem; j++, i++) { (void) strcpy(&propv[j], propvv[i]); j += strlen(propvv[i]); if (i < nelem - 1) propv[j] = ' '; } break; case DATA_TYPE_STRING: (void) nvpair_value_string(pv_nvp, &propv); break; case DATA_TYPE_NVLIST: /* * At least try to collect the protocol * properties */ (void) nvpair_value_nvlist(pv_nvp, &fmri); if (topo_fmri_nvl2str(thp, fmri, &fmristr, &err) < 0) { out(O_ALTFP, "cfgcollect: failed to convert fmri to " "string"); return; } else { propv = fmristr; } break; case DATA_TYPE_UINT64: /* * Convert uint64 to hex strings */ (void) nvpair_value_uint64(pv_nvp, &ui64); (void) snprintf(buf, sizeof (buf), "0x%llx", ui64); propv = buf; break; case DATA_TYPE_BOOLEAN_VALUE: /* * Convert boolean_t to hex strings */ (void) nvpair_value_boolean_value(pv_nvp, &bool); (void) snprintf(buf, sizeof (buf), "0x%llx", (uint64_t)bool); propv = buf; break; case DATA_TYPE_INT32: /* * Convert int32 to hex strings */ (void) nvpair_value_int32(pv_nvp, &i32); (void) snprintf(buf, sizeof (buf), "0x%llx", (uint64_t)(int64_t)i32); propv = buf; break; case DATA_TYPE_INT64: /* * Convert int64 to hex strings */ (void) nvpair_value_int64(pv_nvp, &i64); (void) snprintf(buf, sizeof (buf), "0x%llx", (uint64_t)i64); propv = buf; break; case DATA_TYPE_UINT32: /* * Convert uint32 to hex strings */ (void) nvpair_value_uint32(pv_nvp, &ui32); (void) snprintf(buf, sizeof (buf), "0x%llx", (uint64_t)ui32); propv = buf; break; default: out(O_ALTFP, "cfgcollect: failed to get property value for " "%s", propn); return; } /* = & NULL */ addlen = strlen(propn) + strlen(propv) + 2; cfgadjust(rawdata, addlen); (void) snprintf(rawdata->nextfree, rawdata->end - rawdata->nextfree, "%s=%s", propn, propv); if (strcmp(propn, TOPO_PROP_RESOURCE) == 0) out(O_ALTFP|O_VERB3, "cfgcollect: %s", propv); if (nvpair_type(pv_nvp) == DATA_TYPE_STRING_ARRAY) FREE(propv); rawdata->nextfree += addlen; if (fmristr != NULL) topo_hdl_strfree(thp, fmristr); } /* * cfgcollect -- Assemble raw configuration data in string form suitable * for checkpointing. */ static int cfgcollect(topo_hdl_t *thp, tnode_t *node, void *arg) { struct cfgdata *rawdata = (struct cfgdata *)arg; int err, addlen; char *propn, *path = NULL; nvlist_t *p_nv, *pg_nv, *pv_nv; nvpair_t *nvp, *pg_nvp, *pv_nvp; if (topo_node_flags(node) == TOPO_NODE_FACILITY) return (TOPO_WALK_NEXT); path = hc_path(node); if (path == NULL) return (TOPO_WALK_ERR); addlen = strlen(path) + 1; cfgadjust(rawdata, addlen); (void) strcpy(rawdata->nextfree, path); rawdata->nextfree += addlen; /* * If the prune_raw_config flag is set then we will only include in the * raw config those nodes that are used by the rules remaining after * prune_propagations() has been run - ie only those that could possibly * be relevant to the incoming ereport given the current rules. This * means that any other parts of the config will not get saved to the * checkpoint file (even if they may theoretically be used if the * rules are subsequently modified). * * For now prune_raw_config is 0 for Solaris, though it is expected to * be set to 1 for fmsp. * * Note we only prune the raw config like this if we have been called * from newfme(), not if we have been called when handling dev or cpu * scheme ereports from platform_getpath(), as this is called before * prune_propagations() - again this is not an issue on fmsp as the * ereports are all in hc scheme. */ if (!in_getpath && prune_raw_config && lut_lookup(Usednames, (void *)lastcomp, NULL) == NULL) return (TOPO_WALK_NEXT); /* * Collect properties * * eversholt should support alternate property types * Better yet, topo properties could be represented as * a packed nvlist */ p_nv = topo_prop_getprops(node, &err); for (nvp = nvlist_next_nvpair(p_nv, NULL); nvp != NULL; nvp = nvlist_next_nvpair(p_nv, nvp)) { if (strcmp(TOPO_PROP_GROUP, nvpair_name(nvp)) != 0 || nvpair_type(nvp) != DATA_TYPE_NVLIST) continue; (void) nvpair_value_nvlist(nvp, &pg_nv); for (pg_nvp = nvlist_next_nvpair(pg_nv, NULL); pg_nvp != NULL; pg_nvp = nvlist_next_nvpair(pg_nv, pg_nvp)) { if (strcmp(TOPO_PROP_VAL, nvpair_name(pg_nvp)) != 0 || nvpair_type(pg_nvp) != DATA_TYPE_NVLIST) continue; (void) nvpair_value_nvlist(pg_nvp, &pv_nv); propn = NULL; for (pv_nvp = nvlist_next_nvpair(pv_nv, NULL); pv_nvp != NULL; pv_nvp = nvlist_next_nvpair(pv_nv, pv_nvp)) { /* Get property name */ if (strcmp(TOPO_PROP_VAL_NAME, nvpair_name(pv_nvp)) == 0) (void) nvpair_value_string(pv_nvp, &propn); /* * Get property value */ if (strcmp(TOPO_PROP_VAL_VAL, nvpair_name(pv_nvp)) == 0) add_prop_val(thp, rawdata, propn, pv_nvp); } } } nvlist_free(p_nv); return (TOPO_WALK_NEXT); } void platform_restore_config(fmd_hdl_t *hdl, fmd_case_t *fmcase) { if (hdl == Lasthdl && fmcase == Lastfmcase) { size_t cfglen; fmd_buf_read(Lasthdl, Lastfmcase, WOBUF_CFGLEN, (void *)&cfglen, sizeof (size_t)); Lastcfg->begin = MALLOC(cfglen); Lastcfg->end = Lastcfg->nextfree = Lastcfg->begin + cfglen; fmd_buf_read(Lasthdl, Lastfmcase, WOBUF_CFG, Lastcfg->begin, cfglen); Lasthdl = NULL; Lastfmcase = NULL; } } void platform_save_config(fmd_hdl_t *hdl, fmd_case_t *fmcase) { size_t cfglen; /* * Put the raw config into an fmd_buf. Then we can free it to * save space. */ Lastfmcase = fmcase; Lasthdl = hdl; cfglen = Lastcfg->nextfree - Lastcfg->begin; fmd_buf_create(hdl, fmcase, WOBUF_CFGLEN, sizeof (cfglen)); fmd_buf_write(hdl, fmcase, WOBUF_CFGLEN, (void *)&cfglen, sizeof (cfglen)); if (cfglen != 0) { fmd_buf_create(hdl, fmcase, WOBUF_CFG, cfglen); fmd_buf_write(hdl, fmcase, WOBUF_CFG, Lastcfg->begin, cfglen); } FREE(Lastcfg->begin); Lastcfg->begin = NULL; Lastcfg->end = NULL; Lastcfg->nextfree = NULL; } /* * platform_config_snapshot -- gather a snapshot of the current configuration */ struct cfgdata * platform_config_snapshot(void) { int err; topo_walk_t *twp; static uint64_t lastgen; uint64_t curgen; /* * If the DR generation number has changed, * we need to grab a new snapshot, otherwise we * can simply point them at the last config. */ if (prune_raw_config == 0 && (curgen = fmd_fmri_get_drgen()) <= lastgen && Lastcfg != NULL) { Lastcfg->raw_refcnt++; /* * if config has been backed away to an fmd_buf, restore it */ if (Lastcfg->begin == NULL) platform_restore_config(Lasthdl, Lastfmcase); return (Lastcfg); } lastgen = curgen; /* we're getting a new config, so clean up the last one */ if (Lastcfg != NULL) { config_free(Lastcfg); } Lastcfg = MALLOC(sizeof (struct cfgdata)); Lastcfg->raw_refcnt = 2; /* caller + Lastcfg */ Lastcfg->begin = Lastcfg->nextfree = Lastcfg->end = NULL; Lastcfg->cooked = NULL; Lastcfg->devcache = NULL; Lastcfg->devidcache = NULL; Lastcfg->tpcache = NULL; Lastcfg->cpucache = NULL; fmd_hdl_topo_rele(Hdl, Eft_topo_hdl); Eft_topo_hdl = fmd_hdl_topo_hold(Hdl, TOPO_VERSION); if ((twp = topo_walk_init(Eft_topo_hdl, FM_FMRI_SCHEME_HC, cfgcollect, Lastcfg, &err)) == NULL) { out(O_DIE, "platform_config_snapshot: NULL topology tree: %s", topo_strerror(err)); } if (topo_walk_step(twp, TOPO_WALK_CHILD) == TOPO_WALK_ERR) { topo_walk_fini(twp); out(O_DIE, "platform_config_snapshot: error walking topology " "tree"); } topo_walk_fini(twp); out(O_ALTFP|O_STAMP, "raw config complete"); return (Lastcfg); } static const char * cfgstrprop_lookup(struct config *croot, char *path, const char *pname) { struct config *cresource; const char *fmristr; /* * The first order of business is to find the resource in the * config database so we can examine properties associated with * that node. */ if ((cresource = config_lookup(croot, path, 0)) == NULL) { out(O_ALTFP, "Cannot find config info for %s.", path); return (NULL); } if ((fmristr = config_getprop(cresource, pname)) == NULL) { out(O_ALTFP, "Cannot find %s property for %s resource " "re-write", pname, path); return (NULL); } return (fmristr); } /* * Get FMRI for a particular unit from libtopo. The unit is specified by the * "path" argument (a stringified ipath). "prop" argument should be one * of the constants TOPO_PROP_RESOURCE, TOPO_PROP_ASRU, TOPO_PROP_FRU, etc. */ /*ARGSUSED*/ void platform_unit_translate(int isdefect, struct config *croot, const char *prop, nvlist_t **fmrip, char *path) { const char *fmristr; char *serial; nvlist_t *fmri; int err; fmristr = cfgstrprop_lookup(croot, path, prop); if (fmristr == NULL) { out(O_ALTFP, "Cannot rewrite unit FMRI for %s.", path); return; } if (topo_fmri_str2nvl(Eft_topo_hdl, fmristr, &fmri, &err) < 0) { out(O_ALTFP, "Can not convert config info: %s", topo_strerror(err)); out(O_ALTFP, "Cannot rewrite unit FMRI for %s.", path); return; } /* * If we don't have a serial number in the unit then check if it * is available as a separate property and if so then add it. */ if (nvlist_lookup_string(fmri, FM_FMRI_HC_SERIAL_ID, &serial) != 0) { serial = (char *)cfgstrprop_lookup(croot, path, FM_FMRI_HC_SERIAL_ID); if (serial != NULL) (void) nvlist_add_string(fmri, FM_FMRI_HC_SERIAL_ID, serial); } *fmrip = fmri; } /* * platform_get_files -- return names of all files we should load * * search directories in dirname[] for all files with names ending with the * substring fnstr. dirname[] should be a NULL-terminated array. fnstr * may be set to "*" to indicate all files in a directory. * * if nodups is non-zero, then the first file of a given name found is * the only file added to the list of names. for example if nodups is * set and we're looking for .efts, and find a pci.eft in the dirname[0], * then no pci.eft found in any of the other dirname[] entries will be * included in the final list of names. * * this routine doesn't return NULL, even if no files are found (in that * case, a char ** is returned with the first element NULL). */ static char ** platform_get_files(const char *dirname[], const char *fnstr, int nodups) { DIR *dirp; struct dirent *dp; struct lut *foundnames = NULL; char **files = NULL; /* char * array of filenames found */ int nfiles = 0; /* files found so far */ int slots = 0; /* char * slots allocated in files */ size_t fnlen, d_namelen; size_t totlen; int i; static char *nullav; ASSERT(fnstr != NULL); fnlen = strlen(fnstr); for (i = 0; dirname[i] != NULL; i++) { out(O_VERB, "Looking for %s files in %s", fnstr, dirname[i]); if ((dirp = opendir(dirname[i])) == NULL) { out(O_DEBUG|O_SYS, "platform_get_files: opendir failed for %s", dirname[i]); continue; } while ((dp = readdir(dirp)) != NULL) { if ((fnlen == 1 && *fnstr == '*') || ((d_namelen = strlen(dp->d_name)) >= fnlen && strncmp(dp->d_name + d_namelen - fnlen, fnstr, fnlen) == 0)) { if (nodups != 0) { const char *snm = stable(dp->d_name); if (lut_lookup(foundnames, (void *)snm, NULL) != NULL) { out(O_VERB, "platform_get_files: " "skipping repeated name " "%s/%s", dirname[i], snm); continue; } foundnames = lut_add(foundnames, (void *)snm, (void *)snm, NULL); } if (nfiles > slots - 2) { /* allocate ten more slots */ slots += 10; files = (char **)REALLOC(files, slots * sizeof (char *)); } /* prepend directory name and / */ totlen = strlen(dirname[i]) + 1; totlen += strlen(dp->d_name) + 1; files[nfiles] = MALLOC(totlen); out(O_VERB, "File %d: \"%s/%s\"", nfiles, dirname[i], dp->d_name); (void) snprintf(files[nfiles++], totlen, "%s/%s", dirname[i], dp->d_name); } } (void) closedir(dirp); } if (foundnames != NULL) lut_free(foundnames, NULL, NULL); if (nfiles == 0) return (&nullav); files[nfiles] = NULL; return (files); } /* * search for files in a standard set of directories */ static char ** platform_get_files_stddirs(char *fname, int nodups) { const char *dirlist[4]; char **flist; char *eftgendir, *eftmachdir, *eftplatdir; eftgendir = MALLOC(MAXPATHLEN); eftmachdir = MALLOC(MAXPATHLEN); eftplatdir = MALLOC(MAXPATHLEN); /* Generic files that apply to any machine */ (void) snprintf(eftgendir, MAXPATHLEN, "%s/usr/lib/fm/eft", Root); (void) snprintf(eftmachdir, MAXPATHLEN, "%s/usr/platform/%s/lib/fm/eft", Root, Mach); (void) snprintf(eftplatdir, MAXPATHLEN, "%s/usr/platform/%s/lib/fm/eft", Root, Plat); dirlist[0] = eftplatdir; dirlist[1] = eftmachdir; dirlist[2] = eftgendir; dirlist[3] = NULL; flist = platform_get_files(dirlist, fname, nodups); FREE(eftplatdir); FREE(eftmachdir); FREE(eftgendir); return (flist); } /* * platform_run_poller -- execute a poller * * when eft needs to know if a polled ereport exists this routine * is called so the poller code may be run in a platform-specific way. * there's no return value from this routine -- either the polled ereport * is generated (and delivered *before* this routine returns) or not. * any errors, like "poller unknown" are considered platform-specific * should be handled here rather than passing an error back up. */ /*ARGSUSED*/ void platform_run_poller(const char *poller) { } /* * fork and execve path with argument array argv and environment array * envp. data from stdout and stderr are placed in outbuf and errbuf, * respectively. * * see execve(2) for more descriptions for path, argv and envp. */ static int forkandexecve(const char *path, char *const argv[], char *const envp[], char *outbuf, size_t outbuflen, char *errbuf, size_t errbuflen) { pid_t pid; int outpipe[2], errpipe[2]; int rt = 0; /* * run the cmd and see if it failed. this function is *not* a * generic command runner -- we depend on some knowledge we * have about the commands we run. first of all, we expect * errors to spew something to stdout, and that something is * typically short enough to fit into a pipe so we can wait() * for the command to complete and then fetch the error text * from the pipe. */ if (pipe(outpipe) < 0) if (strlcat(errbuf, ": pipe(outpipe) failed", errbuflen) >= errbuflen) return (1); if (pipe(errpipe) < 0) if (strlcat(errbuf, ": pipe(errpipe) failed", errbuflen) >= errbuflen) return (1); if ((pid = fork()) < 0) { rt = (int)strlcat(errbuf, ": fork() failed", errbuflen); } else if (pid) { int wstat, count; /* parent */ (void) close(errpipe[1]); (void) close(outpipe[1]); /* PHASE2 need to guard against hang in child? */ if (waitpid(pid, &wstat, 0) < 0) if (strlcat(errbuf, ": waitpid() failed", errbuflen) >= errbuflen) return (1); /* check for stderr contents */ if (ioctl(errpipe[0], FIONREAD, &count) >= 0 && count) { if (read(errpipe[0], errbuf, errbuflen) <= 0) { /* * read failed even though ioctl indicated * that nonzero bytes were available for * reading */ if (strlcat(errbuf, ": read(errpipe) failed", errbuflen) >= errbuflen) return (1); } /* * handle case where errbuf is not properly * terminated */ if (count > errbuflen - 1) count = errbuflen - 1; if (errbuf[count - 1] != '\0' && errbuf[count - 1] != '\n') errbuf[count] = '\0'; } else if (WIFSIGNALED(wstat)) if (strlcat(errbuf, ": signaled", errbuflen) >= errbuflen) return (1); else if (WIFEXITED(wstat) && WEXITSTATUS(wstat)) if (strlcat(errbuf, ": abnormal exit", errbuflen) >= errbuflen) return (1); /* check for stdout contents */ if (ioctl(outpipe[0], FIONREAD, &count) >= 0 && count) { if (read(outpipe[0], outbuf, outbuflen) <= 0) { /* * read failed even though ioctl indicated * that nonzero bytes were available for * reading */ if (strlcat(errbuf, ": read(outpipe) failed", errbuflen) >= errbuflen) return (1); } /* * handle case where outbuf is not properly * terminated */ if (count > outbuflen - 1) count = outbuflen - 1; if (outbuf[count - 1] != '\0' && outbuf[count - 1] != '\n') outbuf[count] = '\0'; } (void) close(errpipe[0]); (void) close(outpipe[0]); } else { /* child */ (void) dup2(errpipe[1], fileno(stderr)); (void) close(errpipe[0]); (void) dup2(outpipe[1], fileno(stdout)); (void) close(outpipe[0]); if (execve(path, argv, envp)) perror(path); _exit(1); } return (rt); } #define MAXDIGITIDX 23 static int arglist2argv(struct node *np, struct lut **globals, struct config *croot, struct arrow *arrowp, char ***argv, int *argc, int *argvlen) { struct node *namep; char numbuf[MAXDIGITIDX + 1]; char *numstr, *nullbyte; char *addthisarg = NULL; if (np == NULL) return (0); switch (np->t) { case T_QUOTE: addthisarg = STRDUP(np->u.func.s); break; case T_LIST: if (arglist2argv(np->u.expr.left, globals, croot, arrowp, argv, argc, argvlen)) return (1); /* * only leftmost element of a list can provide the command * name (after which *argc becomes 1) */ ASSERT(*argc > 0); if (arglist2argv(np->u.expr.right, globals, croot, arrowp, argv, argc, argvlen)) return (1); break; case T_FUNC: case T_GLOBID: case T_ASSIGN: case T_CONDIF: case T_CONDELSE: case T_EQ: case T_NE: 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_AND: case T_OR: case T_NOT: case T_ADD: case T_SUB: case T_MUL: case T_DIV: case T_MOD: { struct evalue value; if (!eval_expr(np, NULL, NULL, globals, croot, arrowp, 0, &value)) return (1); switch (value.t) { case UINT64: numbuf[MAXDIGITIDX] = '\0'; nullbyte = &numbuf[MAXDIGITIDX]; numstr = ulltostr(value.v, nullbyte); addthisarg = STRDUP(numstr); break; case STRING: addthisarg = STRDUP((const char *)(uintptr_t)value.v); break; case NODEPTR : namep = (struct node *)(uintptr_t)value.v; addthisarg = ipath2str(NULL, ipath(namep)); break; default: out(O_ERR, "call: arglist2argv: unexpected result from" " operation %s", ptree_nodetype2str(np->t)); return (1); } break; } case T_NUM: case T_TIMEVAL: numbuf[MAXDIGITIDX] = '\0'; nullbyte = &numbuf[MAXDIGITIDX]; numstr = ulltostr(np->u.ull, nullbyte); addthisarg = STRDUP(numstr); break; case T_NAME: addthisarg = ipath2str(NULL, ipath(np)); break; case T_EVENT: addthisarg = ipath2str(np->u.event.ename->u.name.s, ipath(np->u.event.epname)); break; default: out(O_ERR, "call: arglist2argv: node type %s is unsupported", ptree_nodetype2str(np->t)); return (1); /*NOTREACHED*/ break; } if (*argc == 0 && addthisarg != NULL) { /* * first argument added is the command name. */ char **files; files = platform_get_files_stddirs(addthisarg, 0); /* do not proceed if number of files found != 1 */ if (files[0] == NULL) out(O_DIE, "call: function %s not found", addthisarg); if (files[1] != NULL) out(O_DIE, "call: multiple functions %s found", addthisarg); FREE(addthisarg); addthisarg = STRDUP(files[0]); FREE(files[0]); FREE(files); } if (addthisarg != NULL) { if (*argc >= *argvlen - 2) { /* * make sure argv is long enough so it has a * terminating element set to NULL */ *argvlen += 10; *argv = (char **)REALLOC(*argv, sizeof (char *) * *argvlen); } (*argv)[*argc] = addthisarg; (*argc)++; (*argv)[*argc] = NULL; } return (0); } static int generate_envp(struct arrow *arrowp, char ***envp, int *envc, int *envplen) { char *envnames[] = { "EFT_FROM_EVENT", "EFT_TO_EVENT", "EFT_FILE", "EFT_LINE", NULL }; char *envvalues[4]; char *none = "(none)"; size_t elen; int i; *envc = 4; /* * make sure envp is long enough so it has a terminating element * set to NULL */ *envplen = *envc + 1; *envp = (char **)MALLOC(sizeof (char *) * *envplen); envvalues[0] = ipath2str( arrowp->tail->myevent->enode->u.event.ename->u.name.s, arrowp->tail->myevent->ipp); envvalues[1] = ipath2str( arrowp->head->myevent->enode->u.event.ename->u.name.s, arrowp->head->myevent->ipp); if (arrowp->head->myevent->enode->file == NULL) { envvalues[2] = STRDUP(none); envvalues[3] = STRDUP(none); } else { envvalues[2] = STRDUP(arrowp->head->myevent->enode->file); /* large enough for max int */ envvalues[3] = MALLOC(sizeof (char) * 25); (void) snprintf(envvalues[3], sizeof (envvalues[3]), "%d", arrowp->head->myevent->enode->line); } for (i = 0; envnames[i] != NULL && i < *envc; i++) { elen = strlen(envnames[i]) + strlen(envvalues[i]) + 2; (*envp)[i] = MALLOC(elen); (void) snprintf((*envp)[i], elen, "%s=%s", envnames[i], envvalues[i]); FREE(envvalues[i]); } (*envp)[*envc] = NULL; return (0); } /* * platform_call -- call an external function * * evaluate a user-defined function and place result in valuep. return 0 * if function evaluation was successful; 1 if otherwise. */ int platform_call(struct node *np, struct lut **globals, struct config *croot, struct arrow *arrowp, struct evalue *valuep) { /* * use rather short buffers. only the first string on outbuf[] is * taken as output from the called function. any message in * errbuf[] is echoed out as an error message. */ char outbuf[256], errbuf[512]; struct stat buf; char **argv, **envp; int argc, argvlen, envc, envplen; int i, ret; /* * np is the argument list. the user-defined function is the first * element of the list. */ ASSERT(np->t == T_LIST); argv = NULL; argc = 0; argvlen = 0; if (arglist2argv(np, globals, croot, arrowp, &argv, &argc, &argvlen) || argc == 0) return (1); /* * make sure program has executable bit set */ if (stat(argv[0], &buf) == 0) { int exec_bit_set = 0; if (buf.st_uid == geteuid() && buf.st_mode & S_IXUSR) exec_bit_set = 1; else if (buf.st_gid == getegid() && buf.st_mode & S_IXGRP) exec_bit_set = 1; else if (buf.st_mode & S_IXOTH) exec_bit_set = 1; if (exec_bit_set == 0) out(O_DIE, "call: executable bit not set on %s", argv[0]); } else { out(O_DIE, "call: failure in stat(), errno = %d\n", errno); } envp = NULL; envc = 0; envplen = 0; if (generate_envp(arrowp, &envp, &envc, &envplen)) return (1); outbuf[0] = '\0'; errbuf[0] = '\0'; ret = forkandexecve((const char *) argv[0], (char *const *) argv, (char *const *) envp, outbuf, sizeof (outbuf), errbuf, sizeof (errbuf)); for (i = 0; i < envc; i++) FREE(envp[i]); if (envp) FREE(envp); if (ret) { outfl(O_OK, np->file, np->line, "call: failure in fork + exec of %s", argv[0]); } else { char *ptr; /* chomp the result */ for (ptr = outbuf; *ptr; ptr++) if (*ptr == '\n' || *ptr == '\r') { *ptr = '\0'; break; } valuep->t = STRING; valuep->v = (uintptr_t)stable(outbuf); } if (errbuf[0] != '\0') { ret = 1; outfl(O_OK, np->file, np->line, "call: unexpected stderr output from %s: %s", argv[0], errbuf); } for (i = 0; i < argc; i++) FREE(argv[i]); FREE(argv); return (ret); } /* * platform_confcall -- call a configuration database function * * returns result in *valuep, return 0 on success */ /*ARGSUSED*/ int platform_confcall(struct node *np, struct lut **globals, struct config *croot, struct arrow *arrowp, struct evalue *valuep) { outfl(O_ALTFP|O_VERB, np->file, np->line, "unknown confcall"); return (0); } /* * platform_get_eft_files -- return names of all eft files we should load * * this routine doesn't return NULL, even if no files are found (in that * case, a char ** is returned with the first element NULL). */ char ** platform_get_eft_files(void) { return (platform_get_files_stddirs(".eft", 1)); } void platform_free_eft_files(char **flist) { char **f; if (flist == NULL || *flist == NULL) return; /* no files were found so we're done */ f = flist; while (*f != NULL) { FREE(*f); f++; } FREE(flist); } static nvlist_t *payloadnvp = NULL; void platform_set_payloadnvp(nvlist_t *nvlp) { /* * cannot replace a non-NULL payloadnvp with a non-NULL nvlp */ ASSERT(payloadnvp != NULL ? nvlp == NULL : 1); payloadnvp = nvlp; } /* * given array notation in inputstr such as "foo[1]" or "foo [ 1 ]" (spaces * allowed), figure out the array name and index. return 0 if successful, * nonzero if otherwise. */ static int get_array_info(const char *inputstr, const char **name, unsigned int *index) { char *indexptr, *indexend, *dupname, *endname; if (strchr(inputstr, '[') == NULL) return (1); dupname = STRDUP(inputstr); indexptr = strchr(dupname, '['); indexend = strchr(dupname, ']'); /* * return if array notation is not complete or if index is negative */ if (indexend == NULL || indexptr >= indexend || strchr(indexptr, '-') != NULL) { FREE(dupname); return (1); } /* * search past any spaces between the name string and '[' */ endname = indexptr; while (isspace(*(endname - 1)) && dupname < endname) endname--; *endname = '\0'; ASSERT(dupname < endname); /* * search until indexptr points to the first digit and indexend * points to the last digit */ while (!isdigit(*indexptr) && indexptr < indexend) indexptr++; while (!isdigit(*indexend) && indexptr <= indexend) indexend--; *(indexend + 1) = '\0'; *index = (unsigned int)atoi(indexptr); *name = stable(dupname); FREE(dupname); return (0); } /* * platform_payloadprop -- fetch a payload value * * XXX this function should be replaced and eval_func() should be * XXX changed to use the more general platform_payloadprop_values(). */ int platform_payloadprop(struct node *np, struct evalue *valuep) { nvlist_t *basenvp; nvlist_t *embnvp = NULL; nvpair_t *nvpair; const char *nameptr, *propstr, *lastnameptr; int not_array = 0; unsigned int index = 0; uint_t nelem; char *nvpname, *nameslist = NULL; char *scheme = NULL; ASSERT(np->t == T_QUOTE); propstr = np->u.quote.s; if (payloadnvp == NULL) { out(O_ALTFP | O_VERB2, "platform_payloadprop: no nvp for %s", propstr); return (1); } basenvp = payloadnvp; /* * first handle any embedded nvlists. if propstr is "foo.bar[2]" * then lastnameptr should end up being "bar[2]" with basenvp set * to the nvlist for "foo". (the search for "bar" within "foo" * will be done later.) */ if (strchr(propstr, '.') != NULL) { nvlist_t **arraynvp; uint_t nelem; char *w; int ier; nameslist = STRDUP(propstr); lastnameptr = strtok(nameslist, "."); /* * decompose nameslist into its component names while * extracting the embedded nvlist */ while ((w = strtok(NULL, ".")) != NULL) { if (get_array_info(lastnameptr, &nameptr, &index)) { ier = nvlist_lookup_nvlist(basenvp, lastnameptr, &basenvp); } else { /* handle array of nvlists */ ier = nvlist_lookup_nvlist_array(basenvp, nameptr, &arraynvp, &nelem); if (ier == 0) { if ((uint_t)index > nelem - 1) ier = 1; else basenvp = arraynvp[index]; } } if (ier) { out(O_ALTFP, "platform_payloadprop: " " invalid list for %s (in %s)", lastnameptr, propstr); FREE(nameslist); return (1); } lastnameptr = w; } } else { lastnameptr = propstr; } /* if property is an array reference, extract array name and index */ not_array = get_array_info(lastnameptr, &nameptr, &index); if (not_array) nameptr = stable(lastnameptr); if (nameslist != NULL) FREE(nameslist); /* search for nvpair entry */ nvpair = NULL; while ((nvpair = nvlist_next_nvpair(basenvp, nvpair)) != NULL) { nvpname = nvpair_name(nvpair); ASSERT(nvpname != NULL); if (nameptr == stable(nvpname)) break; } if (nvpair == NULL) { out(O_ALTFP, "platform_payloadprop: no entry for %s", propstr); return (1); } else if (valuep == NULL) { /* * caller is interested in the existence of a property with * this name, regardless of type or value */ return (0); } valuep->t = UNDEFINED; /* * get to this point if we found an entry. figure out its data * type and copy its value. */ (void) nvpair_value_nvlist(nvpair, &embnvp); if (nvlist_lookup_string(embnvp, FM_FMRI_SCHEME, &scheme) == 0) { if (strcmp(scheme, FM_FMRI_SCHEME_HC) == 0) { valuep->t = NODEPTR; valuep->v = (uintptr_t)hc_fmri_nodeize(embnvp); return (0); } } switch (nvpair_type(nvpair)) { case DATA_TYPE_BOOLEAN: case DATA_TYPE_BOOLEAN_VALUE: { boolean_t val; (void) nvpair_value_boolean_value(nvpair, &val); valuep->t = UINT64; valuep->v = (unsigned long long)val; break; } case DATA_TYPE_BYTE: { uchar_t val; (void) nvpair_value_byte(nvpair, &val); valuep->t = UINT64; valuep->v = (unsigned long long)val; break; } case DATA_TYPE_STRING: { char *val; valuep->t = STRING; (void) nvpair_value_string(nvpair, &val); valuep->v = (uintptr_t)stable(val); break; } case DATA_TYPE_INT8: { int8_t val; (void) nvpair_value_int8(nvpair, &val); valuep->t = UINT64; valuep->v = (unsigned long long)val; break; } case DATA_TYPE_UINT8: { uint8_t val; (void) nvpair_value_uint8(nvpair, &val); valuep->t = UINT64; valuep->v = (unsigned long long)val; break; } case DATA_TYPE_INT16: { int16_t val; (void) nvpair_value_int16(nvpair, &val); valuep->t = UINT64; valuep->v = (unsigned long long)val; break; } case DATA_TYPE_UINT16: { uint16_t val; (void) nvpair_value_uint16(nvpair, &val); valuep->t = UINT64; valuep->v = (unsigned long long)val; break; } case DATA_TYPE_INT32: { int32_t val; (void) nvpair_value_int32(nvpair, &val); valuep->t = UINT64; valuep->v = (unsigned long long)val; break; } case DATA_TYPE_UINT32: { uint32_t val; (void) nvpair_value_uint32(nvpair, &val); valuep->t = UINT64; valuep->v = (unsigned long long)val; break; } case DATA_TYPE_INT64: { int64_t val; (void) nvpair_value_int64(nvpair, &val); valuep->t = UINT64; valuep->v = (unsigned long long)val; break; } case DATA_TYPE_UINT64: { uint64_t val; (void) nvpair_value_uint64(nvpair, &val); valuep->t = UINT64; valuep->v = (unsigned long long)val; break; } case DATA_TYPE_BOOLEAN_ARRAY: { boolean_t *val; (void) nvpair_value_boolean_array(nvpair, &val, &nelem); if (not_array == 1 || index >= nelem) goto invalid; valuep->t = UINT64; valuep->v = (unsigned long long)val[index]; break; } case DATA_TYPE_BYTE_ARRAY: { uchar_t *val; (void) nvpair_value_byte_array(nvpair, &val, &nelem); if (not_array == 1 || index >= nelem) goto invalid; valuep->t = UINT64; valuep->v = (unsigned long long)val[index]; break; } case DATA_TYPE_STRING_ARRAY: { char **val; (void) nvpair_value_string_array(nvpair, &val, &nelem); if (not_array == 1 || index >= nelem) goto invalid; valuep->t = STRING; valuep->v = (uintptr_t)stable(val[index]); break; } case DATA_TYPE_INT8_ARRAY: { int8_t *val; (void) nvpair_value_int8_array(nvpair, &val, &nelem); if (not_array == 1 || index >= nelem) goto invalid; valuep->t = UINT64; valuep->v = (unsigned long long)val[index]; break; } case DATA_TYPE_UINT8_ARRAY: { uint8_t *val; (void) nvpair_value_uint8_array(nvpair, &val, &nelem); if (not_array == 1 || index >= nelem) goto invalid; valuep->t = UINT64; valuep->v = (unsigned long long)val[index]; break; } case DATA_TYPE_INT16_ARRAY: { int16_t *val; (void) nvpair_value_int16_array(nvpair, &val, &nelem); if (not_array == 1 || index >= nelem) goto invalid; valuep->t = UINT64; valuep->v = (unsigned long long)val[index]; break; } case DATA_TYPE_UINT16_ARRAY: { uint16_t *val; (void) nvpair_value_uint16_array(nvpair, &val, &nelem); if (not_array == 1 || index >= nelem) goto invalid; valuep->t = UINT64; valuep->v = (unsigned long long)val[index]; break; } case DATA_TYPE_INT32_ARRAY: { int32_t *val; (void) nvpair_value_int32_array(nvpair, &val, &nelem); if (not_array == 1 || index >= nelem) goto invalid; valuep->t = UINT64; valuep->v = (unsigned long long)val[index]; break; } case DATA_TYPE_UINT32_ARRAY: { uint32_t *val; (void) nvpair_value_uint32_array(nvpair, &val, &nelem); if (not_array == 1 || index >= nelem) goto invalid; valuep->t = UINT64; valuep->v = (unsigned long long)val[index]; break; } case DATA_TYPE_INT64_ARRAY: { int64_t *val; (void) nvpair_value_int64_array(nvpair, &val, &nelem); if (not_array == 1 || index >= nelem) goto invalid; valuep->t = UINT64; valuep->v = (unsigned long long)val[index]; break; } case DATA_TYPE_UINT64_ARRAY: { uint64_t *val; (void) nvpair_value_uint64_array(nvpair, &val, &nelem); if (not_array == 1 || index >= nelem) goto invalid; valuep->t = UINT64; valuep->v = (unsigned long long)val[index]; break; } default : out(O_ALTFP|O_VERB2, "platform_payloadprop: unsupported data type for %s", propstr); return (1); } return (0); invalid: out(O_ALTFP|O_VERB2, "platform_payloadprop: invalid array reference for %s", propstr); return (1); } /*ARGSUSED*/ int platform_path_exists(nvlist_t *fmri) { return (fmd_nvl_fmri_present(Hdl, fmri)); } struct evalue * platform_payloadprop_values(const char *propstr, int *nvals) { struct evalue *retvals; nvlist_t *basenvp; nvpair_t *nvpair; char *nvpname; *nvals = 0; if (payloadnvp == NULL) return (NULL); basenvp = payloadnvp; /* search for nvpair entry */ nvpair = NULL; while ((nvpair = nvlist_next_nvpair(basenvp, nvpair)) != NULL) { nvpname = nvpair_name(nvpair); ASSERT(nvpname != NULL); if (strcmp(propstr, nvpname) == 0) break; } if (nvpair == NULL) return (NULL); /* property not found */ switch (nvpair_type(nvpair)) { case DATA_TYPE_NVLIST: { nvlist_t *embnvp = NULL; char *scheme = NULL; (void) nvpair_value_nvlist(nvpair, &embnvp); if (nvlist_lookup_string(embnvp, FM_FMRI_SCHEME, &scheme) == 0) { if (strcmp(scheme, FM_FMRI_SCHEME_HC) == 0) { *nvals = 1; retvals = MALLOC(sizeof (struct evalue)); retvals->t = NODEPTR; retvals->v = (uintptr_t)hc_fmri_nodeize(embnvp); return (retvals); } } return (NULL); } case DATA_TYPE_NVLIST_ARRAY: { char *scheme = NULL; nvlist_t **nvap; uint_t nel; int i; int hccount; /* * since we're only willing to handle hc fmri's, we * must count them first before allocating retvals. */ if (nvpair_value_nvlist_array(nvpair, &nvap, &nel) != 0) return (NULL); hccount = 0; for (i = 0; i < nel; i++) { if (nvlist_lookup_string(nvap[i], FM_FMRI_SCHEME, &scheme) == 0 && strcmp(scheme, FM_FMRI_SCHEME_HC) == 0) { hccount++; } } if (hccount == 0) return (NULL); *nvals = hccount; retvals = MALLOC(sizeof (struct evalue) * hccount); hccount = 0; for (i = 0; i < nel; i++) { if (nvlist_lookup_string(nvap[i], FM_FMRI_SCHEME, &scheme) == 0 && strcmp(scheme, FM_FMRI_SCHEME_HC) == 0) { retvals[hccount].t = NODEPTR; retvals[hccount].v = (uintptr_t) hc_fmri_nodeize(nvap[i]); hccount++; } } return (retvals); } case DATA_TYPE_BOOLEAN: case DATA_TYPE_BOOLEAN_VALUE: { boolean_t val; *nvals = 1; retvals = MALLOC(sizeof (struct evalue)); (void) nvpair_value_boolean_value(nvpair, &val); retvals->t = UINT64; retvals->v = (unsigned long long)val; return (retvals); } case DATA_TYPE_BYTE: { uchar_t val; *nvals = 1; retvals = MALLOC(sizeof (struct evalue)); (void) nvpair_value_byte(nvpair, &val); retvals->t = UINT64; retvals->v = (unsigned long long)val; return (retvals); } case DATA_TYPE_STRING: { char *val; *nvals = 1; retvals = MALLOC(sizeof (struct evalue)); retvals->t = STRING; (void) nvpair_value_string(nvpair, &val); retvals->v = (uintptr_t)stable(val); return (retvals); } case DATA_TYPE_INT8: { int8_t val; *nvals = 1; retvals = MALLOC(sizeof (struct evalue)); (void) nvpair_value_int8(nvpair, &val); retvals->t = UINT64; retvals->v = (unsigned long long)val; return (retvals); } case DATA_TYPE_UINT8: { uint8_t val; *nvals = 1; retvals = MALLOC(sizeof (struct evalue)); (void) nvpair_value_uint8(nvpair, &val); retvals->t = UINT64; retvals->v = (unsigned long long)val; return (retvals); } case DATA_TYPE_INT16: { int16_t val; *nvals = 1; retvals = MALLOC(sizeof (struct evalue)); (void) nvpair_value_int16(nvpair, &val); retvals->t = UINT64; retvals->v = (unsigned long long)val; return (retvals); } case DATA_TYPE_UINT16: { uint16_t val; *nvals = 1; retvals = MALLOC(sizeof (struct evalue)); (void) nvpair_value_uint16(nvpair, &val); retvals->t = UINT64; retvals->v = (unsigned long long)val; return (retvals); } case DATA_TYPE_INT32: { int32_t val; *nvals = 1; retvals = MALLOC(sizeof (struct evalue)); (void) nvpair_value_int32(nvpair, &val); retvals->t = UINT64; retvals->v = (unsigned long long)val; return (retvals); } case DATA_TYPE_UINT32: { uint32_t val; *nvals = 1; retvals = MALLOC(sizeof (struct evalue)); (void) nvpair_value_uint32(nvpair, &val); retvals->t = UINT64; retvals->v = (unsigned long long)val; return (retvals); } case DATA_TYPE_INT64: { int64_t val; *nvals = 1; retvals = MALLOC(sizeof (struct evalue)); (void) nvpair_value_int64(nvpair, &val); retvals->t = UINT64; retvals->v = (unsigned long long)val; return (retvals); } case DATA_TYPE_UINT64: { uint64_t val; *nvals = 1; retvals = MALLOC(sizeof (struct evalue)); (void) nvpair_value_uint64(nvpair, &val); retvals->t = UINT64; retvals->v = (unsigned long long)val; return (retvals); } case DATA_TYPE_BOOLEAN_ARRAY: { boolean_t *val; uint_t nel; int i; (void) nvpair_value_boolean_array(nvpair, &val, &nel); *nvals = nel; retvals = MALLOC(sizeof (struct evalue) * nel); for (i = 0; i < nel; i++) { retvals[i].t = UINT64; retvals[i].v = (unsigned long long)val[i]; } return (retvals); } case DATA_TYPE_BYTE_ARRAY: { uchar_t *val; uint_t nel; int i; (void) nvpair_value_byte_array(nvpair, &val, &nel); *nvals = nel; retvals = MALLOC(sizeof (struct evalue) * nel); for (i = 0; i < nel; i++) { retvals[i].t = UINT64; retvals[i].v = (unsigned long long)val[i]; } return (retvals); } case DATA_TYPE_STRING_ARRAY: { char **val; uint_t nel; int i; (void) nvpair_value_string_array(nvpair, &val, &nel); *nvals = nel; retvals = MALLOC(sizeof (struct evalue) * nel); for (i = 0; i < nel; i++) { retvals[i].t = STRING; retvals[i].v = (uintptr_t)stable(val[i]); } return (retvals); } case DATA_TYPE_INT8_ARRAY: { int8_t *val; uint_t nel; int i; (void) nvpair_value_int8_array(nvpair, &val, &nel); *nvals = nel; retvals = MALLOC(sizeof (struct evalue) * nel); for (i = 0; i < nel; i++) { retvals[i].t = UINT64; retvals[i].v = (unsigned long long)val[i]; } return (retvals); } case DATA_TYPE_UINT8_ARRAY: { uint8_t *val; uint_t nel; int i; (void) nvpair_value_uint8_array(nvpair, &val, &nel); *nvals = nel; retvals = MALLOC(sizeof (struct evalue) * nel); for (i = 0; i < nel; i++) { retvals[i].t = UINT64; retvals[i].v = (unsigned long long)val[i]; } return (retvals); } case DATA_TYPE_INT16_ARRAY: { int16_t *val; uint_t nel; int i; (void) nvpair_value_int16_array(nvpair, &val, &nel); *nvals = nel; retvals = MALLOC(sizeof (struct evalue) * nel); for (i = 0; i < nel; i++) { retvals[i].t = UINT64; retvals[i].v = (unsigned long long)val[i]; } return (retvals); } case DATA_TYPE_UINT16_ARRAY: { uint16_t *val; uint_t nel; int i; (void) nvpair_value_uint16_array(nvpair, &val, &nel); *nvals = nel; retvals = MALLOC(sizeof (struct evalue) * nel); for (i = 0; i < nel; i++) { retvals[i].t = UINT64; retvals[i].v = (unsigned long long)val[i]; } return (retvals); } case DATA_TYPE_INT32_ARRAY: { int32_t *val; uint_t nel; int i; (void) nvpair_value_int32_array(nvpair, &val, &nel); *nvals = nel; retvals = MALLOC(sizeof (struct evalue) * nel); for (i = 0; i < nel; i++) { retvals[i].t = UINT64; retvals[i].v = (unsigned long long)val[i]; } return (retvals); } case DATA_TYPE_UINT32_ARRAY: { uint32_t *val; uint_t nel; int i; (void) nvpair_value_uint32_array(nvpair, &val, &nel); *nvals = nel; retvals = MALLOC(sizeof (struct evalue) * nel); for (i = 0; i < nel; i++) { retvals[i].t = UINT64; retvals[i].v = (unsigned long long)val[i]; } return (retvals); } case DATA_TYPE_INT64_ARRAY: { int64_t *val; uint_t nel; int i; (void) nvpair_value_int64_array(nvpair, &val, &nel); *nvals = nel; retvals = MALLOC(sizeof (struct evalue) * nel); for (i = 0; i < nel; i++) { retvals[i].t = UINT64; retvals[i].v = (unsigned long long)val[i]; } return (retvals); } case DATA_TYPE_UINT64_ARRAY: { uint64_t *val; uint_t nel; int i; (void) nvpair_value_uint64_array(nvpair, &val, &nel); *nvals = nel; retvals = MALLOC(sizeof (struct evalue) * nel); for (i = 0; i < nel; i++) { retvals[i].t = UINT64; retvals[i].v = (unsigned long long)val[i]; } return (retvals); } } return (NULL); } /* * When a list.repaired event is seen the following is called for * each fault in the associated fault list to convert the given FMRI * to an instanced path. Only hc scheme is supported. */ const struct ipath * platform_fault2ipath(nvlist_t *flt) { nvlist_t *rsrc; struct node *np; char *scheme; const struct ipath *ip; if (nvlist_lookup_nvlist(flt, FM_FAULT_RESOURCE, &rsrc) != 0) { out(O_ALTFP, "platform_fault2ipath: no resource member"); return (NULL); } else if (nvlist_lookup_string(rsrc, FM_FMRI_SCHEME, &scheme) != 0) { out(O_ALTFP, "platform_fault2ipath: no scheme type for rsrc"); return (NULL); } if (strncmp(scheme, FM_FMRI_SCHEME_HC, sizeof (FM_FMRI_SCHEME_HC) - 1) != 0) { out(O_ALTFP, "platform_fault2ipath: returning NULL for non-hc " "scheme %s", scheme); return (NULL); } if ((np = hc_fmri_nodeize(rsrc)) == NULL) return (NULL); /* nodeize will already have whinged */ ip = ipath(np); tree_free(np); return (ip); } /* * 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. * * platform -- platform-specific access to configuration database */ #ifndef _EFT_PLATFORM_H #define _EFT_PLATFORM_H #include #ifdef __cplusplus extern "C" { #endif #include #include #include extern nvlist_t *Action_nvl; /* nvl for problem with action=... prop on it */ void platform_init(void); void platform_fini(void); void platform_run_poller(const char *poller); void platform_set_payloadnvp(nvlist_t *nvlp); void platform_unit_translate(int, struct config *, const char *, nvlist_t **, char *); struct cfgdata *platform_config_snapshot(void); void platform_restore_config(fmd_hdl_t *hdl, fmd_case_t *fmcase); void platform_save_config(fmd_hdl_t *hdl, fmd_case_t *fmcase); struct node *platform_getpath(nvlist_t *nvl); char **platform_get_eft_files(void); void platform_free_eft_files(char **); int platform_call(struct node *np, struct lut **globals, struct config *croot, struct arrow *arrowp, struct evalue *valuep); int platform_confcall(struct node *np, struct lut **globals, struct config *croot, struct arrow *arrowp, struct evalue *valuep); int platform_payloadprop(struct node *np, struct evalue *valuep); struct evalue *platform_payloadprop_values(const char *s, int *nvals); int platform_path_exists(nvlist_t *fmri); const struct ipath *platform_fault2ipath(nvlist_t *flt); #ifdef __cplusplus } #endif #endif /* _EFT_PLATFORM_H */ /* * 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. * * stats.c -- simple stats tracking table module * * this version of stats.c links with eft and implements the * stats using the fmd's stats API. */ #include #include #include "stats.h" #include "alloc.h" #include "out.h" #include "stats_impl.h" #include extern fmd_hdl_t *Hdl; /* handle from eft.c */ static int Ext; /* true if extended stats are enabled */ /* * stats_init -- initialize the stats module * */ void stats_init(int ext) { Ext = ext; } void stats_fini(void) { } static struct stats * stats_new(const char *name, const char *desc, enum stats_type t) { struct stats *ret = MALLOC(sizeof (*ret)); bzero(ret, sizeof (*ret)); ret->t = t; (void) strlcpy(ret->fmd_stats.fmds_desc, desc, sizeof (ret->fmd_stats.fmds_desc)); /* NULL name means generate a unique name */ if (name == NULL) { static int uniqstat; (void) snprintf(ret->fmd_stats.fmds_name, sizeof (ret->fmd_stats.fmds_name), "stat.rules%d", uniqstat++); } else { (void) strlcpy(ret->fmd_stats.fmds_name, name, sizeof (ret->fmd_stats.fmds_name)); } switch (t) { case STATS_COUNTER: ret->fmd_stats.fmds_type = FMD_TYPE_INT32; break; case STATS_ELAPSE: ret->fmd_stats.fmds_type = FMD_TYPE_TIME; break; case STATS_STRING: ret->fmd_stats.fmds_type = FMD_TYPE_STRING; break; default: out(O_DIE, "stats_new: unknown type %d", t); } (void) fmd_stat_create(Hdl, FMD_STAT_NOALLOC, 1, &(ret->fmd_stats)); return (ret); } void stats_delete(struct stats *sp) { if (sp == NULL) return; fmd_stat_destroy(Hdl, 1, &(sp->fmd_stats)); FREE(sp); } struct stats * stats_new_counter(const char *name, const char *desc, int ext) { if (ext && !Ext) return (NULL); /* extended stats not enabled */ return (stats_new(name, desc, STATS_COUNTER)); } void stats_counter_bump(struct stats *sp) { if (sp == NULL) return; ASSERT(sp->t == STATS_COUNTER); sp->fmd_stats.fmds_value.i32++; } void stats_counter_add(struct stats *sp, int n) { if (sp == NULL) return; ASSERT(sp->t == STATS_COUNTER); sp->fmd_stats.fmds_value.i32 += n; } void stats_counter_reset(struct stats *sp) { if (sp == NULL) return; ASSERT(sp->t == STATS_COUNTER); sp->fmd_stats.fmds_value.i32 = 0; } int stats_counter_value(struct stats *sp) { if (sp == NULL) return (0); ASSERT(sp->t == STATS_COUNTER); return (sp->fmd_stats.fmds_value.i32); } struct stats * stats_new_elapse(const char *name, const char *desc, int ext) { if (ext && !Ext) return (NULL); /* extended stats not enabled */ return (stats_new(name, desc, STATS_ELAPSE)); } void stats_elapse_start(struct stats *sp) { if (sp == NULL) return; ASSERT(sp->t == STATS_ELAPSE); sp->start = gethrtime(); } void stats_elapse_stop(struct stats *sp) { if (sp == NULL) return; ASSERT(sp->t == STATS_ELAPSE); sp->stop = gethrtime(); sp->fmd_stats.fmds_value.ui64 = sp->stop - sp->start; } struct stats * stats_new_string(const char *name, const char *desc, int ext) { struct stats *r; if (ext && !Ext) return (NULL); /* extended stats not enabled */ r = stats_new(name, desc, STATS_STRING); return (r); } void stats_string_set(struct stats *sp, const char *s) { if (sp == NULL) return; ASSERT(sp->t == STATS_STRING); if (sp->fmd_stats.fmds_value.str) fmd_hdl_strfree(Hdl, sp->fmd_stats.fmds_value.str); sp->fmd_stats.fmds_value.str = fmd_hdl_strdup(Hdl, s, FMD_SLEEP); } /* * stats_publish -- spew all stats * */ void stats_publish(void) { /* nothing to do for eft */ } /* * 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. */ #ifndef _EFT_STATS_IMPL_H #define _EFT_STATS_IMPL_H #ifdef __cplusplus extern "C" { #endif #include "fme.h" struct stats { fmd_stat_t fmd_stats; enum stats_type { STATS_COUNTER = 3000, STATS_ELAPSE, STATS_STRING } t; /* used for STATS_ELAPSE */ hrtime_t start; hrtime_t stop; }; #ifdef __cplusplus } #endif #endif /* _EFT_STATS_IMPL_H */