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 2008 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 <fm/topo_hc.h>
45 #include "alloc.h"
46 #include "out.h"
47 #include "literals.h"
48 #include "stable.h"
49 #include "lut.h"
50 #include "tree.h"
51 #include "itree.h"
52 #include "ipath.h"
53 #include "ptree.h"
54 #include "eval.h"
55 #include "config.h"
56 #include "config_impl.h"
57 #include "fme.h"
58 #include "platform.h"
59 
60 static const char *config_lastcomp;
61 
62 /*
63  * newcnode -- local function to allocate new config node
64  */
65 static struct config *
66 newcnode(const char *s, int num)
67 {
68 	struct config *retval;
69 
70 	retval = MALLOC(sizeof (struct config));
71 
72 	retval->s = s;
73 	retval->num = num;
74 	retval->next = NULL;
75 	retval->props = NULL;
76 	retval->child = retval->parent = NULL;
77 
78 	return (retval);
79 }
80 
81 /*
82  * If we need to cache certain types of nodes for reverse look-up or
83  * somesuch, do it here.  Currently we need to cache nodes representing
84  * cpus.
85  */
86 static void
87 config_node_cache(struct cfgdata *cdata, struct config *n)
88 {
89 	if (n->s != stable("cpu"))
90 		return;
91 	cdata->cpucache = lut_add(cdata->cpucache,
92 	    (void *)n->num, (void *)n, NULL);
93 }
94 
95 /*
96  * config_lookup -- lookup/add components in configuration cache
97  */
98 struct config *
99 config_lookup(struct config *croot, char *path, int add)
100 {
101 	char *pathbegin = path;
102 	struct config *parent = croot;
103 	struct config *cp;
104 	struct config *lastcp;
105 	struct config *newnode;
106 	char *thiscom;	/* this component */
107 	char *nextcom;	/* next component */
108 	char svdigit;
109 	int len;
110 	int num;
111 	const char *s;
112 	int exists;
113 
114 	if (parent == NULL)
115 		out(O_DIE, "uninitialized configuration");
116 
117 	while (*path) {
118 		if ((nextcom = strchr(path, '/')) != NULL)
119 			*nextcom = '\0';
120 		if ((len = strlen(path)) == 0)
121 			out(O_DIE, "config_lookup: zero length component");
122 		/* start at end of string and work backwards */
123 		thiscom = &path[len - 1];
124 		if (!isdigit(*thiscom))
125 			out(O_DIE, "config_lookup: "
126 			    "component \"%s\" has no number following it",
127 			    path);
128 		while (thiscom > path && isdigit(*thiscom))
129 			thiscom--;
130 		if (thiscom == path && isdigit(*thiscom))
131 			out(O_DIE, "config_lookup: "
132 			    "component \"%s\" has no name part", path);
133 		thiscom++;	/* move to first numeric character */
134 		num = atoi(thiscom);
135 		svdigit = *thiscom;
136 		*thiscom = '\0';
137 		s = stable(path);
138 		if (add)
139 			config_lastcomp = s;
140 		*thiscom = svdigit;
141 
142 		if (nextcom != NULL)
143 			*nextcom++ = '/';
144 
145 		/* now we have s & num, figure out if it exists already */
146 		exists = 0;
147 		lastcp = NULL;
148 		for (cp = parent->child; cp; lastcp = cp, cp = cp->next)
149 			if (cp->s == s && cp->num == num) {
150 				exists = 1;
151 				parent = cp;
152 			}
153 
154 		if (!exists) {
155 			/* creating new node */
156 			if (!add) {
157 				/*
158 				 * indicate component not found by copying
159 				 * it to path (allows better error messages
160 				 * in the caller).
161 				 */
162 				(void) strcpy(pathbegin, s);
163 				return (NULL);
164 			}
165 
166 			newnode = newcnode(s, num);
167 
168 			if (lastcp)
169 				lastcp->next = newnode;
170 			else
171 				parent->child = newnode;
172 
173 			newnode->parent = parent;
174 			parent = newnode;
175 		}
176 
177 		if (nextcom == NULL)
178 			return (parent);	/* all done */
179 
180 		/* move on to next component */
181 		path = nextcom;
182 	}
183 	return (parent);
184 }
185 
186 /*
187  * addconfigprop -- add a config prop to a config cache entry
188  */
189 static void
190 addconfigprop(const char *lhs, struct node *rhs, void *arg)
191 {
192 	struct config *cp = (struct config *)arg;
193 
194 	ASSERT(cp != NULL);
195 	ASSERT(lhs != NULL);
196 	ASSERT(rhs != NULL);
197 	ASSERT(rhs->t == T_QUOTE);
198 
199 	config_setprop(cp, lhs, STRDUP(rhs->u.quote.s));
200 }
201 
202 /*
203  * addconfig -- add a config from parse tree to given configuration cache
204  */
205 /*ARGSUSED*/
206 static void
207 addconfig(struct node *lhs, struct node *rhs, void *arg)
208 {
209 	struct config *parent = (struct config *)arg;
210 	struct config *cp;
211 	const char *s;
212 	int num;
213 	struct config *lastcp;
214 	struct config *newnode;
215 	int exists;
216 	struct lut *lutp;
217 
218 	ASSERT(rhs->t == T_CONFIG);
219 
220 	lutp = rhs->u.stmt.lutp;
221 	rhs = rhs->u.stmt.np;
222 	while (rhs != NULL) {
223 		ASSERT(rhs->t == T_NAME);
224 		ASSERT(rhs->u.name.child->t == T_NUM);
225 		s = rhs->u.name.s;
226 		num = rhs->u.name.child->u.ull;
227 
228 		/* now we have s & num, figure out if it exists already */
229 		exists = 0;
230 		lastcp = NULL;
231 		for (cp = parent->child; cp; lastcp = cp, cp = cp->next)
232 			if (cp->s == s && cp->num == num) {
233 				exists = 1;
234 				parent = cp;
235 			}
236 
237 		if (!exists) {
238 			/* creating new node */
239 
240 			newnode = newcnode(s, num);
241 
242 			if (lastcp)
243 				lastcp->next = newnode;
244 			else
245 				parent->child = newnode;
246 
247 			newnode->parent = parent;
248 			parent = newnode;
249 		}
250 
251 		/* move on to next component */
252 		rhs = rhs->u.name.next;
253 	}
254 
255 	/* add configuration properties */
256 	lut_walk(lutp, (lut_cb)addconfigprop, (void *)parent);
257 }
258 
259 /*
260  * config_cook -- convert raw config strings to eft internal representation
261  */
262 void
263 config_cook(struct cfgdata *cdata)
264 {
265 	struct config *newnode;
266 	char *cfgstr, *equals;
267 	const char *pn, *sv;
268 	char *pv;
269 	const char *ptr;
270 	extern struct lut *Usedprops;
271 	extern struct lut *Usednames;
272 
273 	cdata->cooked = newcnode(NULL, 0);
274 
275 	if ((cfgstr = cdata->begin) == cdata->nextfree) {
276 		out(O_ALTFP|O_VERB, "Platform provided no config data.");
277 		goto eftcfgs;
278 	}
279 
280 	/*
281 	 * add the following properties to the "usedprops" table as they
282 	 * are used internally by eft
283 	 */
284 	ptr = stable("module");
285 	Usedprops = lut_add(Usedprops, (void *)ptr, (void *)ptr, NULL);
286 	ptr = stable("resource");
287 	Usedprops = lut_add(Usedprops, (void *)ptr, (void *)ptr, NULL);
288 	ptr = stable("ASRU");
289 	Usedprops = lut_add(Usedprops, (void *)ptr, (void *)ptr, NULL);
290 	ptr = stable("FRU");
291 	Usedprops = lut_add(Usedprops, (void *)ptr, (void *)ptr, NULL);
292 
293 	out(O_ALTFP|O_VERB3, "Raw config data follows:");
294 	out(O_ALTFP|O_VERB3|O_NONL,
295 	    "nextfree is %p\n%p ", (void *)cdata->nextfree, (void *)cfgstr);
296 	while (cfgstr < cdata->nextfree) {
297 		if (!*cfgstr)
298 			out(O_ALTFP|O_VERB3|O_NONL, "\n%p ",
299 			    (void *)(cfgstr + 1));
300 		else
301 			out(O_ALTFP|O_VERB3|O_NONL, "%c", *cfgstr);
302 		cfgstr++;
303 	}
304 	out(O_ALTFP|O_VERB3, NULL);
305 
306 	cfgstr = cdata->begin;
307 	while (cfgstr < cdata->nextfree) {
308 		while (*cfgstr == '/' && cfgstr < cdata->nextfree) {
309 			out(O_ALTFP|O_VERB3,
310 			    "next string (%p) is %s", (void *)cfgstr, cfgstr);
311 			/* skip the initial slash from libtopo */
312 			newnode = config_lookup(cdata->cooked, cfgstr + 1, 1);
313 			/*
314 			 * Note we'll only cache nodes that have
315 			 * properties on them.  Intermediate nodes
316 			 * will have been added to the config tree,
317 			 * but we don't have easy means of accessing
318 			 * them except if we climb the tree from this
319 			 * newnode to the root.
320 			 *
321 			 * Luckily, the nodes we care to cache
322 			 * (currently just cpus) always have some
323 			 * properties attached to them
324 			 * so we don't bother climbing the tree.
325 			 */
326 			config_node_cache(cdata, newnode);
327 			cfgstr += strlen(cfgstr) + 1;
328 		}
329 
330 		if (cfgstr >= cdata->nextfree)
331 			break;
332 
333 		out(O_ALTFP|O_VERB3, "next string (%p) is %s", (void *)cfgstr,
334 		    cfgstr);
335 		if ((equals = strchr(cfgstr, '=')) == NULL) {
336 			out(O_ALTFP|O_VERB3, "raw config data bad (%p); "
337 			    "property missing equals.\n", (void *)cfgstr);
338 			break;
339 		}
340 
341 		*equals = '\0';
342 		pn = stable(cfgstr);
343 
344 		/*
345 		 * only actually add the props if the rules use them (saves
346 		 * memory)
347 		 */
348 		if ((lut_lookup(Usedprops, (void *)pn, NULL) != NULL ||
349 		    strncmp(pn, "serd_", 5) == 0) && lut_lookup(Usednames,
350 		    (void *)config_lastcomp, NULL) != NULL) {
351 			pv = STRDUP(equals + 1);
352 			out(O_ALTFP|O_VERB3, "add prop (%s) val %p", pn,
353 			    (void *)pv);
354 			config_setprop(newnode, pn, pv);
355 		}
356 
357 		/*
358 		 * If this property is a device path or devid, cache it
359 		 * for quick lookup.
360 		 */
361 		if (pn == stable(TOPO_IO_DEV)) {
362 			sv = stable(equals + 1);
363 			out(O_ALTFP|O_VERB3, "caching dev %s\n", sv);
364 			cdata->devcache = lut_add(cdata->devcache,
365 			    (void *)sv, (void *)newnode, NULL);
366 		} else if (pn == stable(TOPO_IO_DEVID)) {
367 			sv = stable(equals + 1);
368 			out(O_ALTFP|O_VERB3, "caching devid %s\n", sv);
369 			cdata->devidcache = lut_add(cdata->devidcache,
370 			    (void *)sv, (void *)newnode, NULL);
371 		}
372 
373 		*equals = '=';
374 		cfgstr += strlen(cfgstr) + 1;
375 	}
376 
377 eftcfgs:
378 	/* now run through Configs table, adding to config cache */
379 	lut_walk(Configs, (lut_cb)addconfig, (void *)cdata->cooked);
380 }
381 
382 /*
383  * config_snapshot -- gather a snapshot of the current configuration
384  */
385 struct cfgdata *
386 config_snapshot(void)
387 {
388 	struct cfgdata *rawcfg;
389 
390 	rawcfg = platform_config_snapshot();
391 	config_cook(rawcfg);
392 	return (rawcfg);
393 }
394 
395 /*
396  * prop_destructor -- free a prop value
397  */
398 /*ARGSUSED*/
399 static void
400 prop_destructor(void *left, void *right, void *arg)
401 {
402 	FREE(right);
403 }
404 
405 /*
406  * structconfig_free -- free a struct config pointer and all its relatives
407  */
408 void
409 structconfig_free(struct config *cp)
410 {
411 	if (cp == NULL)
412 		return;
413 
414 	structconfig_free(cp->child);
415 	structconfig_free(cp->next);
416 	lut_free(cp->props, prop_destructor, NULL);
417 	FREE(cp);
418 }
419 
420 /*
421  * config_free -- free a configuration snapshot
422  */
423 void
424 config_free(struct cfgdata *cp)
425 {
426 	if (cp == NULL)
427 		return;
428 
429 	if (--cp->raw_refcnt == 0) {
430 		if (cp->devcache != NULL)
431 			lut_free(cp->devcache, NULL, NULL);
432 		cp->devcache = NULL;
433 		if (cp->devidcache != NULL)
434 			lut_free(cp->devidcache, NULL, NULL);
435 		cp->devidcache = NULL;
436 		if (cp->cpucache != NULL)
437 			lut_free(cp->cpucache, NULL, NULL);
438 		cp->cpucache = NULL;
439 		if (cp->begin != NULL)
440 			FREE(cp->begin);
441 		FREE(cp);
442 	}
443 }
444 
445 /*
446  * config_next -- get the "next" config node
447  */
448 struct config *
449 config_next(struct config *cp)
450 {
451 	ASSERT(cp != NULL);
452 
453 	return ((struct config *)((struct config *)cp)->next);
454 }
455 
456 
457 /*
458  * config_child -- get the "child" of a config node
459  */
460 struct config *
461 config_child(struct config *cp)
462 {
463 	ASSERT(cp != NULL);
464 
465 	return ((struct config *)((struct config *)cp)->child);
466 }
467 
468 /*
469  * config_parent -- get the "parent" of a config node
470  */
471 struct config *
472 config_parent(struct config *cp)
473 {
474 	ASSERT(cp != NULL);
475 
476 	return ((struct config *)((struct config *)cp)->parent);
477 }
478 
479 /*
480  * config_setprop -- add a property to a config node
481  */
482 void
483 config_setprop(struct config *cp, const char *propname, const char *propvalue)
484 {
485 	const char *pn = stable(propname);
486 
487 	cp->props = lut_add(cp->props, (void *)pn, (void *)propvalue, NULL);
488 }
489 
490 /*
491  * config_getprop -- lookup a config property
492  */
493 const char *
494 config_getprop(struct config *cp, const char *propname)
495 {
496 	return (lut_lookup(cp->props, (void *) stable(propname), NULL));
497 }
498 
499 /*
500  * config_getcompname -- get the component name of a config node
501  */
502 void
503 config_getcompname(struct config *cp, char **name, int *inst)
504 {
505 	ASSERT(cp != NULL);
506 
507 	if (name != NULL)
508 		*name = (char *)cp->s;
509 	if (inst != NULL)
510 		*inst = cp->num;
511 }
512 
513 /*
514  * config_nodeize -- convert the config element represented by cp to struct
515  *		     node format
516  */
517 static struct node *
518 config_nodeize(struct config *cp)
519 {
520 	struct node *tmpn, *ptmpn;
521 	struct node *numn;
522 	const char *sname;
523 
524 	if (cp == NULL || cp->s == NULL)
525 		return (NULL);
526 
527 	sname = stable(cp->s);
528 	numn = newnode(T_NUM, NULL, 0);
529 	numn->u.ull = cp->num;
530 
531 	tmpn = tree_name_iterator(tree_name(sname, IT_VERTICAL, NULL, 0), numn);
532 	if ((ptmpn = config_nodeize(cp->parent)) == NULL)
533 		return (tmpn);
534 	return (tree_name_append(ptmpn, tmpn));
535 }
536 
537 /*ARGSUSED*/
538 static void
539 prtdevcache(void *lhs, void *rhs, void *arg)
540 {
541 	out(O_ALTFP|O_VERB3, "%s -> %p", (char *)lhs, rhs);
542 }
543 
544 /*ARGSUSED*/
545 static void
546 prtdevidcache(void *lhs, void *rhs, void *arg)
547 {
548 	out(O_ALTFP|O_VERB3, "%s -> %p", (char *)lhs, rhs);
549 }
550 
551 /*ARGSUSED*/
552 static void
553 prtcpucache(void *lhs, void *rhs, void *arg)
554 {
555 	out(O_ALTFP|O_VERB, "%u -> %p", (uint32_t)lhs, rhs);
556 }
557 
558 /*
559  * config_bydev_lookup -- look up the path in our devcache lut.  If we find
560  * it return the config path, but as a struct node.
561  */
562 struct node *
563 config_bydev_lookup(struct cfgdata *fromcfg, const char *path)
564 {
565 	struct config *find;
566 	struct node *np;
567 
568 	out(O_ALTFP|O_VERB3, "Device path cache:");
569 	lut_walk(fromcfg->devcache, (lut_cb)prtdevcache, NULL);
570 
571 	if ((find = lut_lookup(fromcfg->devcache,
572 	    (void *) stable(path), NULL)) == NULL)
573 		return (NULL);
574 
575 	np = config_nodeize(find);
576 	if (np != NULL) {
577 		out(O_ALTFP|O_VERB, "Matching config entry:");
578 		ptree_name_iter(O_ALTFP|O_VERB|O_NONL, np);
579 		out(O_ALTFP|O_VERB, NULL);
580 	}
581 	return (np);
582 }
583 
584 /*
585  * config_bydevid_lookup -- look up the path in our DEVIDcache lut.
586  * If we find it return the config path, but as a struct node.
587  */
588 struct node *
589 config_bydevid_lookup(struct cfgdata *fromcfg, const char *devid)
590 {
591 	struct config *find;
592 	struct node *np;
593 
594 	out(O_ALTFP|O_VERB3, "Device id cache:");
595 	lut_walk(fromcfg->devcache, (lut_cb)prtdevidcache, NULL);
596 
597 	if ((find = lut_lookup(fromcfg->devidcache,
598 	    (void *) stable(devid), NULL)) == NULL)
599 		return (NULL);
600 
601 	np = config_nodeize(find);
602 	if (np != NULL) {
603 		out(O_ALTFP|O_VERB, "Matching config entry:");
604 		ptree_name_iter(O_ALTFP|O_VERB|O_NONL, np);
605 		out(O_ALTFP|O_VERB, NULL);
606 	}
607 	return (np);
608 }
609 
610 /*
611  * config_bycpuid_lookup -- look up the cpu id in our CPUcache lut.
612  * If we find it return the config path, but as a struct node.
613  */
614 struct node *
615 config_bycpuid_lookup(struct cfgdata *fromcfg, uint32_t id)
616 {
617 	struct config *find;
618 	struct node *np;
619 
620 	out(O_ALTFP|O_VERB, "Cpu cache:");
621 	lut_walk(fromcfg->cpucache, (lut_cb)prtcpucache, NULL);
622 
623 	if ((find = lut_lookup(fromcfg->cpucache,
624 	    (void *)id, NULL)) == NULL)
625 		return (NULL);
626 
627 	np = config_nodeize(find);
628 	if (np != NULL) {
629 		out(O_ALTFP|O_VERB3, "Matching config entry:");
630 		ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, np);
631 		out(O_ALTFP|O_VERB3, NULL);
632 	}
633 	return (np);
634 }
635 
636 /*
637  * printprop -- print prop associated with config node
638  */
639 static void
640 printprop(const char *lhs, const char *rhs, void *arg)
641 {
642 	int flags = (int)arg;
643 
644 	out(flags, "\t%s=%s", lhs, rhs);
645 }
646 
647 /*
648  * pconf -- internal printing function to recurse through the tree
649  */
650 static void
651 pconf(int flags, struct config *cp, char *buf, int offset, int limit)
652 {
653 	char *sep = "/";
654 
655 	if (offset)
656 		sep = "/";
657 	else
658 		sep = "";
659 	(void) snprintf(&buf[offset], limit - offset, "%s%s%d",
660 	    sep, cp->s, cp->num);
661 	if (cp->child == NULL) {
662 		out(flags, "%s", buf);
663 		lut_walk(cp->props, (lut_cb)printprop, (void *)flags);
664 	} else
665 		pconf(flags, cp->child, buf, strlen(buf), limit);
666 	if (cp->next)
667 		pconf(flags, cp->next, buf, offset, limit);
668 }
669 
670 /*
671  * config_print -- spew the current configuration cache
672  */
673 
674 #define	MAXCONFLINE 4096
675 
676 void
677 config_print(int flags, struct config *croot)
678 {
679 	char buf[MAXCONFLINE];
680 
681 	if (croot == NULL)
682 		out(flags, "empty configuration");
683 	else
684 		pconf(flags, croot->child, buf, 0, MAXCONFLINE);
685 }
686