/* * 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); }