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