common/recipes-core/fruid/files/fruid-util.c (1,005 lines of code) (raw):
/*
 * Copyright 2015-present Facebook. All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <syslog.h>
#include <string.h>
#include <openbmc/fruid.h>
#include <openbmc/pal.h>
#include <jansson.h>
#include <getopt.h>
#define FRUID_SIZE      512
#define FLAG_PRINT  (0)
#define FLAG_DUMP   (0x01 << 0)
#define FLAG_WRITE  (0x01 << 1)
#define FLAG_MODIFY (0x01 << 2)
#define FIELD_LEN(x)      (x & ~(0x03 << 6))
#define IGNORE_PATH -2
enum format{
  DEFAULT_FORMAT = 0,
  JSON_FORMAT = 1,
};
static char pal_fru_list_print_t[1024] = {0};
static char pal_fru_list_rw_t[1024] = {0};
static char pal_dev_list_print_t[1024] = {0};
static char pal_dev_list_rw_t[1024] = {0};
static char* rtrim(char* buf)
{
  if(strlen(buf) == 0) {
    return buf;
  }
  char* pback = buf + strlen(buf);
  while ((*(--pback) == ' ') && (pback >= buf));
  pback++;
  *pback = '\0';
  return buf;
}
static void fruid_trim(fruid_info_t *fruid)
{
  if (fruid == NULL) {
    return;
  }
  if (fruid->chassis.flag) {
    rtrim(fruid->chassis.type_str);
    if (FIELD_LEN(fruid->chassis.part_type_len) > 0) rtrim(fruid->chassis.part);
    if (FIELD_LEN(fruid->chassis.serial_type_len) > 0) rtrim(fruid->chassis.serial);
    if (fruid->chassis.custom1 != NULL) rtrim(fruid->chassis.custom1);
    if (fruid->chassis.custom2 != NULL) rtrim(fruid->chassis.custom2);
    if (fruid->chassis.custom3 != NULL) rtrim(fruid->chassis.custom3);
    if (fruid->chassis.custom4 != NULL) rtrim(fruid->chassis.custom4);
    if (fruid->chassis.custom5 != NULL) rtrim(fruid->chassis.custom5);
    if (fruid->chassis.custom6 != NULL) rtrim(fruid->chassis.custom6);
  }
  if (fruid->board.flag) {
    if (FIELD_LEN(fruid->board.mfg_type_len) > 0) rtrim(fruid->board.mfg);
    if (FIELD_LEN(fruid->board.name_type_len)> 0) rtrim(fruid->board.name);
    if (FIELD_LEN(fruid->board.serial_type_len) > 0) rtrim(fruid->board.serial);
    if (FIELD_LEN(fruid->board.part_type_len) > 0) rtrim(fruid->board.part);
    if (FIELD_LEN(fruid->board.fruid_type_len) > 0) rtrim(fruid->board.fruid);
    if (fruid->board.custom1 != NULL) rtrim(fruid->board.custom1);
    if (fruid->board.custom2 != NULL) rtrim(fruid->board.custom2);
    if (fruid->board.custom3 != NULL) rtrim(fruid->board.custom3);
    if (fruid->board.custom4 != NULL) rtrim(fruid->board.custom4);
    if (fruid->board.custom5 != NULL) rtrim(fruid->board.custom5);
    if (fruid->board.custom6 != NULL) rtrim(fruid->board.custom6);
  }
  if (fruid->product.flag) {
    if (FIELD_LEN(fruid->product.mfg_type_len) > 0) rtrim(fruid->product.mfg);
    if (FIELD_LEN(fruid->product.name_type_len) > 0) rtrim(fruid->product.name);
    if (FIELD_LEN(fruid->product.part_type_len) > 0) rtrim(fruid->product.part);
    if (FIELD_LEN(fruid->product.version_type_len) > 0) rtrim(fruid->product.version);
    if (FIELD_LEN(fruid->product.serial_type_len) > 0) rtrim(fruid->product.serial);
    if (FIELD_LEN(fruid->product.asset_tag_type_len) > 0) rtrim(fruid->product.asset_tag);
    if (FIELD_LEN(fruid->product.fruid_type_len) > 0) rtrim(fruid->product.fruid);
    if (fruid->product.custom1 != NULL) rtrim(fruid->product.custom1);
    if (fruid->product.custom2 != NULL) rtrim(fruid->product.custom2);
    if (fruid->product.custom3 != NULL) rtrim(fruid->product.custom3);
    if (fruid->product.custom4 != NULL) rtrim(fruid->product.custom4);
    if (fruid->product.custom5 != NULL) rtrim(fruid->product.custom5);
    if (fruid->product.custom6 != NULL) rtrim(fruid->product.custom6);
  }
  if (fruid->multirecord_smart_fan.flag) {
    if (fruid->multirecord_smart_fan.mfg_line != NULL) rtrim(fruid->multirecord_smart_fan.mfg_line);
    if (fruid->multirecord_smart_fan.clei_code != NULL) rtrim(fruid->multirecord_smart_fan.clei_code);
  }
}
static void prepend_all(char *list, size_t size)
{
  size_t len = strlen(list);
  const char *pre = "all, ";
  size_t pre_len = strlen(pre);
  if ((len + pre_len + 1) > size) {
    return;
  }
  if (len == 0) {
    strcpy(list, "all");
  } else {
    memmove(list + pre_len, list, len);
    memcpy(list, pre, pre_len);
  }
}
static void append_list(char *dest, char *src)
{
  if (dest[0] == '\0') {
    strcpy(dest, src);
  } else {
    strcat(dest, ", ");
    strcat(dest, src);
  }
}
static bool is_in_list(const char *list, const char *name)
{
  int name_len = strlen(name);
  while (list != NULL) {
    list = strstr(list, name);
    if (list) {
      list += name_len;
      if (*list == '\0' || *list == ',')
        return true;
    }
  }
  return false;
}
static void
create_dev_lists(const char *fru_name, uint8_t fru)
{
  uint8_t num_devs, dev;
  unsigned int caps;
  if (pal_get_num_devs(fru, &num_devs)) {
    printf("%s: Cannot get number of devs\n", fru_name);
    return;
  }
  for (dev = 1; dev <= num_devs; dev++) {
    char name[128];
    if (pal_get_dev_name(fru, dev, name)) {
      printf("%s: Cannot get name for device: %u\n", fru_name, dev);
      continue;
    }
    if (pal_get_dev_capability(fru, dev, &caps)) {
      printf("%s:%s cannot get device capability\n", fru_name, name);
      continue;
    }
    if ((caps & FRU_CAPABILITY_FRUID_READ) &&
        !is_in_list(pal_dev_list_print_t, name)) {
      append_list(pal_dev_list_print_t, name);
    }
    if ((caps & FRU_CAPABILITY_FRUID_WRITE) &&
        !is_in_list(pal_dev_list_rw_t, name)) {
      append_list(pal_dev_list_rw_t, name);
    }
  }
}
static void
create_fru_lists(void)
{
  uint8_t fru;
  unsigned int caps;
  for (fru = 1; fru <= MAX_NUM_FRUS; fru++) {
    char name[64] = {0};
    if (pal_get_fru_name(fru, name)) {
      printf("Cannot get FRU Name for %d\n", fru);
      continue;
    }
    if (pal_get_fru_capability(fru, &caps)) {
      printf("%s: Cannot get FRU capability!\n", name);
      continue;
    }
    if ((caps & FRU_CAPABILITY_HAS_DEVICE)) {
      create_dev_lists(name, fru);
    }
    if ((caps & FRU_CAPABILITY_FRUID_READ)) {
      append_list(pal_fru_list_print_t, name);
    }
    if ((caps & FRU_CAPABILITY_FRUID_WRITE)) {
      append_list(pal_fru_list_rw_t, name);
    }
  }
  prepend_all(pal_fru_list_print_t, 1024);
  if (pal_dev_list_print_t[0] != '\0') {
    prepend_all(pal_dev_list_print_t, 1024);
  }
}
/* To copy the bin files */
static int
copy_file(int out, int in, int bs) {
  ssize_t bytes_rd, bytes_wr;
  uint64_t tmp[FRUID_SIZE];
  while ((bytes_rd = read(in, tmp, FRUID_SIZE)) > 0) {
    bytes_wr = write(out, tmp, bytes_rd);
    if (bytes_wr != bytes_rd) {
      return errno;
    }
  }
  return 0;
}
/* Print the FRUID in detail */
static void
print_fruid_info(fruid_info_t *fruid, const char *name)
{
  /* Print format */
  printf("%-27s: %s", "\nFRU Information",
      name /* Name of the FRU device */ );
  printf("%-27s: %s", "\n---------------", "------------------");
  if (fruid->chassis.flag) {
    printf("%-27s: %s", "\nChassis Type",fruid->chassis.type_str);
    if (FIELD_LEN(fruid->chassis.part_type_len) > 0)
      printf("%-27s: %s", "\nChassis Part Number",fruid->chassis.part);
    if (FIELD_LEN(fruid->chassis.serial_type_len) > 0)
      printf("%-27s: %s", "\nChassis Serial Number",fruid->chassis.serial);
    if ((fruid->chassis.custom1 != NULL) && (FIELD_LEN(fruid->chassis.custom1_type_len) > 0))
      printf("%-27s: %s", "\nChassis Custom Data 1",fruid->chassis.custom1);
    if ((fruid->chassis.custom2 != NULL) && (FIELD_LEN(fruid->chassis.custom2_type_len) > 0))
      printf("%-27s: %s", "\nChassis Custom Data 2",fruid->chassis.custom2);
    if ((fruid->chassis.custom3 != NULL) && (FIELD_LEN(fruid->chassis.custom3_type_len) > 0))
      printf("%-27s: %s", "\nChassis Custom Data 3",fruid->chassis.custom3);
    if ((fruid->chassis.custom4 != NULL) && (FIELD_LEN(fruid->chassis.custom4_type_len) > 0))
      printf("%-27s: %s", "\nChassis Custom Data 4",fruid->chassis.custom4);
    if ((fruid->chassis.custom5 != NULL) && (FIELD_LEN(fruid->chassis.custom5_type_len) > 0))
      printf("%-27s: %s", "\nChassis Custom Data 5",fruid->chassis.custom5);
    if ((fruid->chassis.custom6 != NULL) && (FIELD_LEN(fruid->chassis.custom6_type_len) > 0))
      printf("%-27s: %s", "\nChassis Custom Data 6",fruid->chassis.custom6);
  }
  if (fruid->board.flag) {
    printf("%-27s: %s", "\nBoard Mfg Date",fruid->board.mfg_time_str);
    if (FIELD_LEN(fruid->board.mfg_type_len) > 0)
      printf("%-27s: %s", "\nBoard Mfg",fruid->board.mfg);
    if (FIELD_LEN(fruid->board.name_type_len) > 0)
      printf("%-27s: %s", "\nBoard Product",fruid->board.name);
    if (FIELD_LEN(fruid->board.serial_type_len) > 0)
      printf("%-27s: %s", "\nBoard Serial",fruid->board.serial);
    if (FIELD_LEN(fruid->board.part_type_len) > 0)
      printf("%-27s: %s", "\nBoard Part Number",fruid->board.part);
    if (FIELD_LEN(fruid->board.fruid_type_len) > 0)
      printf("%-27s: %s", "\nBoard FRU ID",fruid->board.fruid);
    if ((fruid->board.custom1 != NULL) && (FIELD_LEN(fruid->board.custom1_type_len) > 0))
      printf("%-27s: %s", "\nBoard Custom Data 1",fruid->board.custom1);
    if ((fruid->board.custom2 != NULL) && (FIELD_LEN(fruid->board.custom2_type_len) > 0))
      printf("%-27s: %s", "\nBoard Custom Data 2",fruid->board.custom2);
    if ((fruid->board.custom3 != NULL) && (FIELD_LEN(fruid->board.custom3_type_len) > 0))
      printf("%-27s: %s", "\nBoard Custom Data 3",fruid->board.custom3);
    if ((fruid->board.custom4 != NULL) && (FIELD_LEN(fruid->board.custom4_type_len) > 0))
      printf("%-27s: %s", "\nBoard Custom Data 4",fruid->board.custom4);
    if ((fruid->board.custom5 != NULL) && (FIELD_LEN(fruid->board.custom5_type_len) > 0))
      printf("%-27s: %s", "\nBoard Custom Data 5",fruid->board.custom5);
    if ((fruid->board.custom6 != NULL) && (FIELD_LEN(fruid->board.custom6_type_len) > 0))
      printf("%-27s: %s", "\nBoard Custom Data 6",fruid->board.custom6);
  }
  if (fruid->product.flag) {
    if (FIELD_LEN(fruid->product.mfg_type_len) > 0)
      printf("%-27s: %s", "\nProduct Manufacturer",fruid->product.mfg);
    if (FIELD_LEN(fruid->product.name_type_len) > 0)
      printf("%-27s: %s", "\nProduct Name",fruid->product.name);
    if (FIELD_LEN(fruid->product.part_type_len) > 0)
      printf("%-27s: %s", "\nProduct Part Number",fruid->product.part);
    if (FIELD_LEN(fruid->product.version_type_len) > 0)
      printf("%-27s: %s", "\nProduct Version",fruid->product.version);
    if (FIELD_LEN(fruid->product.serial_type_len) > 0)
      printf("%-27s: %s", "\nProduct Serial",fruid->product.serial);
    if (FIELD_LEN(fruid->product.asset_tag_type_len) > 0)
      printf("%-27s: %s", "\nProduct Asset Tag",fruid->product.asset_tag);
    if (FIELD_LEN(fruid->product.fruid_type_len) > 0)
      printf("%-27s: %s", "\nProduct FRU ID",fruid->product.fruid);
    if ((fruid->product.custom1 != NULL) && (FIELD_LEN(fruid->product.custom1_type_len) > 0))
      printf("%-27s: %s", "\nProduct Custom Data 1",fruid->product.custom1);
    if ((fruid->product.custom2 != NULL) && (FIELD_LEN(fruid->product.custom2_type_len) > 0))
      printf("%-27s: %s", "\nProduct Custom Data 2",fruid->product.custom2);
    if ((fruid->product.custom3 != NULL) && (FIELD_LEN(fruid->product.custom3_type_len) > 0))
      printf("%-27s: %s", "\nProduct Custom Data 3",fruid->product.custom3);
    if ((fruid->product.custom4 != NULL) && (FIELD_LEN(fruid->product.custom4_type_len) > 0))
      printf("%-27s: %s", "\nProduct Custom Data 4",fruid->product.custom4);
    if ((fruid->product.custom5 != NULL) && (FIELD_LEN(fruid->product.custom5_type_len) > 0))
      printf("%-27s: %s", "\nProduct Custom Data 5",fruid->product.custom5);
    if ((fruid->product.custom6 != NULL) && (FIELD_LEN(fruid->product.custom6_type_len) > 0))
      printf("%-27s: %s", "\nProduct Custom Data 6",fruid->product.custom6);
  }
  if (fruid->multirecord_smart_fan.flag) {
    printf("%-27s: %d", "\nSmart Fan Manufacturer ID", fruid->multirecord_smart_fan.manufacturer_id);
    printf("%-27s: %s", "\nSmart Fan Version", fruid->multirecord_smart_fan.smart_fan_ver);
    printf("%-27s: %s", "\nSmart Fan FW Version", fruid->multirecord_smart_fan.fw_ver);
    printf("%-27s: %s", "\nSmart Fan Mfg Date", fruid->multirecord_smart_fan.mfg_time_str);
    if (strlen(fruid->multirecord_smart_fan.mfg_line) != 0) {
      printf("%-27s: %s", "\nSmart Fan Mfg Line", fruid->multirecord_smart_fan.mfg_line);
    }
    if (strlen(fruid->multirecord_smart_fan.clei_code) != 0) {
      printf("%-27s: %s", "\nSmart Fan CLEI Code", fruid->multirecord_smart_fan.clei_code);
    }
    printf("%-27s: %d", "\nSmart Fan Voltage (mV)", fruid->multirecord_smart_fan.voltage);
    printf("%-27s: %d", "\nSmart Fan Current (mA)", fruid->multirecord_smart_fan.current);
    printf("%-27s: %d", "\nSmart Fan Front RPM", fruid->multirecord_smart_fan.rpm_front);
    printf("%-27s: %d", "\nSmart Fan Rear RPM", fruid->multirecord_smart_fan.rpm_rear);
  }
  printf("\n");
}
/* Print the FRUID in json format */
static void
print_json_fruid_info(fruid_info_t *fruid, const char *name,json_t *fru_array)
{
  json_t *fru_object = json_object();
  json_object_set_new(fru_object, "FRU Information", json_string(name));
  if (fruid->chassis.flag) {
    json_object_set_new(fru_object, "Chassis Type", json_string(fruid->chassis.type_str));
    json_object_set_new(fru_object, "Chassis Part Number", json_string(fruid->chassis.part));
    json_object_set_new(fru_object, "Chassis Serial Number", json_string(fruid->chassis.serial));
    if (fruid->chassis.custom1 != NULL)
      json_object_set_new(fru_object, "Chassis Custom Data 1", json_string(fruid->chassis.custom1));
    if (fruid->chassis.custom2 != NULL)
      json_object_set_new(fru_object, "Chassis Custom Data 2", json_string(fruid->chassis.custom2));
    if (fruid->chassis.custom3 != NULL)
      json_object_set_new(fru_object, "Chassis Custom Data 3", json_string(fruid->chassis.custom3));
    if (fruid->chassis.custom4 != NULL)
      json_object_set_new(fru_object, "Chassis Custom Data 4", json_string(fruid->chassis.custom4));
    if (fruid->chassis.custom5 != NULL)
      json_object_set_new(fru_object, "Chassis Custom Data 5", json_string(fruid->chassis.custom5));
    if (fruid->chassis.custom6 != NULL)
      json_object_set_new(fru_object, "Chassis Custom Data 6", json_string(fruid->chassis.custom6));
  }
  if (fruid->board.flag) {
    json_object_set_new(fru_object, "Board Mfg Date", json_string(fruid->board.mfg_time_str));
    json_object_set_new(fru_object, "Board Mfg", json_string(fruid->board.mfg));
    json_object_set_new(fru_object, "Board Product", json_string(fruid->board.name));
    json_object_set_new(fru_object, "Board Serial", json_string(fruid->board.serial));
    json_object_set_new(fru_object, "Board Part Number", json_string(fruid->board.part));
    json_object_set_new(fru_object, "Board FRU ID", json_string(fruid->board.fruid));
    if (fruid->board.custom1 != NULL)
      json_object_set_new(fru_object, "Board Custom Data 1", json_string(fruid->board.custom1));
    if (fruid->board.custom2 != NULL)
      json_object_set_new(fru_object, "Board Custom Data 2", json_string(fruid->board.custom2));
    if (fruid->board.custom3 != NULL)
      json_object_set_new(fru_object, "Board Custom Data 3", json_string(fruid->board.custom3));
    if (fruid->board.custom4 != NULL)
      json_object_set_new(fru_object, "Board Custom Data 4", json_string(fruid->board.custom4));
    if (fruid->board.custom5 != NULL)
      json_object_set_new(fru_object, "Board Custom Data 5", json_string(fruid->board.custom5));
    if (fruid->board.custom6 != NULL)
      json_object_set_new(fru_object, "Board Custom Data 6", json_string(fruid->board.custom6));
  }
  if (fruid->product.flag) {
    json_object_set_new(fru_object, "Product Manufacturer", json_string(fruid->product.mfg));
    json_object_set_new(fru_object, "Product Name", json_string(fruid->product.name));
    json_object_set_new(fru_object, "Product Part Number", json_string(fruid->product.part));
    json_object_set_new(fru_object, "Product Version", json_string(fruid->product.version));
    json_object_set_new(fru_object, "Product Serial", json_string(fruid->product.serial));
    json_object_set_new(fru_object, "Product Asset Tag", json_string(fruid->product.asset_tag));
    json_object_set_new(fru_object, "Product FRU ID", json_string(fruid->product.fruid));
    if (fruid->product.custom1 != NULL)
      json_object_set_new(fru_object, "Product Custom Data 1", json_string(fruid->product.custom1));
    if (fruid->product.custom2 != NULL)
      json_object_set_new(fru_object, "Product Custom Data 2", json_string(fruid->product.custom2));
    if (fruid->product.custom3 != NULL)
      json_object_set_new(fru_object, "Product Custom Data 3", json_string(fruid->product.custom3));
    if (fruid->product.custom4 != NULL)
      json_object_set_new(fru_object, "Product Custom Data 4", json_string(fruid->product.custom4));
    if (fruid->product.custom5 != NULL)
      json_object_set_new(fru_object, "Product Custom Data 5", json_string(fruid->product.custom5));
    if (fruid->product.custom6 != NULL)
      json_object_set_new(fru_object, "Product Custom Data 6", json_string(fruid->product.custom6));
  }
  if (fruid->multirecord_smart_fan.flag) {
    json_object_set_new(fru_object, "Smart Fan Manufacturer ID", json_integer(fruid->multirecord_smart_fan.manufacturer_id));
    json_object_set_new(fru_object, "Smart Fan Version", json_string(fruid->multirecord_smart_fan.smart_fan_ver));
    json_object_set_new(fru_object, "Smart Fan FW Version", json_string(fruid->multirecord_smart_fan.fw_ver));
    json_object_set_new(fru_object, "Smart Fan Mfg Date", json_string(fruid->multirecord_smart_fan.mfg_time_str));
    if (strlen(fruid->multirecord_smart_fan.mfg_line) != 0) {
      json_object_set_new(fru_object, "Smart Fan Mfg Line", json_string(fruid->multirecord_smart_fan.mfg_line));
    }
    if (strlen(fruid->multirecord_smart_fan.clei_code) != 0) {
      json_object_set_new(fru_object, "Smart Fan CLEI Code", json_string(fruid->multirecord_smart_fan.clei_code));
    }
    json_object_set_new(fru_object, "Smart Fan Voltage (10mV)", json_integer(fruid->multirecord_smart_fan.voltage));
    json_object_set_new(fru_object, "Smart Fan Current (10mA)", json_integer(fruid->multirecord_smart_fan.current));
    json_object_set_new(fru_object, "Smart Fan Front RPM", json_integer(fruid->multirecord_smart_fan.rpm_front));
    json_object_set_new(fru_object, "Smart Fan Rear RPM", json_integer(fruid->multirecord_smart_fan.rpm_rear));
  }
  json_array_append_new(fru_array, fru_object);
}
/* Populate and print fruid_info by parsing the fru's binary dump */
int get_fruid_info(uint8_t fru, char *path, char* name, unsigned char print_format,json_t *fru_array) {
  int ret = 0;
  fruid_info_t fruid;
  ret = fruid_parse(path, &fruid);
  if (ret == 0) {
    fruid_trim(&fruid);
  }
  if (print_format == JSON_FORMAT) {
    if (ret) {
      json_t *fru_object = json_object();
      json_array_append_new(fru_array, fru_object);
    } else {
      print_json_fruid_info(&fruid, name,fru_array);
      free_fruid_info(&fruid);
    }
  } else {
    if (ret) {
      fprintf(stderr, "Failed print FRUID for %s\nCheck syslog for errors! (err %xh)\n",name, ret);
    } else {
      print_fruid_info(&fruid, name);
      free_fruid_info(&fruid);
    }
  }
  return ret;
}
static void
print_usage() {
  if ((strlen(pal_dev_list_print_t) != 0) || (strlen(pal_dev_list_rw_t) != 0)) {
    // dev_list is not empty
    printf("Usage: fruid-util [ %s ] [ %s ] [--json]\n"
      "Usage: fruid-util [ %s ] [ %s ] [--dump | --write ] <file>\n",
      pal_fru_list_print_t, pal_dev_list_print_t, pal_fru_list_rw_t, pal_dev_list_rw_t);
    printf("Usage: fruid-util [ %s ] [ %s ] --modify --<field> <data> <file>\n",
      pal_fru_list_rw_t, pal_dev_list_rw_t);
    printf("       <field> : CPN  (Chassis Part Number)\n"
           "                       e.g., fruid-util fru1 --modify --CPN \"xxxxx\" xxx.bin\n"
           "                 CSN  (Chassis Serial Number)\n"
           "                 CCD1 (Chassis Custom Data 1)\n"
           "                 CCD2 (Chassis Custom Data 2)\n"
           "                 CCD3 (Chassis Custom Data 3)\n"
           "                 CCD4 (Chassis Custom Data 4)\n"
           "                 CCD5 (Chassis Custom Data 5)\n"
           "                 CCD6 (Chassis Custom Data 6)\n"
           "                 BMD  (Board Mfg Date)\n"
           "                       e.g., fruid-util fru1 --modify --BMD \"$( date +%%s -d \"2018-01-01 17:00:00\" )\" xxx.bin\n"
           "                 BM   (Board Mfg)\n"
           "                 BP   (Board Product)\n"
           "                 BSN  (Board Serial)\n"
           "                 BPN  (Board Part Number)\n"
           "                 BFI  (Board FRU ID)\n"
           "                 BCD1 (Board Custom Data 1)\n"
           "                 BCD2 (Board Custom Data 2)\n"
           "                 BCD3 (Board Custom Data 3)\n"
           "                 BCD4 (Board Custom Data 4)\n"
           "                 BCD5 (Board Custom Data 5)\n"
           "                 BCD6 (Board Custom Data 6)\n"
           "                 PM   (Product Manufacturer)\n"
           "                 PN   (Product Name)\n"
           "                 PPN  (Product Part Number)\n"
           "                 PV   (Product Version)\n"
           "                 PSN  (Product Serial)\n"
           "                 PAT  (Product Asset Tag)\n"
           "                 PFI  (Product FRU ID)\n"
           "                 PCD1 (Product Custom Data 1)\n"
           "                 PCD2 (Product Custom Data 2)\n"
           "                 PCD3 (Product Custom Data 3)\n"
           "                 PCD4 (Product Custom Data 4)\n"
           "                 PCD5 (Product Custom Data 5)\n"
           "                 PCD6 (Product Custom Data 6)\n");
  } else {
    printf("Usage: fruid-util [ %s ] [--json]\n"
      "Usage: fruid-util [ %s ] [--dump | --write ] <file>\n",
      pal_fru_list_print_t, pal_fru_list_rw_t);
    printf("Usage: fruid-util [ %s ] --modify <field> <data> <file>\n",
      pal_fru_list_rw_t);
    printf("       [field] : CPN  (Chassis Part Number)\n"
           "                       e.g., fruid-util fru1 --modify --CPN \"xxxxx\" xxx.bin\n"
           "                 CSN  (Chassis Serial Number)\n"
           "                 CCD1 (Chassis Custom Data 1)\n"
           "                 CCD2 (Chassis Custom Data 2)\n"
           "                 CCD3 (Chassis Custom Data 3)\n"
           "                 CCD4 (Chassis Custom Data 4)\n"
           "                 CCD5 (Chassis Custom Data 5)\n"
           "                 CCD6 (Chassis Custom Data 6)\n"
           "                 BMD  (Board Mfg Date)\n"
           "                       e.g., fruid-util fru1 --modify --BMD \"$( date +%%s -d \"2018-01-01 17:00:00\" )\" xxx.bin\n"
           "                 BM   (Board Manufacturer)\n"
           "                 BP   (Board Product Number)\n"
           "                 BSN  (Board Serial Number)\n"
           "                 BPN  (Board Part Number)\n"
           "                 BFI  (Board FRU ID)\n"
           "                 BCD1 (Board Custom Data 1)\n"
           "                 BCD2 (Board Custom Data 2)\n"
           "                 BCD3 (Board Custom Data 3)\n"
           "                 BCD4 (Board Custom Data 4)\n"
           "                 BCD5 (Board Custom Data 5)\n"
           "                 BCD6 (Board Custom Data 6)\n"
           "                 PM   (Product Manufacturer)\n"
           "                 PN   (Product Name)\n"
           "                 PPN  (Product Part Number)\n"
           "                 PV   (Product Version)\n"
           "                 PSN  (Product Serial Number)\n"
           "                 PAT  (Product Asset Tag)\n"
           "                 PFI  (Product FRU ID)\n"
           "                 PCD1 (Product Custom Data 1)\n"
           "                 PCD2 (Product Custom Data 2)\n"
           "                 PCD3 (Product Custom Data 3)\n"
           "                 PCD4 (Product Custom Data 4)\n"
           "                 PCD5 (Product Custom Data 5)\n"
           "                 PCD6 (Product Custom Data 6)\n");
  }
  exit(-1);
}
int check_dump_arg(int argc, char * argv[]) {
  /*  example:
      fruid-util  slot  --dump  file
      fruid-util  slot  device  --dump  file
  */
  if (argc != 4 && argc != 5) {
    return -1;
  }
  if (!is_in_list(pal_fru_list_print_t, argv[1])) {
    printf("Cannot dump FRUID for %s\n", argv[1]);
    return -1;
  }
  if (argc == 5) {
    if (strlen(pal_dev_list_rw_t) == 0) {
      return -1;
    }
    if (!is_in_list(pal_dev_list_rw_t, argv[2])) {
      printf("Cannot dump FRUID for %s %s\n", argv[1], argv[2]);
      return -1;
    }
  }
  return 0;
}
int check_write_arg(int argc, char * argv[])
{
  /*  example:
      fruid-util  slot  --write  file
      fruid-util  slot  device  --write  file
  */
  if (argc != 4 && argc != 5) {
    return -1;
  }
  if (!is_in_list(pal_fru_list_print_t, argv[1])) {
    printf("Cannot write FRUID for %s\n", argv[1]);
    return -1;
  }
  if (argc == 5) {
    if (strlen(pal_dev_list_rw_t) == 0) {
      return -1;
    }
    if (!is_in_list(pal_dev_list_rw_t, argv[2])) {
      printf("Cannot write FRUID for %s %s\n", argv[1], argv[2]);
      return -1;
    }
  }
  return 0;
}
int check_modify_arg(int argc, char * argv[])
{
  /*  exampl
      fruid-util  slot  --modify  --CPN  556699  /tmp/modify.bin
      fruid-util  slot  device  ---modify  --CPN  556699  /tmp/modify.bin
  */
  if (argc != 6 && argc != 7) {
    return -1;
  }
  if (!is_in_list(pal_fru_list_print_t, argv[1])) {
    printf("Cannot modify FRUID for %s\n", argv[1]);
    return -1;
  }
  if (argc == 6) {
    if (strcmp(argv[2], "--modify") != 0)
      return -1;
  }
  if (argc == 7) {
    if (strlen(pal_dev_list_rw_t) == 0) {
      return -1;
    }
    if (!is_in_list(pal_dev_list_rw_t, argv[2])) {
      printf("Cannot modify FRUID for %s %s\n", argv[1], argv[2]);
      return -1;
    }
  }
  return 0;
}
int check_print_arg(int argc, char * argv[])
{
  /*  example:
      fruid-util  all
      fruid-util  all  --json
      fruid-util  slot  device
      fruid-util  slot  device  --json
  */
  if (argc != 2 && argc != 3 && argc != 4) {
    return -1;
  }
  if (!is_in_list(pal_fru_list_print_t, argv[optind])) {
    printf("Cannot read FRUID for %s\n", argv[optind]);
    return -1;
  }
  if (argv[optind+1] != NULL) {
    if (strlen(pal_dev_list_print_t) == 0) {
      return -1;
    }
    if (!is_in_list(pal_dev_list_print_t, argv[optind+1])) {
      printf("Cannot print FRUID for %s %s\n", argv[optind], argv[optind+1]);
      return -1;
    }
  }
  if (argc == 4) {
    if (strcmp(argv[1], "--json") != 0)
      return -1;
  }
  return 0;
}
int print_fru(int fru, char * device, bool allow_absent, unsigned char print_format, json_t * fru_array) {
  int ret;
  char path[64] = {0};
  char name[64] = {0};
  char error_mesg [128] = {0};
  uint8_t status;
  uint8_t num_devs = 0;
  uint8_t dev_id = DEV_NONE;
  uint8_t i;
  json_t *fru_object = json_object();
  ret = pal_get_fruid_name(fru, name);
  if (ret < 0) {
    goto error;
  }
  ret = pal_is_fru_prsnt(fru, &status);
  if (ret < 0) {
    sprintf(error_mesg,"pal_is_fru_prsnt failed for fru: %d\n", fru);
    goto error;
  }
  if (status == 0) {
    // Do not fail call if user is interested in all FRUs and this specific
    // FRU is absent.
    if (allow_absent) {
      return 0;
    }
    sprintf(error_mesg,"%s is not present!", name);
    ret = -1;
    goto error;
  }
  ret = pal_is_fru_ready(fru, &status);
  if ((ret < 0) || (status == 0)) {
    sprintf(error_mesg,"%s is unavailable!", name);
    goto error;
  }
  if (device != NULL) {
    pal_get_num_devs(fru,&num_devs);
    ret = pal_get_dev_id(device, &dev_id);
    if (ret < 0) {
      print_usage();
    }
    if (dev_id != DEV_NONE && dev_id != DEV_ALL) {
      if (num_devs == 0)
        return -1;
      pal_get_dev_fruid_name(fru,dev_id,name);
      ret = pal_get_dev_fruid_path(fru, dev_id, path);
    }
    if (dev_id == DEV_ALL) {
      // when use command "fruid-util slot1 all", will show slot1 fru first then show all device fru.
      ret = pal_get_fruid_path(fru, path);
    }
  } else {
    ret = pal_get_fruid_path(fru, path);
  }
  if (ret < 0) {
    if (ret == IGNORE_PATH && allow_absent) {
      return 0;
    }
    sprintf(error_mesg,"%s is unavailable!", name);
    goto error;
  }
  ret = get_fruid_info(fru, path, name, print_format,fru_array);
  if (num_devs && dev_id == DEV_ALL) {
    for (i = 1;i <= num_devs; i++) {
      pal_get_dev_fruid_name(fru,i,name);
      ret = pal_get_dev_fruid_path(fru, i, path);
      if (ret < 0) {
        if (print_format == JSON_FORMAT) {
          json_array_append_new(fru_array, fru_object);
        } else {
          printf("%s is unavailable!\n\n", name);
        }
      } else {
        ret = get_fruid_info(fru, path, name, print_format,fru_array);
      }
    }
  }
  return ret;
error:
 if (print_format == JSON_FORMAT) {
    json_array_append_new(fru_array, fru_object);
  } else {
    printf("%s\n\n", error_mesg);
  }
  return ret;
}
int do_print_fru(int argc, char * argv[], unsigned char print_format)
{
  int ret;
  uint8_t fru;
  char * device = argv[optind+1];
  json_t *fru_array = json_array();
  ret = check_print_arg(argc, argv);
  if (ret) {
    print_usage();
    return ret;
  }
  ret = pal_get_fru_id(argv[optind], &fru);
  if (ret < 0) {
    print_usage();
    return ret;
  }
  if (fru != FRU_ALL) {
    ret = print_fru(fru, device, false, print_format,fru_array);
    if (ret < 0) {
      return ret;
    }
  } else {
    ret = 0;
    for (fru = 1; fru <= MAX_NUM_FRUS; fru++) {
      unsigned int caps;
      if (pal_get_fru_capability(fru, &caps) || !(caps & FRU_CAPABILITY_FRUID_READ)) {
        continue;
      }
      ret |= print_fru(fru, device, true, print_format,fru_array);
    }
  }
  if (print_format == JSON_FORMAT) {
    json_dumpf(fru_array, stdout, 4);
    printf("\n");
  }
  json_decref(fru_array);
  return ret;
}
int do_action(int argc, char * argv[], unsigned char action_flag) {
  int ret;
  int fd_tmpbin;
  int fd_newbin;
  int fru_size;
  char path[64] = {0};
  char name[64] = {0};
  char eeprom_path[64] = {0};
  char command[128] = {0};
  char *file_path;
  uint8_t status;
  uint8_t fru;
  uint8_t num_devs = 0;
  uint8_t dev_id = DEV_NONE;
  fruid_info_t fruid;
  FILE *fp;
  ret = pal_get_fru_id(argv[optind], &fru);
  if (ret < 0) {
    print_usage();
  }
  if (fru == FRU_ALL) {
    print_usage();
    return -1;
  }
  ret = pal_get_fruid_name(fru, name);
  if (ret < 0) {
    printf("pal_get_fruid_name failed for fru: %d\n", fru);
    return ret;
  }
  ret = pal_is_fru_prsnt(fru, &status);
  if (ret < 0) {
    printf("pal_is_fru_prsnt failed for fru: %d\n", fru);
    return ret;
  }
  if (status == 0) {
    printf("%s is not present!\n\n", name);
    return ret;
  }
  ret = pal_is_fru_ready(fru, &status);
  if ((ret < 0) || (status == 0)) {
    printf("%s is unavailable!\n\n", name);
    return ret;
  }
  if (argc == 5 || argc == 7) {
    pal_get_num_devs(fru, &num_devs);
    if (num_devs == 0) {
      print_usage();
    }
    ret = pal_get_dev_id(argv[optind+1], &dev_id);
    if (ret < 0) {
      print_usage();
    }
    if ( dev_id == DEV_NONE || dev_id == DEV_ALL ) {
      print_usage();
    }
    pal_get_dev_fruid_name(fru, dev_id,name);
    ret = pal_get_dev_fruid_path(fru, dev_id, path);
  } else {
    ret = pal_get_fruid_path(fru, path);
  }
  if (ret < 0) {
    return ret;
  }
  switch(action_flag) {
    case FLAG_DUMP:
      file_path = argv[optind-1];
      fd_tmpbin = open(path, O_RDONLY);
      if (fd_tmpbin == -1) {
        syslog(LOG_ERR, "Unable to open the %s file: %s", path, strerror(errno));
        return errno;
      }
      fd_newbin = open(file_path, O_WRONLY | O_CREAT, 0644);
      if (fd_newbin == -1) {
        syslog(LOG_ERR, "Unable to create %s file: %s", file_path, strerror(errno));
        return errno;
      }
      ret = copy_file(fd_newbin, fd_tmpbin, FRUID_SIZE);
      if (ret < 0) {
        syslog(LOG_ERR, "copy: write to %s file failed: %s",
            file_path, strerror(errno));
      }
      close(fd_newbin);
      close(fd_tmpbin);
      return ret;
    case FLAG_WRITE:
      file_path = argv[optind-1];
      // Check if the new eeprom binary file exits.
      // TODO: Add file size check before adding to the eeprom
      if (access(file_path, F_OK) == -1) {
        print_usage();
        syslog(LOG_ERR, "Unable to access the %s file: %s", file_path, strerror(errno));
        return -1;
      }
      // Verify the checksum of the new binary
      ret = fruid_parse(file_path, &fruid);
      if(ret != 0) {
        printf("New FRU data checksum is invalid\n");
        syslog(LOG_CRIT, "New FRU data checksum is invalid");
        return -1;
      }
      fd_tmpbin = open(path, O_WRONLY | O_CREAT, 0666);
      if (fd_tmpbin == -1) {
        printf("Unable to open the %s file: %s\n", path, strerror(errno));
        syslog(LOG_ERR, "Unable to open the %s file: %s", path, strerror(errno));
        return errno;
      }
      fd_newbin = open(file_path, O_RDONLY);
      if (fd_newbin == -1) {
        printf("Unable to open the %s file: %s\n", file_path, strerror(errno));
        syslog(LOG_ERR, "Unable to open the %s file: %s", file_path, strerror(errno));
        return errno;
      }
      fp = fopen(file_path, "rb");
      if ( NULL == fp ) {
        printf("Unable to get the %s fp %s\n", file_path, strerror(errno));
        syslog(LOG_ERR, "Unable to get the %s fp %s", file_path, strerror(errno));
        return errno;
      }
      //get the size of the new binary
      fseek(fp, 0L, SEEK_END);
      fru_size = ftell(fp);
      rewind(fp);
      fclose(fp);
      //check the size overflow or not
      fru_size = (fru_size > FRUID_SIZE)?FRUID_SIZE:fru_size;
      ret = pal_get_fruid_eeprom_path(fru, eeprom_path);
      if ((dev_id != DEV_NONE) && (ret < 0)) {
         ret = pal_get_dev_fruid_eeprom_path(fru, dev_id, eeprom_path, sizeof(eeprom_path));
      }
      if (ret < 0) {
        //Can not handle in common, so call pal libray for update
        if (num_devs) {
          if (dev_id == DEV_ALL)
            ret = -1;
          else if (dev_id == DEV_NONE)
            ret = pal_fruid_write(fru, file_path);
          else
            ret = pal_dev_fruid_write(fru, dev_id, file_path);
        } else {
          ret = pal_fruid_write(fru, file_path);
        }
        if (ret < 0) {
          printf("FRU:%d Write failed!\n", fru);
          syslog(LOG_WARNING, "[%s] Please check the fruid: %d dev_id: %d file_path: %s", __func__, fru, dev_id, file_path);
          close(fd_newbin);
          close(fd_tmpbin);
          return -1;
        }
      } else {
        if (access(eeprom_path, F_OK) == -1) {
          printf("Fail to access eeprom file file : %s for fru %d\n", eeprom_path, fru);
          syslog(LOG_ERR, "cannot access the eeprom file : %s for fru %d",
              eeprom_path, fru);
          close(fd_newbin);
          close(fd_tmpbin);
          return -1;
        }
        sprintf(command, "dd if=%s of=%s bs=%d count=1", file_path, eeprom_path, fru_size);
        if (system(command) != 0) {
          printf("Copy of %s to %s failed!\n", file_path, eeprom_path);
          syslog(LOG_ERR, "Copy of %s to %s failed!\n", file_path, eeprom_path);
          return -1;
        }
        ret = pal_compare_fru_data(eeprom_path, file_path, fru_size);
        if (ret < 0) {
          printf("Compare %s with %s failed!\n", file_path, eeprom_path);
          syslog(LOG_ERR, "[%s] FRU:%d Write Fail", __func__, fru);
          close(fd_newbin);
          close(fd_tmpbin);
          return -1;
        }
      }
      ret = copy_file(fd_tmpbin, fd_newbin, fru_size);
      if (ret < 0) {
        printf("Write to %s file failed: %s\n", path, strerror(errno));
        syslog(LOG_ERR, "copy: write to %s file failed: %s", path, strerror(errno));
      }
      close(fd_newbin);
      close(fd_tmpbin);
      return ret;
    case FLAG_MODIFY:
      file_path = argv[argc-1];
      if (optind != 4) { //fail to get field "--XXX"
        printf("Parameter \"%s\" is invalid!\n",  argv[argc-3]);
        printf("Fail to modify %s FRU\n", name);
        return -1;
      }
      if(access(file_path, F_OK) == -1) {  //copy current FRU bin file to specified bin file
        fd_tmpbin = open(path, O_RDONLY);
        if (fd_tmpbin == -1) {
          syslog(LOG_ERR, "Unable to open the %s file: %s", path, strerror(errno));
          return errno;
        }
        fd_newbin = open(file_path, O_WRONLY | O_CREAT, 0644);
        if (fd_newbin == -1) {
          syslog(LOG_ERR, "Unable to create %s file: %s", file_path, strerror(errno));
          return errno;
        }
        ret = copy_file(fd_newbin, fd_tmpbin, FRUID_SIZE);
        if (ret < 0) {
          syslog(LOG_ERR, "copy: write to %s file failed: %s",
              file_path, strerror(errno));
          return ret;
        }
        close(fd_newbin);
        close(fd_tmpbin);
      } else {
        memset(path, 0, sizeof(path));
        sprintf(path, "%s", file_path);
      }
      ret = fruid_modify(path, file_path, argv[optind-2], argv[optind-1]);
      if(ret < 0){
        printf("Fail to modify %s FRU\n", name);
        return ret;
      }
      ret = get_fruid_info(fru, file_path, name, DEFAULT_FORMAT,NULL);
      return ret;
    default:
        return -1;
  }
  return 0;
}
/* Utility to just print the FRUID */
int main(int argc, char * argv[]) {
  int c;
  unsigned char print_format = DEFAULT_FORMAT;
  unsigned char action_flag = 0;
  int ret = 0;
  create_fru_lists();
  struct option opts[] = {
    {"help", 0, NULL, 'h'},
    {"dump", 1, NULL, 'd'},
    {"write", 1, NULL, 'w'},
    {"modify", 0, NULL, 'm'},
    {"json", 0, NULL, 'j'},
    {"CPN", 1, NULL, 'f'},
    {"CSN", 1, NULL, 'f'},
    {"CCD1", 1, NULL, 'f'},
    {"CCD2", 1, NULL, 'f'},
    {"CCD3", 1, NULL, 'f'},
    {"CCD4", 1, NULL, 'f'},
    {"CCD5", 1, NULL, 'f'},
    {"CCD6", 1, NULL, 'f'},
    {"BMD", 1, NULL, 'f'},
    {"BM", 1, NULL, 'f'},
    {"BP", 1, NULL, 'f'},
    {"BSN", 1, NULL, 'f'},
    {"BPN", 1, NULL, 'f'},
    {"BFI", 1, NULL, 'f'},
    {"BCD1", 1, NULL, 'f'},
    {"BCD2", 1, NULL, 'f'},
    {"BCD3", 1, NULL, 'f'},
    {"BCD4", 1, NULL, 'f'},
    {"BCD5", 1, NULL, 'f'},
    {"BCD6", 1, NULL, 'f'},
    {"PM", 1, NULL, 'f'},
    {"PN", 1, NULL, 'f'},
    {"PPN", 1, NULL, 'f'},
    {"PV", 1, NULL, 'f'},
    {"PSN", 1, NULL, 'f'},
    {"PAT", 1, NULL, 'f'},
    {"PFI", 1, NULL, 'f'},
    {"PCD1", 1, NULL, 'f'},
    {"PCD2", 1, NULL, 'f'},
    {"PCD3", 1, NULL, 'f'},
    {"PCD4", 1, NULL, 'f'},
    {"PCD5", 1, NULL, 'f'},
    {"PCD6", 1, NULL, 'f'},
    {NULL, 0, NULL, 0},
  };
  const char *optstring = "";   //not support short option
  while((c = getopt_long(argc, argv, optstring, opts, NULL)) != -1) {
    switch(c) {
      case 'd':
        if (check_dump_arg(argc, argv)) {
          print_usage();
        }
        action_flag = action_flag | FLAG_DUMP;
        break;
      case 'w':
        if (check_write_arg(argc, argv)) {
          print_usage();
        }
        action_flag = action_flag | FLAG_WRITE;
        break;
      case 'm':
        if (check_modify_arg(argc, argv)) {
          print_usage();
        }
        action_flag = action_flag | FLAG_MODIFY;
        break;
      case 'j':
        print_format = JSON_FORMAT;
        break;
      case 'f':
        /* field option */
        break;
      case '?':
        printf("unknown option !!!\n\n");
        print_usage();
        break;
      case 'h':
        print_usage();
        break;
      default:
        print_usage();
    }
  }
  switch(action_flag) {
    case FLAG_DUMP:
    case FLAG_WRITE:
    case FLAG_MODIFY:
      ret = do_action(argc, argv, action_flag);
      break;
    case FLAG_PRINT:
      ret = do_print_fru(argc, argv, print_format);
      break;
    default:
      print_usage();
      ret = -1;
  }
  return ret;
}