xref: /illumos-gate/usr/src/cmd/prtfru/prtfru.c (revision 533affcbc7fc4d0c8132976ea454aaa715fe2307)
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
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
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
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
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
256 xputchar(int c)
257 {
258 	switch (c) {
259 	case '<':
260 		c = fputs("&lt;", stdout);
261 		break;
262 	case '>':
263 		c = fputs("&gt;", stdout);
264 		break;
265 	case '&':
266 		c = fputs("&amp;", stdout);
267 		break;
268 	case '"':
269 		c = fputs("&quot;", 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
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
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  */
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  */
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
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
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
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
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
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
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
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
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
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
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
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
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
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