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