prtfru.c revision bdad7b9cb5784df1403f5f3d188edea03f0fb7cb
2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A#include <alloca.h>
2N/A#include <assert.h>
2N/A#include <errno.h>
2N/A#include <libintl.h>
2N/A#include <stdarg.h>
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <ctype.h>
2N/A
2N/A#include "fru_tag.h"
2N/A#include "libfrup.h"
2N/A#include "libfrureg.h"
2N/A
2N/A
2N/A#define NUM_ITER_BYTES 4
2N/A
2N/A#define HEAD_ITER 0
2N/A#define TAIL_ITER 1 /* not used */
2N/A#define NUM_ITER 2
2N/A#define MAX_ITER 3
2N/A
2N/A#define INDENT 3
2N/A#define TIMESTRINGLEN 128
2N/A#define TEMPERATURE_OFFSET 73
2N/A#define MIN_VERSION 17
2N/A#define GMT "%a, %b %d %Y %H:%M:%S GMT"
2N/A
2N/Astatic void (*print_node)(fru_node_t fru_type, const char *path,
2N/A const char *name, end_node_fp_t *end_node,
2N/A void **end_args);
2N/A
2N/Astatic void print_element(const uint8_t *data, const fru_regdef_t *def,
2N/Aconst char *parent_path, int indent);
2N/A
2N/Astatic char tagname[sizeof ("?_0123456789_0123456789_0123456789")];
2N/A
2N/Astatic int containers_only = 0, list_only = 0, saved_status = 0, xml = 0;
2N/A
2N/Astatic FILE *errlog;
2N/A
2N/Aint iterglobal = 0;
2N/Aint FMAmessageR = -1;
2N/Aint Fault_Install_DataR_flag = 0;
2N/Aint Power_On_DataR_flag = 0;
2N/Aint spd_memtype = 0;
2N/Aint spd_revision = 0;
2N/A/*
2N/A * Definition for data elements found in devices but not found in
2N/A * the system's version of libfrureg
2N/A */
2N/Astatic fru_regdef_t unknown = {
2N/A REGDEF_VERSION,
2N/A tagname,
2N/A -1,
2N/A -1,
2N/A -1,
2N/A -1,
2N/A FDTYPE_ByteArray,
2N/A FDISP_Hex,
2N/A FRU_WHICH_UNDEFINED,
2N/A FRU_WHICH_UNDEFINED,
2N/A 0,
2N/A NULL,
2N/A 0,
2N/A FRU_NOT_ITERATED,
2N/A NULL
2N/A};
2N/A
2N/A
2N/A/*
2N/A * Write message to standard error and possibly the error log buffer
2N/A */
2N/Astatic void
2N/Aerror(const char *format, ...)
2N/A{
2N/A va_list args;
2N/A
2N/A
2N/A /* make relevant output appear before error message */
2N/A if (fflush(stdout) == EOF) {
2N/A (void) fprintf(stderr, "Error flushing output: %s\n",
2N/A strerror(errno));
2N/A exit(1);
2N/A }
2N/A
2N/A va_start(args, format);
2N/A if (vfprintf(stderr, format, args) < 0) exit(1);
2N/A if (errlog && (vfprintf(errlog, format, args) < 0)) exit(1);
2N/A}
2N/A
2N/A/*
2N/A * Write message to standard output
2N/A */
2N/Astatic void
2N/Aoutput(const char *format, ...)
2N/A{
2N/A va_list args;
2N/A
2N/A
2N/A va_start(args, format);
2N/A if (vfprintf(stdout, format, args) < 0) {
2N/A error(gettext("Error writing output: %s\n"),
2N/A strerror(errno));
2N/A exit(1);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Safe wrapper for putchar()
2N/A */
2N/Astatic void
2N/Avoidputchar(int c)
2N/A{
2N/A if (putchar(c) == EOF) {
2N/A error(gettext("Error writing output: %s\n"),
2N/A strerror(errno));
2N/A exit(1);
2N/A }
2N/A}
2N/A
2N/Astatic void (*safeputchar)(int c) = voidputchar;
2N/A
2N/A/*
2N/A * Safe wrapper for puts()
2N/A */
2N/Astatic void
2N/Avoidputs(const char *s)
2N/A{
2N/A if (fputs(s, stdout) == EOF) {
2N/A error(gettext("Error writing output: %s\n"),
2N/A strerror(errno));
2N/A exit(1);
2N/A }
2N/A}
2N/A
2N/Astatic void (*safeputs)(const char *s) = voidputs;
2N/A
2N/A/*
2N/A * XML-safe wrapper for putchar(): quotes XML-special characters
2N/A */
2N/Astatic void
2N/Axputchar(int c)
2N/A{
2N/A switch (c) {
2N/A case '<':
2N/A c = fputs("&lt;", stdout);
2N/A break;
2N/A case '>':
2N/A c = fputs("&gt;", stdout);
2N/A break;
2N/A case '&':
2N/A c = fputs("&amp;", stdout);
2N/A break;
2N/A case '"':
2N/A c = fputs("&quot;", stdout);
2N/A break;
2N/A default:
2N/A c = putchar(c);
2N/A break;
2N/A }
2N/A
2N/A if (c == EOF) {
2N/A error(gettext("Error writing output: %s\n"),
2N/A strerror(errno));
2N/A exit(1);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * XML-safe analog of puts(): quotes XML-special characters
2N/A */
2N/Astatic void
2N/Axputs(const char *s)
2N/A{
2N/A char c;
2N/A
2N/A for (/* */; ((c = *s) != 0); s++)
2N/A xputchar(c);
2N/A}
2N/A
2N/A/*
2N/A * Output the XML DTD derived from the registry provided by libfrureg
2N/A */
2N/Aint
2N/Aoutput_dtd(void)
2N/A{
2N/A char **element;
2N/A
2N/A unsigned int i, j, num_elements = 0;
2N/A
2N/A uint8_t *tagged;
2N/A
2N/A const fru_regdef_t *def;
2N/A
2N/A
2N/A if (((element = fru_reg_list_entries(&num_elements)) == NULL) ||
2N/A (num_elements == 0)) {
2N/A error(gettext("No FRU ID Registry elements"));
2N/A return (1);
2N/A }
2N/A
2N/A if ((tagged = calloc(num_elements, sizeof (*tagged))) == NULL) {
2N/A error(gettext("Unable to get memory for tagged element list"),
2N/A strerror(errno));
2N/A return (1);
2N/A }
2N/A
2N/A /*
2N/A * Output the DTD preamble
2N/A */
2N/A output("<!ELEMENT FRUID_XML_Tree (Parameter*, "
2N/A "(Fru | Location | Container)*,\n"
2N/A " Parameter*, ErrorLog?, Parameter*)>\n"
2N/A "<!ATTLIST FRUID_XML_Tree>\n"
2N/A "\n"
2N/A "<!ELEMENT Parameter EMPTY>\n"
2N/A "<!ATTLIST Parameter type CDATA #REQUIRED>\n"
2N/A "<!ATTLIST Parameter name CDATA #REQUIRED>\n"
2N/A "<!ATTLIST Parameter value CDATA #REQUIRED>\n"
2N/A "\n"
2N/A "<!ELEMENT Fru (Fru | Location | Container)*>\n"
2N/A "<!ATTLIST Fru name CDATA #REQUIRED>\n"
2N/A "\n"
2N/A "<!ELEMENT Location (Fru | Location | Container)*>\n"
2N/A "<!ATTLIST Location\n"
2N/A " name CDATA #IMPLIED\n"
2N/A " value CDATA #IMPLIED\n"
2N/A ">\n"
2N/A "\n"
2N/A "<!ELEMENT Container (ContainerData?, "
2N/A "(Fru | Location | Container)*)>\n"
2N/A "<!ATTLIST Container name CDATA #REQUIRED>\n"
2N/A "<!ATTLIST Container imagefile CDATA #IMPLIED>\n"
2N/A "\n"
2N/A "<!ELEMENT ContainerData (Segment*)>\n"
2N/A "<!ATTLIST ContainerData>\n"
2N/A "\n"
2N/A "<!ATTLIST Segment name CDATA #REQUIRED>\n"
2N/A "\n"
2N/A "<!ELEMENT Index EMPTY>\n"
2N/A "<!ATTLIST Index value CDATA #REQUIRED>\n"
2N/A "\n"
2N/A "<!ELEMENT ErrorLog (#PCDATA)>\n"
2N/A "<!ATTLIST ErrorLog>\n"
2N/A "\n");
2N/A
2N/A /*
2N/A * Output the definition for each element
2N/A */
2N/A for (i = 0; i < num_elements; i++) {
2N/A assert(element[i] != NULL);
2N/A /* Prevent incompatible duplicate defn. from FRUID Registry. */
2N/A if ((strcmp("Location", element[i])) == 0) continue;
2N/A if ((def = fru_reg_lookup_def_by_name(element[i])) == NULL) {
2N/A error(gettext("Error looking up registry "
2N/A "definition for \"%s\"\n"),
2N/A element[i]);
2N/A return (1);
2N/A }
2N/A
2N/A if (def->tagType != FRU_X) tagged[i] = 1;
2N/A
2N/A if (def->dataType == FDTYPE_Record) {
2N/A if (def->iterationType == FRU_NOT_ITERATED)
2N/A output("<!ELEMENT %s (%s", element[i],
2N/A def->enumTable[0].text);
2N/A else
2N/A output("<!ELEMENT %s (Index_%s*)>\n"
2N/A "<!ATTLIST Index_%s>\n"
2N/A "<!ELEMENT Index_%s (%s",
2N/A element[i], element[i], element[i],
2N/A element[i], def->enumTable[0].text);
2N/A
2N/A for (j = 1; j < def->enumCount; j++)
2N/A output(",\n\t%s", def->enumTable[j].text);
2N/A
2N/A output(")>\n");
2N/A } else if (def->iterationType == FRU_NOT_ITERATED) {
2N/A output("<!ELEMENT %s EMPTY>\n"
2N/A "<!ATTLIST %s value CDATA #REQUIRED>\n",
2N/A element[i], element[i]);
2N/A
2N/A if (def->dataType == FDTYPE_Enumeration) {
2N/A output("<!-- %s valid enumeration values\n");
2N/A for (j = 0; j < def->enumCount; j++) {
2N/A output("\t\"");
2N/A xputs(def->enumTable[j].text);
2N/A output("\"\n");
2N/A }
2N/A output("-->\n");
2N/A }
2N/A }
2N/A else
2N/A output("<!ELEMENT %s (Index*)>\n", element[i]);
2N/A
2N/A output("\n");
2N/A }
2N/A
2N/A /* Provide for returning the tag for an "unknown" element */
2N/A output("<!ATTLIST UNKNOWN tag CDATA \"UNKNOWN\">\n\n");
2N/A
2N/A
2N/A /*
2N/A * List all data elements as possible members of "Segment"
2N/A */
2N/A output("<!ELEMENT Segment ((UNKNOWN");
2N/A for (i = 0; i < num_elements; i++) {
2N/A if (tagged[i]) output("\n\t| %s", element[i]);
2N/A free(element[i]);
2N/A }
2N/A output(")*)>\n");
2N/A free(element);
2N/A free(tagged);
2N/A
2N/A return (0);
2N/A}
2N/A/*
2N/A * Function to convert bcd to binary to correct the SPD_Manufacturer_Week
2N/A *
2N/A */
2N/Astatic void convertbcdtobinary(int *val)
2N/A{
2N/A unsigned int newval = (unsigned int)*val, tmpval = 0;
2N/A while (newval > 0) {
2N/A tmpval = (tmpval << 4) | (newval & 0xF);
2N/A newval >>= 4;
2N/A }
2N/A while (tmpval > 0) {
2N/A newval = (newval * 10) + (tmpval & 0xF);
2N/A tmpval >>= 4;
2N/A }
2N/A *val = newval;
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Safely pretty-print the value of a field
2N/A */
2N/Astatic void
2N/Aprint_field(const uint8_t *field, const fru_regdef_t *def)
2N/A{
2N/A char *errmsg = NULL, timestring[TIMESTRINGLEN], path[16384];
2N/A
2N/A int i, valueint;
2N/A
2N/A uint64_t value;
2N/A
2N/A time_t timefield;
2N/A
2N/A struct tm *tm;
2N/A
2N/A uchar_t first_byte, data[128];
2N/A
2N/A const fru_regdef_t *new_def;
2N/A
2N/A const char *elem_name = NULL;
2N/A const char *parent_path;
2N/A switch (def->dataType) {
2N/A case FDTYPE_Binary:
2N/A assert(def->payloadLen <= sizeof (value));
2N/A switch (def->dispType) {
2N/A case FDISP_Binary:
2N/A for (i = 0; i < def->payloadLen; i++)
2N/A output("%c%c%c%c%c%c%c%c",
2N/A ((field[i] & 0x80) ? '1' : '0'),
2N/A ((field[i] & 0x40) ? '1' : '0'),
2N/A ((field[i] & 0x20) ? '1' : '0'),
2N/A ((field[i] & 0x10) ? '1' : '0'),
2N/A ((field[i] & 0x08) ? '1' : '0'),
2N/A ((field[i] & 0x04) ? '1' : '0'),
2N/A ((field[i] & 0x02) ? '1' : '0'),
2N/A ((field[i] & 0x01) ? '1' : '0'));
2N/A return;
2N/A case FDISP_Octal:
2N/A case FDISP_Decimal:
2N/A value = 0;
2N/A valueint = 0;
2N/A (void) memcpy((((uint8_t *)&value) +
2N/A sizeof (value) - def->payloadLen),
2N/A field, def->payloadLen);
2N/A if ((value != 0) &&
2N/A (strcmp(def->name, "SPD_Manufacture_Week") == 0)) {
2N/A valueint = (int)value;
2N/A if (spd_memtype && spd_revision) {
2N/A convertbcdtobinary(&valueint);
2N/A spd_memtype = 0;
2N/A spd_revision = 0;
2N/A }
2N/A output("%d", valueint);
2N/A return;
2N/A }
2N/A if ((value != 0) &&
2N/A ((strcmp(def->name, "Lowest") == 0) ||
2N/A (strcmp(def->name, "Highest") == 0) ||
2N/A (strcmp(def->name, "Latest") == 0)))
2N/A output((def->dispType == FDISP_Octal) ?
2N/A "%llo" : "%lld (%lld degrees C)",
2N/A value, (value - TEMPERATURE_OFFSET));
2N/A else
2N/A output((def->dispType == FDISP_Octal) ?
2N/A "%llo" : "%lld", value);
2N/A return;
2N/A case FDISP_Time:
2N/A if (def->payloadLen > sizeof (timefield)) {
2N/A errmsg = "time value too large for formatting";
2N/A break;
2N/A }
2N/A timefield = 0;
2N/A (void) memcpy((((uint8_t *)&timefield) +
2N/A sizeof (timefield) - def->payloadLen),
2N/A field, def->payloadLen);
2N/A if (timefield == 0) {
2N/A errmsg = "No Value Recorded";
2N/A break;
2N/A }
2N/A if ((tm = gmtime(&timefield)) == NULL) {
2N/A errmsg = "cannot convert time value";
2N/A break;
2N/A }
2N/A if (strftime(timestring, sizeof (timestring), GMT, tm)
2N/A == 0) {
2N/A errmsg = "formatted time would overflow buffer";
2N/A break;
2N/A }
2N/A safeputs(timestring);
2N/A return;
2N/A }
2N/A break;
2N/A case FDTYPE_ASCII:
2N/A if (!xml) {
2N/A if (strcmp(def->name, "Message") == 0) {
2N/A if (FMAmessageR == 0)
2N/A elem_name = "FMA_Event_DataR";
2N/A else if (FMAmessageR == 1)
2N/A elem_name = "FMA_MessageR";
2N/A if (elem_name != NULL) {
2N/A (void) memcpy(data, field,
2N/A def->payloadLen);
2N/A new_def =
2N/A fru_reg_lookup_def_by_name
2N/A (elem_name);
2N/A (void) snprintf(path, sizeof (path),
2N/A "/Status_EventsR[%d]/Message(FMA)",
2N/A iterglobal);
2N/A parent_path = path;
2N/A output("\n");
2N/A print_element(data, new_def,
2N/A parent_path, 2*INDENT);
2N/A return;
2N/A }
2N/A }
2N/A }
2N/A for (i = 0; i < def->payloadLen && field[i]; i++)
2N/A safeputchar(field[i]);
2N/A return;
2N/A case FDTYPE_Enumeration:
2N/A value = 0;
2N/A (void) memcpy((((uint8_t *)&value) + sizeof (value)
2N/A - def->payloadLen),
2N/A field, def->payloadLen);
2N/A for (i = 0; i < def->enumCount; i++)
2N/A if (def->enumTable[i].value == value) {
2N/A if (strcmp(def->name, "Event_Code") == 0) {
2N/A if (strcmp(def->enumTable[i].text,
2N/A"FMA Message R") == 0)
2N/A FMAmessageR = 1;
2N/A else
2N/A if (strcmp(def->enumTable[i].text,
2N/A"FMA Event Data R") == 0)
2N/A FMAmessageR = 0;
2N/A }
2N/A if (strcmp(def->name,
2N/A"SPD_Fundamental_Memory_Type") == 0) {
2N/A if (strcmp(def->enumTable[i].text,
2N/A"DDR II SDRAM") == 0)
2N/A spd_memtype = 1;
2N/A }
2N/A safeputs(def->enumTable[i].text);
2N/A return;
2N/A }
2N/A
2N/A errmsg = "unrecognized value";
2N/A break;
2N/A }
2N/A
2N/A /* If nothing matched above, print the field in hex */
2N/A switch (def->dispType) {
2N/A case FDISP_MSGID:
2N/A (void) memcpy((uchar_t *)&first_byte, field, 1);
2N/A if (isprint(first_byte)) {
2N/A for (i = 0; i < def->payloadLen && field[i];
2N/A i++)
2N/A safeputchar(field[i]);
2N/A }
2N/A break;
2N/A case FDISP_UUID:
2N/A for (i = 0; i < def->payloadLen; i++) {
2N/A if ((i == 4) || (i == 6) ||
2N/A (i == 8) || (i == 10))
2N/A output("-");
2N/A output("%2.2x", field[i]);
2N/A }
2N/A break;
2N/A default:
2N/A if (strcmp(def->name,
2N/A "SPD_Data_Revision_Code") == 0) {
2N/A value = 0;
2N/A valueint = 0;
2N/A (void) memcpy((((uint8_t *)&value)
2N/A + sizeof (value) - def->payloadLen),
2N/A field, def->payloadLen);
2N/A valueint = (int)value;
2N/A if ((valueint >= MIN_VERSION) && (spd_memtype))
2N/A spd_revision = 1;
2N/A }
2N/A for (i = 0; i < def->payloadLen; i++)
2N/A output("%2.2X", field[i]);
2N/A break;
2N/A }
2N/A
2N/A /* Safely print any error message associated with the field */
2N/A if (errmsg) {
2N/A if (strcmp(def->name, "Fault_Diag_Secs") != 0) {
2N/A output(" (");
2N/A safeputs(errmsg);
2N/A output(")");
2N/A }
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Recursively print the contents of a data element
2N/A */
2N/Astatic void
2N/Aprint_element(const uint8_t *data, const fru_regdef_t *def,
2N/A const char *parent_path, int indent)
2N/A{
2N/A char *path;
2N/A size_t len;
2N/A
2N/A int bytes = 0, i;
2N/A
2N/A
2N/A indent = (xml) ? (indent + INDENT) : (2*INDENT);
2N/A if (strcmp(def->name, "Sun_SPD_DataR") == 0) {
2N/A Fault_Install_DataR_flag = indent;
2N/A Power_On_DataR_flag = indent;
2N/A }
2N/A /*
2N/A * Construct the path, or, for XML, the name, for the current
2N/A * data element
2N/A */
2N/A if ((def->iterationCount == 0) &&
2N/A (def->iterationType != FRU_NOT_ITERATED)) {
2N/A if (xml) {
2N/A if (def->dataType == FDTYPE_Record) {
2N/A len = strlen("Index_") + strlen(def->name) + 1;
2N/A path = alloca(len);
2N/A (void) snprintf(path, len,
2N/A "Index_%s", def->name);
2N/A }
2N/A else
2N/A path = "Index";
2N/A }
2N/A else
2N/A path = (char *)parent_path;
2N/A } else {
2N/A if (xml)
2N/A path = (char *)def->name;
2N/A else {
2N/A len = strlen(parent_path) + sizeof ("/") +
2N/A strlen(def->name) +
2N/A (def->iterationCount ? sizeof ("[255]") : 0);
2N/A path = alloca(len);
2N/A bytes = snprintf(path, len,
2N/A "%s/%s", parent_path, def->name);
2N/A }
2N/A }
2N/A
2N/A if ((Fault_Install_DataR_flag) &&
2N/A (strcmp(path, "E_1_46") == 0) || (strcmp(path, "/E_1_46") == 0)) {
2N/A int cnt;
2N/A char timestring[128];
time_t timefield = 0;
struct tm *tm;
indent = Fault_Install_DataR_flag;
(void) memcpy((uint8_t *)&timefield, data, 4);
if (timefield == 0) {
(void) sprintf(timestring,
"00000000 (No Value Recorded)\"");
} else {
if ((tm = gmtime(&timefield)) == NULL)
(void) sprintf(timestring,
"cannot convert time value");
if (strftime(timestring,
sizeof (timestring), GMT, tm) == 0)
(void) sprintf(timestring,
"formatted time would overflow buffer");
}
if (xml) {
(void) sprintf(path, "Fault_Install_DataR");
output("%*s<%s>\n", indent, "", path);
indent = Fault_Install_DataR_flag + INDENT;
(void) sprintf(path, "UNIX_Timestamp32");
output("%*s<%s value=\"", indent, "", path);
/*CSTYLED*/
output("%s\"/>\n", timestring);
(void) sprintf(path, "MACADDR");
output("%*s<%s value=\"", indent, "", path);
for (cnt = 4; cnt < 4 + 6; cnt++) {
output("%2.2x", data[cnt]);
if (cnt < 4 + 6 - 1)
output(":");
}
/*CSTYLED*/
output("\"/>\n");
(void) sprintf(path, "Status");
output("%*s<%s value=\"", indent, "", path);
/*CSTYLED*/
output("%2.2x\"/>\n", data[10]);
(void) sprintf(path, "Initiator");
output("%*s<%s value=\"", indent, "", path);
/*CSTYLED*/
output("%2.2x\"/>\n", data[11]);
(void) sprintf(path, "Message_Type");
output("%*s<%s value=\"", indent, "", path);
/*CSTYLED*/
output("%2.2x\"/>\n", data[12]);
(void) sprintf(path, "Message_32");
output("%*s<%s value=\"", indent, "", path);
for (cnt = 13; cnt < 13 + 32; cnt++)
output("%2.2x", data[cnt]);
/*CSTYLED*/
output("\"/>\n");
indent = Fault_Install_DataR_flag;
(void) sprintf(path, "Fault_Install_DataR");
output("%*s</%s>\n", indent, "", path);
} else {
(void) sprintf(path, "/Fault_Install_DataR");
output("%*s%s\n", indent, "", path);
(void) sprintf(path,
"/Fault_Install_DataR/UNIX_Timestamp32");
output("%*s%s: ", indent, "", path);
output("%s\n", timestring);
(void) sprintf(path, "/Fault_Install_DataR/MACADDR");
output("%*s%s: ", indent, "", path);
for (cnt = 4; cnt < 4 + 6; cnt++) {
output("%2.2x", data[cnt]);
if (cnt < 4 + 6 - 1)
output(":");
}
output("\n");
(void) sprintf(path, "/Fault_Install_DataR/Status");
output("%*s%s: ", indent, "", path);
output("%2.2x\n", data[10]);
(void) sprintf(path, "/Fault_Install_DataR/Initiator");
output("%*s%s: ", indent, "", path);
output("%2.2x\n", data[11]);
(void) sprintf(path,
"/Fault_Install_DataR/Message_Type");
output("%*s%s: ", indent, "", path);
output("%2.2x\n", data[12]);
(void) sprintf(path, "/Fault_Install_DataR/Message_32");
output("%*s%s: ", indent, "", path);
for (cnt = 13; cnt < 13 + 32; cnt++)
output("%2.2x", data[cnt]);
output("\n");
}
Fault_Install_DataR_flag = 0;
return;
} else if ((Power_On_DataR_flag) && (
strcmp(path, "C_10_8") == 0 ||
(strcmp(path, "/C_10_8") == 0))) {
int cnt;
char timestring[128];
time_t timefield = 0;
struct tm *tm;
indent = Power_On_DataR_flag;
(void) memcpy((uint8_t *)&timefield, data, 4);
if (timefield == 0) {
(void) sprintf(timestring,
"00000000 (No Value Recorded)");
} else {
if ((tm = gmtime(&timefield)) == NULL)
(void) sprintf(timestring,
"cannot convert time value");
if (strftime(timestring,
sizeof (timestring), GMT, tm) == 0)
(void) sprintf(timestring,
"formatted time would overflow buffer");
}
if (xml) {
(void) sprintf(path, "Power_On_DataR");
output("%*s<%s>\n", indent, "", path);
indent = Power_On_DataR_flag + INDENT;
(void) sprintf(path, "UNIX_Timestamp32");
output("%*s<%s value=\"", indent, "", path);
/*CSTYLED*/
output("%s\"/>\n", timestring);
(void) sprintf(path, "Power_On_Minutes");
output("%*s<%s value=\"", indent, "", path);
for (cnt = 4; cnt < 4 + 4; cnt++)
output("%2.2x", data[cnt]);
/*CSTYLED*/
output("\"/>\n");
indent = Power_On_DataR_flag;
(void) sprintf(path, "Power_On_DataR");
output("%*s</%s>\n", indent, "", path);
} else {
(void) sprintf(path, "/Power_On_DataR");
output("%*s%s\n", indent, "", path);
(void) sprintf(path,
"/Power_On_DataR/UNIX_Timestamp32");
output("%*s%s: ", indent, "", path);
output("%s\n", timestring);
(void) sprintf(path,
"/Power_On_DataR/Power_On_Minutes");
output("%*s%s: ", indent, "", path);
for (cnt = 4; cnt < 4 + 4; cnt++)
output("%2.2x", data[cnt]);
output("\n");
}
Power_On_DataR_flag = 0;
return;
}
/*
* Handle the various categories of data elements: iteration,
* record, and field
*/
if (def->iterationCount) {
int iterlen = (def->payloadLen - NUM_ITER_BYTES)/
def->iterationCount,
n, valid = 1;
uint8_t head, num;
fru_regdef_t newdef;
/*
* Make a new element definition to describe the components
* of the iteration
*/
(void) memcpy(&newdef, def, sizeof (newdef));
newdef.iterationCount = 0;
newdef.payloadLen = iterlen;
/*
* Validate the contents of the iteration control bytes
*/
if (data[HEAD_ITER] >= def->iterationCount) {
valid = 0;
error(gettext("%s: Invalid iteration head: %d "
"(should be less than %d)\n"),
path, data[HEAD_ITER], def->iterationCount);
}
if (data[NUM_ITER] > def->iterationCount) {
valid = 0;
error(gettext("%s: Invalid iteration count: %d "
"(should not be greater than %d)\n"),
path, data[NUM_ITER], def->iterationCount);
}
if (data[MAX_ITER] != def->iterationCount) {
valid = 0;
error(gettext("%s: Invalid iteration maximum: %d "
"(should equal %d)\n"),
path, data[MAX_ITER], def->iterationCount);
}
if (valid) {
head = data[HEAD_ITER];
num = data[NUM_ITER];
} else {
head = 0;
num = def->iterationCount;
error(gettext("%s: Showing all iterations\n"), path);
}
if (xml)
output("%*s<%s>\n", indent, "", path);
else
output("%*s%s (%d iterations)\n", indent, "", path,
num);
/*
* Print each component of the iteration
*/
for (i = head, n = 0, data += 4;
n < num;
i = ((i + 1) % def->iterationCount), n++) {
if (!xml) (void) sprintf((path + bytes), "[%d]", n);
iterglobal = n;
print_element((data + i*iterlen), &newdef, path,
indent);
}
if (xml) output("%*s</%s>\n", indent, "", path);
} else if (def->dataType == FDTYPE_Record) {
const fru_regdef_t *component;
if (xml)
output("%*s<%s>\n", indent, "", path);
else
output("%*s%s\n", indent, "", path);
/*
* Print each component of the record
*/
for (i = 0; i < def->enumCount;
i++, data += component->payloadLen) {
component = fru_reg_lookup_def_by_name(
def->enumTable[i].text);
assert(component != NULL);
print_element(data, component, path, indent);
}
if (xml) output("%*s</%s>\n", indent, "", path);
} else if (xml) {
/*
* Base case: print the field formatted for XML
*/
char *format = ((def == &unknown)
? "%*s<UNKNOWN tag=\"%s\" value=\""
: "%*s<%s value=\"");
output(format, indent, "", path);
print_field(data, def);
/*CSTYLED*/
output("\"/>\n"); /* \" confuses cstyle */
if ((strcmp(def->name, "Message") == 0) &&
((FMAmessageR == 0) || (FMAmessageR == 1))) {
const char *elem_name = NULL;
const char *parent_path;
uchar_t tmpdata[128];
char path[16384];
const fru_regdef_t *new_def;
if (FMAmessageR == 0)
elem_name = "FMA_Event_DataR";
else if (FMAmessageR == 1)
elem_name = "FMA_MessageR";
if (elem_name != NULL) {
(void) memcpy(tmpdata, data, def->payloadLen);
new_def = fru_reg_lookup_def_by_name(elem_name);
(void) snprintf(path, sizeof (path),
"/Status_EventsR[%d]/Message(FMA)", iterglobal);
parent_path = path;
print_element(tmpdata, new_def,
parent_path, 2*INDENT);
FMAmessageR = -1;
}
}
} else {
/*
* Base case: print the field
*/
output("%*s%s: ", indent, "", path);
print_field(data, def);
output("\n");
}
}
/*
* Print the contents of a packet (i.e., a tagged data element)
*/
/* ARGSUSED */
static int
print_packet(fru_tag_t *tag, uint8_t *payload, size_t length, void *args)
{
int tag_type = get_tag_type(tag);
size_t payload_length = 0;
const fru_regdef_t *def;
/*
* Build a definition for unrecognized tags (e.g., not in libfrureg)
*/
if ((tag_type == -1) ||
((payload_length = get_payload_length(tag)) != length)) {
def = &unknown;
unknown.tagType = -1;
unknown.tagDense = -1;
unknown.payloadLen = length;
unknown.dataLength = unknown.payloadLen;
if (tag_type == -1)
(void) snprintf(tagname, sizeof (tagname), "INVALID");
else
(void) snprintf(tagname, sizeof (tagname),
"%s_%u_%u_%u", get_tagtype_str(tag_type),
get_tag_dense(tag), payload_length, length);
} else if ((def = fru_reg_lookup_def_by_tag(*tag)) == NULL) {
def = &unknown;
unknown.tagType = tag_type;
unknown.tagDense = get_tag_dense(tag);
unknown.payloadLen = payload_length;
unknown.dataLength = unknown.payloadLen;
(void) snprintf(tagname, sizeof (tagname), "%s_%u_%u",
get_tagtype_str(unknown.tagType),
unknown.tagDense, payload_length);
}
/*
* Print the defined element
*/
print_element(payload, def, "", INDENT);
return (FRU_SUCCESS);
}
/*
* Print a segment's name and the contents of each data element in the segment
*/
static int
print_packets_in_segment(fru_seghdl_t segment, void *args)
{
char *name;
int status;
if ((status = fru_get_segment_name(segment, &name)) != FRU_SUCCESS) {
saved_status = status;
name = "";
error(gettext("Error getting segment name: %s\n"),
fru_strerror(status));
}
if (xml)
output("%*s<Segment name=\"%s\">\n", INDENT, "", name);
else
output("%*sSEGMENT: %s\n", INDENT, "", name);
/* Iterate over the packets in the segment, printing the contents */
if ((status = fru_for_each_packet(segment, print_packet, args))
!= FRU_SUCCESS) {
saved_status = status;
error(gettext("Error processing data in segment \"%s\": %s\n"),
name, fru_strerror(status));
}
if (xml) output("%*s</Segment>\n", INDENT, "");
free(name);
return (FRU_SUCCESS);
}
/* ARGSUSED */
static void
print_node_path(fru_node_t fru_type, const char *path, const char *name,
end_node_fp_t *end_node, void **end_args)
{
output("%s%s\n", path,
((fru_type == FRU_NODE_CONTAINER) ? " (container)"
: ((fru_type == FRU_NODE_FRU) ? " (fru)" : "")));
}
/*
* Close the XML element for a "location" node
*/
/* ARGSUSED */
static void
end_location_xml(fru_nodehdl_t node, const char *path, const char *name,
void *args)
{
assert(args != NULL);
output("</Location> <!-- %s -->\n", args);
}
/*
* Close the XML element for a "fru" node
*/
/* ARGSUSED */
static void
end_fru_xml(fru_nodehdl_t node, const char *path, const char *name, void *args)
{
assert(args != NULL);
output("</Fru> <!-- %s -->\n", args);
}
/*
* Close the XML element for a "container" node
*/
/* ARGSUSED */
static void
end_container_xml(fru_nodehdl_t node, const char *path, const char *name,
void *args)
{
assert(args != NULL);
output("</Container> <!-- %s -->\n", args);
}
/*
* Introduce a node in XML and set the appropriate node-closing function
*/
/* ARGSUSED */
static void
print_node_xml(fru_node_t fru_type, const char *path, const char *name,
end_node_fp_t *end_node, void **end_args)
{
switch (fru_type) {
case FRU_NODE_FRU:
output("<Fru name=\"%s\">\n", name);
*end_node = end_fru_xml;
break;
case FRU_NODE_CONTAINER:
output("<Container name=\"%s\">\n", name);
*end_node = end_container_xml;
break;
default:
output("<Location name=\"%s\">\n", name);
*end_node = end_location_xml;
break;
}
*end_args = (void *) name;
}
/*
* Print node info and, where appropriate, node contents
*/
/* ARGSUSED */
static fru_errno_t
process_node(fru_nodehdl_t node, const char *path, const char *name,
void *args, end_node_fp_t *end_node, void **end_args)
{
int status;
fru_node_t fru_type = FRU_NODE_UNKNOWN;
if ((status = fru_get_node_type(node, &fru_type)) != FRU_SUCCESS) {
saved_status = status;
error(gettext("Error getting node type: %s\n"),
fru_strerror(status));
}
if (containers_only) {
if (fru_type != FRU_NODE_CONTAINER)
return (FRU_SUCCESS);
name = path;
}
/* Introduce the node */
assert(print_node != NULL);
print_node(fru_type, path, name, end_node, end_args);
if (list_only)
return (FRU_SUCCESS);
/* Print the contents of each packet in each segment of a container */
if (fru_type == FRU_NODE_CONTAINER) {
if (xml) output("<ContainerData>\n");
if ((status =
fru_for_each_segment(node, print_packets_in_segment,
NULL))
!= FRU_SUCCESS) {
saved_status = status;
error(gettext("Error processing node \"%s\": %s\n"),
name, fru_strerror(status));
}
if (xml) output("</ContainerData>\n");
}
return (FRU_SUCCESS);
}
/*
* Process the node if its path matches the search path in "args"
*/
/* ARGSUSED */
static fru_errno_t
process_matching_node(fru_nodehdl_t node, const char *path, const char *name,
void *args, end_node_fp_t *end_node, void **end_args)
{
int status;
if (!fru_pathmatch(path, args))
return (FRU_SUCCESS);
status = process_node(node, path, path, args, end_node, end_args);
return ((status == FRU_SUCCESS) ? FRU_WALK_TERMINATE : status);
}
/*
* Write the trailer required for well-formed DTD-compliant XML
*/
static void
terminate_xml()
{
errno = 0;
if (ftell(errlog) > 0) {
char c;
output("<ErrorLog>\n");
rewind(errlog);
if (!errno)
while ((c = getc(errlog)) != EOF)
xputchar(c);
output("</ErrorLog>\n");
}
if (errno) {
/*NOTREACHED*/
errlog = NULL;
error(gettext("Error copying error messages to \"ErrorLog\""),
strerror(errno));
}
output("</FRUID_XML_Tree>\n");
}
/*
* Print available FRU ID information
*/
int
prtfru(const char *searchpath, int containers_only_flag, int list_only_flag,
int xml_flag)
{
fru_errno_t status;
fru_nodehdl_t frutree = 0;
/* Copy parameter flags to global flags */
containers_only = containers_only_flag;
list_only = list_only_flag;
xml = xml_flag;
/* Help arrange for correct, efficient interleaving of output */
(void) setvbuf(stderr, NULL, _IOLBF, 0);
/* Initialize for XML--or not */
if (xml) {
safeputchar = xputchar;
safeputs = xputs;
print_node = print_node_xml;
if ((errlog = tmpfile()) == NULL) {
(void) fprintf(stderr,
"Error creating error log file: %s\n",
strerror(errno));
return (1);
}
/* Output the XML preamble */
output("<?xml version=\"1.0\" ?>\n"
"<!--\n"
" Copyright 2000-2002 Sun Microsystems, Inc. "
"All rights reserved.\n"
" Use is subject to license terms.\n"
"-->\n\n"
"<!DOCTYPE FRUID_XML_Tree SYSTEM \"prtfrureg.dtd\">\n\n"
"<FRUID_XML_Tree>\n");
/* Arrange to always properly terminate XML */
if (atexit(terminate_xml))
error(gettext("Warning: XML will not be terminated: "
"%s\n"), strerror(errno));
} else
print_node = print_node_path;
/* Get the root node */
if ((status = fru_get_root(&frutree)) == FRU_NODENOTFOUND) {
error(gettext("This system does not support PICL "
"infrastructure to provide FRUID data\n"
"Please use the platform SP to access the FRUID "
"information\n"));
return (1);
} else if (status != FRU_SUCCESS) {
error(gettext("Unable to access FRU ID data: %s\n"),
fru_strerror(status));
return (1);
}
/* Process the tree */
if (searchpath == NULL) {
status = fru_walk_tree(frutree, "", process_node, NULL);
} else {
status = fru_walk_tree(frutree, "", process_matching_node,
(void *)searchpath);
if (status == FRU_WALK_TERMINATE) {
status = FRU_SUCCESS;
} else if (status == FRU_SUCCESS) {
error(gettext("\"%s\" not found\n"), searchpath);
return (1);
}
}
if (status != FRU_SUCCESS)
error(gettext("Error processing FRU tree: %s\n"),
fru_strerror(status));
return (((status == FRU_SUCCESS) && (saved_status == 0)) ? 0 : 1);
}