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 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  * platform.c -- interfaces to the platform's configuration information
27  *
28  * this platform.c allows eft to run on Solaris systems.
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <strings.h>
37 #include <ctype.h>
38 #include <dirent.h>
39 #include <libnvpair.h>
40 #include <dlfcn.h>
41 #include <unistd.h>
42 #include <errno.h>
43 #include <stropts.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <sys/wait.h>
47 #include <sys/filio.h>
48 #include <sys/param.h>
49 #include <sys/fm/protocol.h>
50 #include <fm/fmd_api.h>
51 #include <fm/libtopo_enum.h>
52 #include "alloc.h"
53 #include "out.h"
54 #include "tree.h"
55 #include "itree.h"
56 #include "ipath.h"
57 #include "ptree.h"
58 #include "fme.h"
59 #include "stable.h"
60 #include "eval.h"
61 #include "config.h"
62 #include "platform.h"
63 
64 extern fmd_hdl_t *Hdl;		/* handle from eft.c */
65 
66 /*
67  * Lastcfg points to the last configuration snapshot we made.  If we
68  * need to make a dev to hc scheme conversion of an event path, we use
69  * the last snapshot as a best guess.  If we don't have a last snapshot
70  * we take one and save it in Initcfg below.
71  */
72 static struct cfgdata *Lastcfg;
73 
74 /*
75  * Initcfg points to any config snapshot we have to make prior
76  * to starting our first fme.
77  */
78 static struct cfgdata *Initcfg;
79 
80 void
81 topo_use_out(const char *obuf)
82 {
83 	out(O_ALTFP, "topo: %s", obuf);
84 }
85 
86 void *
87 topo_use_alloc(size_t bytes)
88 {
89 	void *p = alloc_malloc(bytes, NULL, 0);
90 
91 	bzero(p, bytes);
92 	return (p);
93 }
94 
95 void
96 topo_use_free(void *p)
97 {
98 	alloc_free(p, NULL, 0);
99 }
100 
101 /*ARGSUSED*/
102 static void *
103 alloc_nv_alloc(nv_alloc_t *nva, size_t size)
104 {
105 	return (alloc_malloc(size, NULL, 0));
106 }
107 
108 /*ARGSUSED*/
109 static void
110 alloc_nv_free(nv_alloc_t *nva, void *p, size_t sz)
111 {
112 	alloc_free(p, NULL, 0);
113 }
114 
115 const nv_alloc_ops_t Eft_nv_alloc_ops = {
116 	NULL,		/* nv_ao_init() */
117 	NULL,		/* nv_ao_fini() */
118 	alloc_nv_alloc,	/* nv_ao_alloc() */
119 	alloc_nv_free,	/* nv_ao_free() */
120 	NULL		/* nv_ao_reset() */
121 };
122 
123 nv_alloc_t Eft_nv_hdl;
124 
125 static char *Root;
126 static char *Mach;
127 static char *Plat;
128 
129 /*
130  * platform_globals -- set global variables based on sysinfo() calls
131  */
132 static void
133 platform_globals()
134 {
135 	Root = fmd_prop_get_string(Hdl, "fmd.rootdir");
136 	Mach = fmd_prop_get_string(Hdl, "fmd.machine");
137 	Plat = fmd_prop_get_string(Hdl, "fmd.platform");
138 }
139 
140 static void
141 platform_free_globals()
142 {
143 	fmd_prop_free_string(Hdl, Root);
144 	fmd_prop_free_string(Hdl, Mach);
145 	fmd_prop_free_string(Hdl, Plat);
146 }
147 
148 static void
149 platform_topo_paths(int *n, const char ***p)
150 {
151 	const char **cp;
152 	char *tmpbuf;
153 
154 	*n = 2;
155 	cp = *p = MALLOC(2 * sizeof (const char *));
156 
157 	tmpbuf = MALLOC(MAXPATHLEN);
158 	(void) snprintf(tmpbuf,
159 	    MAXPATHLEN, "%s/usr/lib/fm/topo/%s", Root, Plat);
160 	*cp++ = STRDUP(tmpbuf);
161 	(void) snprintf(tmpbuf, MAXPATHLEN, "%s/usr/lib/fm/topo", Root);
162 	*cp = STRDUP(tmpbuf);
163 	FREE(tmpbuf);
164 }
165 
166 void
167 platform_free_paths(int n, const char **p)
168 {
169 	int i;
170 
171 	for (i = 0; i < n; i++)
172 		FREE((void *)p[i]);
173 	FREE(p);
174 }
175 
176 /*
177  * platform_init -- perform any platform-specific initialization
178  */
179 void
180 platform_init(void)
181 {
182 	const char **paths;
183 	int npaths;
184 
185 	(void) nv_alloc_init(&Eft_nv_hdl, &Eft_nv_alloc_ops);
186 	topo_set_mem_methods(topo_use_alloc, topo_use_free);
187 	topo_set_out_method(topo_use_out);
188 
189 	platform_globals();
190 	platform_topo_paths(&npaths, &paths);
191 	topo_init(npaths, (const char **)paths);
192 	platform_free_paths(npaths, paths);
193 }
194 
195 void
196 platform_fini(void)
197 {
198 	if (Lastcfg != NULL) {
199 		config_free(Lastcfg);
200 		Lastcfg = NULL;
201 	}
202 	if (Initcfg != NULL) {
203 		config_free(Initcfg);
204 		Initcfg = NULL;
205 	}
206 
207 	platform_free_globals();
208 	(void) nv_alloc_fini(&Eft_nv_hdl);
209 	topo_fini();
210 }
211 
212 /*
213  * hc_fmri_nodeize -- convert hc-scheme FMRI to eft compatible format
214  *
215  * this is an internal platform.c helper routine
216  */
217 static struct node *
218 hc_fmri_nodeize(nvlist_t *hcfmri)
219 {
220 	struct node *pathtree = NULL;
221 	struct node *tmpn;
222 	nvlist_t **hc_prs;
223 	uint_t hc_nprs;
224 	const char *sname;
225 	char *ename;
226 	char *eid;
227 	int e, r;
228 
229 	/*
230 	 * What to do with/about hc-root?  Would we have any clue what
231 	 * to do with it if it weren't /?  For now, we don't bother
232 	 * even looking it up.
233 	 */
234 
235 	/*
236 	 * Get the hc-list of elements in the FMRI
237 	 */
238 	if (nvlist_lookup_nvlist_array(hcfmri, FM_FMRI_HC_LIST,
239 	    &hc_prs, &hc_nprs) != 0) {
240 		out(O_ALTFP, "XFILE: hc FMRI missing %s", FM_FMRI_HC_LIST);
241 		return (NULL);
242 	}
243 
244 	for (e = 0; e < hc_nprs; e++) {
245 		ename = NULL;
246 		eid = NULL;
247 		r = nvlist_lookup_string(hc_prs[e], FM_FMRI_HC_NAME, &ename);
248 		r |= nvlist_lookup_string(hc_prs[e], FM_FMRI_HC_ID, &eid);
249 		if (r != 0) {
250 			/* probably should bail */
251 			continue;
252 		}
253 		sname = stable(ename);
254 		tmpn = tree_name_iterator(
255 			tree_name(sname, IT_VERTICAL, NULL, 0),
256 			tree_num(eid, NULL, 0));
257 
258 		if (pathtree == NULL)
259 			pathtree = tmpn;
260 		else
261 			(void) tree_name_append(pathtree, tmpn);
262 	}
263 
264 	return (pathtree);
265 }
266 
267 /*
268  * platform_getpath -- extract eft-compatible path from ereport
269  */
270 struct node *
271 platform_getpath(nvlist_t *nvl)
272 {
273 	struct node *ret;
274 	nvlist_t *dfmri = NULL;
275 	char *scheme = NULL;
276 	char *path = NULL;
277 
278 	/*
279 	 * For now we assume the "path" part of the error report is
280 	 * the detector FMRI
281 	 */
282 	if (nvlist_lookup_nvlist(nvl, FM_EREPORT_DETECTOR, &dfmri) != 0) {
283 		out(O_ALTFP, "XFILE: ereport has no detector FMRI");
284 		return (NULL);
285 	}
286 
287 	if (nvlist_lookup_string(dfmri, FM_FMRI_SCHEME, &scheme) != 0) {
288 		out(O_ALTFP, "XFILE: detector FMRI missing scheme");
289 		return (NULL);
290 	}
291 
292 	if (strcmp(scheme, FM_FMRI_SCHEME_HC) != 0) {
293 		/*
294 		 *  later, if FM_FMRI_SCHEME_DEV or FM_FMRI_SCHEME_CPU
295 		 *  we can look and perform a reverse translation into
296 		 *  an hc node
297 		 */
298 		uint32_t id;
299 		int isdev = 0;
300 
301 		out(O_ALTFP|O_VERB, "Received ereport in scheme %s", scheme);
302 		if (strcmp(scheme, FM_FMRI_SCHEME_DEV) == 0) {
303 			isdev = 1;
304 		} else if (strcmp(scheme, FM_FMRI_SCHEME_CPU) != 0) {
305 			out(O_ALTFP, "XFILE: detector FMRI not recognized "
306 			    "(scheme is %s, expect %s or %s or %s)",
307 			    scheme, FM_FMRI_SCHEME_HC, FM_FMRI_SCHEME_DEV,
308 			    FM_FMRI_SCHEME_CPU);
309 			return (NULL);
310 		}
311 
312 		if (isdev == 1 &&
313 		    nvlist_lookup_string(dfmri, FM_FMRI_DEV_PATH, &path) != 0) {
314 			out(O_ALTFP, "XFILE: detector FMRI missing %s",
315 			    FM_FMRI_DEV_PATH);
316 			return (NULL);
317 		} else if (isdev == 0 &&
318 		    nvlist_lookup_uint32(dfmri, FM_FMRI_CPU_ID, &id) != 0) {
319 			out(O_ALTFP, "XFILE: detector FMRI missing %s",
320 			    FM_FMRI_CPU_ID);
321 			return (NULL);
322 		}
323 
324 		/*
325 		 * If we haven't taken a config snapshot yet, we need
326 		 * to do so now.  The call to config_snapshot() has the
327 		 * side-effect of setting Lastcfg.  We squirrel away the
328 		 * pointer to this snapshot so we may free it later.
329 		 */
330 		if (Lastcfg == NULL)
331 			if ((Initcfg = config_snapshot()) == NULL) {
332 				out(O_ALTFP,
333 				    "XFILE: cannot snapshot configuration");
334 				return (NULL);
335 			}
336 
337 		/*
338 		 * Look up the path or cpu id in the last config snapshot.
339 		 */
340 		if (isdev == 1 &&
341 		    (ret = config_bydev_lookup(Lastcfg, path)) == NULL)
342 			out(O_ALTFP, "XFILE: no configuration node has "
343 			    "device path matching %s.", path);
344 		else if (isdev == 0 &&
345 		    (ret = config_bycpuid_lookup(Lastcfg, id)) == NULL)
346 			out(O_ALTFP, "XFILE: no configuration node has "
347 			    "cpu-id matching %u.", id);
348 
349 		return (ret);
350 	}
351 
352 	return (hc_fmri_nodeize(dfmri));
353 }
354 
355 /* Allocate space for raw config strings in chunks of this size */
356 #define	STRSBUFLEN	512
357 
358 /*
359  * cfgadjust -- Make sure the amount we want to add to the raw config string
360  *		buffer will fit, and if not, increase the size of the buffer.
361  */
362 static void
363 cfgadjust(struct cfgdata *rawdata, int addlen)
364 {
365 	int curnext, newlen;
366 
367 	if (rawdata->nextfree + addlen >= rawdata->end) {
368 		newlen = (((rawdata->nextfree - rawdata->begin + 1 + addlen)
369 		    / STRSBUFLEN) + 1) * STRSBUFLEN;
370 		curnext = rawdata->nextfree - rawdata->begin;
371 		rawdata->begin = REALLOC(rawdata->begin, newlen);
372 		rawdata->nextfree = rawdata->begin + curnext;
373 		rawdata->end = rawdata->begin + newlen;
374 	}
375 }
376 
377 /*
378  * cfgcollect -- Assemble raw configuration data in string form suitable
379  *		 for checkpointing.
380  */
381 static void
382 cfgcollect(tnode_t *node, void *arg)
383 {
384 	struct cfgdata *rawdata = (struct cfgdata *)arg;
385 	const char *propn, *propv;
386 	char *path;
387 	int addlen;
388 
389 	path = topo_hc_path(node);
390 	addlen = strlen(path) + 1;
391 
392 	cfgadjust(rawdata, addlen);
393 	(void) strcpy(rawdata->nextfree, path);
394 	rawdata->nextfree += addlen;
395 
396 	propn = NULL;
397 	while ((propn = topo_next_prop(node, propn)) != NULL) {
398 		propv = topo_get_prop(node, propn);
399 		addlen = strlen(propn) + strlen(propv) + 2; /* = & NULL */
400 		cfgadjust(rawdata, addlen);
401 		(void) snprintf(rawdata->nextfree,
402 		    rawdata->end - rawdata->nextfree, "%s=%s", propn, propv);
403 		rawdata->nextfree += addlen;
404 	}
405 	topo_free_path(path);
406 }
407 
408 /*
409  * platform_config_snapshot -- gather a snapshot of the current configuration
410  */
411 struct cfgdata *
412 platform_config_snapshot(void)
413 {
414 	tnode_t *root;
415 
416 	/*
417 	 *
418 	 * If the DR generation number has changed,
419 	 * we need to grab a new snapshot, otherwise we
420 	 * can simply point them at the last config.
421 	 *
422 	 *	svgen = DRgen;
423 	 *	if (svgen == (Drgen = fmd_drgen_get()) && Lastcfg != NULL) {
424 	 *		Lastcfg->refcnt++;
425 	 *		return (Lastcfg);
426 	 *	}
427 	 */
428 
429 	/* we're getting a new config, so clean up the last one */
430 	if (Lastcfg != NULL)
431 		config_free(Lastcfg);
432 
433 	Lastcfg = MALLOC(sizeof (struct cfgdata));
434 	Lastcfg->refcnt = 2;	/* caller + Lastcfg */
435 	Lastcfg->begin = Lastcfg->nextfree = Lastcfg->end = NULL;
436 	Lastcfg->cooked = NULL;
437 	Lastcfg->devcache = NULL;
438 	Lastcfg->cpucache = NULL;
439 
440 	if ((root = topo_next_sibling(NULL, NULL)) == NULL)
441 		out(O_DIE, "NULL topology tree");
442 
443 	topo_walk(root, TOPO_VISIT_SELF_FIRST, Lastcfg, cfgcollect);
444 	topo_tree_release(root);
445 	topo_reset();
446 
447 	return (Lastcfg);
448 }
449 
450 static nvlist_t **
451 make_hc_pairs(char *fromstr, int *num)
452 {
453 	nvlist_t **pa;
454 	char *starti, *startn, *endi, *endi2;
455 	char *ne, *ns;
456 	char *cname;
457 	char *find;
458 	char *cid;
459 	int nslashes = 0;
460 	int npairs = 0;
461 	int i, e;
462 
463 	/*
464 	 * Count equal signs and slashes to determine how many
465 	 * hc-pairs will be present in the final FMRI.  There should
466 	 * be at least as many slashes as equal signs.  There can be
467 	 * more, though if the string after an = includes them.
468 	 */
469 	find = fromstr;
470 	while ((ne = strchr(find, '=')) != NULL) {
471 		find = ne + 1;
472 		npairs++;
473 	}
474 
475 	find = fromstr;
476 	while ((ns = strchr(find, '/')) != NULL) {
477 		find = ns + 1;
478 		nslashes++;
479 	}
480 
481 	/*
482 	 * Do we appear to have a well-formed string version of the FMRI?
483 	 */
484 	if (nslashes < npairs || npairs == 0)
485 		return (NULL);
486 
487 	*num = npairs;
488 
489 	find = fromstr;
490 	pa = MALLOC(npairs * sizeof (nvlist_t *));
491 	/*
492 	 * We go through a pretty complicated procedure to find the
493 	 * name and id for each pair.  That's because, unfortunately,
494 	 * we have some ids that can have slashes within them.  So
495 	 * we can't just search for the next slash after the equal sign
496 	 * and decide that starts a new pair.  Instead we have to find
497 	 * an equal sign for the next pair and work our way back to the
498 	 * slash from there.
499 	 */
500 	for (i = 0; i < npairs; i++) {
501 		pa[i] = NULL;
502 		startn = strchr(find, '/');
503 		if (startn == NULL)
504 			break;
505 		startn++;
506 		starti = strchr(find, '=');
507 		if (starti == NULL)
508 			break;
509 		*starti = '\0';
510 		cname = STRDUP(startn);
511 		*starti++ = '=';
512 		endi = strchr(starti, '=');
513 		if (endi != NULL) {
514 			*endi = '\0';
515 			endi2 = strrchr(starti, '/');
516 			if (endi2 == NULL)
517 				break;
518 			*endi = '=';
519 			*endi2 = '\0';
520 			cid = STRDUP(starti);
521 			*endi2 = '/';
522 			find = endi2;
523 		} else {
524 			cid = STRDUP(starti);
525 			find = starti + strlen(starti);
526 		}
527 		e = nvlist_xalloc(&pa[i], NV_UNIQUE_NAME, &Eft_nv_hdl);
528 		if (e != 0)
529 			out(O_DIE|O_SYS, "alloc of an fmri nvl failed");
530 		e = nvlist_add_string(pa[i], FM_FMRI_HC_NAME, cname);
531 		e |= nvlist_add_string(pa[i], FM_FMRI_HC_ID, cid);
532 		FREE(cname);
533 		FREE(cid);
534 		if (e != 0) {
535 			out(O_DEBUG|O_SYS,
536 			    "Construction of new hc-pair nvl failed");
537 			break;
538 		}
539 	}
540 	if (i < npairs) {
541 		while (i >= 0)
542 			if (pa[i--] != NULL)
543 				nvlist_free(pa[i + 1]);
544 		FREE(pa);
545 		return (NULL);
546 	}
547 	return (pa);
548 }
549 
550 static nvlist_t *
551 hc_fmri_fromstr(const char *str)
552 {
553 	nvlist_t **pa = NULL;
554 	nvlist_t *na = NULL;
555 	nvlist_t *nf = NULL;
556 	char *copy;
557 	int npairs;
558 	int i, e;
559 
560 	/* We're expecting a string version of an hc scheme FMRI */
561 	if (strncmp(str, "hc:///", 6) != 0)
562 		return (NULL);
563 
564 	copy = STRDUP(str + 5);
565 	if ((pa = make_hc_pairs(copy, &npairs)) == NULL) {
566 		FREE(copy);
567 		return (NULL);
568 	}
569 
570 	FREE(copy);
571 	if ((e = nvlist_xalloc(&na, NV_UNIQUE_NAME, &Eft_nv_hdl)) != 0) {
572 		out(O_DEBUG|O_SYS, "alloc of an fmri nvl failed");
573 		goto hcfmbail;
574 	}
575 	e = nvlist_add_string(na, FM_FMRI_AUTH_PRODUCT, Plat);
576 	if (e != 0) {
577 		out(O_DEBUG|O_SYS, "Construction of new authority nvl failed");
578 		goto hcfmbail;
579 	}
580 
581 	if ((e = nvlist_xalloc(&nf, NV_UNIQUE_NAME, &Eft_nv_hdl)) != 0) {
582 		out(O_DEBUG|O_SYS, "alloc of an fmri nvl failed");
583 		goto hcfmbail;
584 	}
585 	e = nvlist_add_string(nf, FM_FMRI_SCHEME, FM_FMRI_SCHEME_HC);
586 	e |= nvlist_add_nvlist(nf, FM_FMRI_AUTHORITY, na);
587 	e |= nvlist_add_uint8(nf, FM_VERSION, FM_HC_SCHEME_VERSION);
588 	e |= nvlist_add_string(nf, FM_FMRI_HC_ROOT, "");
589 	e |= nvlist_add_uint32(nf, FM_FMRI_HC_LIST_SZ, npairs);
590 	if (e == 0)
591 		e = nvlist_add_nvlist_array(nf, FM_FMRI_HC_LIST, pa, npairs);
592 	if (e != 0) {
593 		out(O_DEBUG|O_SYS, "Construction of new hc nvl failed");
594 		goto hcfmbail;
595 	}
596 	nvlist_free(na);
597 	for (i = 0; i < npairs; i++)
598 		nvlist_free(pa[i]);
599 	FREE(pa);
600 	return (nf);
601 
602 hcfmbail:
603 	if (nf != NULL)
604 		nvlist_free(nf);
605 	if (na != NULL)
606 		nvlist_free(na);
607 	for (i = 0; i < npairs; i++)
608 		nvlist_free(pa[i]);
609 	FREE(pa);
610 	return (NULL);
611 }
612 
613 static nvlist_t *
614 cpu_fmri(struct config *cpu, int cpu_id)
615 {
616 	nvlist_t *na = NULL;
617 	const char *propv;
618 	uint64_t ser_id;
619 	int e;
620 
621 	if ((propv = config_getprop(cpu, "SERIAL-ID")) == NULL) {
622 		out(O_DEBUG|O_SYS, "cpu serial id missing");
623 		return (NULL);
624 	}
625 	ser_id = strtoll(propv, NULL, 0);
626 
627 	if ((e = nvlist_xalloc(&na, NV_UNIQUE_NAME, &Eft_nv_hdl)) != 0)
628 		out(O_DIE|O_SYS, "alloc of an fmri nvl failed");
629 
630 	e = nvlist_add_string(na, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU);
631 	e |= nvlist_add_uint8(na, FM_VERSION, FM_CPU_SCHEME_VERSION);
632 	e |= nvlist_add_uint32(na, FM_FMRI_CPU_ID, cpu_id);
633 	e |= nvlist_add_uint64(na, FM_FMRI_CPU_SERIAL_ID, ser_id);
634 	if (e != 0) {
635 		out(O_DEBUG|O_SYS, "Construction of new ASRU nvl failed");
636 		nvlist_free(na);
637 		return (NULL);
638 	}
639 	return (na);
640 }
641 
642 static nvlist_t *
643 dev_fmri(const char *devpath)
644 {
645 	nvlist_t *na = NULL;
646 	int e;
647 
648 	if (strcmp(devpath, "none") == 0)
649 		return (NULL);
650 
651 	if ((e = nvlist_xalloc(&na, NV_UNIQUE_NAME, &Eft_nv_hdl)) != 0)
652 		out(O_DIE|O_SYS, "alloc of an fmri nvl failed");
653 	e = nvlist_add_string(na, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV);
654 	e |= nvlist_add_uint8(na, FM_VERSION, FM_DEV_SCHEME_VERSION);
655 	e |= nvlist_add_string(na, FM_FMRI_DEV_PATH, devpath);
656 	if (e != 0) {
657 		out(O_DEBUG|O_SYS, "Construction of new ASRU nvl failed");
658 		nvlist_free(na);
659 		return (NULL);
660 	}
661 	return (na);
662 }
663 
664 static void
665 rewrite_asru(nvlist_t **ap, struct config *croot, char *path)
666 {
667 	struct config *casru;
668 	nvlist_t *na = NULL;
669 	const char *propv;
670 	char *cname;
671 	int cinst;
672 
673 	/*
674 	 * The first order of business is to find the ASRU in the
675 	 * config database so we can examine properties associated with
676 	 * that node.
677 	 */
678 	if ((casru = config_lookup(croot, path, 0)) == NULL) {
679 		out(O_DEBUG, "Cannot find config info for %s.", path);
680 		return;
681 	}
682 
683 	/*
684 	 * CPUs have their own scheme.
685 	 */
686 	config_getcompname(casru, &cname, &cinst);
687 	if (cname == NULL) {
688 		out(O_DEBUG,
689 		    "Final component of ASRU path (%s) has no name ?", path);
690 		return;
691 	} else if (strcmp(cname, "cpu") == 0) {
692 		if ((na = cpu_fmri(casru, cinst)) != NULL)
693 			*ap = na;
694 		return;
695 	}
696 
697 	/*
698 	 * Look for a PLAT-ASRU property.
699 	 */
700 	if ((propv = config_getprop(casru, PLATASRU)) != NULL) {
701 		if ((na = hc_fmri_fromstr(propv)) != NULL)
702 			*ap = na;
703 		return;
704 	}
705 	out(O_DEBUG, "No " PLATASRU " prop for constructing "
706 	    "rewritten version of %s.", path);
707 
708 	/*
709 	 * No, PLAT-ASRU, how about DEV?
710 	 */
711 	if ((propv = config_getprop(casru, DEV)) == NULL) {
712 		out(O_DEBUG, "No " DEV " prop for constructing "
713 		    "dev scheme version of %s.", path);
714 		return;
715 	}
716 	if ((na = dev_fmri(propv)) != NULL)
717 		*ap = na;
718 }
719 
720 static void
721 rewrite_fru(nvlist_t **fp, struct config *croot, char *path)
722 {
723 	struct config *cfru;
724 	const char *propv;
725 	nvlist_t *na = NULL;
726 
727 	/*
728 	 * The first order of business is to find the FRU in the
729 	 * config database so we can examine properties associated with
730 	 * that node.
731 	 */
732 	if ((cfru = config_lookup(croot, path, 0)) == NULL) {
733 		out(O_DEBUG, "Cannot find config info for %s.", path);
734 		return;
735 	}
736 
737 	/*
738 	 * Look first for a PLAT-FRU property.
739 	 */
740 	if ((propv = config_getprop(cfru, PLATFRU)) != NULL) {
741 		if ((na = hc_fmri_fromstr(propv)) != NULL)
742 			*fp = na;
743 		return;
744 	}
745 	out(O_DEBUG, "No " PLATFRU " prop for constructing "
746 	    "rewritten version of %s.", path);
747 }
748 
749 static void
750 defect_units(nvlist_t **ap, nvlist_t **fp, struct config *croot, char *path)
751 {
752 	struct config *cnode;
753 	const char *drvname;
754 	nvlist_t *nf = NULL;
755 	nvlist_t *na;
756 
757 	/*
758 	 * Defects aren't required to have ASRUs and FRUs defined with
759 	 * them in the eversholt fault tree, so usually we'll be
760 	 * creating original FMRIs here.  If either the ASRU or FRU
761 	 * is defined when we get here, we won't replace it.
762 	 */
763 	if (*ap != NULL && *fp != NULL)
764 		return;
765 
766 	/*
767 	 * In order to find an ASRU and FRU for the defect we need
768 	 * the name of the driver.
769 	 */
770 	if ((cnode = config_lookup(croot, path, 0)) == NULL) {
771 		out(O_DEBUG, "Cannot find config info for %s.", path);
772 		return;
773 	}
774 	if ((drvname = config_getprop(cnode, DRIVER)) == NULL) {
775 		out(O_DEBUG, "No " DRIVER "prop for constructing "
776 		    "mod scheme version of %s.", path);
777 		return;
778 	}
779 	if ((na = topo_driver_asru(drvname, &nf)) == NULL)
780 		return;
781 
782 	if (*ap == NULL)
783 		*ap = na;
784 
785 	if (*fp == NULL)
786 		*fp = nf;
787 }
788 
789 /*
790  * platform_units_translate
791  *	This routines offers a chance for platform-specific rewrites of
792  *	the hc scheme FRU and ASRUs associated with a suspect fault.
793  */
794 /*ARGSUSED*/
795 void
796 platform_units_translate(int isdefect, struct config *croot,
797     nvlist_t **dfltasru, nvlist_t **dfltfru, nvlist_t **dfltrsrc, char *path)
798 {
799 	nvlist_t *sva;
800 	nvlist_t *svf;
801 
802 	out(O_DEBUG, "platform_units_translate(%d, ....)", isdefect);
803 
804 	sva = *dfltasru;
805 	svf = *dfltfru;
806 
807 	/*
808 	 * If there's room, keep a copy of our original ASRU as the rsrc
809 	 */
810 	if (*dfltrsrc == NULL)
811 		*dfltrsrc = *dfltasru;
812 
813 	/*
814 	 * If it is a defect we want to re-write the FRU as the pkg
815 	 * scheme fmri of the package containing the buggy driver, and
816 	 * the ASRU as the mod scheme fmri of the driver's kernel
817 	 * module.
818 	 */
819 	if (isdefect) {
820 		defect_units(dfltasru, dfltfru, croot, path);
821 		if (sva != *dfltasru && sva != *dfltrsrc && sva != NULL)
822 			nvlist_free(sva);
823 		if (svf != *dfltfru && svf != NULL)
824 			nvlist_free(svf);
825 		return;
826 	}
827 
828 	if (*dfltasru != NULL) {
829 		/*
830 		 * The ASRU will be re-written per the following rules:
831 		 *
832 		 * 1) If there's a PLAT-ASRU property, we convert it into
833 		 *	a real hc FMRI nvlist.
834 		 * 2) Otherwise, if we find a DEV property, we make a DEV
835 		 *	scheme FMRI of it
836 		 * 3) Otherwise, we leave the ASRU as is.
837 		 */
838 		rewrite_asru(dfltasru, croot, path);
839 	}
840 
841 	if (*dfltfru != NULL) {
842 		/*
843 		 * The FRU will be re-written per the following rules:
844 		 *
845 		 * 1) If there's a PLAT-FRU property, we convert it into
846 		 *	a real hc FMRI nvlist.
847 		 * 2) Otherwise, we leave the ASRU as is, but include a
848 		 *	FRU label property if possible.
849 		 */
850 		rewrite_fru(dfltfru, croot, path);
851 	}
852 
853 	if (sva != *dfltasru && sva != *dfltrsrc && sva != NULL)
854 		nvlist_free(sva);
855 	if (svf != *dfltfru && svf != NULL)
856 		nvlist_free(svf);
857 }
858 
859 /*
860  * platform_get_files -- return names of all files we should load
861  *
862  * search directories in dirname[] for all files with names ending with the
863  * substring fnstr.  dirname[] should be a NULL-terminated array.  fnstr
864  * may be set to "*" to indicate all files in a directory.
865  *
866  * if nodups is non-zero, then the first file of a given name found is
867  * the only file added to the list of names.  for example if nodups is
868  * set and we're looking for .efts, and find a pci.eft in the dirname[0],
869  * then no pci.eft found in any of the other dirname[] entries will be
870  * included in the final list of names.
871  *
872  * this routine doesn't return NULL, even if no files are found (in that
873  * case, a char ** is returned with the first element NULL).
874  */
875 static char **
876 platform_get_files(const char *dirname[], const char *fnstr, int nodups)
877 {
878 	DIR *dirp;
879 	struct dirent *dp;
880 	struct lut *foundnames = NULL;
881 	char **files = NULL;	/* char * array of filenames found */
882 	int nfiles = 0;		/* files found so far */
883 	int slots = 0;		/* char * slots allocated in files */
884 	size_t fnlen, d_namelen;
885 	size_t totlen;
886 	int i;
887 	static char *nullav;
888 
889 	ASSERT(fnstr != NULL);
890 	fnlen = strlen(fnstr);
891 
892 	for (i = 0; dirname[i] != NULL; i++) {
893 		out(O_DEBUG, "Looking for %s files in %s", fnstr, dirname[i]);
894 		if ((dirp = opendir(dirname[i])) == NULL) {
895 			out(O_DEBUG|O_SYS,
896 			    "platform_get_files: opendir failed for %s",
897 			    dirname[i]);
898 			continue;
899 		}
900 		while ((dp = readdir(dirp)) != NULL) {
901 			if ((fnlen == 1 && *fnstr == '*') ||
902 			    ((d_namelen = strlen(dp->d_name)) >= fnlen &&
903 			    strncmp(dp->d_name + d_namelen - fnlen,
904 			    fnstr, fnlen) == 0)) {
905 
906 				if (nodups != 0) {
907 					const char *snm = stable(dp->d_name);
908 
909 					if (lut_lookup(foundnames,
910 					    (void *)snm,
911 					    NULL) != NULL) {
912 						out(O_DEBUG,
913 						    "platform_get_files: "
914 						    "skipping repeated name "
915 						    "%s/%s",
916 						    dirname[i],
917 						    snm);
918 						continue;
919 					}
920 					foundnames = lut_add(foundnames,
921 					    (void *)snm,
922 					    (void *)snm,
923 					    NULL);
924 				}
925 
926 				if (nfiles > slots - 2) {
927 					/* allocate ten more slots */
928 					slots += 10;
929 					files = (char **)REALLOC(files,
930 						slots * sizeof (char *));
931 				}
932 				/* prepend directory name and / */
933 				totlen = strlen(dirname[i]) + 1;
934 				totlen += strlen(dp->d_name) + 1;
935 				files[nfiles] = MALLOC(totlen);
936 				(void) snprintf(files[nfiles++], totlen,
937 				    "%s/%s", dirname[i], dp->d_name);
938 			}
939 		}
940 		(void) closedir(dirp);
941 	}
942 
943 	if (foundnames != NULL)
944 		lut_free(foundnames, NULL, NULL);
945 
946 	if (nfiles == 0)
947 		return (&nullav);
948 
949 	files[nfiles] = NULL;
950 	return (files);
951 }
952 
953 /*
954  * search for files in a standard set of directories
955  */
956 static char **
957 platform_get_files_stddirs(char *fname, int nodups)
958 {
959 	const char *dirlist[4];
960 	char **flist;
961 	char *eftgendir, *eftmachdir, *eftplatdir;
962 
963 	eftgendir = MALLOC(MAXPATHLEN);
964 	eftmachdir = MALLOC(MAXPATHLEN);
965 	eftplatdir = MALLOC(MAXPATHLEN);
966 
967 	/* Generic files that apply to any machine */
968 	(void) snprintf(eftgendir, MAXPATHLEN, "%s/usr/lib/fm/eft", Root);
969 
970 	(void) snprintf(eftmachdir,
971 	    MAXPATHLEN, "%s/usr/platform/%s/lib/fm/eft", Root, Mach);
972 
973 	(void) snprintf(eftplatdir,
974 	    MAXPATHLEN, "%s/usr/platform/%s/lib/fm/eft", Root, Plat);
975 
976 	dirlist[0] = eftplatdir;
977 	dirlist[1] = eftmachdir;
978 	dirlist[2] = eftgendir;
979 	dirlist[3] = NULL;
980 
981 	flist = platform_get_files(dirlist, fname, nodups);
982 
983 	FREE(eftplatdir);
984 	FREE(eftmachdir);
985 	FREE(eftgendir);
986 
987 	return (flist);
988 }
989 
990 /*
991  * platform_run_poller -- execute a poller
992  *
993  * when eft needs to know if a polled ereport exists this routine
994  * is called so the poller code may be run in a platform-specific way.
995  * there's no return value from this routine -- either the polled ereport
996  * is generated (and delivered *before* this routine returns) or not.
997  * any errors, like "poller unknown" are considered platform-specific
998  * should be handled here rather than passing an error back up.
999  */
1000 /*ARGSUSED*/
1001 void
1002 platform_run_poller(const char *poller)
1003 {
1004 }
1005 
1006 /*
1007  * fork and execve path with argument array argv and environment array
1008  * envp.  data from stdout and stderr are placed in outbuf and errbuf,
1009  * respectively.
1010  *
1011  * see execve(2) for more descriptions for path, argv and envp.
1012  */
1013 static int
1014 forkandexecve(const char *path, char *const argv[], char *const envp[],
1015 	char *outbuf, size_t outbuflen, char *errbuf, size_t errbuflen)
1016 {
1017 	pid_t pid;
1018 	int outpipe[2], errpipe[2];
1019 	int rt = 0;
1020 
1021 	/*
1022 	 * run the cmd and see if it failed.  this function is *not* a
1023 	 * generic command runner -- we depend on some knowledge we
1024 	 * have about the commands we run.  first of all, we expect
1025 	 * errors to spew something to stdout, and that something is
1026 	 * typically short enough to fit into a pipe so we can wait()
1027 	 * for the command to complete and then fetch the error text
1028 	 * from the pipe.
1029 	 */
1030 	if (pipe(outpipe) < 0)
1031 		if (strlcat(errbuf, ": pipe(outpipe) failed",
1032 			    errbuflen) >= errbuflen)
1033 			return (1);
1034 	if (pipe(errpipe) < 0)
1035 		if (strlcat(errbuf, ": pipe(errpipe) failed",
1036 			    errbuflen) >= errbuflen)
1037 			return (1);
1038 
1039 	if ((pid = fork()) < 0)
1040 		rt = (int)strlcat(errbuf, ": fork() failed", errbuflen);
1041 	else if (pid) {
1042 		int wstat, count;
1043 
1044 		/* parent */
1045 		(void) close(errpipe[1]);
1046 		(void) close(outpipe[1]);
1047 
1048 		/* PHASE2 need to guard against hang in child? */
1049 		if (waitpid(pid, &wstat, 0) < 0)
1050 			if (strlcat(errbuf, ": waitpid() failed",
1051 				    errbuflen) >= errbuflen)
1052 				return (1);
1053 
1054 		/* check for stderr contents */
1055 		if (ioctl(errpipe[0], FIONREAD, &count) >= 0 && count) {
1056 			if (read(errpipe[0], errbuf, errbuflen) <= 0) {
1057 				/*
1058 				 * read failed even though ioctl indicated
1059 				 * that nonzero bytes were available for
1060 				 * reading
1061 				 */
1062 				if (strlcat(errbuf, ": read(errpipe) failed",
1063 					    errbuflen) >= errbuflen)
1064 					return (1);
1065 			}
1066 			/*
1067 			 * handle case where errbuf is not properly
1068 			 * terminated
1069 			 */
1070 			if (count > errbuflen - 1)
1071 				count = errbuflen - 1;
1072 			if (errbuf[count - 1] != '\0' &&
1073 			    errbuf[count - 1] != '\n')
1074 				errbuf[count] = '\0';
1075 		} else if (WIFSIGNALED(wstat))
1076 			if (strlcat(errbuf, ": signaled",
1077 				    errbuflen) >= errbuflen)
1078 				return (1);
1079 		else if (WIFEXITED(wstat) && WEXITSTATUS(wstat))
1080 			if (strlcat(errbuf, ": abnormal exit",
1081 				    errbuflen) >= errbuflen)
1082 				return (1);
1083 
1084 		/* check for stdout contents */
1085 		if (ioctl(outpipe[0], FIONREAD, &count) >= 0 && count) {
1086 			if (read(outpipe[0], outbuf, outbuflen) <= 0) {
1087 				/*
1088 				 * read failed even though ioctl indicated
1089 				 * that nonzero bytes were available for
1090 				 * reading
1091 				 */
1092 				if (strlcat(errbuf, ": read(outpipe) failed",
1093 					    errbuflen) >= errbuflen)
1094 					return (1);
1095 			}
1096 			/*
1097 			 * handle case where outbuf is not properly
1098 			 * terminated
1099 			 */
1100 			if (count > outbuflen - 1)
1101 				count = outbuflen - 1;
1102 			if (outbuf[count - 1] != '\0' &&
1103 			    outbuf[count - 1] != '\n')
1104 				outbuf[count] = '\0';
1105 		}
1106 
1107 		(void) close(errpipe[0]);
1108 		(void) close(outpipe[0]);
1109 	} else {
1110 		/* child */
1111 		(void) dup2(errpipe[1], fileno(stderr));
1112 		(void) close(errpipe[0]);
1113 		(void) dup2(outpipe[1], fileno(stdout));
1114 		(void) close(outpipe[0]);
1115 
1116 		if (execve(path, argv, envp))
1117 			perror(path);
1118 		_exit(1);
1119 	}
1120 
1121 	return (rt);
1122 }
1123 
1124 /*
1125  * extract the first string in outbuf, either
1126  *   a) convert it to a number, or
1127  *   b) convert it to an address via stable()
1128  * and place the result (number or address) in valuep.
1129  *
1130  * return 0 if conversion was successful, nonzero if otherwise
1131  */
1132 static int
1133 string2number(char *outbuf, size_t outbuflen, struct evalue *valuep)
1134 {
1135 	char *ptr, *startptr, *endptr;
1136 	int spval;
1137 	size_t nchars, i, ier;
1138 
1139 	/* determine start and length of first string */
1140 	nchars = 0;
1141 	for (i = 0; i < outbuflen && *(outbuf + i) != '\0'; i++) {
1142 		spval = isspace((int)*(outbuf + i));
1143 		if (spval != 0 && nchars > 0)
1144 			break;
1145 		if (spval == 0) {
1146 			/* startptr: first nonspace character */
1147 			if (nchars == 0)
1148 				startptr = outbuf + i;
1149 			nchars++;
1150 		}
1151 	}
1152 	if (nchars == 0)
1153 		return (1);
1154 
1155 	ptr = MALLOC(sizeof (char) * (nchars + 1));
1156 	(void) strncpy(ptr, startptr, nchars);
1157 	*(ptr + nchars) = '\0';
1158 
1159 	/* attempt conversion to number */
1160 	errno = 0;
1161 	valuep->t = UINT64;
1162 	valuep->v = strtoull(ptr, &endptr, 0);
1163 	ier = errno;
1164 
1165 	/*
1166 	 * test for endptr since the call to strtoull() should be
1167 	 * considered a success only if the whole string was converted
1168 	 */
1169 	if (ier != 0 || endptr != (ptr + nchars)) {
1170 		valuep->t = STRING;
1171 		valuep->v = (unsigned long long)stable(ptr);
1172 	}
1173 	FREE(ptr);
1174 
1175 	return (0);
1176 }
1177 
1178 #define	MAXDIGITIDX	23
1179 
1180 static int
1181 arglist2argv(struct node *np, struct lut **globals, struct config *croot,
1182 	struct arrow *arrowp, char ***argv, int *argc, int *argvlen)
1183 {
1184 	struct node *namep;
1185 	char numbuf[MAXDIGITIDX + 1];
1186 	char *numstr, *nullbyte;
1187 	char *addthisarg = NULL;
1188 
1189 	if (np == NULL)
1190 		return (0);
1191 
1192 	switch (np->t) {
1193 	case T_QUOTE:
1194 		addthisarg = STRDUP(np->u.func.s);
1195 		break;
1196 	case T_LIST:
1197 		if (arglist2argv(np->u.expr.left, globals, croot, arrowp,
1198 				argv, argc, argvlen))
1199 			return (1);
1200 		/*
1201 		 * only leftmost element of a list can provide the command
1202 		 * name (after which *argc becomes 1)
1203 		 */
1204 		ASSERT(*argc > 0);
1205 		if (arglist2argv(np->u.expr.right, globals, croot, arrowp,
1206 				argv, argc, argvlen))
1207 			return (1);
1208 		break;
1209 	case T_FUNC:
1210 	case T_GLOBID:
1211 	case T_ASSIGN:
1212 	case T_CONDIF:
1213 	case T_CONDELSE:
1214 	case T_EQ:
1215 	case T_NE:
1216 	case T_LT:
1217 	case T_LE:
1218 	case T_GT:
1219 	case T_GE:
1220 	case T_BITAND:
1221 	case T_BITOR:
1222 	case T_BITXOR:
1223 	case T_BITNOT:
1224 	case T_LSHIFT:
1225 	case T_RSHIFT:
1226 	case T_AND:
1227 	case T_OR:
1228 	case T_NOT:
1229 	case T_ADD:
1230 	case T_SUB:
1231 	case T_MUL:
1232 	case T_DIV:
1233 	case T_MOD: {
1234 		struct evalue value;
1235 
1236 		if (!eval_expr(np, NULL, NULL, globals, croot, arrowp,
1237 			    0, &value))
1238 			return (1);
1239 
1240 		switch (value.t) {
1241 		case UINT64:
1242 			numbuf[MAXDIGITIDX] = '\0';
1243 			nullbyte = &numbuf[MAXDIGITIDX];
1244 			numstr = ulltostr(value.v, nullbyte);
1245 			addthisarg = STRDUP(numstr);
1246 			break;
1247 		case STRING:
1248 			addthisarg = STRDUP((const char *)value.v);
1249 			break;
1250 		case NODEPTR :
1251 			namep = (struct node *)value.v;
1252 			addthisarg = ipath2str(NULL, ipath(namep));
1253 			break;
1254 		default:
1255 			out(O_ERR,
1256 			    "call: arglist2argv: unexpected result from"
1257 			    " operation %s",
1258 			    ptree_nodetype2str(np->t));
1259 			return (1);
1260 		}
1261 		break;
1262 	}
1263 	case T_NUM:
1264 	case T_TIMEVAL:
1265 		numbuf[MAXDIGITIDX] = '\0';
1266 		nullbyte = &numbuf[MAXDIGITIDX];
1267 		numstr = ulltostr(np->u.ull, nullbyte);
1268 		addthisarg = STRDUP(numstr);
1269 		break;
1270 	case T_NAME:
1271 		addthisarg = ipath2str(NULL, ipath(np));
1272 		break;
1273 	case T_EVENT:
1274 		addthisarg = ipath2str(np->u.event.ename->u.name.s,
1275 		    ipath(np->u.event.epname));
1276 		break;
1277 	default:
1278 		out(O_ERR, "call: arglist2argv: node type %s is unsupported",
1279 		    ptree_nodetype2str(np->t));
1280 		return (1);
1281 		/*NOTREACHED*/
1282 		break;
1283 	}
1284 
1285 	if (*argc == 0 && addthisarg != NULL) {
1286 		/*
1287 		 * first argument added is the command name.
1288 		 */
1289 		char **files;
1290 
1291 		files = platform_get_files_stddirs(addthisarg, 0);
1292 
1293 		/* do not proceed if number of files found != 1 */
1294 		if (files[0] == NULL)
1295 			out(O_DIE, "call: function %s not found", addthisarg);
1296 		if (files[1] != NULL)
1297 			out(O_DIE, "call: multiple functions %s found",
1298 			    addthisarg);
1299 		FREE(addthisarg);
1300 
1301 		addthisarg = STRDUP(files[0]);
1302 		FREE(files[0]);
1303 		FREE(files);
1304 	}
1305 
1306 	if (addthisarg != NULL) {
1307 		if (*argc >= *argvlen - 2) {
1308 			/*
1309 			 * make sure argv is long enough so it has a
1310 			 * terminating element set to NULL
1311 			 */
1312 			*argvlen += 10;
1313 			*argv = (char **)REALLOC(*argv,
1314 						sizeof (char *) * *argvlen);
1315 		}
1316 		(*argv)[*argc] = addthisarg;
1317 		(*argc)++;
1318 		(*argv)[*argc] = NULL;
1319 	}
1320 
1321 	return (0);
1322 }
1323 
1324 static int
1325 generate_envp(struct arrow *arrowp, char ***envp, int *envc, int *envplen)
1326 {
1327 	char *envnames[] = { "EFT_FROM_EVENT", "EFT_TO_EVENT",
1328 			    "EFT_FILE", "EFT_LINE", NULL };
1329 	char *envvalues[4];
1330 	char *none = "(none)";
1331 	size_t elen;
1332 	int i;
1333 
1334 	*envc = 4;
1335 
1336 	/*
1337 	 * make sure envp is long enough so it has a terminating element
1338 	 * set to NULL
1339 	 */
1340 	*envplen = *envc + 1;
1341 	*envp = (char **)MALLOC(sizeof (char *) * *envplen);
1342 
1343 	envvalues[0] = ipath2str(
1344 	    arrowp->tail->myevent->enode->u.event.ename->u.name.s,
1345 	    arrowp->tail->myevent->ipp);
1346 	envvalues[1] = ipath2str(
1347 	    arrowp->head->myevent->enode->u.event.ename->u.name.s,
1348 	    arrowp->head->myevent->ipp);
1349 
1350 	if (arrowp->head->myevent->enode->file == NULL) {
1351 		envvalues[2] = STRDUP(none);
1352 		envvalues[3] = STRDUP(none);
1353 	} else {
1354 		envvalues[2] = STRDUP(arrowp->head->myevent->enode->file);
1355 
1356 		/* large enough for max int */
1357 		envvalues[3] = MALLOC(sizeof (char) * 25);
1358 		(void) snprintf(envvalues[3], sizeof (envvalues[3]), "%d",
1359 				arrowp->head->myevent->enode->line);
1360 	}
1361 
1362 	for (i = 0; envnames[i] != NULL && i < *envc; i++) {
1363 		elen = strlen(envnames[i]) + strlen(envvalues[i]) + 2;
1364 		(*envp)[i] = MALLOC(elen);
1365 		(void) snprintf((*envp)[i], elen, "%s=%s",
1366 		    envnames[i], envvalues[i]);
1367 		FREE(envvalues[i]);
1368 	}
1369 	(*envp)[*envc] = NULL;
1370 
1371 	return (0);
1372 }
1373 
1374 /*
1375  * platform_call -- call an external function
1376  *
1377  * evaluate a user-defined function and place result in valuep.  return 0
1378  * if function evaluation was successful; 1 if otherwise.
1379  */
1380 int
1381 platform_call(struct node *np, struct lut **globals, struct config *croot,
1382 	struct arrow *arrowp, struct evalue *valuep)
1383 {
1384 	/*
1385 	 * use rather short buffers.  only the first string on outbuf[] is
1386 	 * taken as output from the called function.  any message in
1387 	 * errbuf[] is echoed out as an error message.
1388 	 */
1389 	char outbuf[256], errbuf[512];
1390 	struct stat buf;
1391 	char **argv, **envp;
1392 	int argc, argvlen, envc, envplen;
1393 	int i, ret;
1394 
1395 	/*
1396 	 * np is the argument list.  the user-defined function is the first
1397 	 * element of the list.
1398 	 */
1399 	ASSERT(np->t == T_LIST);
1400 
1401 	argv = NULL;
1402 	argc = 0;
1403 	argvlen = 0;
1404 	if (arglist2argv(np, globals, croot, arrowp, &argv, &argc, &argvlen) ||
1405 	    argc == 0)
1406 		return (1);
1407 
1408 	/*
1409 	 * make sure program has executable bit set
1410 	 */
1411 	if (stat(argv[0], &buf) == 0) {
1412 		int exec_bit_set = 0;
1413 
1414 		if (buf.st_uid == geteuid() && buf.st_mode & S_IXUSR)
1415 			exec_bit_set = 1;
1416 		else if (buf.st_gid == getegid() && buf.st_mode & S_IXGRP)
1417 			exec_bit_set = 1;
1418 		else if (buf.st_mode & S_IXOTH)
1419 			exec_bit_set = 1;
1420 
1421 		if (exec_bit_set == 0)
1422 			out(O_DIE, "call: executable bit not set on %s",
1423 			    argv[0]);
1424 	} else {
1425 		out(O_DIE, "call: failure in stat(), errno = %d\n", errno);
1426 	}
1427 
1428 	envp = NULL;
1429 	envc = 0;
1430 	envplen = 0;
1431 	if (generate_envp(arrowp, &envp, &envc, &envplen))
1432 		return (1);
1433 
1434 	outbuf[0] = '\0';
1435 	errbuf[0] = '\0';
1436 
1437 	ret = forkandexecve((const char *) argv[0], (char *const *) argv,
1438 			    (char *const *) envp, outbuf, sizeof (outbuf),
1439 			    errbuf, sizeof (errbuf));
1440 
1441 	for (i = 0; i < envc; i++)
1442 		FREE(envp[i]);
1443 	if (envp)
1444 		FREE(envp);
1445 
1446 	if (ret) {
1447 		outfl(O_OK, np->file, np->line,
1448 			"call: failure in fork + exec of %s", argv[0]);
1449 	} else {
1450 		ret = string2number(outbuf, sizeof (outbuf), valuep);
1451 		if (ret)
1452 			outfl(O_OK, np->file, np->line,
1453 				"call: no result from %s", argv[0]);
1454 	}
1455 
1456 	if (errbuf[0] != '\0') {
1457 		ret = 1;
1458 		outfl(O_OK, np->file, np->line,
1459 			"call: unexpected stderr output from %s: %s",
1460 			argv[0], errbuf);
1461 	}
1462 
1463 	for (i = 0; i < argc; i++)
1464 		FREE(argv[i]);
1465 	FREE(argv);
1466 
1467 	return (ret);
1468 }
1469 
1470 /*
1471  * platform_get_eft_files -- return names of all eft files we should load
1472  *
1473  * this routine doesn't return NULL, even if no files are found (in that
1474  * case, a char ** is returned with the first element NULL).
1475  */
1476 char **
1477 platform_get_eft_files(void)
1478 {
1479 	return (platform_get_files_stddirs(".eft", 1));
1480 }
1481 
1482 void
1483 platform_free_eft_files(char **flist)
1484 {
1485 	char **f;
1486 
1487 	if (flist == NULL || *flist == NULL)
1488 		return;	/* no files were found so we're done */
1489 
1490 	f = flist;
1491 	while (*f != NULL) {
1492 		FREE(*f);
1493 		f++;
1494 	}
1495 	FREE(flist);
1496 }
1497 
1498 static nvlist_t *payloadnvp = NULL;
1499 
1500 void
1501 platform_set_payloadnvp(nvlist_t *nvlp)
1502 {
1503 	/*
1504 	 * cannot replace a non-NULL payloadnvp with a non-NULL nvlp
1505 	 */
1506 	ASSERT(payloadnvp != NULL ? nvlp == NULL : 1);
1507 	payloadnvp = nvlp;
1508 }
1509 
1510 /*
1511  * given array notation in inputstr such as "foo[1]" or "foo [ 1 ]" (spaces
1512  * allowed), figure out the array name and index.  return 0 if successful,
1513  * nonzero if otherwise.
1514  */
1515 static int
1516 get_array_info(const char *inputstr, const char **name, unsigned int *index)
1517 {
1518 	char *indexptr, *indexend, *dupname, *endname;
1519 
1520 	if (strchr(inputstr, '[') == NULL)
1521 		return (1);
1522 
1523 	dupname = STRDUP(inputstr);
1524 	indexptr = strchr(dupname, '[');
1525 	indexend = strchr(dupname, ']');
1526 
1527 	/*
1528 	 * return if array notation is not complete or if index is negative
1529 	 */
1530 	if (indexend == NULL || indexptr >= indexend ||
1531 	    strchr(indexptr, '-') != NULL) {
1532 		FREE(dupname);
1533 		return (1);
1534 	}
1535 
1536 	/*
1537 	 * search past any spaces between the name string and '['
1538 	 */
1539 	endname = indexptr;
1540 	while (isspace(*(endname - 1)) && dupname < endname)
1541 		endname--;
1542 	*endname = '\0';
1543 	ASSERT(dupname < endname);
1544 
1545 	/*
1546 	 * search until indexptr points to the first digit and indexend
1547 	 * points to the last digit
1548 	 */
1549 	while (!isdigit(*indexptr) && indexptr < indexend)
1550 		indexptr++;
1551 	while (!isdigit(*indexend) && indexptr <= indexend)
1552 		indexend--;
1553 
1554 	*(indexend + 1) = '\0';
1555 	*index = (unsigned int)atoi(indexptr);
1556 
1557 	*name = stable(dupname);
1558 	FREE(dupname);
1559 
1560 	return (0);
1561 }
1562 
1563 int
1564 platform_payloadprop(struct node *np, struct evalue *valuep)
1565 {
1566 	nvlist_t *basenvp;
1567 	nvpair_t *nvpair;
1568 	const char *nameptr, *propstr, *lastnameptr;
1569 	int not_array = 0;
1570 	unsigned int index = 0;
1571 	uint_t nelem;
1572 	char *nvpname, *nameslist = NULL;
1573 
1574 	ASSERT(np->t == T_QUOTE);
1575 	valuep->t = UNDEFINED;
1576 
1577 	propstr = np->u.quote.s;
1578 	if (payloadnvp == NULL) {
1579 		out(O_ALTFP, "platform_payloadprop: no nvp for %s",
1580 		    propstr);
1581 		return (1);
1582 	}
1583 	basenvp = payloadnvp;
1584 
1585 	/*
1586 	 * first handle any embedded nvlists.  if propstr is "foo.bar[2]"
1587 	 * then lastnameptr should end up being "bar[2]" with basenvp set
1588 	 * to the nvlist for "foo".  (the search for "bar" within "foo"
1589 	 * will be done later.)
1590 	 */
1591 	if (strchr(propstr, '.') != NULL) {
1592 		nvlist_t **arraynvp;
1593 		uint_t nelem;
1594 		char *w;
1595 		int ier;
1596 
1597 		nameslist = STRDUP(propstr);
1598 		lastnameptr = strtok(nameslist, ".");
1599 
1600 		/*
1601 		 * decompose nameslist into its component names while
1602 		 * extracting the embedded nvlist
1603 		 */
1604 		while ((w = strtok(NULL, ".")) != NULL) {
1605 			if (get_array_info(lastnameptr, &nameptr, &index)) {
1606 				ier = nvlist_lookup_nvlist(basenvp,
1607 						    lastnameptr, &basenvp);
1608 			} else {
1609 				/* handle array of nvlists */
1610 				ier = nvlist_lookup_nvlist_array(basenvp,
1611 					    nameptr, &arraynvp, &nelem);
1612 				if (ier == 0) {
1613 					if ((uint_t)index > nelem - 1)
1614 						ier = 1;
1615 					else
1616 						basenvp = arraynvp[index];
1617 				}
1618 			}
1619 
1620 			if (ier) {
1621 				out(O_ALTFP, "platform_payloadprop: "
1622 				    " invalid list for %s (in %s)",
1623 				    lastnameptr, propstr);
1624 				FREE(nameslist);
1625 				return (1);
1626 			}
1627 
1628 			lastnameptr = w;
1629 		}
1630 	} else {
1631 		lastnameptr = propstr;
1632 	}
1633 
1634 	/* if property is an array reference, extract array name and index */
1635 	not_array = get_array_info(lastnameptr, &nameptr, &index);
1636 	if (not_array)
1637 		nameptr = stable(lastnameptr);
1638 
1639 	if (nameslist != NULL)
1640 		FREE(nameslist);
1641 
1642 	/* search for nvpair entry */
1643 	nvpair = NULL;
1644 	while ((nvpair = nvlist_next_nvpair(basenvp, nvpair)) != NULL) {
1645 		nvpname = nvpair_name(nvpair);
1646 		ASSERT(nvpname != NULL);
1647 
1648 		if (nameptr == stable(nvpname))
1649 			break;
1650 	}
1651 
1652 	if (nvpair == NULL) {
1653 		out(O_ALTFP, "platform_payloadprop: no entry for %s", propstr);
1654 		return (1);
1655 	}
1656 
1657 	/*
1658 	 * get to this point if we found an entry.  figure out its data
1659 	 * type and copy its value.
1660 	 */
1661 	switch (nvpair_type(nvpair)) {
1662 	case DATA_TYPE_BOOLEAN:
1663 	case DATA_TYPE_BOOLEAN_VALUE: {
1664 		boolean_t val;
1665 		(void) nvpair_value_boolean_value(nvpair, &val);
1666 		valuep->t = UINT64;
1667 		valuep->v = (unsigned long long)val;
1668 		break;
1669 	}
1670 	case DATA_TYPE_BYTE: {
1671 		uchar_t val;
1672 		(void) nvpair_value_byte(nvpair, &val);
1673 		valuep->t = UINT64;
1674 		valuep->v = (unsigned long long)val;
1675 		break;
1676 	}
1677 	case DATA_TYPE_STRING: {
1678 		char *val;
1679 		valuep->t = STRING;
1680 		(void) nvpair_value_string(nvpair, &val);
1681 		valuep->v = (unsigned long long)stable(val);
1682 		break;
1683 	}
1684 
1685 	case DATA_TYPE_INT8: {
1686 		int8_t val;
1687 		(void) nvpair_value_int8(nvpair, &val);
1688 		valuep->t = UINT64;
1689 		valuep->v = (unsigned long long)val;
1690 		break;
1691 	}
1692 	case DATA_TYPE_UINT8: {
1693 		uint8_t val;
1694 		(void) nvpair_value_uint8(nvpair, &val);
1695 		valuep->t = UINT64;
1696 		valuep->v = (unsigned long long)val;
1697 		break;
1698 	}
1699 
1700 	case DATA_TYPE_INT16: {
1701 		int16_t val;
1702 		(void) nvpair_value_int16(nvpair, &val);
1703 		valuep->t = UINT64;
1704 		valuep->v = (unsigned long long)val;
1705 		break;
1706 	}
1707 	case DATA_TYPE_UINT16: {
1708 		uint16_t val;
1709 		(void) nvpair_value_uint16(nvpair, &val);
1710 		valuep->t = UINT64;
1711 		valuep->v = (unsigned long long)val;
1712 		break;
1713 	}
1714 
1715 	case DATA_TYPE_INT32: {
1716 		int32_t val;
1717 		(void) nvpair_value_int32(nvpair, &val);
1718 		valuep->t = UINT64;
1719 		valuep->v = (unsigned long long)val;
1720 		break;
1721 	}
1722 	case DATA_TYPE_UINT32: {
1723 		uint32_t val;
1724 		(void) nvpair_value_uint32(nvpair, &val);
1725 		valuep->t = UINT64;
1726 		valuep->v = (unsigned long long)val;
1727 		break;
1728 	}
1729 
1730 	case DATA_TYPE_INT64: {
1731 		int64_t val;
1732 		(void) nvpair_value_int64(nvpair, &val);
1733 		valuep->t = UINT64;
1734 		valuep->v = (unsigned long long)val;
1735 		break;
1736 	}
1737 	case DATA_TYPE_UINT64: {
1738 		uint64_t val;
1739 		(void) nvpair_value_uint64(nvpair, &val);
1740 		valuep->t = UINT64;
1741 		valuep->v = (unsigned long long)val;
1742 		break;
1743 	}
1744 
1745 	case DATA_TYPE_BOOLEAN_ARRAY: {
1746 		boolean_t *val;
1747 		(void) nvpair_value_boolean_array(nvpair, &val, &nelem);
1748 		if (not_array == 1 || index >= nelem)
1749 			goto invalid;
1750 		valuep->t = UINT64;
1751 		valuep->v = (unsigned long long)val[index];
1752 		break;
1753 	}
1754 	case DATA_TYPE_BYTE_ARRAY: {
1755 		uchar_t *val;
1756 		(void) nvpair_value_byte_array(nvpair, &val, &nelem);
1757 		if (not_array == 1 || index >= nelem)
1758 			goto invalid;
1759 		valuep->t = UINT64;
1760 		valuep->v = (unsigned long long)val[index];
1761 		break;
1762 	}
1763 	case DATA_TYPE_STRING_ARRAY: {
1764 		char **val;
1765 		(void) nvpair_value_string_array(nvpair, &val, &nelem);
1766 		if (not_array == 1 || index >= nelem)
1767 			goto invalid;
1768 		valuep->t = STRING;
1769 		valuep->v = (unsigned long long)stable(val[index]);
1770 		break;
1771 	}
1772 
1773 	case DATA_TYPE_INT8_ARRAY: {
1774 		int8_t *val;
1775 		(void) nvpair_value_int8_array(nvpair, &val, &nelem);
1776 		if (not_array == 1 || index >= nelem)
1777 			goto invalid;
1778 		valuep->t = UINT64;
1779 		valuep->v = (unsigned long long)val[index];
1780 		break;
1781 	}
1782 	case DATA_TYPE_UINT8_ARRAY: {
1783 		uint8_t *val;
1784 		(void) nvpair_value_uint8_array(nvpair, &val, &nelem);
1785 		if (not_array == 1 || index >= nelem)
1786 			goto invalid;
1787 		valuep->t = UINT64;
1788 		valuep->v = (unsigned long long)val[index];
1789 		break;
1790 	}
1791 	case DATA_TYPE_INT16_ARRAY: {
1792 		int16_t *val;
1793 		(void) nvpair_value_int16_array(nvpair, &val, &nelem);
1794 		if (not_array == 1 || index >= nelem)
1795 			goto invalid;
1796 		valuep->t = UINT64;
1797 		valuep->v = (unsigned long long)val[index];
1798 		break;
1799 	}
1800 	case DATA_TYPE_UINT16_ARRAY: {
1801 		uint16_t *val;
1802 		(void) nvpair_value_uint16_array(nvpair, &val, &nelem);
1803 		if (not_array == 1 || index >= nelem)
1804 			goto invalid;
1805 		valuep->t = UINT64;
1806 		valuep->v = (unsigned long long)val[index];
1807 		break;
1808 	}
1809 	case DATA_TYPE_INT32_ARRAY: {
1810 		int32_t *val;
1811 		(void) nvpair_value_int32_array(nvpair, &val, &nelem);
1812 		if (not_array == 1 || index >= nelem)
1813 			goto invalid;
1814 		valuep->t = UINT64;
1815 		valuep->v = (unsigned long long)val[index];
1816 		break;
1817 	}
1818 	case DATA_TYPE_UINT32_ARRAY: {
1819 		uint32_t *val;
1820 		(void) nvpair_value_uint32_array(nvpair, &val, &nelem);
1821 		if (not_array == 1 || index >= nelem)
1822 			goto invalid;
1823 		valuep->t = UINT64;
1824 		valuep->v = (unsigned long long)val[index];
1825 		break;
1826 	}
1827 	case DATA_TYPE_INT64_ARRAY: {
1828 		int64_t *val;
1829 		(void) nvpair_value_int64_array(nvpair, &val, &nelem);
1830 		if (not_array == 1 || index >= nelem)
1831 			goto invalid;
1832 		valuep->t = UINT64;
1833 		valuep->v = (unsigned long long)val[index];
1834 		break;
1835 	}
1836 	case DATA_TYPE_UINT64_ARRAY: {
1837 		uint64_t *val;
1838 		(void) nvpair_value_uint64_array(nvpair, &val, &nelem);
1839 		if (not_array == 1 || index >= nelem)
1840 			goto invalid;
1841 		valuep->t = UINT64;
1842 		valuep->v = (unsigned long long)val[index];
1843 		break;
1844 	}
1845 
1846 	default :
1847 		out(O_DEBUG,
1848 		    "platform_payloadprop: unsupported data type for %s",
1849 		    propstr);
1850 		return (1);
1851 	}
1852 
1853 	return (0);
1854 
1855 invalid:
1856 	out(O_DEBUG, "platform_payloadprop: invalid array reference for %s",
1857 	    propstr);
1858 	return (1);
1859 }
1860