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