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