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 (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2019, Joyent, Inc. All rights reserved.
25 */
26
27
28#include <sys/fm/protocol.h>
29#include <fm/libtopo.h>
30#include <ctype.h>
31#include <fnmatch.h>
32#include <limits.h>
33#include <strings.h>
34#include <stdio.h>
35#include <errno.h>
36#include <umem.h>
37#include <zone.h>
38#include <sys/param.h>
39
40#define	FMTOPO_EXIT_SUCCESS	0
41#define	FMTOPO_EXIT_ERROR	1
42#define	FMTOPO_EXIT_USAGE	2
43
44#define	STDERR	"stderr"
45#define	ALL	"all"
46
47static const char *g_pname;
48static const char *g_fmri = NULL;
49
50static const char *opt_R = "/";
51static const char *opt_s = FM_FMRI_SCHEME_HC;
52static const char optstr[] = "bCdem:P:pR:s:StVx";
53static const char *opt_m;
54
55static int opt_b = 0;
56static int opt_d = 0;
57static int opt_e = 0;
58static int opt_p = 0;
59static int opt_S = 0;
60static int opt_t = 0;
61static int opt_V = 0;
62static int opt_x = 0;
63static int opt_all = 0;
64
65struct prop_args {
66	const char *group;
67	const char *prop;
68	const char *type;
69	const char *value;
70};
71
72static struct prop_args **pargs = NULL;
73static int pcnt = 0;
74
75static int
76usage(FILE *fp)
77{
78	(void) fprintf(fp,
79	    "Usage: %s [-bCedpSVx] [-P group.property[=type:value]] "
80	    "[-R root] [-m method] [-s scheme] [fmri]\n", g_pname);
81
82	(void) fprintf(fp,
83	    "\t-b  walk in sibling-first order (default is child-first)\n"
84	    "\t-C  dump core after completing execution\n"
85	    "\t-d  set debug mode for libtopo modules\n"
86	    "\t-e  display FMRIs as paths using esc/eft notation\n"
87	    "\t-m  execute given method\n"
88	    "\t-P  get/set specified properties\n"
89	    "\t-p  display of FMRI protocol properties\n"
90	    "\t-R  set root directory for libtopo plug-ins and other files\n"
91	    "\t-s  display topology for the specified FMRI scheme\n"
92	    "\t-S  display FMRI status (present/usable/occupied)\n"
93	    "\t-V  set verbose mode\n"
94	    "\t-x  display a xml formatted topology\n");
95
96	return (FMTOPO_EXIT_USAGE);
97}
98
99static topo_type_t
100str2type(const char *tstr)
101{
102	topo_type_t type;
103
104	if (tstr == NULL)
105		return (TOPO_TYPE_INVALID);
106
107	if (strcmp(tstr, "int32") == 0)
108		type = TOPO_TYPE_INT32;
109	else if (strcmp(tstr, "uint32") == 0)
110		type = TOPO_TYPE_UINT32;
111	else if (strcmp(tstr, "int64") == 0)
112		type = TOPO_TYPE_INT64;
113	else if (strcmp(tstr, "uint64") == 0)
114		type = TOPO_TYPE_UINT64;
115	else if (strcmp(tstr, "string") == 0)
116		type = TOPO_TYPE_STRING;
117	else if (strcmp(tstr, "fmri") == 0)
118		type = TOPO_TYPE_FMRI;
119	else {
120		type = TOPO_TYPE_INVALID;
121	}
122
123	return (type);
124}
125
126static void
127print_node(topo_hdl_t *thp, tnode_t *node, nvlist_t *nvl, const char *fmri)
128{
129	int err, ret;
130	boolean_t is_occupied;
131
132	(void) printf("%s\n", (char *)fmri);
133
134	if (opt_p && !(pcnt > 0 || opt_V || opt_all)) {
135		char *aname = NULL, *fname = NULL, *lname = NULL;
136		nvlist_t *asru = NULL;
137		nvlist_t *fru = NULL;
138
139		if (topo_node_asru(node, &asru, NULL, &err) == 0)
140			(void) topo_fmri_nvl2str(thp, asru, &aname, &err);
141		if (topo_node_fru(node, &fru, NULL, &err) == 0)
142			(void) topo_fmri_nvl2str(thp, fru, &fname, &err);
143		(void) topo_node_label(node, &lname, &err);
144		if (aname != NULL) {
145			nvlist_free(asru);
146			(void) printf("\tASRU: %s\n", aname);
147			topo_hdl_strfree(thp, aname);
148		} else {
149			(void) printf("\tASRU: -\n");
150		}
151		if (fname != NULL) {
152			nvlist_free(fru);
153			(void) printf("\tFRU: %s\n", fname);
154			topo_hdl_strfree(thp, fname);
155		} else {
156			(void) printf("\tFRU: -\n");
157		}
158		if (lname != NULL) {
159			(void) printf("\tLabel: %s\n", lname);
160			topo_hdl_strfree(thp, lname);
161		} else {
162			(void) printf("\tLabel: -\n");
163		}
164	}
165
166	if (opt_S) {
167		if ((ret = topo_fmri_present(thp, nvl, &err)) < 0)
168			(void) printf("\tPresent: -\n");
169		else
170			(void) printf("\tPresent: %s\n",
171			    ret ? "true" : "false");
172
173		if ((ret = topo_fmri_unusable(thp, nvl, &err)) < 0)
174			(void) printf("\tUnusable: -\n");
175		else
176			(void) printf("\tUnusable: %s\n",
177			    ret ? "true" : "false");
178
179		ret = topo_node_occupied(node, &is_occupied);
180		if (ret == 0)
181			(void) printf("\tOccupied: %s\n",
182			    is_occupied ? "true" : "false");
183		else if (ret != ETOPO_METHOD_NOTSUP)
184			(void) printf("\tOccupied: -\n");
185	}
186}
187
188static void
189print_everstyle(tnode_t *node)
190{
191	char buf[PATH_MAX], numbuf[64];
192	nvlist_t *fmri, **hcl;
193	int i, err;
194	uint_t n;
195
196	if (topo_prop_get_fmri(node, TOPO_PGROUP_PROTOCOL,
197	    TOPO_PROP_RESOURCE, &fmri, &err) < 0) {
198		(void) fprintf(stderr, "%s: failed to get fmri for %s=%d: %s\n",
199		    g_pname, topo_node_name(node),
200		    topo_node_instance(node), topo_strerror(err));
201		return;
202	}
203
204	if (nvlist_lookup_nvlist_array(fmri, FM_FMRI_HC_LIST, &hcl, &n) != 0) {
205		(void) fprintf(stderr, "%s: failed to find %s for %s=%d\n",
206		    g_pname, FM_FMRI_HC_LIST, topo_node_name(node),
207		    topo_node_instance(node));
208		nvlist_free(fmri);
209		return;
210	}
211
212	buf[0] = '\0';
213
214	for (i = 0; i < n; i++) {
215		char *name, *inst, *estr;
216		ulong_t ul;
217
218		if (nvlist_lookup_string(hcl[i], FM_FMRI_HC_NAME, &name) != 0 ||
219		    nvlist_lookup_string(hcl[i], FM_FMRI_HC_ID, &inst) != 0) {
220			(void) fprintf(stderr, "%s: failed to get "
221			    "name-instance for %s=%d\n", g_pname,
222			    topo_node_name(node), topo_node_instance(node));
223			nvlist_free(fmri);
224			return;
225		}
226
227		errno = 0;
228		ul = strtoul(inst, &estr, 10);
229
230		if (errno != 0 || estr == inst) {
231			(void) fprintf(stderr, "%s: instance %s does not "
232			    "convert to an unsigned integer\n", g_pname, inst);
233		}
234
235		(void) strlcat(buf, "/", sizeof (buf));
236		(void) strlcat(buf, name, sizeof (buf));
237		(void) snprintf(numbuf, sizeof (numbuf), "%u", ul);
238		(void) strlcat(buf, numbuf, sizeof (buf));
239	}
240	nvlist_free(fmri);
241
242	(void) printf("%s\n", buf);
243}
244
245static void
246print_prop_nameval(topo_hdl_t *thp, tnode_t *node, nvlist_t *nvl)
247{
248	int err;
249	topo_type_t type;
250	char *tstr, *propn, *factype;
251	nvpair_t *pv_nvp;
252	int i;
253	uint_t nelem;
254
255	if ((pv_nvp = nvlist_next_nvpair(nvl, NULL)) == NULL)
256		return;
257
258	/* Print property name */
259	if ((pv_nvp = nvlist_next_nvpair(nvl, NULL)) == NULL ||
260	    nvpair_name(pv_nvp) == NULL ||
261	    strcmp(TOPO_PROP_VAL_NAME, nvpair_name(pv_nvp)) != 0) {
262		(void) fprintf(stderr, "%s: malformed property name\n",
263		    g_pname);
264		return;
265	} else {
266		(void) nvpair_value_string(pv_nvp, &propn);
267	}
268
269	if ((pv_nvp = nvlist_next_nvpair(nvl, pv_nvp)) == NULL ||
270	    nvpair_name(pv_nvp) == NULL ||
271	    strcmp(nvpair_name(pv_nvp), TOPO_PROP_VAL_TYPE) != 0 ||
272	    nvpair_type(pv_nvp) != DATA_TYPE_UINT32)  {
273		(void) fprintf(stderr, "%s: malformed property type for %s\n",
274		    g_pname, propn);
275		return;
276	} else {
277		(void) nvpair_value_uint32(pv_nvp, (uint32_t *)&type);
278	}
279
280	switch (type) {
281		case TOPO_TYPE_BOOLEAN: tstr = "boolean"; break;
282		case TOPO_TYPE_INT32: tstr = "int32"; break;
283		case TOPO_TYPE_UINT32: tstr = "uint32"; break;
284		case TOPO_TYPE_INT64: tstr = "int64"; break;
285		case TOPO_TYPE_UINT64: tstr = "uint64"; break;
286		case TOPO_TYPE_DOUBLE: tstr = "double"; break;
287		case TOPO_TYPE_STRING: tstr = "string"; break;
288		case TOPO_TYPE_FMRI: tstr = "fmri"; break;
289		case TOPO_TYPE_INT32_ARRAY: tstr = "int32[]"; break;
290		case TOPO_TYPE_UINT32_ARRAY: tstr = "uint32[]"; break;
291		case TOPO_TYPE_INT64_ARRAY: tstr = "int64[]"; break;
292		case TOPO_TYPE_UINT64_ARRAY: tstr = "uint64[]"; break;
293		case TOPO_TYPE_STRING_ARRAY: tstr = "string[]"; break;
294		case TOPO_TYPE_FMRI_ARRAY: tstr = "fmri[]"; break;
295		default: tstr = "unknown type";
296	}
297
298	(void) printf("    %-17s %-8s ", propn, tstr);
299
300	/*
301	 * Get property value
302	 */
303	if (nvpair_name(pv_nvp) == NULL ||
304	    (pv_nvp = nvlist_next_nvpair(nvl, pv_nvp)) == NULL) {
305		(void) fprintf(stderr, "%s: malformed property value\n",
306		    g_pname);
307		return;
308	}
309
310	switch (nvpair_type(pv_nvp)) {
311		case DATA_TYPE_INT32: {
312			int32_t val;
313			(void) nvpair_value_int32(pv_nvp, &val);
314			(void) printf(" %d", val);
315			break;
316		}
317		case DATA_TYPE_UINT32: {
318			uint32_t val, type;
319			char val_str[49];
320			nvlist_t *fac, *rsrc = NULL;
321
322			(void) nvpair_value_uint32(pv_nvp, &val);
323			if (node == NULL || topo_node_flags(node) !=
324			    TOPO_NODE_FACILITY)
325				goto uint32_def;
326
327			if (topo_node_resource(node, &rsrc, &err) != 0)
328				goto uint32_def;
329
330			if (nvlist_lookup_nvlist(rsrc, "facility", &fac) != 0)
331				goto uint32_def;
332
333			if (nvlist_lookup_string(fac, FM_FMRI_FACILITY_TYPE,
334			    &factype) != 0)
335				goto uint32_def;
336
337			nvlist_free(rsrc);
338			rsrc = NULL;
339
340			/*
341			 * Special case code to do friendlier printing of
342			 * facility node properties
343			 */
344			if ((strcmp(propn, TOPO_FACILITY_TYPE) == 0) &&
345			    (strcmp(factype, TOPO_FAC_TYPE_SENSOR) == 0)) {
346				topo_sensor_type_name(val, val_str, 48);
347				(void) printf(" 0x%x (%s)", val, val_str);
348				break;
349			} else if ((strcmp(propn, TOPO_FACILITY_TYPE) == 0) &&
350			    (strcmp(factype, TOPO_FAC_TYPE_INDICATOR) == 0)) {
351				topo_led_type_name(val, val_str, 48);
352				(void) printf(" 0x%x (%s)", val, val_str);
353				break;
354			} else if (strcmp(propn, TOPO_SENSOR_UNITS) == 0) {
355				topo_sensor_units_name(val, val_str, 48);
356				(void) printf(" 0x%x (%s)", val, val_str);
357				break;
358			} else if (strcmp(propn, TOPO_LED_MODE) == 0) {
359				topo_led_state_name(val, val_str, 48);
360				(void) printf(" 0x%x (%s)", val, val_str);
361				break;
362			} else if ((strcmp(propn, TOPO_SENSOR_STATE) == 0) &&
363			    (strcmp(factype, TOPO_FAC_TYPE_SENSOR) == 0)) {
364				if (topo_prop_get_uint32(node,
365				    TOPO_PGROUP_FACILITY, TOPO_FACILITY_TYPE,
366				    &type, &err) != 0) {
367					goto uint32_def;
368				}
369				topo_sensor_state_name(type, val, val_str, 48);
370				(void) printf(" 0x%x (%s)", val, val_str);
371				break;
372			}
373uint32_def:
374			(void) printf(" 0x%x", val);
375			nvlist_free(rsrc);
376			break;
377		}
378		case DATA_TYPE_INT64: {
379			int64_t val;
380			(void) nvpair_value_int64(pv_nvp, &val);
381			(void) printf(" %lld", (longlong_t)val);
382			break;
383		}
384		case DATA_TYPE_UINT64: {
385			uint64_t val;
386			(void) nvpair_value_uint64(pv_nvp, &val);
387			(void) printf(" 0x%llx", (u_longlong_t)val);
388			break;
389		}
390		case DATA_TYPE_DOUBLE: {
391			double val;
392			(void) nvpair_value_double(pv_nvp, &val);
393			(void) printf(" %lf", (double)val);
394			break;
395		}
396		case DATA_TYPE_STRING: {
397			char *val;
398			(void) nvpair_value_string(pv_nvp, &val);
399			(void) printf(" %s", val);
400			break;
401		}
402		case DATA_TYPE_NVLIST: {
403			nvlist_t *val;
404			char *fmri;
405			(void) nvpair_value_nvlist(pv_nvp, &val);
406			if (topo_fmri_nvl2str(thp, val, &fmri, &err) != 0) {
407				(void) fprintf(stderr, "failed to convert "
408				    "FMRI to string: (%s)\n",
409				    topo_strerror(err));
410				nvlist_print(stdout, nvl);
411				break;
412			}
413			(void) printf(" %s", fmri);
414			topo_hdl_strfree(thp, fmri);
415			break;
416		}
417		case DATA_TYPE_INT32_ARRAY: {
418			int32_t *val;
419
420			(void) nvpair_value_int32_array(pv_nvp, &val, &nelem);
421			(void) printf(" [ ");
422			for (i = 0; i < nelem; i++)
423				(void) printf("%d ", val[i]);
424			(void) printf("]");
425			break;
426		}
427		case DATA_TYPE_UINT32_ARRAY: {
428			uint32_t *val;
429
430			(void) nvpair_value_uint32_array(pv_nvp, &val, &nelem);
431			(void) printf(" [ ");
432			for (i = 0; i < nelem; i++)
433				(void) printf("%u ", val[i]);
434			(void) printf("]");
435			break;
436		}
437		case DATA_TYPE_INT64_ARRAY: {
438			int64_t *val;
439
440			(void) nvpair_value_int64_array(pv_nvp, &val, &nelem);
441			(void) printf(" [ ");
442			for (i = 0; i < nelem; i++)
443				(void) printf("%lld ", val[i]);
444			(void) printf("]");
445			break;
446		}
447		case DATA_TYPE_UINT64_ARRAY: {
448			uint64_t *val;
449
450			(void) nvpair_value_uint64_array(pv_nvp, &val, &nelem);
451			(void) printf(" [ ");
452			for (i = 0; i < nelem; i++)
453				(void) printf("%llu ", val[i]);
454			(void) printf("]");
455			break;
456		}
457		case DATA_TYPE_STRING_ARRAY: {
458			char **val;
459
460			(void) nvpair_value_string_array(pv_nvp, &val, &nelem);
461			(void) printf(" [ ");
462			for (i = 0; i < nelem; i++)
463				(void) printf("\"%s\" ", val[i]);
464			(void) printf("]");
465			break;
466		}
467		case DATA_TYPE_NVLIST_ARRAY: {
468			nvlist_t **val;
469			char *fmri;
470			int ret;
471
472			(void) nvpair_value_nvlist_array(pv_nvp, &val, &nelem);
473			(void) printf(" [ ");
474			for (i = 0; i < nelem; i++) {
475				ret = topo_fmri_nvl2str(thp, val[i], &fmri,
476				    &err);
477				if (ret != 0) {
478					(void) fprintf(stderr, "failed to "
479					    "convert FMRI to string (%s)\n",
480					    topo_strerror(err));
481					nvlist_print(stdout, val[i]);
482					break;
483				}
484				(void) printf("\"%s\" ", fmri);
485				topo_hdl_strfree(thp, fmri);
486			}
487			(void) printf("]");
488			break;
489		}
490		default:
491			(void) fprintf(stderr, " unknown data type (%d)",
492			    nvpair_type(pv_nvp));
493			break;
494		}
495		(void) printf("\n");
496}
497
498static void
499print_pgroup(topo_hdl_t *thp, tnode_t *node, const char *pgn, char *dstab,
500    char *nstab, int32_t version)
501{
502	int err;
503	topo_pgroup_info_t *pgi = NULL;
504
505	if (pgn == NULL)
506		return;
507
508	if (node != NULL && (dstab == NULL || nstab == NULL || version == -1)) {
509		if ((pgi = topo_pgroup_info(node, pgn, &err)) != NULL) {
510			dstab = (char *)topo_stability2name(pgi->tpi_datastab);
511			nstab = (char *)topo_stability2name(pgi->tpi_namestab);
512			version = pgi->tpi_version;
513		}
514	}
515
516	if (dstab == NULL || nstab == NULL || version == -1) {
517		(void) printf("  group: %-30s version: - stability: -/-\n",
518		    pgn);
519	} else {
520		(void) printf("  group: %-30s version: %-3d stability: %s/%s\n",
521		    pgn, version, nstab, dstab);
522	}
523
524	if (pgi != NULL) {
525		topo_hdl_strfree(thp, (char *)pgi->tpi_name);
526		topo_hdl_free(thp, pgi, sizeof (topo_pgroup_info_t));
527	}
528}
529
530static void
531print_all_props(topo_hdl_t *thp, tnode_t *node, nvlist_t *p_nv,
532    const char *group)
533{
534	char *pgn = NULL, *dstab = NULL, *nstab = NULL;
535	int32_t version;
536	nvlist_t *pg_nv, *pv_nv;
537	nvpair_t *nvp, *pg_nvp;
538	int pg_done, match, all = strcmp(group, ALL) == 0;
539
540	for (nvp = nvlist_next_nvpair(p_nv, NULL); nvp != NULL;
541	    nvp = nvlist_next_nvpair(p_nv, nvp)) {
542		if (strcmp(TOPO_PROP_GROUP, nvpair_name(nvp)) != 0 ||
543		    nvpair_type(nvp) != DATA_TYPE_NVLIST)
544			continue;
545
546		nstab = NULL;
547		dstab = NULL;
548		version = -1;
549		pg_done = match = 0;
550		(void) nvpair_value_nvlist(nvp, &pg_nv);
551		for (pg_nvp = nvlist_next_nvpair(pg_nv, NULL); pg_nvp != NULL;
552		    pg_nvp = nvlist_next_nvpair(pg_nv, pg_nvp)) {
553			/*
554			 * Print property group name and stability levels
555			 */
556			if (strcmp(TOPO_PROP_GROUP_NAME, nvpair_name(pg_nvp))
557			    == 0 && nvpair_type(pg_nvp) == DATA_TYPE_STRING) {
558				(void) nvpair_value_string(pg_nvp, &pgn);
559				match = strcmp(group, pgn) == 0;
560				continue;
561			}
562
563			if (strcmp(TOPO_PROP_GROUP_NSTAB,
564			    nvpair_name(pg_nvp)) == 0 &&
565			    nvpair_type(pg_nvp) == DATA_TYPE_STRING) {
566				(void) nvpair_value_string(pg_nvp, &nstab);
567				continue;
568			}
569
570			if (strcmp(TOPO_PROP_GROUP_DSTAB,
571			    nvpair_name(pg_nvp)) == 0 &&
572			    nvpair_type(pg_nvp) == DATA_TYPE_STRING) {
573				(void) nvpair_value_string(pg_nvp, &dstab);
574				continue;
575			}
576
577			if (strcmp(TOPO_PROP_GROUP_VERSION,
578			    nvpair_name(pg_nvp)) == 0 &&
579			    nvpair_type(pg_nvp) == DATA_TYPE_INT32) {
580				(void) nvpair_value_int32(pg_nvp, &version);
581				continue;
582			}
583
584			if ((match || all) && !pg_done) {
585				print_pgroup(thp, node, pgn, dstab, nstab,
586				    version);
587				pg_done++;
588			}
589
590			/*
591			 * Print property group and property name-value pair
592			 */
593			if (strcmp(TOPO_PROP_VAL, nvpair_name(pg_nvp))
594			    == 0 && nvpair_type(pg_nvp) == DATA_TYPE_NVLIST) {
595				(void) nvpair_value_nvlist(pg_nvp, &pv_nv);
596				if ((match || all) && pg_done) {
597					print_prop_nameval(thp, node, pv_nv);
598				}
599
600			}
601
602		}
603		if (match && !all)
604			return;
605	}
606}
607
608static void
609set_prop(topo_hdl_t *thp, tnode_t *node, nvlist_t *fmri, struct prop_args *pp)
610{
611	int ret, err = 0;
612	topo_type_t type;
613	nvlist_t *nvl = NULL;
614	char *end;
615
616	if (pp->prop == NULL || pp->type == NULL || pp->value == NULL)
617		goto out;
618
619	if ((type = str2type(pp->type)) == TOPO_TYPE_INVALID) {
620		(void) fprintf(stderr, "%s: invalid property type %s for %s\n",
621		    g_pname, pp->type, pp->prop);
622		goto out;
623	}
624
625	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
626		(void) fprintf(stderr, "%s: nvlist allocation failed for "
627		    "%s=%s:%s\n", g_pname, pp->prop, pp->type, pp->value);
628		goto out;
629	}
630	ret = nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, pp->prop);
631	ret |= nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, type);
632	if (ret != 0) {
633		(void) fprintf(stderr, "%s: invalid property type %s for %s\n",
634		    g_pname, pp->type, pp->prop);
635		goto out;
636	}
637
638	errno = 0;
639	switch (type) {
640		case TOPO_TYPE_INT32:
641		{
642			int32_t val;
643
644			val = strtol(pp->value, &end, 0);
645			if (errno == ERANGE) {
646				ret = -1;
647				break;
648			}
649			ret = nvlist_add_int32(nvl, TOPO_PROP_VAL_VAL, val);
650			break;
651		}
652		case TOPO_TYPE_UINT32:
653		{
654			uint32_t val;
655
656			val = strtoul(pp->value, &end, 0);
657			if (errno == ERANGE) {
658				ret = -1;
659				break;
660			}
661			ret = nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, val);
662			break;
663		}
664		case TOPO_TYPE_INT64:
665		{
666			int64_t val;
667
668			val = strtoll(pp->value, &end, 0);
669			if (errno == ERANGE) {
670				ret = -1;
671				break;
672			}
673			ret = nvlist_add_int64(nvl, TOPO_PROP_VAL_VAL, val);
674			break;
675		}
676		case TOPO_TYPE_UINT64:
677		{
678			uint64_t val;
679
680			val = strtoull(pp->value, &end, 0);
681			if (errno == ERANGE) {
682				ret = -1;
683				break;
684			}
685			ret = nvlist_add_uint64(nvl, TOPO_PROP_VAL_VAL, val);
686			break;
687		}
688		case TOPO_TYPE_STRING:
689		{
690			ret = nvlist_add_string(nvl, TOPO_PROP_VAL_VAL,
691			    pp->value);
692			break;
693		}
694		case TOPO_TYPE_FMRI:
695		{
696			nvlist_t *val = NULL;
697
698			if ((ret = topo_fmri_str2nvl(thp, pp->value, &val,
699			    &err)) < 0)
700				break;
701
702			if ((ret = nvlist_add_nvlist(nvl, TOPO_PROP_VAL_VAL,
703			    val)) != 0)
704				err = ETOPO_PROP_NVL;
705
706			nvlist_free(val);
707			break;
708		}
709		default:
710			ret = -1;
711	}
712
713	if (ret != 0) {
714		(void) fprintf(stderr, "%s: unable to set property value for "
715		    "%s: %s\n", g_pname, pp->prop,  topo_strerror(err));
716		goto out;
717	}
718
719	if (node != NULL) {
720		if ((ret = topo_prop_setprop(node, pp->group, nvl,
721		    TOPO_PROP_MUTABLE, nvl, &err)) < 0) {
722			(void) fprintf(stderr, "%s: unable to set property "
723			    "value for " "%s=%s:%s: %s\n", g_pname, pp->prop,
724			    pp->type, pp->value, topo_strerror(err));
725			goto out;
726		}
727	} else {
728		if ((ret = topo_fmri_setprop(thp, fmri,  pp->group, nvl,
729		    TOPO_PROP_MUTABLE, nvl, &err)) < 0) {
730			(void) fprintf(stderr, "%s: unable to set property "
731			    "value for " "%s=%s:%s: %s\n", g_pname, pp->prop,
732			    pp->type, pp->value, topo_strerror(err));
733			goto out;
734		}
735	}
736
737	nvlist_free(nvl);
738	nvl = NULL;
739
740	/*
741	 * Now, get the property back for printing
742	 */
743	if (node != NULL) {
744		if ((ret = topo_prop_getprop(node, pp->group, pp->prop, NULL,
745		    &nvl, &err)) < 0) {
746			(void) fprintf(stderr, "%s: failed to get %s.%s: %s\n",
747			    g_pname, pp->group, pp->prop, topo_strerror(err));
748			goto out;
749		}
750	} else {
751		if ((ret = topo_fmri_getprop(thp, fmri, pp->group, pp->prop,
752		    NULL, &nvl, &err)) < 0) {
753			(void) fprintf(stderr, "%s: failed to get %s.%s: %s\n",
754			    g_pname, pp->group, pp->prop, topo_strerror(err));
755			goto out;
756		}
757	}
758
759	print_pgroup(thp, node, pp->group, NULL, NULL, 0);
760	print_prop_nameval(thp, node, nvl);
761
762out:
763	nvlist_free(nvl);
764}
765
766static void
767print_props(topo_hdl_t *thp, tnode_t *node)
768{
769	int i, err;
770	nvlist_t *nvl;
771	struct prop_args *pp;
772
773	if (pcnt == 0)
774		return;
775
776	for (i = 0; i < pcnt; ++i) {
777		pp = pargs[i];
778
779		if (pp->group == NULL)
780			continue;
781
782		/*
783		 * If we have a valid value, this is a request to
784		 * set a property.  Otherwise, just print the property
785		 * group and any specified properties.
786		 */
787		if (pp->value == NULL) {
788			if (pp->prop == NULL) {
789
790				/*
791				 * Print all properties in this group
792				 */
793				if ((nvl = topo_prop_getprops(node, &err))
794				    == NULL) {
795					(void) fprintf(stderr, "%s: failed to "
796					    "get %s: %s\n", g_pname,
797					    pp->group,
798					    topo_strerror(err));
799					continue;
800				} else {
801					print_all_props(thp, node, nvl,
802					    pp->group);
803					nvlist_free(nvl);
804					continue;
805				}
806			}
807			if (topo_prop_getprop(node, pp->group, pp->prop,
808			    NULL, &nvl, &err) < 0) {
809				(void) fprintf(stderr, "%s: failed to get "
810				    "%s.%s: %s\n", g_pname,
811				    pp->group, pp->prop,
812				    topo_strerror(err));
813				continue;
814			} else {
815				print_pgroup(thp, node, pp->group, NULL,
816				    NULL, 0);
817				print_prop_nameval(thp, node, nvl);
818				nvlist_free(nvl);
819			}
820		} else {
821			set_prop(thp, node, NULL, pp);
822		}
823	}
824}
825
826/*ARGSUSED*/
827static int
828walk_node(topo_hdl_t *thp, tnode_t *node, void *arg)
829{
830	int err;
831	nvlist_t *nvl;
832	nvlist_t *rsrc, *out;
833	char *s;
834
835	if (opt_e && strcmp(opt_s, FM_FMRI_SCHEME_HC) == 0) {
836		print_everstyle(node);
837		return (TOPO_WALK_NEXT);
838	}
839
840	if (topo_node_resource(node, &rsrc, &err) < 0) {
841		(void) fprintf(stderr, "%s: failed to get resource: "
842		    "%s", g_pname, topo_strerror(err));
843		return (TOPO_WALK_NEXT);
844	}
845	if (topo_fmri_nvl2str(thp, rsrc, &s, &err) < 0) {
846		(void) fprintf(stderr, "%s: failed to convert "
847		    "resource to FMRI string: %s", g_pname,
848		    topo_strerror(err));
849		nvlist_free(rsrc);
850		return (TOPO_WALK_NEXT);
851	}
852
853	if (g_fmri != NULL && fnmatch(g_fmri, s, 0) != 0) {
854		nvlist_free(rsrc);
855		topo_hdl_strfree(thp, s);
856		return (TOPO_WALK_NEXT);
857	}
858
859	print_node(thp, node, rsrc, s);
860	topo_hdl_strfree(thp, s);
861	nvlist_free(rsrc);
862
863	if (opt_m != NULL) {
864		if (topo_method_invoke(node, opt_m, 0, NULL, &out, &err) == 0) {
865			nvlist_print(stdout, out);
866			nvlist_free(out);
867		} else if (err != ETOPO_METHOD_NOTSUP)
868			(void) fprintf(stderr, "%s: method failed unexpectedly "
869			    "on %s=%d (%s)\n", g_pname, topo_node_name(node),
870			    topo_node_instance(node), topo_strerror(err));
871	}
872
873	if (opt_V || opt_all) {
874		if ((nvl = topo_prop_getprops(node, &err)) == NULL) {
875			(void) fprintf(stderr, "%s: failed to get "
876			    "properties for %s=%d: %s\n", g_pname,
877			    topo_node_name(node), topo_node_instance(node),
878			    topo_strerror(err));
879		} else {
880			print_all_props(thp, node, nvl, ALL);
881			nvlist_free(nvl);
882		}
883	} else if (pcnt > 0)
884		print_props(thp, node);
885
886	(void) printf("\n");
887
888	return (TOPO_WALK_NEXT);
889}
890
891static void
892get_pargs(int argc, char *argv[])
893{
894	struct prop_args *pp;
895	char c, *s, *p;
896	int i = 0;
897
898	if ((pargs = malloc(sizeof (struct prop_args *) * pcnt)) == NULL) {
899		(void) fprintf(stderr, "%s: failed to allocate property "
900		    "arguments\n", g_pname);
901		return;
902	}
903
904	for (optind = 1; (c = getopt(argc, argv, optstr)) != EOF; ) {
905		if (c == 'P') {
906
907			if (strcmp(optarg, ALL) == 0) {
908				opt_all++;
909				break;
910			}
911
912			if ((pp = pargs[i] = malloc(sizeof (struct prop_args)))
913			    == NULL) {
914				(void) fprintf(stderr, "%s: failed to "
915				    "allocate propertyarguments\n", g_pname);
916				return;
917			}
918			++i;
919			pp->group = NULL;
920			pp->prop = NULL;
921			pp->type = NULL;
922			pp->value = NULL;
923
924			p = optarg;
925			if ((s = strchr(p, '.')) != NULL) {
926				*s++ = '\0'; /* strike out delimiter */
927				pp->group = p;
928				p = s;
929				if ((s = strchr(p, '=')) != NULL) {
930					*s++ = '\0'; /* strike out delimiter */
931					pp->prop = p;
932					p = s;
933					if ((s = strchr(p, ':')) != NULL) {
934						*s++ = '\0';
935						pp->type = p;
936						pp->value = s;
937					} else {
938						(void) fprintf(stderr, "%s: "
939						    "property type not "
940						    "specified for assignment "
941						    " of %s.%s\n", g_pname,
942						    pp->group, pp->prop);
943						break;
944					}
945				} else {
946					pp->prop = p;
947				}
948			} else {
949				pp->group = p;
950			}
951			if (i >= pcnt)
952				break;
953		}
954	}
955
956	if (opt_all > 0) {
957		int j;
958
959		for (j = 0; j < i; ++j)
960			free(pargs[i]);
961		free(pargs);
962		pargs = NULL;
963	}
964}
965
966static int
967walk_topo(topo_hdl_t *thp, char *uuid)
968{
969	int err;
970	topo_walk_t *twp;
971	int flag;
972
973	if (getzoneid() != GLOBAL_ZONEID &&
974	    strcmp(opt_s, FM_FMRI_SCHEME_HC) == 0) {
975		return (0);
976	}
977
978	if ((twp = topo_walk_init(thp, opt_s, walk_node, NULL, &err))
979	    == NULL) {
980		(void) fprintf(stderr, "%s: failed to walk %s topology:"
981		    " %s\n", g_pname, opt_s, topo_strerror(err));
982
983		return (-1);
984	}
985
986	/*
987	 * Print standard header
988	 */
989	if (!opt_e) {
990		char buf[32];
991		time_t tod = time(NULL);
992
993		(void) printf("TIME                 UUID\n");
994		(void) strftime(buf, sizeof (buf), "%b %d %T", localtime(&tod));
995		(void) printf("%-15s %-32s\n", buf, uuid);
996		(void) printf("\n");
997	}
998
999	flag = opt_b != 0 ? TOPO_WALK_SIBLING : TOPO_WALK_CHILD;
1000
1001	if (topo_walk_step(twp, flag) == TOPO_WALK_ERR) {
1002		(void) fprintf(stderr, "%s: failed to walk topology\n",
1003		    g_pname);
1004		topo_walk_fini(twp);
1005		return (-1);
1006	}
1007
1008	topo_walk_fini(twp);
1009
1010	return (0);
1011}
1012
1013static void
1014print_fmri_pgroup(topo_hdl_t *thp, const char *pgn, nvlist_t *nvl)
1015{
1016	char *dstab = NULL, *nstab = NULL;
1017	int32_t version = -1;
1018	nvlist_t *pnvl;
1019	nvpair_t *pnvp;
1020
1021	(void) nvlist_lookup_string(nvl, TOPO_PROP_GROUP_NSTAB, &nstab);
1022	(void) nvlist_lookup_string(nvl, TOPO_PROP_GROUP_DSTAB, &dstab);
1023	(void) nvlist_lookup_int32(nvl, TOPO_PROP_GROUP_VERSION, &version);
1024
1025	print_pgroup(thp, NULL, pgn, dstab, nstab, version);
1026
1027	for (pnvp = nvlist_next_nvpair(nvl, NULL); pnvp != NULL;
1028	    pnvp = nvlist_next_nvpair(nvl, pnvp)) {
1029
1030		/*
1031		 * Print property group and property name-value pair
1032		 */
1033		if (strcmp(TOPO_PROP_VAL, nvpair_name(pnvp))
1034		    == 0 && nvpair_type(pnvp) == DATA_TYPE_NVLIST) {
1035			(void) nvpair_value_nvlist(pnvp, &pnvl);
1036				print_prop_nameval(thp, NULL, pnvl);
1037
1038		}
1039
1040	}
1041}
1042
1043static void
1044print_fmri_props(topo_hdl_t *thp, nvlist_t *nvl)
1045{
1046	int i, err;
1047	struct prop_args *pp;
1048	nvlist_t *pnvl;
1049
1050	for (i = 0; i < pcnt; ++i) {
1051		pp = pargs[i];
1052
1053		if (pp->group == NULL)
1054			continue;
1055
1056		pnvl = NULL;
1057
1058		/*
1059		 * If we have a valid value, this is a request to
1060		 * set a property.  Otherwise, just print the property
1061		 * group and any specified properties.
1062		 */
1063		if (pp->value == NULL) {
1064			if (pp->prop == NULL) {
1065
1066				/*
1067				 * Print all properties in this group
1068				 */
1069				if (topo_fmri_getpgrp(thp, nvl, pp->group,
1070				    &pnvl, &err) < 0) {
1071					(void) fprintf(stderr, "%s: failed to "
1072					    "get group %s: %s\n", g_pname,
1073					    pp->group, topo_strerror(err));
1074					continue;
1075				} else {
1076					print_fmri_pgroup(thp, pp->group,
1077					    pnvl);
1078					nvlist_free(pnvl);
1079					continue;
1080				}
1081			}
1082			if (topo_fmri_getprop(thp, nvl, pp->group, pp->prop,
1083			    NULL, &pnvl, &err) < 0) {
1084				(void) fprintf(stderr, "%s: failed to get "
1085				    "%s.%s: %s\n", g_pname,
1086				    pp->group, pp->prop,
1087				    topo_strerror(err));
1088				continue;
1089			} else {
1090				print_fmri_pgroup(thp, pp->group, pnvl);
1091				print_prop_nameval(thp, NULL, pnvl);
1092				nvlist_free(nvl);
1093			}
1094		} else {
1095			set_prop(thp, NULL, nvl, pp);
1096		}
1097	}
1098}
1099
1100void
1101print_fmri(topo_hdl_t *thp, char *uuid)
1102{
1103	int ret, err;
1104	nvlist_t *nvl;
1105	char buf[32];
1106	time_t tod = time(NULL);
1107
1108	if (topo_fmri_str2nvl(thp, g_fmri, &nvl, &err) < 0) {
1109		(void) fprintf(stderr, "%s: failed to convert %s to nvlist: "
1110		    "%s\n", g_pname, g_fmri, topo_strerror(err));
1111		return;
1112	}
1113
1114	(void) printf("TIME                 UUID\n");
1115	(void) strftime(buf, sizeof (buf), "%b %d %T", localtime(&tod));
1116	(void) printf("%-15s %-32s\n", buf, uuid);
1117	(void) printf("\n");
1118
1119	(void) printf("%s\n", (char *)g_fmri);
1120
1121	if (opt_p && !(pcnt > 0 || opt_V || opt_all)) {
1122		char *aname = NULL, *fname = NULL, *lname = NULL;
1123		nvlist_t *asru = NULL;
1124		nvlist_t *fru = NULL;
1125
1126		if (topo_fmri_asru(thp, nvl, &asru, &err) == 0)
1127			(void) topo_fmri_nvl2str(thp, asru, &aname, &err);
1128		if (topo_fmri_fru(thp, nvl, &fru, &err) == 0)
1129			(void) topo_fmri_nvl2str(thp, fru, &fname, &err);
1130		(void) topo_fmri_label(thp, nvl, &lname, &err);
1131
1132		nvlist_free(fru);
1133		nvlist_free(asru);
1134
1135		if (aname != NULL) {
1136			(void) printf("\tASRU: %s\n", aname);
1137			topo_hdl_strfree(thp, aname);
1138		} else {
1139			(void) printf("\tASRU: -\n");
1140		}
1141		if (fname != NULL) {
1142			(void) printf("\tFRU: %s\n", fname);
1143			topo_hdl_strfree(thp, fname);
1144		} else {
1145			(void) printf("\tFRU: -\n");
1146		}
1147		if (lname != NULL) {
1148			(void) printf("\tLabel: %s\n", lname);
1149			topo_hdl_strfree(thp, lname);
1150		} else {
1151			(void) printf("\tLabel: -\n");
1152		}
1153	}
1154
1155	if (opt_S) {
1156		if (topo_fmri_str2nvl(thp, g_fmri, &nvl, &err) < 0) {
1157			(void) printf("\tPresent: -\n");
1158			(void) printf("\tUnusable: -\n");
1159			return;
1160		}
1161
1162		if ((ret = topo_fmri_present(thp, nvl, &err)) < 0)
1163			(void) printf("\tPresent: -\n");
1164		else
1165			(void) printf("\tPresent: %s\n",
1166			    ret ? "true" : "false");
1167
1168		if ((ret = topo_fmri_unusable(thp, nvl, &err)) < 0)
1169			(void) printf("\tUnusable: -\n");
1170		else
1171			(void) printf("\tUnusable: %s\n",
1172			    ret ? "true" : "false");
1173
1174		nvlist_free(nvl);
1175	}
1176
1177	if (pargs && pcnt > 0)
1178		print_fmri_props(thp, nvl);
1179}
1180
1181int
1182fmtopo_exit(topo_hdl_t *thp, char *uuid, int err)
1183{
1184	if (uuid != NULL)
1185		topo_hdl_strfree(thp, uuid);
1186
1187	if (thp != NULL) {
1188		topo_snap_release(thp);
1189		topo_close(thp);
1190	}
1191
1192	if (pargs) {
1193		int i;
1194		for (i = 0; i < pcnt; ++i)
1195			free(pargs[i]);
1196		free(pargs);
1197	}
1198
1199	return (err);
1200}
1201
1202int
1203main(int argc, char *argv[])
1204{
1205	topo_hdl_t *thp = NULL;
1206	char *uuid = NULL;
1207	int c, err = 0;
1208
1209	g_pname = argv[0];
1210
1211	while (optind < argc) {
1212		while ((c = getopt(argc, argv, optstr)) != -1) {
1213			switch (c) {
1214			case 'b':
1215				opt_b++;
1216				break;
1217			case 'C':
1218				(void) atexit(abort);
1219				break;
1220			case 'd':
1221				opt_d++;
1222				break;
1223			case 'e':
1224				opt_e++;
1225				break;
1226			case 'm':
1227				opt_m = optarg;
1228				break;
1229			case 'P':
1230				pcnt++;
1231				break;
1232			case 'p':
1233				opt_p++;
1234				break;
1235			case 'V':
1236				opt_V++;
1237				break;
1238			case 'R':
1239				opt_R = optarg;
1240				break;
1241			case 's':
1242				opt_s = optarg;
1243				break;
1244			case 'S':
1245				opt_S++;
1246				break;
1247			case 't':
1248				opt_t++;
1249				break;
1250			case 'x':
1251				opt_x++;
1252				break;
1253			default:
1254				return (usage(stderr));
1255			}
1256		}
1257
1258		if (optind < argc) {
1259			if (g_fmri != NULL) {
1260				(void) fprintf(stderr, "%s: illegal argument "
1261				    "-- %s\n", g_pname, argv[optind]);
1262				return (FMTOPO_EXIT_USAGE);
1263			} else {
1264				g_fmri = argv[optind++];
1265			}
1266		}
1267	}
1268
1269	if (pcnt > 0)
1270		get_pargs(argc, argv);
1271
1272	if ((thp = topo_open(TOPO_VERSION, opt_R, &err)) == NULL) {
1273		(void) fprintf(stderr, "%s: failed to open topology tree: %s\n",
1274		    g_pname, topo_strerror(err));
1275		return (fmtopo_exit(thp, uuid, FMTOPO_EXIT_ERROR));
1276	}
1277
1278	if (opt_d)
1279		topo_debug_set(thp, "module", "stderr");
1280
1281	if ((uuid = topo_snap_hold(thp, NULL, &err)) == NULL) {
1282		(void) fprintf(stderr, "%s: failed to snapshot topology: %s\n",
1283		    g_pname, topo_strerror(err));
1284		return (fmtopo_exit(thp, uuid, FMTOPO_EXIT_ERROR));
1285	} else if (err != 0) {
1286		(void) fprintf(stderr, "%s: topology snapshot incomplete%s\n",
1287		    g_pname, getzoneid() != GLOBAL_ZONEID &&
1288		    strcmp(opt_s, FM_FMRI_SCHEME_HC) == 0 ?
1289		    " (" FM_FMRI_SCHEME_HC " scheme does not enumerate "
1290		    "in a non-global zone)": "");
1291	}
1292
1293	if (opt_x) {
1294		if (opt_b) {
1295			(void) fprintf(stderr,
1296			    "%s: -b and -x cannot be specified together\n",
1297			    g_pname);
1298			return (fmtopo_exit(thp, uuid, FMTOPO_EXIT_USAGE));
1299		}
1300
1301		err = 0;
1302		if (topo_xml_print(thp, stdout, opt_s, &err) < 0)
1303			(void) fprintf(stderr, "%s: failed to print xml "
1304			    "formatted topology:%s",  g_pname,
1305			    topo_strerror(err));
1306
1307		return (fmtopo_exit(thp, uuid, err ? FMTOPO_EXIT_ERROR :
1308		    FMTOPO_EXIT_SUCCESS));
1309	}
1310
1311	if (opt_t || walk_topo(thp, uuid) < 0) {
1312		if (g_fmri != NULL)
1313			/*
1314			 * Try getting some useful information
1315			 */
1316			print_fmri(thp, uuid);
1317
1318		return (fmtopo_exit(thp, uuid, FMTOPO_EXIT_ERROR));
1319	}
1320
1321	return (fmtopo_exit(thp, uuid, FMTOPO_EXIT_SUCCESS));
1322}
1323