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