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