xref: /titanic_50/usr/src/cmd/fm/modules/common/eversholt/config.c (revision 36fe4a92b52649b0979d6a13212f4cea730d19c7)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  * config.c -- system configuration cache module
27  *
28  * this module caches the system configuration in a format useful
29  * to eft.  the information is loaded into this module by
30  * config_snapshot() at the beginning of each FME.  config_snapshot()
31  * calls the platform-specific platform_config_snapshot() to get
32  * the configuration information loaded up.
33  */
34 
35 #pragma ident	"%Z%%M%	%I%	%E% SMI"
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <ctype.h>
40 #include <string.h>
41 #include <strings.h>
42 #include <fm/libtopo_enum.h>
43 #include "alloc.h"
44 #include "out.h"
45 #include "literals.h"
46 #include "stable.h"
47 #include "lut.h"
48 #include "tree.h"
49 #include "itree.h"
50 #include "ipath.h"
51 #include "ptree.h"
52 #include "eval.h"
53 #include "config.h"
54 #include "fme.h"
55 #include "platform.h"
56 
57 /*
58  * private data structure for storing config.  all access to
59  * to this information happens using the config.h interfaces.
60  */
61 struct config {
62 	struct config *next;
63 	struct config *child;
64 	struct config *parent;
65 	const char *s;
66 	int num;
67 	struct lut *props;
68 };
69 
70 /*
71  * newcnode -- local function to allocate new config node
72  */
73 static struct config *
74 newcnode(const char *s, int num)
75 {
76 	struct config *retval;
77 
78 	retval = MALLOC(sizeof (struct config));
79 
80 	retval->s = s;
81 	retval->num = num;
82 	retval->next = NULL;
83 	retval->props = NULL;
84 	retval->child = retval->parent = NULL;
85 
86 	return (retval);
87 }
88 
89 /*
90  * If we need to cache certain types of nodes for reverse look-up or
91  * somesuch, do it here.  Currently we need to cache nodes representing
92  * cpus.
93  */
94 static void
95 config_node_cache(struct cfgdata *cdata, struct config *n)
96 {
97 	if (n->s != stable("cpu"))
98 		return;
99 	cdata->cpucache = lut_add(cdata->cpucache,
100 	    (void *)n->num, (void *)n, NULL);
101 }
102 
103 /*
104  * config_lookup -- lookup/add components in configuration cache
105  */
106 struct config *
107 config_lookup(struct config *croot, char *path, int add)
108 {
109 	char *pathbegin = path;
110 	struct config *parent = croot;
111 	struct config *cp;
112 	struct config *lastcp;
113 	struct config *newnode;
114 	char *thiscom;	/* this component */
115 	char *nextcom;	/* next component */
116 	char svdigit;
117 	int len;
118 	int num;
119 	const char *s;
120 	int exists;
121 
122 	if (parent == NULL)
123 		out(O_DIE, "uninitialized configuration");
124 
125 	while (*path) {
126 		if ((nextcom = strchr(path, '/')) != NULL)
127 			*nextcom = '\0';
128 		if ((len = strlen(path)) == 0)
129 			out(O_DIE, "config_lookup: zero length component");
130 		/* start at end of string and work backwards */
131 		thiscom = &path[len - 1];
132 		if (!isdigit(*thiscom))
133 			out(O_DIE, "config_lookup: "
134 			    "component \"%s\" has no number following it",
135 			    path);
136 		while (thiscom > path && isdigit(*thiscom))
137 			thiscom--;
138 		if (thiscom == path && isdigit(*thiscom))
139 			out(O_DIE, "config_lookup: "
140 			    "component \"%s\" has no name part", path);
141 		thiscom++;	/* move to first numeric character */
142 		num = atoi(thiscom);
143 		svdigit = *thiscom;
144 		*thiscom = '\0';
145 		s = stable(path);
146 		*thiscom = svdigit;
147 
148 		if (nextcom != NULL)
149 			*nextcom++ = '/';
150 
151 		/* now we have s & num, figure out if it exists already */
152 		exists = 0;
153 		lastcp = NULL;
154 		for (cp = parent->child; cp; lastcp = cp, cp = cp->next)
155 			if (cp->s == s && cp->num == num) {
156 				exists = 1;
157 				parent = cp;
158 			}
159 
160 		if (!exists) {
161 			/* creating new node */
162 			if (!add) {
163 				/*
164 				 * indicate component not found by copying
165 				 * it to path (allows better error messages
166 				 * in the caller).
167 				 */
168 				(void) strcpy(pathbegin, s);
169 				return (NULL);
170 			}
171 
172 			newnode = newcnode(s, num);
173 
174 			if (lastcp)
175 				lastcp->next = newnode;
176 			else
177 				parent->child = newnode;
178 
179 			newnode->parent = parent;
180 			parent = newnode;
181 		}
182 
183 		if (nextcom == NULL)
184 			return (parent);	/* all done */
185 
186 		/* move on to next component */
187 		path = nextcom;
188 	}
189 	return (parent);
190 }
191 
192 /*
193  * addconfigprop -- add a config prop to a config cache entry
194  */
195 static void
196 addconfigprop(const char *lhs, struct node *rhs, void *arg)
197 {
198 	struct config *cp = (struct config *)arg;
199 
200 	ASSERT(cp != NULL);
201 	ASSERT(lhs != NULL);
202 	ASSERT(rhs != NULL);
203 	ASSERT(rhs->t == T_QUOTE);
204 
205 	config_setprop(cp, lhs, STRDUP(rhs->u.quote.s));
206 }
207 
208 /*
209  * addconfig -- add a config from parse tree to given configuration cache
210  */
211 /*ARGSUSED*/
212 static void
213 addconfig(struct node *lhs, struct node *rhs, void *arg)
214 {
215 	struct config *parent = (struct config *)arg;
216 	struct config *cp;
217 	const char *s;
218 	int num;
219 	struct config *lastcp;
220 	struct config *newnode;
221 	int exists;
222 	struct lut *lutp;
223 
224 	ASSERT(rhs->t == T_CONFIG);
225 
226 	lutp = rhs->u.stmt.lutp;
227 	rhs = rhs->u.stmt.np;
228 	while (rhs != NULL) {
229 		ASSERT(rhs->t == T_NAME);
230 		ASSERT(rhs->u.name.child->t == T_NUM);
231 		s = rhs->u.name.s;
232 		num = rhs->u.name.child->u.ull;
233 
234 		/* now we have s & num, figure out if it exists already */
235 		exists = 0;
236 		lastcp = NULL;
237 		for (cp = parent->child; cp; lastcp = cp, cp = cp->next)
238 			if (cp->s == s && cp->num == num) {
239 				exists = 1;
240 				parent = cp;
241 			}
242 
243 		if (!exists) {
244 			/* creating new node */
245 
246 			newnode = newcnode(s, num);
247 
248 			if (lastcp)
249 				lastcp->next = newnode;
250 			else
251 				parent->child = newnode;
252 
253 			parent = newnode;
254 		}
255 
256 		/* move on to next component */
257 		rhs = rhs->u.name.next;
258 	}
259 
260 	/* add configuration properties */
261 	lut_walk(lutp, (lut_cb)addconfigprop, (void *)parent);
262 }
263 
264 /*
265  * config_cook -- convert raw config strings to eft internal representation
266  */
267 void
268 config_cook(struct cfgdata *cdata)
269 {
270 	struct config *newnode;
271 	char *cfgstr, *equals;
272 	const char *pn, *sv;
273 	char *pv;
274 
275 	if (cdata->cooked != NULL)
276 		return;
277 
278 	cdata->cooked = newcnode(NULL, 0);
279 
280 	if ((cfgstr = cdata->begin) == cdata->nextfree) {
281 		out(O_ALTFP|O_VERB, "Platform provided no config data.");
282 		goto eftcfgs;
283 	}
284 
285 	out(O_ALTFP|O_VERB3, "Raw config data follows:");
286 	out(O_ALTFP|O_VERB3|O_NONL,
287 	    "nextfree is %p\n%p ", (void *)cdata->nextfree, (void *)cfgstr);
288 	while (cfgstr < cdata->nextfree) {
289 		if (!*cfgstr)
290 			out(O_ALTFP|O_VERB3|O_NONL, "\n%p ",
291 			    (void *)(cfgstr + 1));
292 		else
293 			out(O_ALTFP|O_VERB3|O_NONL, "%c", *cfgstr);
294 		cfgstr++;
295 	}
296 	out(O_ALTFP|O_VERB3, NULL);
297 
298 	cfgstr = cdata->begin;
299 	while (cfgstr < cdata->nextfree) {
300 		while (*cfgstr == '/' && cfgstr < cdata->nextfree) {
301 			out(O_ALTFP|O_VERB3,
302 			    "next string (%p) is %s", (void *)cfgstr, cfgstr);
303 			/* skip the initial slash from libtopo */
304 			newnode = config_lookup(cdata->cooked, cfgstr + 1, 1);
305 			/*
306 			 * Note we'll only cache nodes that have
307 			 * properties on them.  Intermediate nodes
308 			 * will have been added to the config tree,
309 			 * but we don't have easy means of accessing
310 			 * them except if we climb the tree from this
311 			 * newnode to the root.
312 			 *
313 			 * Luckily, the nodes we care to cache
314 			 * (currently just cpus) always have some
315 			 * properties attached to them
316 			 * so we don't bother climbing the tree.
317 			 */
318 			config_node_cache(cdata, newnode);
319 			cfgstr += strlen(cfgstr) + 1;
320 		}
321 
322 		if (cfgstr >= cdata->nextfree)
323 			break;
324 
325 		out(O_ALTFP|O_VERB3, "next string (%p) is %s", (void *)cfgstr,
326 		    cfgstr);
327 		if ((equals = strchr(cfgstr, '=')) == NULL) {
328 			out(O_ALTFP|O_VERB3, "raw config data bad (%p); "
329 			    "property missing equals.\n", (void *)cfgstr);
330 			break;
331 		}
332 
333 		*equals = '\0';
334 		pn = stable(cfgstr);
335 		pv = STRDUP(equals + 1);
336 
337 		out(O_ALTFP|O_VERB3, "add prop (%s) val %p", pn, (void *)pv);
338 		config_setprop(newnode, pn, pv);
339 
340 		/*
341 		 * If this property is a device path, cache it for quick lookup
342 		 */
343 		if (pn == stable(DEV)) {
344 			sv = stable(pv);
345 			out(O_ALTFP|O_VERB3, "caching %s\n", sv);
346 			cdata->devcache = lut_add(cdata->devcache,
347 			    (void *)sv, (void *)newnode, NULL);
348 		}
349 
350 		*equals = '=';
351 		cfgstr += strlen(cfgstr) + 1;
352 	}
353 
354 eftcfgs:
355 	/* now run through Configs table, adding to config cache */
356 	lut_walk(Configs, (lut_cb)addconfig, (void *)cdata->cooked);
357 }
358 
359 /*
360  * config_snapshot -- gather a snapshot of the current configuration
361  */
362 struct cfgdata *
363 config_snapshot(void)
364 {
365 	struct cfgdata *rawcfg;
366 
367 	rawcfg = platform_config_snapshot();
368 	config_cook(rawcfg);
369 	return (rawcfg);
370 }
371 
372 /*
373  * prop_destructor -- free a prop value
374  */
375 /*ARGSUSED*/
376 static void
377 prop_destructor(void *left, void *right, void *arg)
378 {
379 	FREE(right);
380 }
381 
382 /*
383  * structconfig_free -- free a struct config pointer and all its relatives
384  */
385 static void
386 structconfig_free(struct config *cp)
387 {
388 	if (cp == NULL)
389 		return;
390 
391 	structconfig_free(cp->child);
392 	structconfig_free(cp->next);
393 	lut_free(cp->props, prop_destructor, NULL);
394 	FREE(cp);
395 }
396 
397 /*
398  * config_free -- free a configuration snapshot
399  */
400 void
401 config_free(struct cfgdata *cp)
402 {
403 	if (cp == NULL)
404 		return;
405 
406 	if (--cp->refcnt > 0)
407 		return;
408 
409 	if (cp->cooked != NULL)
410 		structconfig_free(cp->cooked);
411 	if (cp->begin != NULL)
412 		FREE(cp->begin);
413 	if (cp->devcache != NULL)
414 		lut_free(cp->devcache, NULL, NULL);
415 	if (cp->cpucache != NULL)
416 		lut_free(cp->cpucache, NULL, NULL);
417 	FREE(cp);
418 }
419 
420 /*
421  * config_next -- get the "next" config node
422  */
423 struct config *
424 config_next(struct config *cp)
425 {
426 	ASSERT(cp != NULL);
427 
428 	return ((struct config *)((struct config *)cp)->next);
429 }
430 
431 
432 /*
433  * config_child -- get the "child" of a config node
434  */
435 struct config *
436 config_child(struct config *cp)
437 {
438 	ASSERT(cp != NULL);
439 
440 	return ((struct config *)((struct config *)cp)->child);
441 }
442 
443 /*
444  * config_setprop -- add a property to a config node
445  */
446 void
447 config_setprop(struct config *cp, const char *propname, const char *propvalue)
448 {
449 	const char *pn = stable(propname);
450 
451 	cp->props = lut_add(cp->props, (void *)pn, (void *)propvalue, NULL);
452 }
453 
454 /*
455  * config_getprop -- lookup a config property
456  */
457 const char *
458 config_getprop(struct config *cp, const char *propname)
459 {
460 	return (lut_lookup(cp->props, (void *) stable(propname), NULL));
461 }
462 
463 /*
464  * config_getcompname -- get the component name of a config node
465  */
466 void
467 config_getcompname(struct config *cp, char **name, int *inst)
468 {
469 	ASSERT(cp != NULL);
470 
471 	if (name != NULL)
472 		*name = (char *)cp->s;
473 	if (inst != NULL)
474 		*inst = cp->num;
475 }
476 
477 /*
478  * config_nodeize -- convert the config element represented by cp to struct
479  *		     node format
480  */
481 static struct node *
482 config_nodeize(struct config *cp)
483 {
484 	struct node *tmpn, *ptmpn;
485 	struct node *numn;
486 	const char *sname;
487 
488 	if (cp == NULL || cp->s == NULL)
489 		return (NULL);
490 
491 	sname = stable(cp->s);
492 	numn = newnode(T_NUM, NULL, 0);
493 	numn->u.ull = cp->num;
494 
495 	tmpn = tree_name_iterator(tree_name(sname, IT_VERTICAL, NULL, 0), numn);
496 	if ((ptmpn = config_nodeize(cp->parent)) == NULL)
497 		return (tmpn);
498 	return (tree_name_append(ptmpn, tmpn));
499 }
500 
501 /*ARGSUSED*/
502 static void
503 prtdevcache(void *lhs, void *rhs, void *arg)
504 {
505 	out(O_ALTFP|O_VERB3, "%s -> %p", (char *)lhs, rhs);
506 }
507 
508 /*ARGSUSED*/
509 static void
510 prtcpucache(void *lhs, void *rhs, void *arg)
511 {
512 	out(O_ALTFP|O_VERB, "%u -> %p", (uint32_t)lhs, rhs);
513 }
514 
515 /*
516  * config_bydev_lookup -- look up the path in our DEVcache lut.  If we find
517  * it return the config path, but as a struct node.
518  */
519 struct node *
520 config_bydev_lookup(struct cfgdata *fromcfg, const char *path)
521 {
522 	struct config *find;
523 	struct node *np;
524 
525 	out(O_ALTFP|O_VERB3, "Device path cache:");
526 	lut_walk(fromcfg->devcache, (lut_cb)prtdevcache, NULL);
527 
528 	if ((find = lut_lookup(fromcfg->devcache,
529 	    (void *) stable(path), NULL)) == NULL)
530 		return (NULL);
531 
532 	np = config_nodeize(find);
533 	if (np != NULL) {
534 		out(O_ALTFP|O_VERB, "Matching config entry:");
535 		ptree_name_iter(O_ALTFP|O_VERB|O_NONL, np);
536 		out(O_ALTFP|O_VERB, NULL);
537 	}
538 	return (np);
539 }
540 
541 /*
542  * config_bycpuid_lookup -- look up the cpu id in our CPUcache lut.
543  * If we find it return the config path, but as a struct node.
544  */
545 struct node *
546 config_bycpuid_lookup(struct cfgdata *fromcfg, uint32_t id)
547 {
548 	struct config *find;
549 	struct node *np;
550 
551 	out(O_ALTFP|O_VERB, "Cpu cache:");
552 	lut_walk(fromcfg->cpucache, (lut_cb)prtcpucache, NULL);
553 
554 	if ((find = lut_lookup(fromcfg->cpucache,
555 	    (void *)id, NULL)) == NULL)
556 		return (NULL);
557 
558 	np = config_nodeize(find);
559 	if (np != NULL) {
560 		out(O_ALTFP|O_VERB3, "Matching config entry:");
561 		ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, np);
562 		out(O_ALTFP|O_VERB3, NULL);
563 	}
564 	return (np);
565 }
566 
567 /*
568  * given the following:
569  *   - np of type T_NAME which denotes a pathname
570  *   - croot, the root node of a configuration
571  *
572  * return the cp for the last component in np's path
573  */
574 static struct config *
575 name2cp(struct node *np, struct config *croot)
576 {
577 	char *path;
578 	struct config *cp;
579 
580 	if (np->u.name.last->u.name.cp != NULL)
581 		return (np->u.name.last->u.name.cp);
582 
583 	path = ipath2str(NULL, ipath(np));
584 
585 	cp = config_lookup(croot, path, 0);
586 	FREE((void *)path);
587 
588 	return (cp);
589 }
590 
591 int
592 config_confprop(struct node *np, struct config *croot, struct evalue *valuep)
593 {
594 	struct node *nodep;
595 	struct config *cp;
596 	const char *s;
597 
598 	if (np->u.expr.left->u.func.s == L_fru)
599 		nodep = eval_fru(np->u.expr.left->u.func.arglist);
600 	else if (np->u.expr.left->u.func.s == L_asru)
601 		nodep = eval_asru(np->u.expr.left->u.func.arglist);
602 
603 	cp = name2cp(nodep, croot);
604 	if (cp == NULL)
605 		return (1);
606 
607 	/* for now s will point to a quote [see addconfigprop()] */
608 	ASSERT(np->u.expr.right->t == T_QUOTE);
609 
610 	s = config_getprop(cp, np->u.expr.right->u.quote.s);
611 	if (s == NULL)
612 		return (1);
613 
614 	valuep->t = STRING;
615 	valuep->v = (unsigned long long)stable(s);
616 
617 	return (0);
618 }
619 
620 #define	CONNECTED_SEPCHARS " ,"
621 
622 int
623 config_is_connected(struct node *np, struct config *croot,
624 		    struct evalue *valuep)
625 {
626 	const char *connstrings[] = { "connected", "CONNECTED", NULL };
627 	struct config *cp[2], *compcp;
628 	struct node *nptr[2];
629 	const char *searchforname, *matchthis[2], *s;
630 	char *nameslist, *w;
631 	int i, j;
632 
633 	valuep->t = UINT64;
634 	valuep->v = 0;
635 
636 	if (np->u.expr.left->t == T_NAME)
637 		nptr[0] = np->u.expr.left;
638 	else if (np->u.expr.left->u.func.s == L_fru)
639 		nptr[0] = eval_fru(np->u.expr.left->u.func.arglist);
640 	else if (np->u.expr.left->u.func.s == L_asru)
641 		nptr[0] = eval_asru(np->u.expr.left->u.func.arglist);
642 
643 	if (np->u.expr.right->t == T_NAME)
644 		nptr[1] = np->u.expr.right;
645 	else if (np->u.expr.right->u.func.s == L_fru)
646 		nptr[1] = eval_fru(np->u.expr.right->u.func.arglist);
647 	else if (np->u.expr.right->u.func.s == L_asru)
648 		nptr[1] = eval_asru(np->u.expr.right->u.func.arglist);
649 
650 	for (i = 0; i < 2; i++) {
651 		cp[i] = name2cp(nptr[i], croot);
652 		if (cp[i] == NULL)
653 			return (1);
654 	}
655 
656 	/* to thine self always be connected */
657 	if (cp[0] == cp[1]) {
658 		valuep->v = 1;
659 		return (0);
660 	}
661 
662 	/*
663 	 * set one of the cp[]s to compcp and extract its "connected"
664 	 * property.  search this property for the name associated with the
665 	 * other cp[].
666 	 */
667 	for (i = 0; i < 2 && valuep->v == 0; i++) {
668 		compcp = cp[i];
669 
670 		searchforname = ipath2str(NULL, ipath(nptr[(i == 0 ? 1 : 0)]));
671 		matchthis[i] = stable(searchforname);
672 		FREE((void *)searchforname);
673 
674 		for (j = 0; connstrings[j] != NULL && valuep->v == 0; j++) {
675 			s = config_getprop(compcp, stable(connstrings[j]));
676 			if (s != NULL) {
677 				nameslist = STRDUP(s);
678 				w = strtok(nameslist, CONNECTED_SEPCHARS);
679 				while (w != NULL) {
680 					if (stable(w) == matchthis[i]) {
681 						valuep->v = 1;
682 						break;
683 					}
684 					w = strtok(NULL, CONNECTED_SEPCHARS);
685 				}
686 				FREE(nameslist);
687 			}
688 		}
689 	}
690 
691 	/* a path shouldn't have more than one cp node */
692 	if (valuep->v == 0)
693 		ASSERT(matchthis[0] != matchthis[1]);
694 
695 	return (0);
696 }
697 
698 int
699 config_is_type(struct node *np, struct config *croot, struct evalue *valuep)
700 {
701 	const char *typestrings[] = { "type", "TYPE", NULL };
702 	struct config *cp;
703 	struct node *nodep;
704 	const char *s;
705 	int i;
706 
707 	valuep->t = STRING;
708 	valuep->v = 0;
709 
710 	if (np->u.func.s == L_fru)
711 		nodep = eval_fru(np->u.func.arglist);
712 	else if (np->u.func.s == L_asru)
713 		nodep = eval_asru(np->u.func.arglist);
714 
715 	cp = name2cp(nodep, croot);
716 	if (cp == NULL)
717 		return (1);
718 
719 	for (i = 0; typestrings[i] != NULL; i++) {
720 		s = config_getprop(cp, stable(typestrings[i]));
721 		if (s != NULL) {
722 			valuep->v = (unsigned long long)stable(s);
723 			break;
724 		}
725 	}
726 
727 	/* no entry for "type" */
728 	if (valuep->v == 0)
729 		return (1);
730 
731 	return (0);
732 }
733 
734 int
735 config_is_on(struct node *np, struct config *croot, struct evalue *valuep)
736 {
737 	const char *onstrings[] = { "on", "ON", NULL };
738 	const char *truestrings[] = { "yes", "YES", "y", "Y",
739 				    "true", "TRUE", "t", "T",
740 				    "1", NULL };
741 	struct config *cp;
742 	struct node *nodep;
743 	const char *s;
744 	int i, j;
745 
746 	valuep->t = UINT64;
747 	valuep->v = 0;
748 
749 	if (np->u.func.s == L_fru)
750 		nodep = eval_fru(np->u.func.arglist);
751 	else if (np->u.func.s == L_asru)
752 		nodep = eval_asru(np->u.func.arglist);
753 
754 	cp = name2cp(nodep, croot);
755 	if (cp == NULL)
756 		return (1);
757 
758 	for (i = 0; onstrings[i] != NULL; i++) {
759 		s = config_getprop(cp, stable(onstrings[i]));
760 		if (s != NULL) {
761 			s = stable(s);
762 			for (j = 0; truestrings[j] != NULL; j++) {
763 				if (s == stable(truestrings[j])) {
764 					valuep->v = 1;
765 					return (0);
766 				}
767 			}
768 		}
769 	}
770 
771 	return (0);
772 }
773 
774 int
775 config_is_present(struct node *np, struct config *croot, struct evalue *valuep)
776 {
777 	struct config *cp;
778 	struct node *nodep;
779 
780 	valuep->t = UINT64;
781 	valuep->v = 0;
782 
783 	if (np->u.func.s == L_fru)
784 		nodep = eval_fru(np->u.func.arglist);
785 	else if (np->u.func.s == L_asru)
786 		nodep = eval_asru(np->u.func.arglist);
787 
788 	cp = name2cp(nodep, croot);
789 	if (cp != NULL)
790 		valuep->v = 1;
791 
792 	return (0);
793 }
794 
795 /*
796  * printprop -- print prop associated with config node
797  */
798 static void
799 printprop(const char *lhs, const char *rhs, void *arg)
800 {
801 	int flags = (int)arg;
802 
803 	out(flags, "\t%s=%s", lhs, rhs);
804 }
805 
806 /*
807  * pconf -- internal printing function to recurse through the tree
808  */
809 static void
810 pconf(int flags, struct config *cp, char *buf, int offset, int limit)
811 {
812 	char *sep = "/";
813 
814 	if (offset)
815 		sep = "/";
816 	else
817 		sep = "";
818 	(void) snprintf(&buf[offset], limit - offset, "%s%s%d",
819 	    sep, cp->s, cp->num);
820 	if (cp->child == NULL) {
821 		out(flags, "%s", buf);
822 		lut_walk(cp->props, (lut_cb)printprop, (void *)flags);
823 	} else
824 		pconf(flags, cp->child, buf, strlen(buf), limit);
825 	if (cp->next)
826 		pconf(flags, cp->next, buf, offset, limit);
827 }
828 
829 /*
830  * config_print -- spew the current configuration cache
831  */
832 
833 #define	MAXCONFLINE 4096
834 
835 void
836 config_print(int flags, struct config *croot)
837 {
838 	char buf[MAXCONFLINE];
839 
840 	if (croot == NULL)
841 		out(flags, "empty configuration");
842 	else
843 		pconf(flags, croot->child, buf, 0, MAXCONFLINE);
844 }
845