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("<", stdout); 261 break; 262 case '>': 263 c = fputs(">", stdout); 264 break; 265 case '&': 266 c = fputs("&", stdout); 267 break; 268 case '"': 269 c = fputs(""", 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