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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <alloca.h>
27 #include <assert.h>
28 #include <errno.h>
29 #include <libintl.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <wchar.h>
36 #include <wctype.h>
37
38 #include "fru_tag.h"
39 #include "libfrup.h"
40 #include "libfrureg.h"
41
42
43 #define NUM_ITER_BYTES 4
44
45 #define HEAD_ITER 0
46 #define TAIL_ITER 1 /* not used */
47 #define NUM_ITER 2
48 #define MAX_ITER 3
49
50 #define INDENT 3
51 #define TIMESTRINGLEN 128
52 #define TEMPERATURE_OFFSET 73
53 #define MIN_VERSION 17
54 #define GMT "%a, %b %d %Y %H:%M:%S GMT"
55
56 typedef struct
57 {
58 uint8_t value;
59 char *data;
60 } Status_CurrentR;
61
62 Status_CurrentR Status_CurrentR_status[] = {
63 { 0x00, "OK"},
64 { 0x04, "DEEMED FAULTY"},
65 { 0x08, "FRU DETECTED"},
66 { 0x0c, "FRU DETECTED, DEEMED FAULTY"},
67 { 0x10, "PROXIED FAULT"},
68 { 0x14, "DEEMED FAULTY. Also PROXIED FAULT"},
69 { 0x18, "FRU DETECTED. Also PROXIED FAULT"},
70 { 0x1c, "FRU DETECTED, DEEMED FAULTY. Also PROXIED FAULT"},
71 { 0x20, "SUSPECT"},
72 { 0x24, "SUSPECT, DEEMED FAULTY"},
73 { 0x28, "SUSPECT, FRU DETECTED"},
74 { 0x2c, "SUSPECT, FRU DETECTED, DEEMED FAULTY"},
75 { 0x30, "SUSPECT. Also PROXIED FAULT"},
76 { 0x34, "SUSPECT, DEEMED FAULTY. Also PROXIED FAULT"},
77 { 0x38, "SUSPECT, FRU DETECTED. Also PROXIED FAULT"},
78 { 0x3c, "SUSPECT, FRU DETECTED, DEEMED FAULTY. Also PROXIED FAULT"},
79 { 0x40, "MAINTENANCE REQUIRED"},
80 { 0x44, "MAINTENANCE REQUIRED, DEEMED FAULTY"},
81 { 0x48, "MAINTENANCE REQUIRED, FRU DETECTED"},
82 { 0x4c, "MAINTENANCE REQUIRED, FRU DETECTED, DEEMED FAULTY"},
83 { 0x50, "MAINTENANCE REQUIRED. Also PROXIED FAULT"},
84 { 0x54, "MAINTENANCE REQUIRED, DEEMED FAULTY. Also PROXIED FAULT"},
85 { 0x58, "MAINTENANCE REQUIRED, FRU DETECTED. Also PROXIED FAULT"},
86 { 0x5c, "MAINTENANCE REQUIRED, FRU DETECTED, DEEMED FAULTY. \
87 Also PROXIED FAULT"},
88 { 0x60, "MAINTENANCE REQUIRED, SUSPECT"},
89 { 0x64, "MAINTENANCE REQUIRED, SUSPECT, DEEMED FAULTY"},
90 { 0x68, "MAINTENANCE REQUIRED, SUSPECT, FRU DETECTED"},
91 { 0x6c, "MAINTENANCE REQUIRED, SUSPECT, FRU DETECTED, DEEMED FAULTY"},
92 { 0x70, "MAINTENANCE REQUIRED, SUSPECT. Also PROXIED FAULT"},
93 { 0x74, "MAINTENANCE REQUIRED, SUSPECT, DEEMED FAULTY.\
94 Also PROXIED FAULT"},
95 { 0x78, "MAINTENANCE REQUIRED, SUSPECT, FRU DETECTED. \
96 Also PROXIED FAULT"},
97 { 0x7c, "MAINTENANCE REQUIRED, SUSPECT, FRU DETECTED, \
98 DEEMED FAULTY. Also PROXIED FAULT"},
99 { 0x80, "DISABLED"},
100 { 0x84, "DISABLED, DEEMED FAULTY"},
101 { 0x88, "DISABLED, FRU DETECTED"},
102 { 0x8c, "DISABLED, FRU DETECTED, DEEMED FAULTY"},
103 { 0x90, "DISABLED. Also PROXIED FAULT"},
104 { 0x94, "DISABLED, DEEMED FAULTY. Also PROXIED FAULT"},
105 { 0x98, "DISABLED, FRU DETECTED. Also PROXIED FAULT"},
106 { 0x9c, "DISABLED, FRU DETECTED, DEEMED FAULTY. Also PROXIED FAULT"},
107 { 0xa0, "DISABLED, SUSPECT"},
108 { 0xa4, "DISABLED, SUSPECT, DEEMED FAULTY"},
109 { 0xa8, "DISABLED, SUSPECT, FRU DETECTED"},
110 { 0xac, "DISABLED, SUSPECT, FRU DETECTED, DEEMED FAULTY"},
111 { 0xb0, "DISABLED, SUSPECT. Also PROXIED FAULT"},
112 { 0xb4, "DISABLED, SUSPECT, DEEMED FAULTY. Also PROXIED FAULT"},
113 { 0xb8, "DISABLED, SUSPECT, FRU DETECTED. Also PROXIED FAULT"},
114 { 0xbc, "DISABLED, SUSPECT, FRU DETECTED, \
115 DEEMED FAULTY. Also PROXIED FAULT"},
116 { 0xc0, "DISABLED, MAINTENANCE REQUIRED"},
117 { 0xc4, "DISABLED, MAINTENANCE REQUIRED, DEEMED FAULTY"},
118 { 0xc8, "DISABLED, MAINTENANCE REQUIRED, FRU DETECTED"},
119 { 0xcc, "DISABLED, MAINTENANCE REQUIRED, FRU DETECTED, DEEMED FAULTY"},
120 { 0xd0, "DISABLED, MAINTENANCE REQUIRED. Also PROXIED FAULT"},
121 { 0xd4, "DISABLED, MAINTENANCE REQUIRED, \
122 DEEMED FAULTY. Also PROXIED FAULT"},
123 { 0xd8, "DISABLED, MAINTENANCE REQUIRED, \
124 FRU DETECTED. Also PROXIED FAULT"},
125 { 0xdc, "DISABLED, MAINTENANCE REQUIRED, FRU DETECTED, \
126 DEEMED FAULTY. Also PROXIED FAULT"},
127 { 0xe0, "DISABLED, MAINTENANCE REQUIRED, SUSPECT"},
128 { 0xe4, "DISABLED, MAINTENANCE REQUIRED, SUSPECT, DEEMED FAULTY"},
129 { 0xe8, "DISABLED, MAINTENANCE REQUIRED, SUSPECT, FRU DETECTED"},
130 { 0xec, "DISABLED, MAINTENANCE REQUIRED, SUSPECT, \
131 FRU DETECTED, DEEMED FAULTY"},
132 { 0xf0, "DISABLED, MAINTENANCE REQUIRED, SUSPECT. Also PROXIED FAULT"},
133 { 0xf4, "DISABLED, MAINTENANCE REQUIRED, SUSPECT, \
134 DEEMED FAULTY. Also PROXIED FAULT"},
135 { 0xf8, "DISABLED, MAINTENANCE REQUIRED, SUSPECT, \
136 FRU DETECTED. Also PROXIED FAULT"},
137 { 0xfc, "DISABLED, MAINTENANCE REQUIRED, SUSPECT, \
138 FRU DETECTED, DEEMED FAULTY. Also PROXIED FAULT"},
139 { 0xff, "RETIRED"}
140 };
141
142 static void (*print_node)(fru_node_t fru_type, const char *path,
143 const char *name, end_node_fp_t *end_node,
144 void **end_args);
145
146 static void print_element(const uint8_t *data, const fru_regdef_t *def,
147 const char *parent_path, int indent);
148
149 static char tagname[sizeof ("?_0123456789_0123456789_0123456789")];
150
151 static int containers_only = 0, list_only = 0, saved_status = 0, xml = 0;
152
153 static FILE *errlog;
154
155 int iterglobal = 0;
156 int FMAmessageR = -1;
157 int Fault_Install_DataR_flag = 0;
158 int Power_On_DataR_flag = 0;
159 int spd_memtype = 0;
160 int spd_revision = 0;
161 /*
162 * Definition for data elements found in devices but not found in
163 * the system's version of libfrureg
164 */
165 static fru_regdef_t unknown = {
166 REGDEF_VERSION,
167 tagname,
168 -1,
169 -1,
170 -1,
171 -1,
172 FDTYPE_ByteArray,
173 FDISP_Hex,
174 FRU_WHICH_UNDEFINED,
175 FRU_WHICH_UNDEFINED,
176 0,
177 NULL,
178 0,
179 FRU_NOT_ITERATED,
180 NULL
181 };
182
183
184 /*
185 * Write message to standard error and possibly the error log buffer
186 */
187 static void
error(const char * format,...)188 error(const char *format, ...)
189 {
190 va_list args;
191
192
193 /* make relevant output appear before error message */
194 if (fflush(stdout) == EOF) {
195 (void) fprintf(stderr, "Error flushing output: %s\n",
196 strerror(errno));
197 exit(1);
198 }
199
200 va_start(args, format);
201 if (vfprintf(stderr, format, args) < 0) exit(1);
202 if (errlog && (vfprintf(errlog, format, args) < 0)) exit(1);
203 }
204
205 /*
206 * Write message to standard output
207 */
208 static void
output(const char * format,...)209 output(const char *format, ...)
210 {
211 va_list args;
212
213
214 va_start(args, format);
215 if (vfprintf(stdout, format, args) < 0) {
216 error(gettext("Error writing output: %s\n"),
217 strerror(errno));
218 exit(1);
219 }
220 }
221
222 /*
223 * Safe wrapper for putchar()
224 */
225 static void
voidputchar(int c)226 voidputchar(int c)
227 {
228 if (putchar(c) == EOF) {
229 error(gettext("Error writing output: %s\n"),
230 strerror(errno));
231 exit(1);
232 }
233 }
234
235 static void (*safeputchar)(int c) = voidputchar;
236
237 /*
238 * Safe wrapper for puts()
239 */
240 static void
voidputs(const char * s)241 voidputs(const char *s)
242 {
243 if (fputs(s, stdout) == EOF) {
244 error(gettext("Error writing output: %s\n"),
245 strerror(errno));
246 exit(1);
247 }
248 }
249
250 static void (*safeputs)(const char *s) = voidputs;
251
252 /*
253 * XML-safe wrapper for putchar(): quotes XML-special characters
254 */
255 static void
xputchar(int c)256 xputchar(int c)
257 {
258 switch (c) {
259 case '<':
260 c = fputs("<", stdout);
261 break;
262 case '>':
263 c = fputs(">", stdout);
264 break;
265 case '&':
266 c = fputs("&", stdout);
267 break;
268 case '"':
269 c = fputs(""", stdout);
270 break;
271 default:
272 c = putchar(c);
273 break;
274 }
275
276 if (c == EOF) {
277 error(gettext("Error writing output: %s\n"),
278 strerror(errno));
279 exit(1);
280 }
281 }
282
283 /*
284 * XML-safe analog of puts(): quotes XML-special characters
285 */
286 static void
xputs(const char * s)287 xputs(const char *s)
288 {
289 char c;
290
291 for (/* */; ((c = *s) != 0); s++)
292 xputchar(c);
293 }
294
295 /*
296 * Output the XML DTD derived from the registry provided by libfrureg
297 */
298 int
output_dtd(void)299 output_dtd(void)
300 {
301 char **element;
302
303 unsigned int i, j, num_elements = 0;
304
305 uint8_t *tagged;
306
307 const fru_regdef_t *def;
308
309
310 if (((element = fru_reg_list_entries(&num_elements)) == NULL) ||
311 (num_elements == 0)) {
312 error(gettext("No FRU ID Registry elements"));
313 return (1);
314 }
315
316 if ((tagged = calloc(num_elements, sizeof (*tagged))) == NULL) {
317 error(gettext("Unable to get memory for tagged element list"),
318 strerror(errno));
319 return (1);
320 }
321
322 /*
323 * Output the DTD preamble
324 */
325 output("<!ELEMENT FRUID_XML_Tree (Parameter*, "
326 "(Fru | Location | Container)*,\n"
327 " Parameter*, ErrorLog?, Parameter*)>\n"
328 "<!ATTLIST FRUID_XML_Tree>\n"
329 "\n"
330 "<!ELEMENT Parameter EMPTY>\n"
331 "<!ATTLIST Parameter type CDATA #REQUIRED>\n"
332 "<!ATTLIST Parameter name CDATA #REQUIRED>\n"
333 "<!ATTLIST Parameter value CDATA #REQUIRED>\n"
334 "\n"
335 "<!ELEMENT Fru (Fru | Location | Container)*>\n"
336 "<!ATTLIST Fru name CDATA #REQUIRED>\n"
337 "\n"
338 "<!ELEMENT Location (Fru | Location | Container)*>\n"
339 "<!ATTLIST Location\n"
340 " name CDATA #IMPLIED\n"
341 " value CDATA #IMPLIED\n"
342 ">\n"
343 "\n"
344 "<!ELEMENT Container (ContainerData?, "
345 "(Fru | Location | Container)*)>\n"
346 "<!ATTLIST Container name CDATA #REQUIRED>\n"
347 "<!ATTLIST Container imagefile CDATA #IMPLIED>\n"
348 "\n"
349 "<!ELEMENT ContainerData (Segment*)>\n"
350 "<!ATTLIST ContainerData>\n"
351 "\n"
352 "<!ATTLIST Segment name CDATA #REQUIRED>\n"
353 "\n"
354 "<!ELEMENT Index EMPTY>\n"
355 "<!ATTLIST Index value CDATA #REQUIRED>\n"
356 "\n"
357 "<!ELEMENT ErrorLog (#PCDATA)>\n"
358 "<!ATTLIST ErrorLog>\n"
359 "\n");
360
361 /*
362 * Output the definition for each element
363 */
364 for (i = 0; i < num_elements; i++) {
365 assert(element[i] != NULL);
366 /* Prevent incompatible duplicate defn. from FRUID Registry. */
367 if ((strcmp("Location", element[i])) == 0) continue;
368 if ((def = fru_reg_lookup_def_by_name(element[i])) == NULL) {
369 error(gettext("Error looking up registry "
370 "definition for \"%s\"\n"),
371 element[i]);
372 return (1);
373 }
374
375 if (def->tagType != FRU_X) tagged[i] = 1;
376
377 if (def->dataType == FDTYPE_Record) {
378 if (def->iterationType == FRU_NOT_ITERATED)
379 output("<!ELEMENT %s (%s", element[i],
380 def->enumTable[0].text);
381 else
382 output("<!ELEMENT %s (Index_%s*)>\n"
383 "<!ATTLIST Index_%s>\n"
384 "<!ELEMENT Index_%s (%s",
385 element[i], element[i], element[i],
386 element[i], def->enumTable[0].text);
387
388 for (j = 1; j < def->enumCount; j++)
389 output(",\n\t%s", def->enumTable[j].text);
390
391 output(")>\n");
392 } else if (def->iterationType == FRU_NOT_ITERATED) {
393 output("<!ELEMENT %s EMPTY>\n"
394 "<!ATTLIST %s value CDATA #REQUIRED>\n",
395 element[i], element[i]);
396
397 if (def->dataType == FDTYPE_Enumeration) {
398 output("<!-- %s valid enumeration values\n");
399 for (j = 0; j < def->enumCount; j++) {
400 output("\t\"");
401 xputs(def->enumTable[j].text);
402 output("\"\n");
403 }
404 output("-->\n");
405 }
406 }
407 else
408 output("<!ELEMENT %s (Index*)>\n", element[i]);
409
410 output("\n");
411 }
412
413 /* Provide for returning the tag for an "unknown" element */
414 output("<!ATTLIST UNKNOWN tag CDATA \"UNKNOWN\">\n\n");
415
416
417 /*
418 * List all data elements as possible members of "Segment"
419 */
420 output("<!ELEMENT Segment ((UNKNOWN");
421 for (i = 0; i < num_elements; i++) {
422 if (tagged[i]) output("\n\t| %s", element[i]);
423 free(element[i]);
424 }
425 output(")*)>\n");
426 free(element);
427 free(tagged);
428
429 return (0);
430 }
431 /*
432 * Function to convert bcd to binary to correct the SPD_Manufacturer_Week
433 *
434 */
convertbcdtobinary(int * val)435 static void convertbcdtobinary(int *val)
436 {
437 unsigned int newval = (unsigned int)*val, tmpval = 0;
438 while (newval > 0) {
439 tmpval = (tmpval << 4) | (newval & 0xF);
440 newval >>= 4;
441 }
442 while (tmpval > 0) {
443 newval = (newval * 10) + (tmpval & 0xF);
444 tmpval >>= 4;
445 }
446 *val = newval;
447 }
448
449 /*
450 * Checking UTF-8 printable charecter
451 */
check_utf_char(const uint8_t * field,int len)452 static int check_utf_char(const uint8_t *field, int len)
453 {
454 int i, status = 0;
455 char tmp[128], tmp1[128], tmp2[128];
456 (void) sprintf(tmp, " (Invalid Data");
457 (void) sprintf(tmp2, "0x");
458 for (i = 0; i < len && field[i]; i++) {
459 (void) sprintf(tmp1, "%2.2X", field[i]);
460 (void) strcat(tmp2, tmp1);
461 if (iswprint(field[i]) == 0) {
462 status = 1;
463 (void) sprintf(tmp1, " : 0x%2.2X", field[i]);
464 (void) strcat(tmp, tmp1);
465 }
466 }
467 if (status) {
468 (void) sprintf(tmp1, ")");
469 (void) strcat(tmp, tmp1);
470 (void) strcat(tmp2, tmp);
471 output("%s", tmp2);
472 }
473 return (status);
474 }
475
476 /*
477 * Safely pretty-print the value of a field
478 */
479 static void
print_field(const uint8_t * field,const fru_regdef_t * def)480 print_field(const uint8_t *field, const fru_regdef_t *def)
481 {
482 char *errmsg = NULL, timestring[TIMESTRINGLEN], path[16384];
483
484 int i, valueint;
485
486 uint64_t value;
487
488 time_t timefield;
489
490 struct tm *tm;
491
492 uchar_t first_byte, data[128];
493
494 const fru_regdef_t *new_def;
495
496 const char *elem_name = NULL;
497 const char *parent_path;
498 switch (def->dataType) {
499 case FDTYPE_Binary:
500 assert(def->payloadLen <= sizeof (value));
501 switch (def->dispType) {
502 case FDISP_Binary:
503 for (i = 0; i < def->payloadLen; i++)
504 output("%c%c%c%c%c%c%c%c",
505 ((field[i] & 0x80) ? '1' : '0'),
506 ((field[i] & 0x40) ? '1' : '0'),
507 ((field[i] & 0x20) ? '1' : '0'),
508 ((field[i] & 0x10) ? '1' : '0'),
509 ((field[i] & 0x08) ? '1' : '0'),
510 ((field[i] & 0x04) ? '1' : '0'),
511 ((field[i] & 0x02) ? '1' : '0'),
512 ((field[i] & 0x01) ? '1' : '0'));
513 return;
514 case FDISP_Octal:
515 case FDISP_Decimal:
516 value = 0;
517 valueint = 0;
518 (void) memcpy((((uint8_t *)&value) +
519 sizeof (value) - def->payloadLen),
520 field, def->payloadLen);
521 if ((value != 0) &&
522 (strcmp(def->name, "SPD_Manufacture_Week") == 0)) {
523 valueint = (int)value;
524 if (spd_memtype && spd_revision) {
525 convertbcdtobinary(&valueint);
526 spd_memtype = 0;
527 spd_revision = 0;
528 }
529 output("%d", valueint);
530 return;
531 }
532 if ((value != 0) &&
533 ((strcmp(def->name, "Lowest") == 0) ||
534 (strcmp(def->name, "Highest") == 0) ||
535 (strcmp(def->name, "Latest") == 0)))
536 output((def->dispType == FDISP_Octal) ?
537 "%llo" : "%lld (%lld degrees C)",
538 value, (value - TEMPERATURE_OFFSET));
539 else
540 output((def->dispType == FDISP_Octal) ?
541 "%llo" : "%lld", value);
542 return;
543 case FDISP_Time:
544 if (def->payloadLen > sizeof (timefield)) {
545 errmsg = "time value too large for formatting";
546 break;
547 }
548 timefield = 0;
549 (void) memcpy((((uint8_t *)&timefield) +
550 sizeof (timefield) - def->payloadLen),
551 field, def->payloadLen);
552 if (timefield == 0) {
553 errmsg = "No Value Recorded";
554 break;
555 }
556 if ((tm = gmtime(&timefield)) == NULL) {
557 errmsg = "cannot convert time value";
558 break;
559 }
560 if (strftime(timestring, sizeof (timestring), GMT, tm)
561 == 0) {
562 errmsg = "formatted time would overflow buffer";
563 break;
564 }
565 safeputs(timestring);
566 return;
567 }
568 break;
569 case FDTYPE_ASCII:
570 if (!xml) {
571 if (strcmp(def->name, "Message") == 0) {
572 if (FMAmessageR == 0)
573 elem_name = "FMA_Event_DataR";
574 else if (FMAmessageR == 1)
575 elem_name = "FMA_MessageR";
576 if (elem_name != NULL) {
577 (void) memcpy(data, field,
578 def->payloadLen);
579 new_def =
580 fru_reg_lookup_def_by_name
581 (elem_name);
582 (void) snprintf(path, sizeof (path),
583 "/Status_EventsR[%d]/Message(FMA)",
584 iterglobal);
585 parent_path = path;
586 output("\n");
587 print_element(data, new_def,
588 parent_path, 2*INDENT);
589 return;
590 }
591 }
592 }
593 if (strcmp(def->name, "Fru_Path") == 0) {
594 if (check_utf_char(field, def->payloadLen) == 1)
595 return;
596 }
597 for (i = 0; i < def->payloadLen && field[i]; i++)
598 safeputchar(field[i]);
599 return;
600 case FDTYPE_Enumeration:
601 value = 0;
602 (void) memcpy((((uint8_t *)&value) + sizeof (value)
603 - def->payloadLen),
604 field, def->payloadLen);
605 for (i = 0; i < def->enumCount; i++)
606 if (def->enumTable[i].value == value) {
607 if (strcmp(def->name, "Event_Code") == 0) {
608 if (strcmp(def->enumTable[i].text,
609 "FMA Message R") == 0)
610 FMAmessageR = 1;
611 else
612 if (strcmp(def->enumTable[i].text,
613 "FMA Event Data R") == 0)
614 FMAmessageR = 0;
615 }
616 if (strcmp(def->name,
617 "SPD_Fundamental_Memory_Type") == 0) {
618 if (strcmp(def->enumTable[i].text,
619 "DDR II SDRAM") == 0)
620 spd_memtype = 1;
621 }
622 safeputs(def->enumTable[i].text);
623 return;
624 }
625
626 errmsg = "unrecognized value";
627 break;
628 }
629
630 /* If nothing matched above, print the field in hex */
631 switch (def->dispType) {
632 case FDISP_MSGID:
633 (void) memcpy((uchar_t *)&first_byte, field, 1);
634 if (isprint(first_byte)) {
635 for (i = 0; i < def->payloadLen && field[i];
636 i++)
637 safeputchar(field[i]);
638 }
639 break;
640 case FDISP_UUID:
641 for (i = 0; i < def->payloadLen; i++) {
642 if ((i == 4) || (i == 6) ||
643 (i == 8) || (i == 10))
644 output("-");
645 output("%2.2x", field[i]);
646 }
647 break;
648 default:
649 if ((strcmp(def->name, "Status") == 0) ||
650 (strcmp(def->name, "Old_Status") == 0) ||
651 (strcmp(def->name, "New_Status") == 0)) {
652 int status_length = \
653 sizeof (Status_CurrentR_status) / \
654 sizeof (*(Status_CurrentR_status));
655 i = 0;
656 do {
657 if (Status_CurrentR_status[i].value == \
658 *(field))
659 break;
660 i++;
661 } while (i < status_length);
662 if (i < status_length)
663 output("0x%2.2X (%s)", *(field),
664 Status_CurrentR_status[i].data);
665 else
666 output("0x%2.2X (UNKNOWN)", *(field));
667 break;
668 }
669 if (strcmp(def->name,
670 "SPD_Data_Revision_Code") == 0) {
671 value = 0;
672 valueint = 0;
673 (void) memcpy((((uint8_t *)&value)
674 + sizeof (value) - def->payloadLen),
675 field, def->payloadLen);
676 valueint = (int)value;
677 if ((valueint >= MIN_VERSION) && (spd_memtype))
678 spd_revision = 1;
679 }
680 for (i = 0; i < def->payloadLen; i++)
681 output("%2.2X", field[i]);
682 break;
683 }
684
685 /* Safely print any error message associated with the field */
686 if (errmsg) {
687 if (strcmp(def->name, "Fault_Diag_Secs") != 0) {
688 output(" (");
689 safeputs(errmsg);
690 output(")");
691 }
692 }
693 }
694
695 /*
696 * Recursively print the contents of a data element
697 */
698 static void
print_element(const uint8_t * data,const fru_regdef_t * def,const char * parent_path,int indent)699 print_element(const uint8_t *data, const fru_regdef_t *def,
700 const char *parent_path, int indent)
701 {
702 char *path;
703 size_t len;
704
705 int bytes = 0, i;
706
707
708 indent = (xml) ? (indent + INDENT) : (2*INDENT);
709 if (strcmp(def->name, "Sun_SPD_DataR") == 0) {
710 Fault_Install_DataR_flag = indent;
711 Power_On_DataR_flag = indent;
712 }
713 /*
714 * Construct the path, or, for XML, the name, for the current
715 * data element
716 */
717 if ((def->iterationCount == 0) &&
718 (def->iterationType != FRU_NOT_ITERATED)) {
719 if (xml) {
720 if (def->dataType == FDTYPE_Record) {
721 len = strlen("Index_") + strlen(def->name) + 1;
722 path = alloca(len);
723 (void) snprintf(path, len,
724 "Index_%s", def->name);
725 }
726 else
727 path = "Index";
728 }
729 else
730 path = (char *)parent_path;
731 } else {
732 if (xml)
733 path = (char *)def->name;
734 else {
735 len = strlen(parent_path) + sizeof ("/") +
736 strlen(def->name) +
737 (def->iterationCount ? sizeof ("[255]") : 0);
738 path = alloca(len);
739 bytes = snprintf(path, len,
740 "%s/%s", parent_path, def->name);
741 }
742 }
743
744 if ((Fault_Install_DataR_flag) &&
745 (strcmp(path, "E_1_46") == 0) || (strcmp(path, "/E_1_46") == 0)) {
746 int cnt;
747 char timestring[128];
748 time_t timefield = 0;
749 struct tm *tm;
750 indent = Fault_Install_DataR_flag;
751 (void) memcpy((uint8_t *)&timefield, data, 4);
752 if (timefield == 0) {
753 (void) sprintf(timestring,
754 "00000000 (No Value Recorded)\"");
755 } else {
756 if ((tm = gmtime(&timefield)) == NULL)
757 (void) sprintf(timestring,
758 "cannot convert time value");
759 if (strftime(timestring,
760 sizeof (timestring), GMT, tm) == 0)
761 (void) sprintf(timestring,
762 "formatted time would overflow buffer");
763 }
764 if (xml) {
765 (void) sprintf(path, "Fault_Install_DataR");
766 output("%*s<%s>\n", indent, "", path);
767 indent = Fault_Install_DataR_flag + INDENT;
768 (void) sprintf(path, "UNIX_Timestamp32");
769 output("%*s<%s value=\"", indent, "", path);
770 /*CSTYLED*/
771 output("%s\"/>\n", timestring);
772 (void) sprintf(path, "MACADDR");
773 output("%*s<%s value=\"", indent, "", path);
774 for (cnt = 4; cnt < 4 + 6; cnt++) {
775 output("%2.2x", data[cnt]);
776 if (cnt < 4 + 6 - 1)
777 output(":");
778 }
779 /*CSTYLED*/
780 output("\"/>\n");
781 (void) sprintf(path, "Status");
782 output("%*s<%s value=\"", indent, "", path);
783 /*CSTYLED*/
784 output("%2.2x\"/>\n", data[10]);
785 (void) sprintf(path, "Initiator");
786 output("%*s<%s value=\"", indent, "", path);
787 /*CSTYLED*/
788 output("%2.2x\"/>\n", data[11]);
789 (void) sprintf(path, "Message_Type");
790 output("%*s<%s value=\"", indent, "", path);
791 /*CSTYLED*/
792 output("%2.2x\"/>\n", data[12]);
793 (void) sprintf(path, "Message_32");
794 output("%*s<%s value=\"", indent, "", path);
795 for (cnt = 13; cnt < 13 + 32; cnt++)
796 output("%2.2x", data[cnt]);
797 /*CSTYLED*/
798 output("\"/>\n");
799 indent = Fault_Install_DataR_flag;
800 (void) sprintf(path, "Fault_Install_DataR");
801 output("%*s</%s>\n", indent, "", path);
802 } else {
803 (void) sprintf(path, "/Fault_Install_DataR");
804 output("%*s%s\n", indent, "", path);
805 (void) sprintf(path,
806 "/Fault_Install_DataR/UNIX_Timestamp32");
807 output("%*s%s: ", indent, "", path);
808 output("%s\n", timestring);
809 (void) sprintf(path, "/Fault_Install_DataR/MACADDR");
810 output("%*s%s: ", indent, "", path);
811 for (cnt = 4; cnt < 4 + 6; cnt++) {
812 output("%2.2x", data[cnt]);
813 if (cnt < 4 + 6 - 1)
814 output(":");
815 }
816 output("\n");
817 (void) sprintf(path, "/Fault_Install_DataR/Status");
818 output("%*s%s: ", indent, "", path);
819 output("%2.2x\n", data[10]);
820 (void) sprintf(path, "/Fault_Install_DataR/Initiator");
821 output("%*s%s: ", indent, "", path);
822 output("%2.2x\n", data[11]);
823 (void) sprintf(path,
824 "/Fault_Install_DataR/Message_Type");
825 output("%*s%s: ", indent, "", path);
826 output("%2.2x\n", data[12]);
827 (void) sprintf(path, "/Fault_Install_DataR/Message_32");
828 output("%*s%s: ", indent, "", path);
829 for (cnt = 13; cnt < 13 + 32; cnt++)
830 output("%2.2x", data[cnt]);
831 output("\n");
832 }
833 Fault_Install_DataR_flag = 0;
834 return;
835 } else if ((Power_On_DataR_flag) && (
836 strcmp(path, "C_10_8") == 0 ||
837 (strcmp(path, "/C_10_8") == 0))) {
838 int cnt;
839 char timestring[128];
840 time_t timefield = 0;
841 struct tm *tm;
842 indent = Power_On_DataR_flag;
843 (void) memcpy((uint8_t *)&timefield, data, 4);
844 if (timefield == 0) {
845 (void) sprintf(timestring,
846 "00000000 (No Value Recorded)");
847 } else {
848 if ((tm = gmtime(&timefield)) == NULL)
849 (void) sprintf(timestring,
850 "cannot convert time value");
851 if (strftime(timestring,
852 sizeof (timestring), GMT, tm) == 0)
853 (void) sprintf(timestring,
854 "formatted time would overflow buffer");
855 }
856 if (xml) {
857 (void) sprintf(path, "Power_On_DataR");
858 output("%*s<%s>\n", indent, "", path);
859 indent = Power_On_DataR_flag + INDENT;
860 (void) sprintf(path, "UNIX_Timestamp32");
861 output("%*s<%s value=\"", indent, "", path);
862 /*CSTYLED*/
863 output("%s\"/>\n", timestring);
864 (void) sprintf(path, "Power_On_Minutes");
865 output("%*s<%s value=\"", indent, "", path);
866 for (cnt = 4; cnt < 4 + 4; cnt++)
867 output("%2.2x", data[cnt]);
868 /*CSTYLED*/
869 output("\"/>\n");
870 indent = Power_On_DataR_flag;
871 (void) sprintf(path, "Power_On_DataR");
872 output("%*s</%s>\n", indent, "", path);
873 } else {
874 (void) sprintf(path, "/Power_On_DataR");
875 output("%*s%s\n", indent, "", path);
876 (void) sprintf(path,
877 "/Power_On_DataR/UNIX_Timestamp32");
878 output("%*s%s: ", indent, "", path);
879 output("%s\n", timestring);
880 (void) sprintf(path,
881 "/Power_On_DataR/Power_On_Minutes");
882 output("%*s%s: ", indent, "", path);
883 for (cnt = 4; cnt < 4 + 4; cnt++)
884 output("%2.2x", data[cnt]);
885 output("\n");
886 }
887 Power_On_DataR_flag = 0;
888 return;
889 }
890 /*
891 * Handle the various categories of data elements: iteration,
892 * record, and field
893 */
894 if (def->iterationCount) {
895 int iterlen = (def->payloadLen - NUM_ITER_BYTES)/
896 def->iterationCount,
897 n, valid = 1;
898
899 uint8_t head, num;
900
901 fru_regdef_t newdef;
902
903
904 /*
905 * Make a new element definition to describe the components
906 * of the iteration
907 */
908 (void) memcpy(&newdef, def, sizeof (newdef));
909 newdef.iterationCount = 0;
910 newdef.payloadLen = iterlen;
911
912 /*
913 * Validate the contents of the iteration control bytes
914 */
915 if (data[HEAD_ITER] >= def->iterationCount) {
916 valid = 0;
917 error(gettext("%s: Invalid iteration head: %d "
918 "(should be less than %d)\n"),
919 path, data[HEAD_ITER], def->iterationCount);
920 }
921
922 if (data[NUM_ITER] > def->iterationCount) {
923 valid = 0;
924 error(gettext("%s: Invalid iteration count: %d "
925 "(should not be greater than %d)\n"),
926 path, data[NUM_ITER], def->iterationCount);
927 }
928
929 if (data[MAX_ITER] != def->iterationCount) {
930 valid = 0;
931 error(gettext("%s: Invalid iteration maximum: %d "
932 "(should equal %d)\n"),
933 path, data[MAX_ITER], def->iterationCount);
934 }
935
936 if (valid) {
937 head = data[HEAD_ITER];
938 num = data[NUM_ITER];
939 } else {
940 head = 0;
941 num = def->iterationCount;
942 error(gettext("%s: Showing all iterations\n"), path);
943 }
944
945 if (xml)
946 output("%*s<%s>\n", indent, "", path);
947 else
948 output("%*s%s (%d iterations)\n", indent, "", path,
949 num);
950
951 /*
952 * Print each component of the iteration
953 */
954 for (i = head, n = 0, data += 4;
955 n < num;
956 i = ((i + 1) % def->iterationCount), n++) {
957 if (!xml) (void) sprintf((path + bytes), "[%d]", n);
958 iterglobal = n;
959 print_element((data + i*iterlen), &newdef, path,
960 indent);
961 }
962
963 if (xml) output("%*s</%s>\n", indent, "", path);
964
965 } else if (def->dataType == FDTYPE_Record) {
966 const fru_regdef_t *component;
967
968 if (xml)
969 output("%*s<%s>\n", indent, "", path);
970 else
971 output("%*s%s\n", indent, "", path);
972
973 /*
974 * Print each component of the record
975 */
976 for (i = 0; i < def->enumCount;
977 i++, data += component->payloadLen) {
978 component = fru_reg_lookup_def_by_name(
979 def->enumTable[i].text);
980 assert(component != NULL);
981 print_element(data, component, path, indent);
982 }
983
984 if (xml) output("%*s</%s>\n", indent, "", path);
985 } else if (xml) {
986 /*
987 * Base case: print the field formatted for XML
988 */
989 char *format = ((def == &unknown)
990 ? "%*s<UNKNOWN tag=\"%s\" value=\""
991 : "%*s<%s value=\"");
992
993 output(format, indent, "", path);
994 print_field(data, def);
995 /*CSTYLED*/
996 output("\"/>\n"); /* \" confuses cstyle */
997
998 if ((strcmp(def->name, "Message") == 0) &&
999 ((FMAmessageR == 0) || (FMAmessageR == 1))) {
1000 const char *elem_name = NULL;
1001 const char *parent_path;
1002 uchar_t tmpdata[128];
1003 char path[16384];
1004 const fru_regdef_t *new_def;
1005
1006 if (FMAmessageR == 0)
1007 elem_name = "FMA_Event_DataR";
1008 else if (FMAmessageR == 1)
1009 elem_name = "FMA_MessageR";
1010 if (elem_name != NULL) {
1011 (void) memcpy(tmpdata, data, def->payloadLen);
1012 new_def = fru_reg_lookup_def_by_name(elem_name);
1013 (void) snprintf(path, sizeof (path),
1014 "/Status_EventsR[%d]/Message(FMA)", iterglobal);
1015 parent_path = path;
1016 print_element(tmpdata, new_def,
1017 parent_path, 2*INDENT);
1018 FMAmessageR = -1;
1019 }
1020 }
1021
1022 } else {
1023 /*
1024 * Base case: print the field
1025 */
1026 output("%*s%s: ", indent, "", path);
1027 print_field(data, def);
1028 output("\n");
1029 }
1030 }
1031
1032 /*
1033 * Print the contents of a packet (i.e., a tagged data element)
1034 */
1035 /* ARGSUSED */
1036 static int
print_packet(fru_tag_t * tag,uint8_t * payload,size_t length,void * args)1037 print_packet(fru_tag_t *tag, uint8_t *payload, size_t length, void *args)
1038 {
1039 int tag_type = get_tag_type(tag);
1040
1041 size_t payload_length = 0;
1042
1043 const fru_regdef_t *def;
1044
1045 /*
1046 * Build a definition for unrecognized tags (e.g., not in libfrureg)
1047 */
1048 if ((tag_type == -1) ||
1049 ((payload_length = get_payload_length(tag)) != length)) {
1050 def = &unknown;
1051
1052 unknown.tagType = -1;
1053 unknown.tagDense = -1;
1054 unknown.payloadLen = length;
1055 unknown.dataLength = unknown.payloadLen;
1056
1057 if (tag_type == -1)
1058 (void) snprintf(tagname, sizeof (tagname), "INVALID");
1059 else
1060 (void) snprintf(tagname, sizeof (tagname),
1061 "%s_%u_%u_%u", get_tagtype_str(tag_type),
1062 get_tag_dense(tag), payload_length, length);
1063 } else if ((def = fru_reg_lookup_def_by_tag(*tag)) == NULL) {
1064 def = &unknown;
1065
1066 unknown.tagType = tag_type;
1067 unknown.tagDense = get_tag_dense(tag);
1068 unknown.payloadLen = payload_length;
1069 unknown.dataLength = unknown.payloadLen;
1070
1071 (void) snprintf(tagname, sizeof (tagname), "%s_%u_%u",
1072 get_tagtype_str(unknown.tagType),
1073 unknown.tagDense, payload_length);
1074 }
1075
1076
1077 /*
1078 * Print the defined element
1079 */
1080 print_element(payload, def, "", INDENT);
1081
1082 return (FRU_SUCCESS);
1083 }
1084
1085 /*
1086 * Print a segment's name and the contents of each data element in the segment
1087 */
1088 static int
print_packets_in_segment(fru_seghdl_t segment,void * args)1089 print_packets_in_segment(fru_seghdl_t segment, void *args)
1090 {
1091 char *name;
1092
1093 int status;
1094
1095
1096 if ((status = fru_get_segment_name(segment, &name)) != FRU_SUCCESS) {
1097 saved_status = status;
1098 name = "";
1099 error(gettext("Error getting segment name: %s\n"),
1100 fru_strerror(status));
1101 }
1102
1103
1104 if (xml)
1105 output("%*s<Segment name=\"%s\">\n", INDENT, "", name);
1106 else
1107 output("%*sSEGMENT: %s\n", INDENT, "", name);
1108
1109 if (strcmp(name, "ED") == 0) {
1110 if (xml) output("%*s</Segment>\n", INDENT, "");
1111 free(name);
1112 return (FRU_SUCCESS);
1113 }
1114 /* Iterate over the packets in the segment, printing the contents */
1115 if ((status = fru_for_each_packet(segment, print_packet, args))
1116 != FRU_SUCCESS) {
1117 saved_status = status;
1118 error(gettext("Error processing data in segment \"%s\": %s\n"),
1119 name, fru_strerror(status));
1120 }
1121
1122 if (xml) output("%*s</Segment>\n", INDENT, "");
1123
1124 free(name);
1125
1126 return (FRU_SUCCESS);
1127 }
1128
1129 /* ARGSUSED */
1130 static void
print_node_path(fru_node_t fru_type,const char * path,const char * name,end_node_fp_t * end_node,void ** end_args)1131 print_node_path(fru_node_t fru_type, const char *path, const char *name,
1132 end_node_fp_t *end_node, void **end_args)
1133 {
1134 output("%s%s\n", path,
1135 ((fru_type == FRU_NODE_CONTAINER) ? " (container)"
1136 : ((fru_type == FRU_NODE_FRU) ? " (fru)" : "")));
1137 }
1138
1139 /*
1140 * Close the XML element for a "location" node
1141 */
1142 /* ARGSUSED */
1143 static void
end_location_xml(fru_nodehdl_t node,const char * path,const char * name,void * args)1144 end_location_xml(fru_nodehdl_t node, const char *path, const char *name,
1145 void *args)
1146 {
1147 assert(args != NULL);
1148 output("</Location> <!-- %s -->\n", args);
1149 }
1150
1151 /*
1152 * Close the XML element for a "fru" node
1153 */
1154 /* ARGSUSED */
1155 static void
end_fru_xml(fru_nodehdl_t node,const char * path,const char * name,void * args)1156 end_fru_xml(fru_nodehdl_t node, const char *path, const char *name, void *args)
1157 {
1158 assert(args != NULL);
1159 output("</Fru> <!-- %s -->\n", args);
1160 }
1161
1162 /*
1163 * Close the XML element for a "container" node
1164 */
1165 /* ARGSUSED */
1166 static void
end_container_xml(fru_nodehdl_t node,const char * path,const char * name,void * args)1167 end_container_xml(fru_nodehdl_t node, const char *path, const char *name,
1168 void *args)
1169 {
1170 assert(args != NULL);
1171 output("</Container> <!-- %s -->\n", args);
1172 }
1173
1174 /*
1175 * Introduce a node in XML and set the appropriate node-closing function
1176 */
1177 /* ARGSUSED */
1178 static void
print_node_xml(fru_node_t fru_type,const char * path,const char * name,end_node_fp_t * end_node,void ** end_args)1179 print_node_xml(fru_node_t fru_type, const char *path, const char *name,
1180 end_node_fp_t *end_node, void **end_args)
1181 {
1182 switch (fru_type) {
1183 case FRU_NODE_FRU:
1184 output("<Fru name=\"%s\">\n", name);
1185 *end_node = end_fru_xml;
1186 break;
1187 case FRU_NODE_CONTAINER:
1188 output("<Container name=\"%s\">\n", name);
1189 *end_node = end_container_xml;
1190 break;
1191 default:
1192 output("<Location name=\"%s\">\n", name);
1193 *end_node = end_location_xml;
1194 break;
1195 }
1196
1197 *end_args = (void *) name;
1198 }
1199
1200 /*
1201 * Print node info and, where appropriate, node contents
1202 */
1203 /* ARGSUSED */
1204 static fru_errno_t
process_node(fru_nodehdl_t node,const char * path,const char * name,void * args,end_node_fp_t * end_node,void ** end_args)1205 process_node(fru_nodehdl_t node, const char *path, const char *name,
1206 void *args, end_node_fp_t *end_node, void **end_args)
1207 {
1208 int status;
1209
1210 fru_node_t fru_type = FRU_NODE_UNKNOWN;
1211
1212
1213 if ((status = fru_get_node_type(node, &fru_type)) != FRU_SUCCESS) {
1214 saved_status = status;
1215 error(gettext("Error getting node type: %s\n"),
1216 fru_strerror(status));
1217 }
1218
1219 if (containers_only) {
1220 if (fru_type != FRU_NODE_CONTAINER)
1221 return (FRU_SUCCESS);
1222 name = path;
1223 }
1224
1225 /* Introduce the node */
1226 assert(print_node != NULL);
1227 print_node(fru_type, path, name, end_node, end_args);
1228
1229 if (list_only)
1230 return (FRU_SUCCESS);
1231
1232 /* Print the contents of each packet in each segment of a container */
1233 if (fru_type == FRU_NODE_CONTAINER) {
1234 if (xml) output("<ContainerData>\n");
1235 if ((status =
1236 fru_for_each_segment(node, print_packets_in_segment,
1237 NULL))
1238 != FRU_SUCCESS) {
1239 saved_status = status;
1240 error(gettext("Error processing node \"%s\": %s\n"),
1241 name, fru_strerror(status));
1242 }
1243 if (xml) output("</ContainerData>\n");
1244 }
1245
1246 return (FRU_SUCCESS);
1247 }
1248
1249 /*
1250 * Process the node if its path matches the search path in "args"
1251 */
1252 /* ARGSUSED */
1253 static fru_errno_t
process_matching_node(fru_nodehdl_t node,const char * path,const char * name,void * args,end_node_fp_t * end_node,void ** end_args)1254 process_matching_node(fru_nodehdl_t node, const char *path, const char *name,
1255 void *args, end_node_fp_t *end_node, void **end_args)
1256 {
1257 int status;
1258
1259
1260 if (!fru_pathmatch(path, args))
1261 return (FRU_SUCCESS);
1262
1263 status = process_node(node, path, path, args, end_node, end_args);
1264
1265 return ((status == FRU_SUCCESS) ? FRU_WALK_TERMINATE : status);
1266 }
1267
1268 /*
1269 * Write the trailer required for well-formed DTD-compliant XML
1270 */
1271 static void
terminate_xml()1272 terminate_xml()
1273 {
1274 errno = 0;
1275 if (ftell(errlog) > 0) {
1276 char c;
1277
1278 output("<ErrorLog>\n");
1279 rewind(errlog);
1280 if (!errno)
1281 while ((c = getc(errlog)) != EOF)
1282 xputchar(c);
1283 output("</ErrorLog>\n");
1284 }
1285
1286 if (errno) {
1287 /*NOTREACHED*/
1288 errlog = NULL;
1289 error(gettext("Error copying error messages to \"ErrorLog\""),
1290 strerror(errno));
1291 }
1292
1293 output("</FRUID_XML_Tree>\n");
1294 }
1295
1296 /*
1297 * Print available FRU ID information
1298 */
1299 int
prtfru(const char * searchpath,int containers_only_flag,int list_only_flag,int xml_flag)1300 prtfru(const char *searchpath, int containers_only_flag, int list_only_flag,
1301 int xml_flag)
1302 {
1303 fru_errno_t status;
1304
1305 fru_nodehdl_t frutree = 0;
1306
1307
1308 /* Copy parameter flags to global flags */
1309 containers_only = containers_only_flag;
1310 list_only = list_only_flag;
1311 xml = xml_flag;
1312
1313
1314 /* Help arrange for correct, efficient interleaving of output */
1315 (void) setvbuf(stderr, NULL, _IOLBF, 0);
1316
1317
1318 /* Initialize for XML--or not */
1319 if (xml) {
1320 safeputchar = xputchar;
1321 safeputs = xputs;
1322
1323 print_node = print_node_xml;
1324
1325 if ((errlog = tmpfile()) == NULL) {
1326 (void) fprintf(stderr,
1327 "Error creating error log file: %s\n",
1328 strerror(errno));
1329 return (1);
1330 }
1331
1332 /* Output the XML preamble */
1333 output("<?xml version=\"1.0\" ?>\n"
1334 "<!--\n"
1335 " Copyright 2000-2002 Sun Microsystems, Inc. "
1336 "All rights reserved.\n"
1337 " Use is subject to license terms.\n"
1338 "-->\n\n"
1339 "<!DOCTYPE FRUID_XML_Tree SYSTEM \"prtfrureg.dtd\">\n\n"
1340 "<FRUID_XML_Tree>\n");
1341
1342 /* Arrange to always properly terminate XML */
1343 if (atexit(terminate_xml))
1344 error(gettext("Warning: XML will not be terminated: "
1345 "%s\n"), strerror(errno));
1346 } else
1347 print_node = print_node_path;
1348
1349
1350 /* Get the root node */
1351 if ((status = fru_get_root(&frutree)) == FRU_NODENOTFOUND) {
1352 error(gettext("This system does not support PICL "
1353 "infrastructure to provide FRUID data\n"
1354 "Please use the platform SP to access the FRUID "
1355 "information\n"));
1356 return (1);
1357 } else if (status != FRU_SUCCESS) {
1358 error(gettext("Unable to access FRU ID data: %s\n"),
1359 fru_strerror(status));
1360 return (1);
1361 }
1362
1363 /* Process the tree */
1364 if (searchpath == NULL) {
1365 status = fru_walk_tree(frutree, "", process_node, NULL);
1366 } else {
1367 status = fru_walk_tree(frutree, "", process_matching_node,
1368 (void *)searchpath);
1369 if (status == FRU_WALK_TERMINATE) {
1370 status = FRU_SUCCESS;
1371 } else if (status == FRU_SUCCESS) {
1372 error(gettext("\"%s\" not found\n"), searchpath);
1373 return (1);
1374 }
1375 }
1376
1377 if (status != FRU_SUCCESS)
1378 error(gettext("Error processing FRU tree: %s\n"),
1379 fru_strerror(status));
1380
1381 return (((status == FRU_SUCCESS) && (saved_status == 0)) ? 0 : 1);
1382 }
1383