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