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