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 /*
  23  * Copyright (c) 2014 Gary Mills
  24  *
  25  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  26  * Use is subject to license terms.
  27  *
  28  * Copyright (c) 2018, Joyent, Inc.
  29  */
  30 
  31 #include <limits.h>
  32 #include <stdio.h>
  33 #include <stdlib.h>
  34 #include <string.h>
  35 #include <libintl.h>
  36 #include <libfru.h>
  37 #include <errno.h>
  38 #include <math.h>
  39 #include <alloca.h>
  40 #include <assert.h>
  41 #include <sys/systeminfo.h>
  42 
  43 #define NUM_OF_SEGMENT  1
  44 #define SEGMENT_NAME_SIZE       2
  45 
  46 #define FD_SEGMENT_SIZE 2949
  47 
  48 static char  *command, *customer_data = NULL, *frupath = NULL, **svcargv;
  49 
  50 /* DataElement supported in the customer operation */
  51 static  char    *cust_data_list[] = {"Customer_DataR"};
  52 
  53 /* DataElement supported in the service operation */
  54 static  char    *serv_data_list[] = {"InstallationR", "ECO_CurrentR"};
  55 
  56 /* currently supported segment name */
  57 static  char    *segment_name[] = {"FD"};
  58 
  59 static int   found_frupath = 0, list_only = 0, recursive = 0,
  60     service_mode = 0, svcargc, update = 0;
  61 
  62 
  63 static void
  64 usage(void)
  65 {
  66         (void) fprintf(stderr,
  67             gettext("Usage:  %s [ -l ] | [ [ -r ] frupath [ text ] ]\n"),
  68             command);
  69 }
  70 
  71 static int
  72 validate_fieldnames(int argc, char *argv[])
  73 {
  74         static int      num = sizeof (serv_data_list)/sizeof (*serv_data_list);
  75 
  76         char            *fieldname;
  77 
  78         int             i, j, match, status;
  79 
  80         fru_elemdef_t   definition;
  81 
  82 
  83         for (i = 0; i < argc; i += 2) {
  84                 if (argv[i][0] == '/') {
  85                         fieldname = &argv[i][1];
  86                 } else {
  87                         fieldname = &argv[i][0];
  88                 }
  89 
  90                 match = 0;
  91                 for (j = 0; j < num; j++) {
  92                         if (strncmp(fieldname, serv_data_list[j],
  93                             strlen(serv_data_list[j])) == 0) {
  94                                 match = 1;
  95                         }
  96                 }
  97                 if (!match) {
  98                         (void) fprintf(stderr,
  99                             gettext("\"%s\" is not a supported field\n"),
 100                             argv[i]);
 101                         return (1);
 102                 }
 103 
 104                 if ((status = fru_get_definition(argv[i], &definition))
 105                     != FRU_SUCCESS) {
 106                         (void) fprintf(stderr, gettext("\"%s\":  %s\n"),
 107                             argv[i],
 108                             fru_strerror(status));
 109                         return (1);
 110                 } else if ((definition.data_type == FDTYPE_Record) ||
 111                     (definition.data_type == FDTYPE_UNDEFINED)) {
 112                         (void) fprintf(stderr,
 113                             gettext("\"%s\" is not a field\n"), argv[i]);
 114                         return (1);
 115                 }
 116         }
 117 
 118         return (0);
 119 }
 120 
 121 static int
 122 pathmatch(const char *path)
 123 {
 124         char  *match;
 125 
 126         if ((frupath != NULL) &&
 127             ((match = strstr(path, frupath)) != NULL) &&
 128             ((match + strlen(frupath)) == (path + strlen(path))) &&
 129             ((match == path) || (*(match - 1) == '/'))) {
 130                 found_frupath = 1;
 131                 return (1);
 132         }
 133         return (0);
 134 }
 135 
 136 static void
 137 displayBinary(unsigned char *data, size_t length, fru_elemdef_t *def)
 138 {
 139         int     i = 0;
 140         uint64_t        lldata;
 141         uint64_t        mask;
 142 
 143         if (def->disp_type == FDISP_Hex) {
 144                 for (i = 0; i < length; i++) {
 145                         (void) printf("%02X", data[i]);
 146                 }
 147                 return;
 148         }
 149 
 150         (void) memcpy(&lldata, data, sizeof (lldata));
 151         switch (def->disp_type) {
 152                 case FDISP_Binary:
 153                 {
 154                         mask = 0x8000000000000000ULL;
 155                         for (i = 0; i < (sizeof (uint64_t) *8); i++) {
 156                                 if (lldata & (mask >> i)) {
 157                                         (void) printf("1");
 158                                 } else {
 159                                         (void) printf("0");
 160                                 }
 161                         }
 162                         return;
 163                 }
 164                 case FDISP_Octal:
 165                 {
 166                         (void) printf("%llo", lldata);
 167                         return;
 168                 }
 169                 case FDISP_Decimal:
 170                 {
 171                         (void) printf("%lld", lldata);
 172                         return;
 173                 }
 174                 case FDISP_Time:
 175                 {
 176                         char buffer[PATH_MAX];
 177                         time_t time;
 178                         time = (time_t)lldata;
 179                         (void) strftime(buffer, PATH_MAX, "%+",
 180                             localtime(&time));
 181                         (void) printf("%s", buffer);
 182                         return;
 183                 }
 184         }
 185 }
 186 
 187 static void
 188 displayBAasBinary(unsigned char *data, size_t length)
 189 {
 190         int i;
 191         unsigned char mask;
 192 
 193         for (i = 0; i < length; i++) {
 194                 /*
 195                  * make a mask for the high order bit and adjust down through
 196                  * all the bits.
 197                  */
 198                 for (mask = 0x80; mask > 0; mask /= 2) {
 199                         if ((data[i] & mask) != 0) /* bit must be on */
 200                                 (void) printf("1");
 201                         else /* bit is off... */
 202                                 (void) printf("0");
 203                 }
 204         }
 205         (void) printf("\n");
 206 }
 207 
 208 static void
 209 display_data(unsigned char *data, size_t length, fru_elemdef_t *def)
 210 {
 211         int i = 0;
 212         uint64_t        lldata;
 213 
 214         if (data == 0x00) {
 215                 (void) printf("\n");
 216                 return;
 217         }
 218 
 219         switch (def->data_type) {
 220         case FDTYPE_Binary:
 221         {
 222                 displayBinary(data, length, def);
 223                 return;
 224         }
 225 
 226         case FDTYPE_ByteArray:
 227         {
 228                 switch (def->disp_type) {
 229                 case FDISP_Binary:
 230                         displayBAasBinary(data, length);
 231                         return;
 232                 case FDISP_Hex:
 233                         for (i = 0; i < length; i++) {
 234                                 (void) printf("%02X", data[i]);
 235                         }
 236                         return;
 237                 }
 238                 return;
 239         }
 240         case FDTYPE_Unicode:
 241                 assert(gettext("Unicode not yet supported") == 0);
 242                 break;
 243         case FDTYPE_ASCII:
 244         {
 245                 char *disp_str = (char *)alloca(length+1);
 246                 for (i = 0; i < length; i++)
 247                         disp_str[i] = data[i];
 248                 disp_str[i] = '\0';
 249                 (void) printf("%s", disp_str);
 250                 return;
 251         }
 252 
 253         case FDTYPE_Enumeration:
 254         {
 255                 lldata = strtoull((const char *)data, NULL, 0);
 256                 for (i = 0; i < def->enum_count; i++) {
 257                         if (def->enum_table[i].value == lldata) {
 258                         /* strdup such that map_... can realloc if necessary. */
 259                                 char *tmp = strdup(def->enum_table[i].text);
 260                                 (void) printf("%s", tmp);
 261                                 free(tmp);
 262                                 return;
 263                         }
 264                 }
 265                 (void) printf(gettext("Unrecognized Value:  0x"));
 266                 for (i = 0; i < sizeof (uint64_t); i++)
 267                         (void) printf("%02X", data[i]);
 268                 break;
 269         }
 270         default:
 271                 break;
 272         }
 273 }
 274 
 275 static void
 276 print_node_data(fru_nodehdl_t cont_hdl)
 277 {
 278         int     iter_cnt = 0;
 279         int     iter;
 280         int     numseg;
 281         int     list_cnt;
 282         unsigned char   *data;
 283         size_t  dataLen;
 284         int     total_cnt;
 285         char    *found_path = NULL;
 286         fru_elemdef_t    def, def1;
 287         int     instance = 0;
 288         char    **ptr;
 289         char    **tmp_ptr;
 290         int     count = 0;
 291         char    elem_name[PATH_MAX];
 292 
 293         if (service_mode) {
 294                 total_cnt = sizeof (serv_data_list)/sizeof (*serv_data_list);
 295                 ptr = serv_data_list;
 296         } else {
 297                 total_cnt = sizeof (cust_data_list)/sizeof (*cust_data_list);
 298                 ptr = cust_data_list;
 299         }
 300         tmp_ptr = ptr;
 301 
 302         for (numseg = 0; numseg < NUM_OF_SEGMENT; numseg++) {
 303                 ptr = tmp_ptr;
 304                 for (list_cnt = 0; list_cnt < total_cnt; list_cnt++) {
 305                         if ((fru_get_definition(*ptr, &def)) != FRU_SUCCESS) {
 306                                 continue;
 307                         }
 308                         if ((fru_get_num_iterations(cont_hdl,
 309                             &segment_name[numseg], 0, *ptr,
 310                             &iter_cnt, NULL)) != FRU_SUCCESS) {
 311                                 iter_cnt = 0;
 312                         }
 313                         iter = 0;
 314                         do {
 315                                 for (count = 0; count < def.enum_count;
 316                                     count++) {
 317                                         if (def.iteration_type !=
 318                                             FRU_NOT_ITERATED) {
 319                                                 (void) snprintf(elem_name,
 320                                                     sizeof (elem_name),
 321                         "/%s[%d]/%s", *ptr, iter, def.enum_table[count].text);
 322                                         } else {
 323                                                 (void) snprintf(elem_name,
 324                                                     sizeof (elem_name),
 325                         "/%s/%s", *ptr, def.enum_table[count].text);
 326                                         }
 327 
 328                                         if ((fru_read_field(cont_hdl,
 329                                             &segment_name[numseg], instance,
 330                                             elem_name, (void**)&data, &dataLen,
 331                                             &found_path)) != FRU_SUCCESS) {
 332                                                 break;
 333                                         }
 334 
 335                                         if ((fru_get_definition(
 336                         def.enum_table[count].text, &def1)) != FRU_SUCCESS) {
 337                                                 break;
 338                                         }
 339                                         (void) printf(" %s:  ",\
 340                                             elem_name);
 341                                         display_data(data, dataLen, &def1);
 342                                         (void) printf("\n");
 343                                 }
 344                                 iter ++;
 345                         } while (iter < iter_cnt);
 346                         ptr++;
 347                 }
 348         }
 349 }
 350 
 351 static char *
 352 convertBinaryToDecimal(char *ptr)
 353 {
 354         int     cnt = 0;
 355         char    *data;
 356         int     str_len;
 357         char    *ret = NULL;
 358         uint64_t        result = 0;
 359 
 360         str_len = strlen(ptr);
 361         data = ptr;
 362 
 363         while (str_len >= 1) {
 364                 str_len -= 1;
 365                 if (data[str_len] == '0') {
 366                         result += (0 * pow(2, cnt));
 367                 }
 368                 if (data[str_len] == '1') {
 369                         result += (1 * pow(2, cnt));
 370                 }
 371                 cnt++;
 372         }
 373         ret = (char *)lltostr(result, "\n");
 374         return (ret);
 375 }
 376 
 377 /*
 378  * called update_field() to update the field with specific field value.
 379  * nodehdl represents the fru, segment represents the segment name in the fru.
 380  * field_name represents the field to be updated with the value field_value.
 381  */
 382 
 383 static int
 384 convert_update(fru_nodehdl_t nodehdl, char *segment, char *field_name,
 385                                                         char *field_value)
 386 {
 387         uint64_t num = 0;
 388         fru_elemdef_t def;
 389         fru_errno_t     err;
 390         void    *data = NULL;
 391         size_t  dataLen = 0;
 392         int     i;
 393 
 394         if ((err = fru_get_definition(field_name, &def)) != FRU_SUCCESS) {
 395                 (void) fprintf(stderr,
 396                     gettext("Failed to get definition %s:  %s\n"),
 397                     field_name, fru_strerror(err));
 398                 return (1);
 399         }
 400 
 401         if (field_value == NULL) {
 402                 return (1);
 403         }
 404 
 405         switch (def.data_type) {
 406                 case    FDTYPE_Binary:
 407                         if (def.disp_type != FDISP_Time) {
 408                                 if (field_value[0] == 'b') {
 409                                         field_value =
 410                                             convertBinaryToDecimal((field_value
 411                                             +1));
 412                                 }
 413                                 num = strtoll(field_value, (char **)NULL, 0);
 414                                 if ((num == 0) && (errno == 0)) {
 415                                         return (1);
 416                                 }
 417                                 data = (void*)&num;
 418                                 dataLen = sizeof (uint64_t);
 419                         }
 420                         break;
 421                 case    FDTYPE_ByteArray:
 422                         return (1);
 423                 case    FDTYPE_Unicode:
 424                         return (1);
 425                 case    FDTYPE_ASCII:
 426                         data = (void *) field_value;
 427                         dataLen = strlen(field_value);
 428                         if (dataLen < def.data_length) {
 429                                 dataLen++;
 430                         }
 431                         break;
 432                 case    FDTYPE_Enumeration:
 433                         for (i = 0; i < def.enum_count; i++) {
 434                                 if (strcmp(def.enum_table[i].text,
 435                                     field_value) == 0) {
 436                                         data = (void *)(uintptr_t)
 437                                             def.enum_table[i].value;
 438                                         dataLen = sizeof (uint64_t);
 439                                         break;
 440                                 }
 441                         }
 442                         return (1);
 443                 case    FDTYPE_Record:
 444                         if (def.iteration_count == 0) {
 445                                 return (1);
 446                         }
 447                         data = NULL;
 448                         dataLen = 0;
 449                         break;
 450                 case    FDTYPE_UNDEFINED:
 451                         return (1);
 452         }
 453 
 454         if ((err = fru_update_field(nodehdl, segment, 0, field_name, data,
 455             dataLen)) != FRU_SUCCESS) {
 456                 (void) fprintf(stderr, gettext("fru_update_field():  %s\n"),
 457                     fru_strerror(err));
 458                 return (1);
 459         }
 460         return (0);
 461 }
 462 /*
 463  * called by update_field() when a new data element is created.
 464  * it updates the UNIX_Timestamp32 field with the current system time.
 465  */
 466 
 467 static int
 468 update_unixtimestamp(fru_nodehdl_t nodehdl, char *segment, char **ptr)
 469 {
 470         char    *field_name;
 471         time_t  clock;
 472         struct  tm *sp_tm;
 473         fru_errno_t     err = FRU_SUCCESS;
 474         uint64_t        time_data;
 475         size_t          len;
 476 
 477         len = strlen(*ptr) + strlen("UNIX_Timestamp32") + 3;
 478         field_name = alloca(len);
 479 
 480         (void) snprintf(field_name, len, "/%s/UNIX_Timestamp32", *ptr);
 481 
 482         clock = time(NULL);
 483         sp_tm = localtime(&clock);
 484         time_data = (uint64_t)mktime(sp_tm);
 485 
 486         if ((err = fru_update_field(nodehdl, segment, 0, field_name,
 487             (void *)&time_data, sizeof (time_data))) != FRU_SUCCESS) {
 488                 (void) fprintf(stderr, gettext("fru_update_field():  %s\n"),
 489                     fru_strerror(err));
 490                 return (1);
 491         }
 492         return (0);
 493 }
 494 
 495 /*
 496  * create segment on the specified fru represented by nodehdl.
 497  */
 498 
 499 static int
 500 create_segment(fru_nodehdl_t nodehdl)
 501 {
 502         fru_segdesc_t   seg_desc;
 503         fru_segdef_t    def;
 504         int     cnt;
 505         int     status;
 506 
 507         (void) memset(&seg_desc, 0, sizeof (seg_desc));
 508         seg_desc.field.field_perm = 0x6;
 509         seg_desc.field.operations_perm = 0x6;
 510         seg_desc.field.engineering_perm = 0x6;
 511         seg_desc.field.repair_perm = 0x6;
 512 
 513         (void) memset(&def, 0, sizeof (def));
 514         def.address = 0;
 515         def.desc.raw_data = seg_desc.raw_data;
 516         def.hw_desc.all_bits = 0;
 517 
 518         for (cnt = 0; cnt < NUM_OF_SEGMENT; cnt++) {
 519                 (void) strncpy(def.name, segment_name[cnt], SEGMENT_NAME_SIZE);
 520                 if (cnt == 0) {
 521                         def.size = FD_SEGMENT_SIZE;
 522                 }
 523                 if ((status = fru_create_segment(nodehdl, &def))
 524                     != FRU_SUCCESS) {
 525                         continue;
 526                 }
 527                 return (cnt);
 528         }
 529         if (status != FRU_SUCCESS)
 530                 (void) fprintf(stderr, gettext("fru_create_segment():  %s\n"),
 531                     fru_strerror(status));
 532         return (1);
 533 }
 534 
 535 /*
 536  * called from update_field() when service flag is ON. currently
 537  * supported iterated record is InstallationR and fields supported for
 538  * update are Geo_North, Geo_East, Geo_Alt, Geo_Location.
 539  */
 540 
 541 static int
 542 updateiter_record(fru_nodehdl_t nodehdl, int cnt, char **ptr,
 543                         char *field_name, char  *field_value)
 544 {
 545         int     iter_cnt  = 0;
 546         char    rec_name[512];
 547         void    *data = NULL;
 548         char    *tmpptr = NULL;
 549         size_t  dataLen = 0;
 550         char    **elem_ptr;
 551         int     found = 0;
 552         int     index;
 553         int     total_cnt;
 554         fru_errno_t     err;
 555 
 556         static  char    *elem_list[] = {"/Geo_North", "/Geo_East",\
 557                                 "/Geo_Alt", "/Geo_Location"};
 558 
 559         elem_ptr = elem_list;
 560         total_cnt = sizeof (elem_list)/sizeof (*elem_list);
 561 
 562         for (index = 0; index < total_cnt; index++) {
 563                 tmpptr = strrchr(field_name, '/');
 564                 if (tmpptr == NULL) {
 565                         (void) fprintf(stderr,
 566                             gettext("Error:  Data Element not known\n"));
 567                         return (1);
 568                 }
 569                 if ((strncmp(*elem_ptr, tmpptr, strlen(*elem_ptr)) != 0)) {
 570                         elem_ptr++;
 571                         continue;
 572                 }
 573                 found = 1;
 574                 break;
 575         }
 576 
 577         if (found == 0) {
 578                 (void) fprintf(stderr,
 579                     gettext("Error:  Update not allowed for field:  %s\n"),
 580                     field_name);
 581                 return (1);
 582         }
 583 
 584         if ((fru_get_num_iterations(nodehdl, &segment_name[cnt], 0,
 585             *ptr, &iter_cnt, NULL)) != FRU_SUCCESS) {
 586                 return (1);
 587         }
 588 
 589         /* add a new Iterated Record if complete path is not given */
 590         if (iter_cnt == 0) {
 591                 (void) snprintf(rec_name, sizeof (rec_name), "/%s[+]", *ptr);
 592                 if ((err = fru_update_field(nodehdl, segment_name[cnt], 0,
 593                     rec_name, data, dataLen)) != FRU_SUCCESS) {
 594                         (void) fprintf(stderr,
 595                         gettext("fru_update_field():  %s\n"),
 596                             fru_strerror(err));
 597                         return (1);
 598                 }
 599 
 600                 iter_cnt = 1;
 601         }
 602 
 603         (void) snprintf(rec_name, sizeof (rec_name), "/%s[%d]%s",
 604             *ptr, iter_cnt-1, strrchr(field_name, '/'));
 605 
 606         if ((convert_update(nodehdl, segment_name[cnt], rec_name,
 607             field_value)) != 0) {
 608                 return (1);
 609         }
 610 
 611         /* update success  now update the unix timestamp */
 612 
 613         (void) snprintf(rec_name, sizeof (rec_name), "/%s[%d]",
 614             *ptr, iter_cnt-1);
 615         tmpptr = rec_name;
 616 
 617         /* update UNIX_Timestamp32 with creation time */
 618         if ((update_unixtimestamp(nodehdl, segment_name[cnt],
 619             &tmpptr)) != 0) {
 620                 return (1);
 621         }
 622 
 623         return (0);
 624 }
 625 
 626 static int
 627 update_field(fru_nodehdl_t nodehdl, char *field_name, char *field_value)
 628 {
 629         fru_elemdef_t   def;
 630         unsigned char   *data;
 631         size_t  dataLen;
 632         char    *found_path = NULL;
 633         int     cnt;
 634         char    **ptr;
 635         fru_strlist_t   elem;
 636         int     elem_cnt;
 637         int     add_flag = 1;
 638         int     total_cnt;
 639         int     status;
 640 
 641         if (service_mode) {
 642                 ptr = serv_data_list;
 643                 total_cnt = sizeof (serv_data_list)/sizeof (*serv_data_list);
 644 
 645                 for (cnt = 0; cnt < total_cnt; cnt++) {
 646                         if ((strncmp(*ptr, &field_name[1], strlen(*ptr)) \
 647                             != 0) && (strncmp(*ptr, &field_name[0],
 648                             strlen(*ptr)) != 0)) {
 649                                 ptr++;
 650                                 add_flag = 0;
 651                                 continue;
 652                         }
 653                         add_flag = 1;
 654                         break;
 655                 }
 656         } else {
 657                 ptr = cust_data_list;
 658         }
 659 
 660         /* look for the field in either of the segment if found update it */
 661         for (cnt = 0; cnt < NUM_OF_SEGMENT; cnt++) {
 662                 if ((fru_read_field(nodehdl, &segment_name[cnt], 0, field_name,
 663                     (void **) &data, &dataLen, &found_path)) != FRU_SUCCESS) {
 664                         continue;
 665                 }
 666                 if ((fru_get_definition(*ptr, &def)) == FRU_SUCCESS) {
 667                         if (def.iteration_count != 0) {
 668                                 if ((updateiter_record(nodehdl, cnt, ptr,
 669                                     field_name, field_value)) != 0) {
 670                                         return (1);
 671                                 }
 672                                 return (0);
 673                         }
 674                 }
 675 
 676                 if ((convert_update(nodehdl, segment_name[cnt],
 677                     field_name, field_value)) != 0) {
 678                         return (1);
 679                 }
 680 
 681                 /* update UNIX_Timestamp32 with update time */
 682                 if ((update_unixtimestamp(nodehdl, segment_name[cnt],
 683                     ptr)) != 0) {
 684                         return (1);
 685                 }
 686                 return (0);
 687         }
 688 
 689         elem.num = 0;
 690 
 691         /* field not found add the the record in one of the segment */
 692         for (cnt = 0; cnt < NUM_OF_SEGMENT; cnt++) {
 693                 (void) fru_list_elems_in(nodehdl, segment_name[cnt], &elem);
 694                 for (elem_cnt = 0; elem_cnt < elem.num; elem_cnt++) {
 695                         if ((strcmp(*ptr, elem.strs[elem_cnt])) == 0) {
 696                                 add_flag = 0;
 697                         }
 698                 }
 699 
 700                 if (add_flag) {
 701                         if ((fru_add_element(nodehdl, segment_name[cnt],
 702                             *ptr)) != FRU_SUCCESS) {
 703                                 continue;
 704                         }
 705                 }
 706 
 707                 if ((fru_get_definition(*ptr, &def)) == FRU_SUCCESS) {
 708                         if (def.iteration_count != 0) {
 709                                 if ((updateiter_record(nodehdl, cnt, ptr,
 710                                     field_name, field_value)) != 0) {
 711                                         return (1);
 712                                 }
 713                                 return (0);
 714                         }
 715                 }
 716 
 717                 /* update UNIX_Timestamp32 with creation time */
 718                 if ((update_unixtimestamp(nodehdl, segment_name[cnt],
 719                     ptr)) != 0) {
 720                         return (1);
 721                 }
 722 
 723                 /* record added update the field with the value */
 724                 if ((convert_update(nodehdl, segment_name[cnt], field_name,
 725                     field_value)) != 0) {
 726                         return (1);
 727                 }
 728                 return (0);
 729         }
 730 
 731         /* segment not present, create one and add the record */
 732         cnt = create_segment(nodehdl);
 733         if (cnt == 1) {
 734                 return (1);
 735         }
 736 
 737         if ((status = fru_add_element(nodehdl, segment_name[cnt], *ptr))
 738             != FRU_SUCCESS) {
 739                 (void) fprintf(stderr, gettext("fru_add_element():  %s\n"),
 740                     fru_strerror(status));
 741                 return (1);
 742         }
 743 
 744         if ((fru_get_definition(*ptr, &def)) == FRU_SUCCESS) {
 745                 if (def.iteration_count != 0) {
 746                         if ((updateiter_record(nodehdl,  cnt, ptr,
 747                             field_name, field_value)) != 0) {
 748                                 return (1);
 749                         }
 750                         return (0);
 751                 }
 752         }
 753 
 754         /* update UNIX_Timestamp32 with creation time */
 755         if ((update_unixtimestamp(nodehdl, segment_name[cnt],
 756             ptr)) != 0) {
 757                 return (1);
 758         }
 759 
 760         if ((convert_update(nodehdl, segment_name[cnt], field_name,
 761             field_value)) != 0) {
 762                 return (1);
 763         }
 764         return (0);
 765 }
 766 
 767 static int
 768 update_node_data(fru_nodehdl_t node)
 769 {
 770         int     i;
 771         int     status = 0;
 772 
 773         if (service_mode) {
 774                 for (i = 0; i < svcargc; i += 2)
 775                         if (update_field(node, svcargv[i], svcargv[i + 1])) {
 776                                 status = 1;
 777                         }
 778         } else {
 779                 status = update_field(node, "/Customer_DataR/Cust_Data",
 780                     customer_data);
 781         }
 782         return (status);
 783 }
 784 
 785 static void
 786 walk_tree(fru_nodehdl_t node, const char *prior_path, int process_tree)
 787 {
 788         char    *name, path[PATH_MAX];
 789         int     process_self = process_tree, status, update_status = 0;
 790         fru_nodehdl_t    next_node;
 791         fru_node_t      type;
 792 
 793         if ((status = fru_get_node_type(node, &type)) != FRU_SUCCESS) {
 794                 (void) fprintf(stderr,
 795                     gettext("Error getting FRU tree node type:  %s\n"),
 796                     fru_strerror(status));
 797                 exit(1);
 798         }
 799 
 800         if ((status = fru_get_name_from_hdl(node, &name)) != FRU_SUCCESS) {
 801                 (void) fprintf(stderr,
 802                     gettext("Error getting name of FRU tree node:  %s\n"),
 803                     fru_strerror(status));
 804                 exit(1);
 805         }
 806 
 807 
 808         /*
 809          * Build the current path
 810          */
 811         if (snprintf(path, sizeof (path), "%s/%s", prior_path, name)
 812             >= sizeof (path)) {
 813                 (void) fprintf(stderr,
 814                     gettext("FRU tree path would overflow buffer\n"));
 815                 exit(1);
 816         }
 817 
 818         free(name);
 819 
 820         /*
 821          * Process the node
 822          */
 823         if (list_only) {
 824                 (void) printf("%s%s\n", path, ((type == FRU_NODE_FRU) ?
 825                     " (fru)" : ((type == FRU_NODE_CONTAINER) ?
 826                     " (container)" : "")));
 827         } else if ((process_tree || (process_self = pathmatch(path))) &&
 828             (type == FRU_NODE_CONTAINER)) {
 829                 (void) printf("%s\n", path);
 830                 if (update) {
 831                         status = update_node_data(node);
 832                         update_status = status;
 833                 }
 834                 print_node_data(node);
 835                 if (!recursive) {
 836                         exit(status);
 837                 }
 838         } else if (process_self && !recursive) {
 839                 (void) fprintf(stderr,
 840                     gettext("\"%s\" is not a container\n"), path);
 841                 exit(1);
 842         }
 843 
 844 
 845         /*
 846          * Recurse
 847          */
 848         if (fru_get_child(node, &next_node) == FRU_SUCCESS)
 849                 walk_tree(next_node, path, process_self);
 850 
 851         if (fru_get_peer(node, &next_node) == FRU_SUCCESS)
 852                 walk_tree(next_node, prior_path, process_tree);
 853 
 854         /*
 855          * when update_node_data failed, need to exit with return value 1
 856          */
 857         if (update_status)
 858                 exit(1);
 859 }
 860 
 861 int
 862 main(int argc, char *argv[])
 863 {
 864         int     process_tree = 0, option, status;
 865 
 866         fru_nodehdl_t  root;
 867 
 868 
 869         command = argv[0];
 870 
 871         opterr = 0;     /*  "getopt" should not print to "stderr"  */
 872         while ((option = getopt(argc, argv, "lrs")) != EOF) {
 873         switch (option) {
 874                 case 'l':
 875                         list_only = 1;
 876                         break;
 877                 case 'r':
 878                         recursive = 1;
 879                         break;
 880                 case 's':
 881                         service_mode = 1;
 882                         break;
 883                 default:
 884                         usage();
 885                         return (1);
 886                 }
 887         }
 888 
 889         argc -= optind;
 890         argv += optind;
 891 
 892         if (argc == 0) {
 893                 process_tree   = 1;
 894                 recursive = 1;
 895         } else {
 896                 if (list_only) {
 897                         usage();
 898                         return (1);
 899                 }
 900 
 901                 frupath = argv[0];
 902                 if (*frupath == 0) {
 903                         usage();
 904                         (void) fprintf(stderr,
 905                             gettext("\"frupath\" should not be empty\n"));
 906                         return (1);
 907                 }
 908 
 909                 argc--;
 910                 argv++;
 911 
 912                 if (argc > 0) {
 913                         update = 1;
 914                         if (service_mode) {
 915                                 if ((argc % 2) != 0) {
 916                                         (void) fprintf(stderr,
 917                                             gettext("Must specify "
 918                                             "field-value pairs "
 919                                             "for update\n"));
 920                                         return (1);
 921                                 }
 922 
 923                                 if (validate_fieldnames(argc, argv) != 0) {
 924                                         return (1);
 925                                 }
 926 
 927                                 svcargc = argc;
 928                                 svcargv = argv;
 929                         } else if (argc == 1)
 930                                 customer_data = argv[0];
 931                         else {
 932                                 usage();
 933                                 return (1);
 934                         }
 935                 }
 936         }
 937 
 938         if ((status = fru_open_data_source("picl", NULL)) != FRU_SUCCESS) {
 939                 (void) fprintf(stderr,
 940                     gettext("Unable to access FRU data source:  %s\n"),
 941                     fru_strerror(status));
 942                 return (1);
 943         }
 944 
 945         if ((status = fru_get_root(&root)) == FRU_NODENOTFOUND) {
 946                 (void) fprintf(stderr,
 947                     gettext("This system does not support PICL "
 948                     "infrastructure to provide FRUID data\n"
 949                     "Please use the platform SP to access the FRUID "
 950                     "information\n"));
 951                 return (1);
 952         } else if (status != FRU_SUCCESS) {
 953                 (void) fprintf(stderr,
 954                     gettext("Unable to access FRU ID data "
 955                     "due to data source error\n"));
 956                 return (1);
 957         }
 958 
 959         walk_tree(root, "", process_tree);
 960 
 961         if ((frupath != NULL) && (!found_frupath)) {
 962                 (void) fprintf(stderr,
 963                     gettext("\"%s\" not found\n"),
 964                     frupath);
 965                 return (1);
 966         }
 967 
 968         return (0);
 969 }