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;
}