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