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