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