10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*1490Stimh * Common Development and Distribution License (the "License"). 6*1490Stimh * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 21*1490Stimh 220Sstevel@tonic-gate /* 23*1490Stimh * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 240Sstevel@tonic-gate * Use is subject to license terms. 25*1490Stimh */ 26*1490Stimh 27*1490Stimh #pragma ident "%Z%%M% %I% %E% SMI" 28*1490Stimh 29*1490Stimh /* 300Sstevel@tonic-gate * config.c -- system configuration cache module 310Sstevel@tonic-gate * 320Sstevel@tonic-gate * this module caches the system configuration in a format useful 330Sstevel@tonic-gate * to eft. the information is loaded into this module by 340Sstevel@tonic-gate * config_snapshot() at the beginning of each FME. config_snapshot() 350Sstevel@tonic-gate * calls the platform-specific platform_config_snapshot() to get 360Sstevel@tonic-gate * the configuration information loaded up. 370Sstevel@tonic-gate */ 380Sstevel@tonic-gate 390Sstevel@tonic-gate #include <stdio.h> 400Sstevel@tonic-gate #include <stdlib.h> 410Sstevel@tonic-gate #include <ctype.h> 420Sstevel@tonic-gate #include <string.h> 430Sstevel@tonic-gate #include <strings.h> 440Sstevel@tonic-gate #include "alloc.h" 450Sstevel@tonic-gate #include "out.h" 460Sstevel@tonic-gate #include "literals.h" 470Sstevel@tonic-gate #include "stable.h" 480Sstevel@tonic-gate #include "lut.h" 490Sstevel@tonic-gate #include "tree.h" 500Sstevel@tonic-gate #include "itree.h" 510Sstevel@tonic-gate #include "ipath.h" 520Sstevel@tonic-gate #include "ptree.h" 530Sstevel@tonic-gate #include "eval.h" 540Sstevel@tonic-gate #include "config.h" 550Sstevel@tonic-gate #include "fme.h" 560Sstevel@tonic-gate #include "platform.h" 570Sstevel@tonic-gate 580Sstevel@tonic-gate /* 590Sstevel@tonic-gate * private data structure for storing config. all access to 600Sstevel@tonic-gate * to this information happens using the config.h interfaces. 610Sstevel@tonic-gate */ 620Sstevel@tonic-gate struct config { 630Sstevel@tonic-gate struct config *next; 640Sstevel@tonic-gate struct config *child; 650Sstevel@tonic-gate struct config *parent; 660Sstevel@tonic-gate const char *s; 670Sstevel@tonic-gate int num; 680Sstevel@tonic-gate struct lut *props; 690Sstevel@tonic-gate }; 700Sstevel@tonic-gate 710Sstevel@tonic-gate /* 720Sstevel@tonic-gate * newcnode -- local function to allocate new config node 730Sstevel@tonic-gate */ 740Sstevel@tonic-gate static struct config * 750Sstevel@tonic-gate newcnode(const char *s, int num) 760Sstevel@tonic-gate { 770Sstevel@tonic-gate struct config *retval; 780Sstevel@tonic-gate 790Sstevel@tonic-gate retval = MALLOC(sizeof (struct config)); 800Sstevel@tonic-gate 810Sstevel@tonic-gate retval->s = s; 820Sstevel@tonic-gate retval->num = num; 830Sstevel@tonic-gate retval->next = NULL; 840Sstevel@tonic-gate retval->props = NULL; 850Sstevel@tonic-gate retval->child = retval->parent = NULL; 860Sstevel@tonic-gate 870Sstevel@tonic-gate return (retval); 880Sstevel@tonic-gate } 890Sstevel@tonic-gate 900Sstevel@tonic-gate /* 910Sstevel@tonic-gate * If we need to cache certain types of nodes for reverse look-up or 920Sstevel@tonic-gate * somesuch, do it here. Currently we need to cache nodes representing 930Sstevel@tonic-gate * cpus. 940Sstevel@tonic-gate */ 950Sstevel@tonic-gate static void 960Sstevel@tonic-gate config_node_cache(struct cfgdata *cdata, struct config *n) 970Sstevel@tonic-gate { 980Sstevel@tonic-gate if (n->s != stable("cpu")) 990Sstevel@tonic-gate return; 1000Sstevel@tonic-gate cdata->cpucache = lut_add(cdata->cpucache, 1010Sstevel@tonic-gate (void *)n->num, (void *)n, NULL); 1020Sstevel@tonic-gate } 1030Sstevel@tonic-gate 1040Sstevel@tonic-gate /* 1050Sstevel@tonic-gate * config_lookup -- lookup/add components in configuration cache 1060Sstevel@tonic-gate */ 1070Sstevel@tonic-gate struct config * 1080Sstevel@tonic-gate config_lookup(struct config *croot, char *path, int add) 1090Sstevel@tonic-gate { 1100Sstevel@tonic-gate char *pathbegin = path; 1110Sstevel@tonic-gate struct config *parent = croot; 1120Sstevel@tonic-gate struct config *cp; 1130Sstevel@tonic-gate struct config *lastcp; 1140Sstevel@tonic-gate struct config *newnode; 1150Sstevel@tonic-gate char *thiscom; /* this component */ 1160Sstevel@tonic-gate char *nextcom; /* next component */ 1170Sstevel@tonic-gate char svdigit; 1180Sstevel@tonic-gate int len; 1190Sstevel@tonic-gate int num; 1200Sstevel@tonic-gate const char *s; 1210Sstevel@tonic-gate int exists; 1220Sstevel@tonic-gate 1230Sstevel@tonic-gate if (parent == NULL) 1240Sstevel@tonic-gate out(O_DIE, "uninitialized configuration"); 1250Sstevel@tonic-gate 1260Sstevel@tonic-gate while (*path) { 1270Sstevel@tonic-gate if ((nextcom = strchr(path, '/')) != NULL) 1280Sstevel@tonic-gate *nextcom = '\0'; 1290Sstevel@tonic-gate if ((len = strlen(path)) == 0) 1300Sstevel@tonic-gate out(O_DIE, "config_lookup: zero length component"); 1310Sstevel@tonic-gate /* start at end of string and work backwards */ 1320Sstevel@tonic-gate thiscom = &path[len - 1]; 1330Sstevel@tonic-gate if (!isdigit(*thiscom)) 1340Sstevel@tonic-gate out(O_DIE, "config_lookup: " 1350Sstevel@tonic-gate "component \"%s\" has no number following it", 1360Sstevel@tonic-gate path); 1370Sstevel@tonic-gate while (thiscom > path && isdigit(*thiscom)) 1380Sstevel@tonic-gate thiscom--; 1390Sstevel@tonic-gate if (thiscom == path && isdigit(*thiscom)) 1400Sstevel@tonic-gate out(O_DIE, "config_lookup: " 1410Sstevel@tonic-gate "component \"%s\" has no name part", path); 1420Sstevel@tonic-gate thiscom++; /* move to first numeric character */ 1430Sstevel@tonic-gate num = atoi(thiscom); 1440Sstevel@tonic-gate svdigit = *thiscom; 1450Sstevel@tonic-gate *thiscom = '\0'; 1460Sstevel@tonic-gate s = stable(path); 1470Sstevel@tonic-gate *thiscom = svdigit; 1480Sstevel@tonic-gate 1490Sstevel@tonic-gate if (nextcom != NULL) 1500Sstevel@tonic-gate *nextcom++ = '/'; 1510Sstevel@tonic-gate 1520Sstevel@tonic-gate /* now we have s & num, figure out if it exists already */ 1530Sstevel@tonic-gate exists = 0; 1540Sstevel@tonic-gate lastcp = NULL; 1550Sstevel@tonic-gate for (cp = parent->child; cp; lastcp = cp, cp = cp->next) 1560Sstevel@tonic-gate if (cp->s == s && cp->num == num) { 1570Sstevel@tonic-gate exists = 1; 1580Sstevel@tonic-gate parent = cp; 1590Sstevel@tonic-gate } 1600Sstevel@tonic-gate 1610Sstevel@tonic-gate if (!exists) { 1620Sstevel@tonic-gate /* creating new node */ 1630Sstevel@tonic-gate if (!add) { 1640Sstevel@tonic-gate /* 1650Sstevel@tonic-gate * indicate component not found by copying 1660Sstevel@tonic-gate * it to path (allows better error messages 1670Sstevel@tonic-gate * in the caller). 1680Sstevel@tonic-gate */ 1690Sstevel@tonic-gate (void) strcpy(pathbegin, s); 1700Sstevel@tonic-gate return (NULL); 1710Sstevel@tonic-gate } 1720Sstevel@tonic-gate 1730Sstevel@tonic-gate newnode = newcnode(s, num); 1740Sstevel@tonic-gate 1750Sstevel@tonic-gate if (lastcp) 1760Sstevel@tonic-gate lastcp->next = newnode; 1770Sstevel@tonic-gate else 1780Sstevel@tonic-gate parent->child = newnode; 1790Sstevel@tonic-gate 1800Sstevel@tonic-gate newnode->parent = parent; 1810Sstevel@tonic-gate parent = newnode; 1820Sstevel@tonic-gate } 1830Sstevel@tonic-gate 1840Sstevel@tonic-gate if (nextcom == NULL) 1850Sstevel@tonic-gate return (parent); /* all done */ 1860Sstevel@tonic-gate 1870Sstevel@tonic-gate /* move on to next component */ 1880Sstevel@tonic-gate path = nextcom; 1890Sstevel@tonic-gate } 1900Sstevel@tonic-gate return (parent); 1910Sstevel@tonic-gate } 1920Sstevel@tonic-gate 1930Sstevel@tonic-gate /* 1940Sstevel@tonic-gate * addconfigprop -- add a config prop to a config cache entry 1950Sstevel@tonic-gate */ 1960Sstevel@tonic-gate static void 1970Sstevel@tonic-gate addconfigprop(const char *lhs, struct node *rhs, void *arg) 1980Sstevel@tonic-gate { 1990Sstevel@tonic-gate struct config *cp = (struct config *)arg; 2000Sstevel@tonic-gate 2010Sstevel@tonic-gate ASSERT(cp != NULL); 2020Sstevel@tonic-gate ASSERT(lhs != NULL); 2030Sstevel@tonic-gate ASSERT(rhs != NULL); 2040Sstevel@tonic-gate ASSERT(rhs->t == T_QUOTE); 2050Sstevel@tonic-gate 2060Sstevel@tonic-gate config_setprop(cp, lhs, STRDUP(rhs->u.quote.s)); 2070Sstevel@tonic-gate } 2080Sstevel@tonic-gate 2090Sstevel@tonic-gate /* 2100Sstevel@tonic-gate * addconfig -- add a config from parse tree to given configuration cache 2110Sstevel@tonic-gate */ 2120Sstevel@tonic-gate /*ARGSUSED*/ 2130Sstevel@tonic-gate static void 2140Sstevel@tonic-gate addconfig(struct node *lhs, struct node *rhs, void *arg) 2150Sstevel@tonic-gate { 2160Sstevel@tonic-gate struct config *parent = (struct config *)arg; 2170Sstevel@tonic-gate struct config *cp; 2180Sstevel@tonic-gate const char *s; 2190Sstevel@tonic-gate int num; 2200Sstevel@tonic-gate struct config *lastcp; 2210Sstevel@tonic-gate struct config *newnode; 2220Sstevel@tonic-gate int exists; 2230Sstevel@tonic-gate struct lut *lutp; 2240Sstevel@tonic-gate 2250Sstevel@tonic-gate ASSERT(rhs->t == T_CONFIG); 2260Sstevel@tonic-gate 2270Sstevel@tonic-gate lutp = rhs->u.stmt.lutp; 2280Sstevel@tonic-gate rhs = rhs->u.stmt.np; 2290Sstevel@tonic-gate while (rhs != NULL) { 2300Sstevel@tonic-gate ASSERT(rhs->t == T_NAME); 2310Sstevel@tonic-gate ASSERT(rhs->u.name.child->t == T_NUM); 2320Sstevel@tonic-gate s = rhs->u.name.s; 2330Sstevel@tonic-gate num = rhs->u.name.child->u.ull; 2340Sstevel@tonic-gate 2350Sstevel@tonic-gate /* now we have s & num, figure out if it exists already */ 2360Sstevel@tonic-gate exists = 0; 2370Sstevel@tonic-gate lastcp = NULL; 2380Sstevel@tonic-gate for (cp = parent->child; cp; lastcp = cp, cp = cp->next) 2390Sstevel@tonic-gate if (cp->s == s && cp->num == num) { 2400Sstevel@tonic-gate exists = 1; 2410Sstevel@tonic-gate parent = cp; 2420Sstevel@tonic-gate } 2430Sstevel@tonic-gate 2440Sstevel@tonic-gate if (!exists) { 2450Sstevel@tonic-gate /* creating new node */ 2460Sstevel@tonic-gate 2470Sstevel@tonic-gate newnode = newcnode(s, num); 2480Sstevel@tonic-gate 2490Sstevel@tonic-gate if (lastcp) 2500Sstevel@tonic-gate lastcp->next = newnode; 2510Sstevel@tonic-gate else 2520Sstevel@tonic-gate parent->child = newnode; 2530Sstevel@tonic-gate 2540Sstevel@tonic-gate parent = newnode; 2550Sstevel@tonic-gate } 2560Sstevel@tonic-gate 2570Sstevel@tonic-gate /* move on to next component */ 2580Sstevel@tonic-gate rhs = rhs->u.name.next; 2590Sstevel@tonic-gate } 2600Sstevel@tonic-gate 2610Sstevel@tonic-gate /* add configuration properties */ 2620Sstevel@tonic-gate lut_walk(lutp, (lut_cb)addconfigprop, (void *)parent); 2630Sstevel@tonic-gate } 2640Sstevel@tonic-gate 2650Sstevel@tonic-gate /* 2660Sstevel@tonic-gate * config_cook -- convert raw config strings to eft internal representation 2670Sstevel@tonic-gate */ 2680Sstevel@tonic-gate void 2690Sstevel@tonic-gate config_cook(struct cfgdata *cdata) 2700Sstevel@tonic-gate { 2710Sstevel@tonic-gate struct config *newnode; 2720Sstevel@tonic-gate char *cfgstr, *equals; 2730Sstevel@tonic-gate const char *pn, *sv; 2740Sstevel@tonic-gate char *pv; 2750Sstevel@tonic-gate 2760Sstevel@tonic-gate if (cdata->cooked != NULL) 2770Sstevel@tonic-gate return; 2780Sstevel@tonic-gate 2790Sstevel@tonic-gate cdata->cooked = newcnode(NULL, 0); 2800Sstevel@tonic-gate 2810Sstevel@tonic-gate if ((cfgstr = cdata->begin) == cdata->nextfree) { 2820Sstevel@tonic-gate out(O_ALTFP|O_VERB, "Platform provided no config data."); 2830Sstevel@tonic-gate goto eftcfgs; 2840Sstevel@tonic-gate } 2850Sstevel@tonic-gate 2860Sstevel@tonic-gate out(O_ALTFP|O_VERB3, "Raw config data follows:"); 2870Sstevel@tonic-gate out(O_ALTFP|O_VERB3|O_NONL, 2880Sstevel@tonic-gate "nextfree is %p\n%p ", (void *)cdata->nextfree, (void *)cfgstr); 2890Sstevel@tonic-gate while (cfgstr < cdata->nextfree) { 2900Sstevel@tonic-gate if (!*cfgstr) 2910Sstevel@tonic-gate out(O_ALTFP|O_VERB3|O_NONL, "\n%p ", 2920Sstevel@tonic-gate (void *)(cfgstr + 1)); 2930Sstevel@tonic-gate else 2940Sstevel@tonic-gate out(O_ALTFP|O_VERB3|O_NONL, "%c", *cfgstr); 2950Sstevel@tonic-gate cfgstr++; 2960Sstevel@tonic-gate } 2970Sstevel@tonic-gate out(O_ALTFP|O_VERB3, NULL); 2980Sstevel@tonic-gate 2990Sstevel@tonic-gate cfgstr = cdata->begin; 3000Sstevel@tonic-gate while (cfgstr < cdata->nextfree) { 3010Sstevel@tonic-gate while (*cfgstr == '/' && cfgstr < cdata->nextfree) { 3020Sstevel@tonic-gate out(O_ALTFP|O_VERB3, 3030Sstevel@tonic-gate "next string (%p) is %s", (void *)cfgstr, cfgstr); 3040Sstevel@tonic-gate /* skip the initial slash from libtopo */ 3050Sstevel@tonic-gate newnode = config_lookup(cdata->cooked, cfgstr + 1, 1); 3060Sstevel@tonic-gate /* 3070Sstevel@tonic-gate * Note we'll only cache nodes that have 3080Sstevel@tonic-gate * properties on them. Intermediate nodes 3090Sstevel@tonic-gate * will have been added to the config tree, 3100Sstevel@tonic-gate * but we don't have easy means of accessing 3110Sstevel@tonic-gate * them except if we climb the tree from this 3120Sstevel@tonic-gate * newnode to the root. 3130Sstevel@tonic-gate * 3140Sstevel@tonic-gate * Luckily, the nodes we care to cache 3150Sstevel@tonic-gate * (currently just cpus) always have some 3160Sstevel@tonic-gate * properties attached to them 3170Sstevel@tonic-gate * so we don't bother climbing the tree. 3180Sstevel@tonic-gate */ 3190Sstevel@tonic-gate config_node_cache(cdata, newnode); 3200Sstevel@tonic-gate cfgstr += strlen(cfgstr) + 1; 3210Sstevel@tonic-gate } 3220Sstevel@tonic-gate 3230Sstevel@tonic-gate if (cfgstr >= cdata->nextfree) 3240Sstevel@tonic-gate break; 3250Sstevel@tonic-gate 3260Sstevel@tonic-gate out(O_ALTFP|O_VERB3, "next string (%p) is %s", (void *)cfgstr, 3270Sstevel@tonic-gate cfgstr); 3280Sstevel@tonic-gate if ((equals = strchr(cfgstr, '=')) == NULL) { 3290Sstevel@tonic-gate out(O_ALTFP|O_VERB3, "raw config data bad (%p); " 3300Sstevel@tonic-gate "property missing equals.\n", (void *)cfgstr); 3310Sstevel@tonic-gate break; 3320Sstevel@tonic-gate } 3330Sstevel@tonic-gate 3340Sstevel@tonic-gate *equals = '\0'; 3350Sstevel@tonic-gate pn = stable(cfgstr); 3360Sstevel@tonic-gate pv = STRDUP(equals + 1); 3370Sstevel@tonic-gate 3380Sstevel@tonic-gate out(O_ALTFP|O_VERB3, "add prop (%s) val %p", pn, (void *)pv); 3390Sstevel@tonic-gate config_setprop(newnode, pn, pv); 3400Sstevel@tonic-gate 3410Sstevel@tonic-gate /* 3420Sstevel@tonic-gate * If this property is a device path, cache it for quick lookup 3430Sstevel@tonic-gate */ 344*1490Stimh if (pn == stable("DEV")) { 3450Sstevel@tonic-gate sv = stable(pv); 3460Sstevel@tonic-gate out(O_ALTFP|O_VERB3, "caching %s\n", sv); 3470Sstevel@tonic-gate cdata->devcache = lut_add(cdata->devcache, 3480Sstevel@tonic-gate (void *)sv, (void *)newnode, NULL); 3490Sstevel@tonic-gate } 3500Sstevel@tonic-gate 3510Sstevel@tonic-gate *equals = '='; 3520Sstevel@tonic-gate cfgstr += strlen(cfgstr) + 1; 3530Sstevel@tonic-gate } 3540Sstevel@tonic-gate 3550Sstevel@tonic-gate eftcfgs: 3560Sstevel@tonic-gate /* now run through Configs table, adding to config cache */ 3570Sstevel@tonic-gate lut_walk(Configs, (lut_cb)addconfig, (void *)cdata->cooked); 3580Sstevel@tonic-gate } 3590Sstevel@tonic-gate 3600Sstevel@tonic-gate /* 3610Sstevel@tonic-gate * config_snapshot -- gather a snapshot of the current configuration 3620Sstevel@tonic-gate */ 3630Sstevel@tonic-gate struct cfgdata * 3640Sstevel@tonic-gate config_snapshot(void) 3650Sstevel@tonic-gate { 3660Sstevel@tonic-gate struct cfgdata *rawcfg; 3670Sstevel@tonic-gate 3680Sstevel@tonic-gate rawcfg = platform_config_snapshot(); 3690Sstevel@tonic-gate config_cook(rawcfg); 3700Sstevel@tonic-gate return (rawcfg); 3710Sstevel@tonic-gate } 3720Sstevel@tonic-gate 3730Sstevel@tonic-gate /* 3740Sstevel@tonic-gate * prop_destructor -- free a prop value 3750Sstevel@tonic-gate */ 3760Sstevel@tonic-gate /*ARGSUSED*/ 3770Sstevel@tonic-gate static void 3780Sstevel@tonic-gate prop_destructor(void *left, void *right, void *arg) 3790Sstevel@tonic-gate { 3800Sstevel@tonic-gate FREE(right); 3810Sstevel@tonic-gate } 3820Sstevel@tonic-gate 3830Sstevel@tonic-gate /* 3840Sstevel@tonic-gate * structconfig_free -- free a struct config pointer and all its relatives 3850Sstevel@tonic-gate */ 3860Sstevel@tonic-gate static void 3870Sstevel@tonic-gate structconfig_free(struct config *cp) 3880Sstevel@tonic-gate { 3890Sstevel@tonic-gate if (cp == NULL) 3900Sstevel@tonic-gate return; 3910Sstevel@tonic-gate 3920Sstevel@tonic-gate structconfig_free(cp->child); 3930Sstevel@tonic-gate structconfig_free(cp->next); 3940Sstevel@tonic-gate lut_free(cp->props, prop_destructor, NULL); 3950Sstevel@tonic-gate FREE(cp); 3960Sstevel@tonic-gate } 3970Sstevel@tonic-gate 3980Sstevel@tonic-gate /* 3990Sstevel@tonic-gate * config_free -- free a configuration snapshot 4000Sstevel@tonic-gate */ 4010Sstevel@tonic-gate void 4020Sstevel@tonic-gate config_free(struct cfgdata *cp) 4030Sstevel@tonic-gate { 4040Sstevel@tonic-gate if (cp == NULL) 4050Sstevel@tonic-gate return; 4060Sstevel@tonic-gate 4070Sstevel@tonic-gate if (--cp->refcnt > 0) 4080Sstevel@tonic-gate return; 4090Sstevel@tonic-gate 4100Sstevel@tonic-gate if (cp->cooked != NULL) 4110Sstevel@tonic-gate structconfig_free(cp->cooked); 4120Sstevel@tonic-gate if (cp->begin != NULL) 4130Sstevel@tonic-gate FREE(cp->begin); 4140Sstevel@tonic-gate if (cp->devcache != NULL) 4150Sstevel@tonic-gate lut_free(cp->devcache, NULL, NULL); 4160Sstevel@tonic-gate if (cp->cpucache != NULL) 4170Sstevel@tonic-gate lut_free(cp->cpucache, NULL, NULL); 4180Sstevel@tonic-gate FREE(cp); 4190Sstevel@tonic-gate } 4200Sstevel@tonic-gate 4210Sstevel@tonic-gate /* 4220Sstevel@tonic-gate * config_next -- get the "next" config node 4230Sstevel@tonic-gate */ 4240Sstevel@tonic-gate struct config * 4250Sstevel@tonic-gate config_next(struct config *cp) 4260Sstevel@tonic-gate { 4270Sstevel@tonic-gate ASSERT(cp != NULL); 4280Sstevel@tonic-gate 4290Sstevel@tonic-gate return ((struct config *)((struct config *)cp)->next); 4300Sstevel@tonic-gate } 4310Sstevel@tonic-gate 4320Sstevel@tonic-gate 4330Sstevel@tonic-gate /* 4340Sstevel@tonic-gate * config_child -- get the "child" of a config node 4350Sstevel@tonic-gate */ 4360Sstevel@tonic-gate struct config * 4370Sstevel@tonic-gate config_child(struct config *cp) 4380Sstevel@tonic-gate { 4390Sstevel@tonic-gate ASSERT(cp != NULL); 4400Sstevel@tonic-gate 4410Sstevel@tonic-gate return ((struct config *)((struct config *)cp)->child); 4420Sstevel@tonic-gate } 4430Sstevel@tonic-gate 4440Sstevel@tonic-gate /* 4450Sstevel@tonic-gate * config_setprop -- add a property to a config node 4460Sstevel@tonic-gate */ 4470Sstevel@tonic-gate void 4480Sstevel@tonic-gate config_setprop(struct config *cp, const char *propname, const char *propvalue) 4490Sstevel@tonic-gate { 4500Sstevel@tonic-gate const char *pn = stable(propname); 4510Sstevel@tonic-gate 4520Sstevel@tonic-gate cp->props = lut_add(cp->props, (void *)pn, (void *)propvalue, NULL); 4530Sstevel@tonic-gate } 4540Sstevel@tonic-gate 4550Sstevel@tonic-gate /* 4560Sstevel@tonic-gate * config_getprop -- lookup a config property 4570Sstevel@tonic-gate */ 4580Sstevel@tonic-gate const char * 4590Sstevel@tonic-gate config_getprop(struct config *cp, const char *propname) 4600Sstevel@tonic-gate { 4610Sstevel@tonic-gate return (lut_lookup(cp->props, (void *) stable(propname), NULL)); 4620Sstevel@tonic-gate } 4630Sstevel@tonic-gate 4640Sstevel@tonic-gate /* 4650Sstevel@tonic-gate * config_getcompname -- get the component name of a config node 4660Sstevel@tonic-gate */ 4670Sstevel@tonic-gate void 4680Sstevel@tonic-gate config_getcompname(struct config *cp, char **name, int *inst) 4690Sstevel@tonic-gate { 4700Sstevel@tonic-gate ASSERT(cp != NULL); 4710Sstevel@tonic-gate 4720Sstevel@tonic-gate if (name != NULL) 4730Sstevel@tonic-gate *name = (char *)cp->s; 4740Sstevel@tonic-gate if (inst != NULL) 4750Sstevel@tonic-gate *inst = cp->num; 4760Sstevel@tonic-gate } 4770Sstevel@tonic-gate 4780Sstevel@tonic-gate /* 4790Sstevel@tonic-gate * config_nodeize -- convert the config element represented by cp to struct 4800Sstevel@tonic-gate * node format 4810Sstevel@tonic-gate */ 4820Sstevel@tonic-gate static struct node * 4830Sstevel@tonic-gate config_nodeize(struct config *cp) 4840Sstevel@tonic-gate { 4850Sstevel@tonic-gate struct node *tmpn, *ptmpn; 4860Sstevel@tonic-gate struct node *numn; 4870Sstevel@tonic-gate const char *sname; 4880Sstevel@tonic-gate 4890Sstevel@tonic-gate if (cp == NULL || cp->s == NULL) 4900Sstevel@tonic-gate return (NULL); 4910Sstevel@tonic-gate 4920Sstevel@tonic-gate sname = stable(cp->s); 4930Sstevel@tonic-gate numn = newnode(T_NUM, NULL, 0); 4940Sstevel@tonic-gate numn->u.ull = cp->num; 4950Sstevel@tonic-gate 4960Sstevel@tonic-gate tmpn = tree_name_iterator(tree_name(sname, IT_VERTICAL, NULL, 0), numn); 4970Sstevel@tonic-gate if ((ptmpn = config_nodeize(cp->parent)) == NULL) 4980Sstevel@tonic-gate return (tmpn); 4990Sstevel@tonic-gate return (tree_name_append(ptmpn, tmpn)); 5000Sstevel@tonic-gate } 5010Sstevel@tonic-gate 5020Sstevel@tonic-gate /*ARGSUSED*/ 5030Sstevel@tonic-gate static void 5040Sstevel@tonic-gate prtdevcache(void *lhs, void *rhs, void *arg) 5050Sstevel@tonic-gate { 5060Sstevel@tonic-gate out(O_ALTFP|O_VERB3, "%s -> %p", (char *)lhs, rhs); 5070Sstevel@tonic-gate } 5080Sstevel@tonic-gate 5090Sstevel@tonic-gate /*ARGSUSED*/ 5100Sstevel@tonic-gate static void 5110Sstevel@tonic-gate prtcpucache(void *lhs, void *rhs, void *arg) 5120Sstevel@tonic-gate { 5130Sstevel@tonic-gate out(O_ALTFP|O_VERB, "%u -> %p", (uint32_t)lhs, rhs); 5140Sstevel@tonic-gate } 5150Sstevel@tonic-gate 5160Sstevel@tonic-gate /* 5170Sstevel@tonic-gate * config_bydev_lookup -- look up the path in our DEVcache lut. If we find 5180Sstevel@tonic-gate * it return the config path, but as a struct node. 5190Sstevel@tonic-gate */ 5200Sstevel@tonic-gate struct node * 5210Sstevel@tonic-gate config_bydev_lookup(struct cfgdata *fromcfg, const char *path) 5220Sstevel@tonic-gate { 5230Sstevel@tonic-gate struct config *find; 5240Sstevel@tonic-gate struct node *np; 5250Sstevel@tonic-gate 5260Sstevel@tonic-gate out(O_ALTFP|O_VERB3, "Device path cache:"); 5270Sstevel@tonic-gate lut_walk(fromcfg->devcache, (lut_cb)prtdevcache, NULL); 5280Sstevel@tonic-gate 5290Sstevel@tonic-gate if ((find = lut_lookup(fromcfg->devcache, 5300Sstevel@tonic-gate (void *) stable(path), NULL)) == NULL) 5310Sstevel@tonic-gate return (NULL); 5320Sstevel@tonic-gate 5330Sstevel@tonic-gate np = config_nodeize(find); 5340Sstevel@tonic-gate if (np != NULL) { 5350Sstevel@tonic-gate out(O_ALTFP|O_VERB, "Matching config entry:"); 5360Sstevel@tonic-gate ptree_name_iter(O_ALTFP|O_VERB|O_NONL, np); 5370Sstevel@tonic-gate out(O_ALTFP|O_VERB, NULL); 5380Sstevel@tonic-gate } 5390Sstevel@tonic-gate return (np); 5400Sstevel@tonic-gate } 5410Sstevel@tonic-gate 5420Sstevel@tonic-gate /* 5430Sstevel@tonic-gate * config_bycpuid_lookup -- look up the cpu id in our CPUcache lut. 5440Sstevel@tonic-gate * If we find it return the config path, but as a struct node. 5450Sstevel@tonic-gate */ 5460Sstevel@tonic-gate struct node * 5470Sstevel@tonic-gate config_bycpuid_lookup(struct cfgdata *fromcfg, uint32_t id) 5480Sstevel@tonic-gate { 5490Sstevel@tonic-gate struct config *find; 5500Sstevel@tonic-gate struct node *np; 5510Sstevel@tonic-gate 5520Sstevel@tonic-gate out(O_ALTFP|O_VERB, "Cpu cache:"); 5530Sstevel@tonic-gate lut_walk(fromcfg->cpucache, (lut_cb)prtcpucache, NULL); 5540Sstevel@tonic-gate 5550Sstevel@tonic-gate if ((find = lut_lookup(fromcfg->cpucache, 5560Sstevel@tonic-gate (void *)id, NULL)) == NULL) 5570Sstevel@tonic-gate return (NULL); 5580Sstevel@tonic-gate 5590Sstevel@tonic-gate np = config_nodeize(find); 5600Sstevel@tonic-gate if (np != NULL) { 5610Sstevel@tonic-gate out(O_ALTFP|O_VERB3, "Matching config entry:"); 5620Sstevel@tonic-gate ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, np); 5630Sstevel@tonic-gate out(O_ALTFP|O_VERB3, NULL); 5640Sstevel@tonic-gate } 5650Sstevel@tonic-gate return (np); 5660Sstevel@tonic-gate } 5670Sstevel@tonic-gate 5680Sstevel@tonic-gate /* 5690Sstevel@tonic-gate * given the following: 5700Sstevel@tonic-gate * - np of type T_NAME which denotes a pathname 5710Sstevel@tonic-gate * - croot, the root node of a configuration 5720Sstevel@tonic-gate * 5730Sstevel@tonic-gate * return the cp for the last component in np's path 5740Sstevel@tonic-gate */ 5750Sstevel@tonic-gate static struct config * 5760Sstevel@tonic-gate name2cp(struct node *np, struct config *croot) 5770Sstevel@tonic-gate { 5780Sstevel@tonic-gate char *path; 5790Sstevel@tonic-gate struct config *cp; 5800Sstevel@tonic-gate 5810Sstevel@tonic-gate if (np->u.name.last->u.name.cp != NULL) 5820Sstevel@tonic-gate return (np->u.name.last->u.name.cp); 5830Sstevel@tonic-gate 5840Sstevel@tonic-gate path = ipath2str(NULL, ipath(np)); 5850Sstevel@tonic-gate 5860Sstevel@tonic-gate cp = config_lookup(croot, path, 0); 5870Sstevel@tonic-gate FREE((void *)path); 5880Sstevel@tonic-gate 5890Sstevel@tonic-gate return (cp); 5900Sstevel@tonic-gate } 5910Sstevel@tonic-gate 5920Sstevel@tonic-gate int 5930Sstevel@tonic-gate config_confprop(struct node *np, struct config *croot, struct evalue *valuep) 5940Sstevel@tonic-gate { 5950Sstevel@tonic-gate struct node *nodep; 5960Sstevel@tonic-gate struct config *cp; 5970Sstevel@tonic-gate const char *s; 5980Sstevel@tonic-gate 5990Sstevel@tonic-gate if (np->u.expr.left->u.func.s == L_fru) 6000Sstevel@tonic-gate nodep = eval_fru(np->u.expr.left->u.func.arglist); 6010Sstevel@tonic-gate else if (np->u.expr.left->u.func.s == L_asru) 6020Sstevel@tonic-gate nodep = eval_asru(np->u.expr.left->u.func.arglist); 6030Sstevel@tonic-gate 6040Sstevel@tonic-gate cp = name2cp(nodep, croot); 6050Sstevel@tonic-gate if (cp == NULL) 6060Sstevel@tonic-gate return (1); 6070Sstevel@tonic-gate 6080Sstevel@tonic-gate /* for now s will point to a quote [see addconfigprop()] */ 6090Sstevel@tonic-gate ASSERT(np->u.expr.right->t == T_QUOTE); 6100Sstevel@tonic-gate 6110Sstevel@tonic-gate s = config_getprop(cp, np->u.expr.right->u.quote.s); 6120Sstevel@tonic-gate if (s == NULL) 6130Sstevel@tonic-gate return (1); 6140Sstevel@tonic-gate 6150Sstevel@tonic-gate valuep->t = STRING; 6160Sstevel@tonic-gate valuep->v = (unsigned long long)stable(s); 6170Sstevel@tonic-gate 6180Sstevel@tonic-gate return (0); 6190Sstevel@tonic-gate } 6200Sstevel@tonic-gate 6210Sstevel@tonic-gate #define CONNECTED_SEPCHARS " ," 6220Sstevel@tonic-gate 6230Sstevel@tonic-gate int 6240Sstevel@tonic-gate config_is_connected(struct node *np, struct config *croot, 6250Sstevel@tonic-gate struct evalue *valuep) 6260Sstevel@tonic-gate { 6270Sstevel@tonic-gate const char *connstrings[] = { "connected", "CONNECTED", NULL }; 6280Sstevel@tonic-gate struct config *cp[2], *compcp; 6290Sstevel@tonic-gate struct node *nptr[2]; 6300Sstevel@tonic-gate const char *searchforname, *matchthis[2], *s; 6310Sstevel@tonic-gate char *nameslist, *w; 6320Sstevel@tonic-gate int i, j; 6330Sstevel@tonic-gate 6340Sstevel@tonic-gate valuep->t = UINT64; 6350Sstevel@tonic-gate valuep->v = 0; 6360Sstevel@tonic-gate 6370Sstevel@tonic-gate if (np->u.expr.left->t == T_NAME) 6380Sstevel@tonic-gate nptr[0] = np->u.expr.left; 6390Sstevel@tonic-gate else if (np->u.expr.left->u.func.s == L_fru) 6400Sstevel@tonic-gate nptr[0] = eval_fru(np->u.expr.left->u.func.arglist); 6410Sstevel@tonic-gate else if (np->u.expr.left->u.func.s == L_asru) 6420Sstevel@tonic-gate nptr[0] = eval_asru(np->u.expr.left->u.func.arglist); 6430Sstevel@tonic-gate 6440Sstevel@tonic-gate if (np->u.expr.right->t == T_NAME) 6450Sstevel@tonic-gate nptr[1] = np->u.expr.right; 6460Sstevel@tonic-gate else if (np->u.expr.right->u.func.s == L_fru) 6470Sstevel@tonic-gate nptr[1] = eval_fru(np->u.expr.right->u.func.arglist); 6480Sstevel@tonic-gate else if (np->u.expr.right->u.func.s == L_asru) 6490Sstevel@tonic-gate nptr[1] = eval_asru(np->u.expr.right->u.func.arglist); 6500Sstevel@tonic-gate 6510Sstevel@tonic-gate for (i = 0; i < 2; i++) { 6520Sstevel@tonic-gate cp[i] = name2cp(nptr[i], croot); 6530Sstevel@tonic-gate if (cp[i] == NULL) 6540Sstevel@tonic-gate return (1); 6550Sstevel@tonic-gate } 6560Sstevel@tonic-gate 6570Sstevel@tonic-gate /* to thine self always be connected */ 6580Sstevel@tonic-gate if (cp[0] == cp[1]) { 6590Sstevel@tonic-gate valuep->v = 1; 6600Sstevel@tonic-gate return (0); 6610Sstevel@tonic-gate } 6620Sstevel@tonic-gate 6630Sstevel@tonic-gate /* 6640Sstevel@tonic-gate * set one of the cp[]s to compcp and extract its "connected" 6650Sstevel@tonic-gate * property. search this property for the name associated with the 6660Sstevel@tonic-gate * other cp[]. 6670Sstevel@tonic-gate */ 6680Sstevel@tonic-gate for (i = 0; i < 2 && valuep->v == 0; i++) { 6690Sstevel@tonic-gate compcp = cp[i]; 6700Sstevel@tonic-gate 6710Sstevel@tonic-gate searchforname = ipath2str(NULL, ipath(nptr[(i == 0 ? 1 : 0)])); 6720Sstevel@tonic-gate matchthis[i] = stable(searchforname); 6730Sstevel@tonic-gate FREE((void *)searchforname); 6740Sstevel@tonic-gate 6750Sstevel@tonic-gate for (j = 0; connstrings[j] != NULL && valuep->v == 0; j++) { 6760Sstevel@tonic-gate s = config_getprop(compcp, stable(connstrings[j])); 6770Sstevel@tonic-gate if (s != NULL) { 6780Sstevel@tonic-gate nameslist = STRDUP(s); 6790Sstevel@tonic-gate w = strtok(nameslist, CONNECTED_SEPCHARS); 6800Sstevel@tonic-gate while (w != NULL) { 6810Sstevel@tonic-gate if (stable(w) == matchthis[i]) { 6820Sstevel@tonic-gate valuep->v = 1; 6830Sstevel@tonic-gate break; 6840Sstevel@tonic-gate } 6850Sstevel@tonic-gate w = strtok(NULL, CONNECTED_SEPCHARS); 6860Sstevel@tonic-gate } 6870Sstevel@tonic-gate FREE(nameslist); 6880Sstevel@tonic-gate } 6890Sstevel@tonic-gate } 6900Sstevel@tonic-gate } 6910Sstevel@tonic-gate 6920Sstevel@tonic-gate /* a path shouldn't have more than one cp node */ 6930Sstevel@tonic-gate if (valuep->v == 0) 6940Sstevel@tonic-gate ASSERT(matchthis[0] != matchthis[1]); 6950Sstevel@tonic-gate 6960Sstevel@tonic-gate return (0); 6970Sstevel@tonic-gate } 6980Sstevel@tonic-gate 6990Sstevel@tonic-gate int 7000Sstevel@tonic-gate config_is_type(struct node *np, struct config *croot, struct evalue *valuep) 7010Sstevel@tonic-gate { 7020Sstevel@tonic-gate const char *typestrings[] = { "type", "TYPE", NULL }; 7030Sstevel@tonic-gate struct config *cp; 7040Sstevel@tonic-gate struct node *nodep; 7050Sstevel@tonic-gate const char *s; 7060Sstevel@tonic-gate int i; 7070Sstevel@tonic-gate 7080Sstevel@tonic-gate valuep->t = STRING; 7090Sstevel@tonic-gate valuep->v = 0; 7100Sstevel@tonic-gate 7110Sstevel@tonic-gate if (np->u.func.s == L_fru) 7120Sstevel@tonic-gate nodep = eval_fru(np->u.func.arglist); 7130Sstevel@tonic-gate else if (np->u.func.s == L_asru) 7140Sstevel@tonic-gate nodep = eval_asru(np->u.func.arglist); 7150Sstevel@tonic-gate 7160Sstevel@tonic-gate cp = name2cp(nodep, croot); 7170Sstevel@tonic-gate if (cp == NULL) 7180Sstevel@tonic-gate return (1); 7190Sstevel@tonic-gate 7200Sstevel@tonic-gate for (i = 0; typestrings[i] != NULL; i++) { 7210Sstevel@tonic-gate s = config_getprop(cp, stable(typestrings[i])); 7220Sstevel@tonic-gate if (s != NULL) { 7230Sstevel@tonic-gate valuep->v = (unsigned long long)stable(s); 7240Sstevel@tonic-gate break; 7250Sstevel@tonic-gate } 7260Sstevel@tonic-gate } 7270Sstevel@tonic-gate 7280Sstevel@tonic-gate /* no entry for "type" */ 7290Sstevel@tonic-gate if (valuep->v == 0) 7300Sstevel@tonic-gate return (1); 7310Sstevel@tonic-gate 7320Sstevel@tonic-gate return (0); 7330Sstevel@tonic-gate } 7340Sstevel@tonic-gate 7350Sstevel@tonic-gate int 7360Sstevel@tonic-gate config_is_on(struct node *np, struct config *croot, struct evalue *valuep) 7370Sstevel@tonic-gate { 7380Sstevel@tonic-gate const char *onstrings[] = { "on", "ON", NULL }; 7390Sstevel@tonic-gate const char *truestrings[] = { "yes", "YES", "y", "Y", 7400Sstevel@tonic-gate "true", "TRUE", "t", "T", 7410Sstevel@tonic-gate "1", NULL }; 7420Sstevel@tonic-gate struct config *cp; 7430Sstevel@tonic-gate struct node *nodep; 7440Sstevel@tonic-gate const char *s; 7450Sstevel@tonic-gate int i, j; 7460Sstevel@tonic-gate 7470Sstevel@tonic-gate valuep->t = UINT64; 7480Sstevel@tonic-gate valuep->v = 0; 7490Sstevel@tonic-gate 7500Sstevel@tonic-gate if (np->u.func.s == L_fru) 7510Sstevel@tonic-gate nodep = eval_fru(np->u.func.arglist); 7520Sstevel@tonic-gate else if (np->u.func.s == L_asru) 7530Sstevel@tonic-gate nodep = eval_asru(np->u.func.arglist); 7540Sstevel@tonic-gate 7550Sstevel@tonic-gate cp = name2cp(nodep, croot); 7560Sstevel@tonic-gate if (cp == NULL) 7570Sstevel@tonic-gate return (1); 7580Sstevel@tonic-gate 7590Sstevel@tonic-gate for (i = 0; onstrings[i] != NULL; i++) { 7600Sstevel@tonic-gate s = config_getprop(cp, stable(onstrings[i])); 7610Sstevel@tonic-gate if (s != NULL) { 7620Sstevel@tonic-gate s = stable(s); 7630Sstevel@tonic-gate for (j = 0; truestrings[j] != NULL; j++) { 7640Sstevel@tonic-gate if (s == stable(truestrings[j])) { 7650Sstevel@tonic-gate valuep->v = 1; 7660Sstevel@tonic-gate return (0); 7670Sstevel@tonic-gate } 7680Sstevel@tonic-gate } 7690Sstevel@tonic-gate } 7700Sstevel@tonic-gate } 7710Sstevel@tonic-gate 7720Sstevel@tonic-gate return (0); 7730Sstevel@tonic-gate } 7740Sstevel@tonic-gate 7750Sstevel@tonic-gate int 7760Sstevel@tonic-gate config_is_present(struct node *np, struct config *croot, struct evalue *valuep) 7770Sstevel@tonic-gate { 7780Sstevel@tonic-gate struct config *cp; 7790Sstevel@tonic-gate struct node *nodep; 7800Sstevel@tonic-gate 7810Sstevel@tonic-gate valuep->t = UINT64; 7820Sstevel@tonic-gate valuep->v = 0; 7830Sstevel@tonic-gate 7840Sstevel@tonic-gate if (np->u.func.s == L_fru) 7850Sstevel@tonic-gate nodep = eval_fru(np->u.func.arglist); 7860Sstevel@tonic-gate else if (np->u.func.s == L_asru) 7870Sstevel@tonic-gate nodep = eval_asru(np->u.func.arglist); 7880Sstevel@tonic-gate 7890Sstevel@tonic-gate cp = name2cp(nodep, croot); 7900Sstevel@tonic-gate if (cp != NULL) 7910Sstevel@tonic-gate valuep->v = 1; 7920Sstevel@tonic-gate 7930Sstevel@tonic-gate return (0); 7940Sstevel@tonic-gate } 7950Sstevel@tonic-gate 7960Sstevel@tonic-gate /* 7970Sstevel@tonic-gate * printprop -- print prop associated with config node 7980Sstevel@tonic-gate */ 7990Sstevel@tonic-gate static void 8000Sstevel@tonic-gate printprop(const char *lhs, const char *rhs, void *arg) 8010Sstevel@tonic-gate { 8020Sstevel@tonic-gate int flags = (int)arg; 8030Sstevel@tonic-gate 8040Sstevel@tonic-gate out(flags, "\t%s=%s", lhs, rhs); 8050Sstevel@tonic-gate } 8060Sstevel@tonic-gate 8070Sstevel@tonic-gate /* 8080Sstevel@tonic-gate * pconf -- internal printing function to recurse through the tree 8090Sstevel@tonic-gate */ 8100Sstevel@tonic-gate static void 8110Sstevel@tonic-gate pconf(int flags, struct config *cp, char *buf, int offset, int limit) 8120Sstevel@tonic-gate { 8130Sstevel@tonic-gate char *sep = "/"; 8140Sstevel@tonic-gate 8150Sstevel@tonic-gate if (offset) 8160Sstevel@tonic-gate sep = "/"; 8170Sstevel@tonic-gate else 8180Sstevel@tonic-gate sep = ""; 8190Sstevel@tonic-gate (void) snprintf(&buf[offset], limit - offset, "%s%s%d", 8200Sstevel@tonic-gate sep, cp->s, cp->num); 8210Sstevel@tonic-gate if (cp->child == NULL) { 8220Sstevel@tonic-gate out(flags, "%s", buf); 8230Sstevel@tonic-gate lut_walk(cp->props, (lut_cb)printprop, (void *)flags); 8240Sstevel@tonic-gate } else 8250Sstevel@tonic-gate pconf(flags, cp->child, buf, strlen(buf), limit); 8260Sstevel@tonic-gate if (cp->next) 8270Sstevel@tonic-gate pconf(flags, cp->next, buf, offset, limit); 8280Sstevel@tonic-gate } 8290Sstevel@tonic-gate 8300Sstevel@tonic-gate /* 8310Sstevel@tonic-gate * config_print -- spew the current configuration cache 8320Sstevel@tonic-gate */ 8330Sstevel@tonic-gate 8340Sstevel@tonic-gate #define MAXCONFLINE 4096 8350Sstevel@tonic-gate 8360Sstevel@tonic-gate void 8370Sstevel@tonic-gate config_print(int flags, struct config *croot) 8380Sstevel@tonic-gate { 8390Sstevel@tonic-gate char buf[MAXCONFLINE]; 8400Sstevel@tonic-gate 8410Sstevel@tonic-gate if (croot == NULL) 8420Sstevel@tonic-gate out(flags, "empty configuration"); 8430Sstevel@tonic-gate else 8440Sstevel@tonic-gate pconf(flags, croot->child, buf, 0, MAXCONFLINE); 8450Sstevel@tonic-gate } 846