common/recipes-lib/fruid/files/fruid.c (1,530 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 <errno.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include "fruid.h"
#include <stdbool.h>
#define FIELD_TYPE(x)     ((x & (0x03 << 6)) >> 6)
#define FIELD_LEN(x)      (x & ~(0x03 << 6))
#define FIELD_EMPTY       "N/A"
#define NO_MORE_DATA_BYTE 0xC1
#define MAX_FIELD_LENGTH  63  // 6-bit for length
/* Unix time difference between 1970 and 1996. */
#define UNIX_TIMESTAMP_1996   820454400
/* Array for BCD Plus definition. */
const char bcd_plus_array[] = "0123456789 -.XXX";
/* Array for 6-Bit ASCII definition. */
const char * ascii_6bit[4] = {
  " !\"#$%&'()*+,-./",
  "0123456789:;<=>?",
  "@ABCDEFGHIJKLMNO",
  "PQRSTUVWXYZ[\\]^_"
};
/*
 * calculate_time - calculate time from the unix time stamp stored
 *
 * @mfg_time    : Unix timestamp since 1996
 *
 * returns char * for mfg_time_str
 * returns NULL for memory allocation failure
 */
static char * calculate_time(uint8_t * mfg_time)
{
  int len;
  struct tm * local;
  time_t unix_time = 0;
  unix_time = ((mfg_time[2] << 16) + (mfg_time[1] << 8) + mfg_time[0]) * 60;
  unix_time += UNIX_TIMESTAMP_1996;
  local = localtime(&unix_time);
  char * str = asctime(local);
  len = strlen(str);
  char * mfg_time_str = (char *) malloc(len);
  if (!mfg_time_str) {
#ifdef DEBUG
    syslog(LOG_WARNING, "fruid: malloc: memory allocation failed\n");
#endif
    return NULL;
  }
  memset(mfg_time_str, 0, len);
  memcpy(mfg_time_str, str, len);
  mfg_time_str[len - 1] = '\0';
  return mfg_time_str;
}
/*
 * verify_chksum - verify the zero checksum of the data
 *
 * @area        : offset of the area
 * @len         : len of the area in bytes
 * @chksum_read : stored checksum in the data.
 *
 * returns 0 if chksum is verified
 * returns -1 if there exist a mismatch
 */
static int verify_chksum(uint8_t * area, uint8_t len, uint8_t chksum_read)
{
  int i;
  uint8_t chksum = 0;
  for (i = 0; i < len - 1; i++)
    chksum += area[i];
  /* Zero checksum calculation */
  chksum = ~(chksum) + 1;
  return (chksum == chksum_read) ? 0 : -1;
}
/*
 * get_chassis_type - get the Chassis type
 *
 * @type_hex  : type stored in the data
 *
 * returns char ptr for chassis type string
 * returns NULL if type not in the list
 */
static char * get_chassis_type(uint8_t type_hex)
{
  int type;
  char type_int[4];
  sprintf(type_int, "%u", type_hex);
  type = atoi(type_int) - 1;
  /* If the type is not in the list defined.*/
  if (type > FRUID_CHASSIS_TYPECODE_MAX || type < FRUID_CHASSIS_TYPECODE_MIN) {
#ifdef DEBUG
    syslog(LOG_INFO, "fruid: chassis area: invalid chassis type\n");
#endif
    return NULL;
  }
  char * type_str = (char *) malloc(strlen(fruid_chassis_type[type])+1);
  if (!type_str) {
#ifdef DEBUG
    syslog(LOG_WARNING, "fruid: malloc: memory allocation failed\n");
#endif
    return NULL;
  }
  memcpy(type_str, fruid_chassis_type[type], strlen(fruid_chassis_type[type]));
  type_str[strlen(fruid_chassis_type[type])] = '\0';
  return type_str;
}
/*
 * _fruid_area_field_read - read the field data
 *
 * @offset    : offset of the field
 *
 * returns char ptr for the field data string
 */
static char * _fruid_area_field_read(uint8_t *offset)
{
  int field_type, field_len, field_len_eff, alloc_len;
  int idx, idx_eff, val;
  char * field;
  /* Bits 7:6 */
  field_type = FIELD_TYPE(offset[0]);
  /* Bits 5:0 */
  field_len = FIELD_LEN(offset[0]);
  /* Calculate the effective length of the field data based on type stored. */
  switch (field_type) {
  case TYPE_BINARY:
    /* TODO: Need to add support to read data stored in binary type. */
    field_len_eff = 1;
    break;
  case TYPE_ASCII_6BIT:
    /*
     * Every 3 bytes have four 6-bit packed values
     * + 6-bit values from the remaining field bytes.
     */
    field_len_eff = (field_len / 3) * 4 + (field_len % 3);
    break;
  case TYPE_BCD_PLUS:
    field_len_eff = ((field_len * 2) + 1);
    break;
  case TYPE_ASCII_8BIT:
    field_len_eff = field_len;
    break;
  }
  /* If field data is zero, store 'N/A' for that field. */
  alloc_len = ((field_len_eff > 0) ? field_len_eff : strlen(FIELD_EMPTY)) + 1;
  field = (char *) malloc(alloc_len);
  if (!field) {
#ifdef DEBUG
    syslog(LOG_WARNING, "fruid: malloc: memory allocation failed\n");
#endif
    return NULL;
  }
  memset(field, 0, alloc_len);
  if (field_len_eff < 1) {
    strcpy(field, FIELD_EMPTY);
    return field;
  }
  /* Retrieve field data depending on the type it was stored. */
  switch (field_type) {
  case TYPE_BINARY:
   /* TODO: Need to add support to read data stored in binary type. */
    break;
  case TYPE_BCD_PLUS:
    for (idx = 0; idx < field_len; idx++) {
      field[idx * 2] = bcd_plus_array[(offset[idx + 1] >> 4) & 0x0F];
      field[idx * 2 + 1] = bcd_plus_array[(offset[idx + 1]) & 0x0F];
    }
    field[idx * 2] = '\0';
    break;
  case TYPE_ASCII_6BIT:
    idx_eff = 0, idx = 1;
    while (field_len > 0) {
      /* 6-Bits => Bits 5:0 of the first byte */
      val = offset[idx] & 0x3F;
      field[idx_eff++] = ascii_6bit[(val & 0xF0) >> 4][val & 0x0F];
      field_len--;
      if (field_len > 0) {
        /* 6-Bits => Bits 3:0 of second byte + Bits 7:6 of first byte. */
        val = ((offset[idx] & 0xC0) >> 6) |
              ((offset[idx + 1] & 0x0F) << 2);
        field[idx_eff++] = ascii_6bit[(val & 0xF0) >> 4][val & 0x0F];
        field_len--;
      }
      if (field_len > 0) {
        /* 6-Bits => Bits 1:0 of third byte + Bits 7:4 of second byte. */
        val = ((offset[idx + 1] & 0xF0) >> 4) |
              ((offset[idx + 2] & 0x03) << 4);
        field[idx_eff++] = ascii_6bit[(val & 0xF0) >> 4][val & 0x0F];
        /* 6-Bits => Bits 7:2 of third byte. */
        val = ((offset[idx + 2] & 0xFC) >> 2);
        field[idx_eff++] = ascii_6bit[(val & 0xF0) >> 4][val & 0x0F];
        field_len--;
        idx += 3;
      }
    }
    /* Add Null terminator */
    field[idx_eff] = '\0';
    break;
  case TYPE_ASCII_8BIT:
    memcpy(field, offset + 1, field_len);
    /* Add Null terminator */
    field[field_len] = '\0';
    break;
  }
  return field;
}
/* Free all the memory allocated for fruid information */
void free_fruid_info(fruid_info_t * fruid)
{
  if (fruid->chassis.flag) {
    free(fruid->chassis.type_str);
    free(fruid->chassis.part);
    free(fruid->chassis.serial);
    free(fruid->chassis.custom1);
    free(fruid->chassis.custom2);
    free(fruid->chassis.custom3);
    free(fruid->chassis.custom4);
    free(fruid->chassis.custom5);
    free(fruid->chassis.custom6);
  }
  if (fruid->board.flag) {
    free(fruid->board.mfg_time_str);
    free(fruid->board.mfg_time);
    free(fruid->board.mfg);
    free(fruid->board.name);
    free(fruid->board.serial);
    free(fruid->board.part);
    free(fruid->board.fruid);
    free(fruid->board.custom1);
    free(fruid->board.custom2);
    free(fruid->board.custom3);
    free(fruid->board.custom4);
    free(fruid->board.custom5);
    free(fruid->board.custom6);
  }
  if (fruid->product.flag) {
    free(fruid->product.mfg);
    free(fruid->product.name);
    free(fruid->product.part);
    free(fruid->product.version);
    free(fruid->product.serial);
    free(fruid->product.asset_tag);
    free(fruid->product.fruid);
    free(fruid->product.custom1);
    free(fruid->product.custom2);
    free(fruid->product.custom3);
    free(fruid->product.custom4);
    free(fruid->product.custom5);
    free(fruid->product.custom6);
  }
  if (fruid->multirecord_smart_fan.flag) {
    free(fruid->multirecord_smart_fan.smart_fan_ver);
    free(fruid->multirecord_smart_fan.fw_ver);
    free(fruid->multirecord_smart_fan.mfg_time);
    free(fruid->multirecord_smart_fan.mfg_time_str);
    free(fruid->multirecord_smart_fan.mfg_line);
    free(fruid->multirecord_smart_fan.clei_code);
  }
}
static uint32_t get_dword(uint8_t * buf, uint8_t len) {
  uint32_t dword_value = 0;
  int i = 0;
  if (buf == NULL) {
#ifdef DEBUG
    syslog(LOG_ERR, "fruid: get_dword failed, read buffer is null");
#endif
    return 0;
  }
  if (len > 4) {
#ifdef DEBUG
    syslog(LOG_ERR, "fruid: multi_record_area: get_dword failed, invalid length %u", len);
#endif
    return 0;
  }
  for (i = 0; i < len; i++) {
    dword_value |= (buf[i] << (8 * i));
  }
  return dword_value;
}
static char * get_bcd_plus_string(uint8_t * buf, uint8_t len) {
  char * bcd_plus_str = NULL;
  int i = 0;
  int shift = 0;
  if (buf == NULL) {
#ifdef DEBUG
    syslog(LOG_ERR, "fruid: multi_record_area: get_bcd_plus_string failed, read buffer is null");
#endif
    return NULL;
  }
  bcd_plus_str = (char *) malloc((len * 2) + 1);
  if (bcd_plus_str == NULL) {
#ifdef DEBUG
    syslog(LOG_WARNING, "fruid: get_bcd_plus_string: malloc: memory allocation failed\n");
#endif
    return NULL;
  }
  memset(bcd_plus_str, 0, (len * 2) + 1); // null terminated
  for (i = 0; i < len * 2; i++) {
    if ((i % 2) == 0) {
      shift = 4;
    } else {
      shift = 0;
    }
    bcd_plus_str[i] = bcd_plus_array[((buf[i / 2] >> shift) & 0x0F)];
  }
  return bcd_plus_str;
}
int parse_fruid_area_multirecord_smart_fan(uint8_t * multirecord,
      fruid_area_multirecord_smart_fan_t * fruid_multirecord_smart_fan)
{
  int index = 0;
  int i = 0;
  /* Reset the struct to zero */
  memset(fruid_multirecord_smart_fan, 0, sizeof(fruid_area_multirecord_smart_fan_t));
  fruid_multirecord_smart_fan->manufacturer_id = get_dword(multirecord + index, MANUFACTURER_ID_DATA_LENGTH);
  index += MANUFACTURER_ID_DATA_LENGTH;
  fruid_multirecord_smart_fan->smart_fan_ver = get_bcd_plus_string(multirecord + index, SMART_FAN_VERSION_LENGTH);
  index += SMART_FAN_VERSION_LENGTH;
  fruid_multirecord_smart_fan->fw_ver = get_bcd_plus_string(multirecord + index, SMART_FAN_FW_VERSION_LENGTH);
  index += SMART_FAN_FW_VERSION_LENGTH;
  fruid_multirecord_smart_fan->mfg_time = (uint8_t *) malloc(MFG_DATE_TIME_LENGTH);
  if (fruid_multirecord_smart_fan->mfg_time == NULL) {
#ifdef DEBUG
    syslog(LOG_ERR, "fruid: multi_record_area: mfg_time: out of memory");
#endif
    return ENOMEM;
  }
  for (i = 0; i < MFG_DATE_TIME_LENGTH; i++) {
    fruid_multirecord_smart_fan->mfg_time[i] = multirecord[index++];
  }
  fruid_multirecord_smart_fan->mfg_time_str = calculate_time(fruid_multirecord_smart_fan->mfg_time);
  if (fruid_multirecord_smart_fan->mfg_time_str == NULL) {
#ifdef DEBUG
    syslog(LOG_ERR, "fruid: multi_record_area: mfg_time_str: out of memory");
#endif
    return ENOMEM;
  }
  fruid_multirecord_smart_fan->mfg_line = (char *) calloc(SMART_FAN_MFG_LINE_LENGTH + 1, sizeof(char));
  if (fruid_multirecord_smart_fan->mfg_line == NULL) {
#ifdef DEBUG
    syslog(LOG_ERR, "fruid: multi_record_area: mfg_line: out of memory");
#endif
    return ENOMEM;
  }
  for (i = 0; i < SMART_FAN_MFG_LINE_LENGTH; i++) {
    fruid_multirecord_smart_fan->mfg_line[i] = multirecord[index++];
  }
  fruid_multirecord_smart_fan->mfg_line[SMART_FAN_MFG_LINE_LENGTH] = '\0';
  fruid_multirecord_smart_fan->clei_code = (char *) calloc(SMART_FAN_CLEI_CODE_LENGTH + 1, sizeof(char));
  if (fruid_multirecord_smart_fan->clei_code == NULL) {
#ifdef DEBUG
    syslog(LOG_ERR, "fruid: multi_record_area: clei_code: out of memory");
#endif
    return ENOMEM;
  }
  for (i = 0; i < SMART_FAN_CLEI_CODE_LENGTH; i++) {
    fruid_multirecord_smart_fan->clei_code[i] = multirecord[index++];
  }
  fruid_multirecord_smart_fan->clei_code[SMART_FAN_CLEI_CODE_LENGTH] = '\0';
  fruid_multirecord_smart_fan->voltage = (get_dword(multirecord + index, SMART_FAN_VOL_DATA_LENGTH) * SMART_FAN_VOL_CUR_MULTIPLIER);
  index += SMART_FAN_VOL_DATA_LENGTH;
  fruid_multirecord_smart_fan->current = (get_dword(multirecord + index, SMART_FAN_CUR_DATA_LENGTH) * SMART_FAN_VOL_CUR_MULTIPLIER);
  index += SMART_FAN_CUR_DATA_LENGTH;
  fruid_multirecord_smart_fan->rpm_front = get_dword(multirecord + index, SMART_FAN_RPM_DATA_LENGTH);
  index += SMART_FAN_RPM_DATA_LENGTH;
  fruid_multirecord_smart_fan->rpm_rear = get_dword(multirecord + index, SMART_FAN_RPM_DATA_LENGTH);
  return 0;
}
/* Parse the Product area data */
int parse_fruid_area_product(uint8_t * product,
      fruid_area_product_t * fruid_product)
{
  int ret, index;
  index = 0;
  /* Reset the struct to zero */
  memset(fruid_product, 0, sizeof(fruid_area_product_t));
  /* Check if the format version is as per IPMI FRUID v1.0 format spec */
  fruid_product->format_ver = product[index++];
  if (fruid_product->format_ver != FRUID_FORMAT_VER) {
#ifdef DEBUG
    syslog(LOG_ERR, "fruid: product_area: format version not supported");
#endif
    return EPROTONOSUPPORT;
  }
  fruid_product->area_len = product[index++] * FRUID_AREA_LEN_MULTIPLIER;
  fruid_product->lang_code = product[index++];
  fruid_product->chksum = product[fruid_product->area_len - 1];
  ret = verify_chksum((uint8_t *) product,
          fruid_product->area_len, fruid_product->chksum);
  if (ret) {
#ifdef DEBUG
    syslog(LOG_ERR, "fruid: product_area: chksum not verified.");
#endif
    return EBADF;
  }
  fruid_product->mfg_type_len = product[index];
  fruid_product->mfg = _fruid_area_field_read(&product[index]);
  if (fruid_product->mfg == NULL)
    return ENOMEM;
  index += FIELD_LEN(product[index]) + 1;
  fruid_product->name_type_len = product[index];
  fruid_product->name = _fruid_area_field_read(&product[index]);
  if (fruid_product->name == NULL)
    return ENOMEM;
  index += FIELD_LEN(product[index]) + 1;
  fruid_product->part_type_len = product[index];
  fruid_product->part = _fruid_area_field_read(&product[index]);
  if (fruid_product->part == NULL)
    return ENOMEM;
  index += FIELD_LEN(product[index]) + 1;
  fruid_product->version_type_len = product[index];
  fruid_product->version = _fruid_area_field_read(&product[index]);
  if (fruid_product->version == NULL)
    return ENOMEM;
  index += FIELD_LEN(product[index]) + 1;
  fruid_product->serial_type_len = product[index];
  fruid_product->serial = _fruid_area_field_read(&product[index]);
  if (fruid_product->serial == NULL)
    return ENOMEM;
  index += FIELD_LEN(product[index]) + 1;
  fruid_product->asset_tag_type_len = product[index];
  fruid_product->asset_tag = _fruid_area_field_read(&product[index]);
  if (fruid_product->asset_tag == NULL)
    return ENOMEM;
  index += FIELD_LEN(product[index]) + 1;
  fruid_product->fruid_type_len = product[index];
  fruid_product->fruid = _fruid_area_field_read(&product[index]);
  if (fruid_product->fruid == NULL)
    return ENOMEM;
  index += FIELD_LEN(product[index]) + 1;
  /* Check if this field was last and there is no more custom data */
  fruid_product->custom1_type_len = product[index];
  if (product[index] == NO_MORE_DATA_BYTE)
    return 0;
  fruid_product->custom1 = _fruid_area_field_read(&product[index]);
  if (fruid_product->custom1 == NULL)
    return ENOMEM;
  index += FIELD_LEN(product[index]) + 1;
  /* Check if this field was last and there is no more custom data */
  fruid_product->custom2_type_len = product[index];
  if (product[index] == NO_MORE_DATA_BYTE)
    return 0;
  fruid_product->custom2 = _fruid_area_field_read(&product[index]);
  if (fruid_product->custom2 == NULL)
    return ENOMEM;
  index += FIELD_LEN(product[index]) + 1;
  /* Check if this field was last and there is no more custom data */
  fruid_product->custom3_type_len = product[index];
  if (product[index] == NO_MORE_DATA_BYTE)
    return 0;
  fruid_product->custom3 = _fruid_area_field_read(&product[index]);
  if (fruid_product->custom3 == NULL)
    return ENOMEM;
  index += FIELD_LEN(product[index]) + 1;
  /* Check if this field was last and there is no more custom data */
  fruid_product->custom4_type_len = product[index];
  if (product[index] == NO_MORE_DATA_BYTE)
    return 0;
  fruid_product->custom4 = _fruid_area_field_read(&product[index]);
  if (fruid_product->custom4 == NULL)
    return ENOMEM;
  index += FIELD_LEN(product[index]) + 1;
  /* Check if this field was last and there is no more custom data */
  fruid_product->custom5_type_len = product[index];
  if (product[index] == NO_MORE_DATA_BYTE)
    return 0;
  fruid_product->custom5 = _fruid_area_field_read(&product[index]);
  if (fruid_product->custom5 == NULL)
    return ENOMEM;
  index += FIELD_LEN(product[index]) + 1;
  /* Check if this field was last and there is no more custom data */
  fruid_product->custom6_type_len = product[index];
  if (product[index] == NO_MORE_DATA_BYTE)
    return 0;
  fruid_product->custom6 = _fruid_area_field_read(&product[index]);
  if (fruid_product->custom6 == NULL)
    return ENOMEM;
  return 0;
}
/* Parse the Board area data */
int parse_fruid_area_board(uint8_t * board,
      fruid_area_board_t * fruid_board)
{
  int ret, index, i;
  index = 0;
  /* Reset the struct to zero */
  memset(fruid_board, 0, sizeof(fruid_area_board_t));
  /* Check if the format version is as per IPMI FRUID v1.0 format spec */
  fruid_board->format_ver = board[index++];
  if (fruid_board->format_ver != FRUID_FORMAT_VER) {
#ifdef DEBUG
    syslog(LOG_ERR, "fruid: board_area: format version not supported");
#endif
    return EPROTONOSUPPORT;
  }
  fruid_board->area_len = board[index++] * FRUID_AREA_LEN_MULTIPLIER;
  fruid_board->lang_code = board[index++];
  fruid_board->chksum = board[fruid_board->area_len - 1];
  ret = verify_chksum((uint8_t *) board,
          fruid_board->area_len, fruid_board->chksum);
  if (ret) {
#ifdef DEBUG
    syslog(LOG_ERR, "fruid: board_area: chksum not verified.");
#endif
    return EBADF;
  }
  fruid_board->mfg_time = (uint8_t *) malloc(3*sizeof(uint8_t));
  if (fruid_board->mfg_time == NULL)
    return ENOMEM;
  for (i = 0; i < 3; i++) {
    fruid_board->mfg_time[i] = board[index++];
  }
  fruid_board->mfg_time_str = calculate_time(fruid_board->mfg_time);
  if (fruid_board->mfg_time_str == NULL)
    return ENOMEM;
  fruid_board->mfg_type_len = board[index];
  fruid_board->mfg = _fruid_area_field_read(&board[index]);
  if (fruid_board->mfg == NULL)
    return ENOMEM;
  index += FIELD_LEN(board[index]) + 1;
  fruid_board->name_type_len = board[index];
  fruid_board->name = _fruid_area_field_read(&board[index]);
  if (fruid_board->name == NULL)
    return ENOMEM;
  index += FIELD_LEN(board[index]) + 1;
  fruid_board->serial_type_len = board[index];
  fruid_board->serial = _fruid_area_field_read(&board[index]);
  if (fruid_board->serial == NULL)
    return ENOMEM;
  index += FIELD_LEN(board[index]) + 1;
  fruid_board->part_type_len = board[index];
  fruid_board->part = _fruid_area_field_read(&board[index]);
  if (fruid_board->part == NULL)
    return ENOMEM;
  index += FIELD_LEN(board[index]) + 1;
  fruid_board->fruid_type_len = board[index];
  fruid_board->fruid = _fruid_area_field_read(&board[index]);
  if (fruid_board->fruid == NULL)
    return ENOMEM;
  index += FIELD_LEN(board[index]) + 1;
  /* Check if this field was last and there is no more custom data */
  fruid_board->custom1_type_len = board[index];
  if (board[index] == NO_MORE_DATA_BYTE)
    return 0;
  fruid_board->custom1 = _fruid_area_field_read(&board[index]);
  if (fruid_board->custom1 == NULL)
    return ENOMEM;
  index += FIELD_LEN(board[index]) + 1;
  /* Check if this field was last and there is no more custom data */
  fruid_board->custom2_type_len = board[index];
  if (board[index] == NO_MORE_DATA_BYTE)
    return 0;
  fruid_board->custom2 = _fruid_area_field_read(&board[index]);
  if (fruid_board->custom2 == NULL)
    return ENOMEM;
  index += FIELD_LEN(board[index]) + 1;
  /* Check if this field was last and there is no more custom data */
  fruid_board->custom3_type_len = board[index];
  if (board[index] == NO_MORE_DATA_BYTE)
    return 0;
  fruid_board->custom3 = _fruid_area_field_read(&board[index]);
  if (fruid_board->custom3 == NULL)
    return ENOMEM;
  index += FIELD_LEN(board[index]) + 1;
  /* Check if this field was last and there is no more custom data */
  fruid_board->custom4_type_len = board[index];
  if (board[index] == NO_MORE_DATA_BYTE)
    return 0;
  fruid_board->custom4 = _fruid_area_field_read(&board[index]);
  if (fruid_board->custom4 == NULL)
    return ENOMEM;
  index += FIELD_LEN(board[index]) + 1;
  /* Check if this field was last and there is no more custom data */
  fruid_board->custom5_type_len = board[index];
  if (board[index] == NO_MORE_DATA_BYTE)
    return 0;
  fruid_board->custom5 = _fruid_area_field_read(&board[index]);
  if (fruid_board->custom5 == NULL)
    return ENOMEM;
  index += FIELD_LEN(board[index]) + 1;
  /* Check if this field was last and there is no more custom data */
  fruid_board->custom6_type_len = board[index];
  if (board[index] == NO_MORE_DATA_BYTE)
    return 0;
  fruid_board->custom6 = _fruid_area_field_read(&board[index]);
  if (fruid_board->custom6 == NULL)
    return ENOMEM;
  return 0;
}
/* Parse the Chassis area data */
int parse_fruid_area_chassis(uint8_t * chassis,
      fruid_area_chassis_t * fruid_chassis)
{
  int ret, index;
  index = 0;
  /* Reset the struct to zero */
  memset(fruid_chassis, 0, sizeof(fruid_area_chassis_t));
  /* Check if the format version is as per IPMI FRUID v1.0 format spec */
  fruid_chassis->format_ver = chassis[index++];
  if (fruid_chassis->format_ver != FRUID_FORMAT_VER) {
#ifdef DEBUG
    syslog(LOG_ERR, "fruid: chassis_area: format version not supported");
#endif
    return EPROTONOSUPPORT;
  }
  fruid_chassis->area_len = chassis[index++] * FRUID_AREA_LEN_MULTIPLIER;
  fruid_chassis->type = chassis[index++];
  fruid_chassis->chksum = chassis[fruid_chassis->area_len - 1];
  ret = verify_chksum((uint8_t *) chassis,
          fruid_chassis->area_len, fruid_chassis->chksum);
  if (ret) {
#ifdef DEBUG
    syslog(LOG_ERR, "fruid: chassis_area: chksum not verified.");
#endif
    return EBADF;
  }
  fruid_chassis->type_str = get_chassis_type(fruid_chassis->type);
  if (fruid_chassis->type_str == NULL)
    return ENOMSG;
  fruid_chassis->part_type_len = chassis[index];
  fruid_chassis->part = _fruid_area_field_read(&chassis[index]);
  if (fruid_chassis->part == NULL)
    return ENOMEM;
  index += FIELD_LEN(chassis[index]) + 1;
  fruid_chassis->serial_type_len = chassis[index];
  fruid_chassis->serial = _fruid_area_field_read(&chassis[index]);
  if (fruid_chassis->serial == NULL)
    return ENOMEM;
  index += FIELD_LEN(chassis[index]) + 1;
  /* Check if this field was last and there is no more custom data */
  fruid_chassis->custom1_type_len = chassis[index];
  if (chassis[index] == NO_MORE_DATA_BYTE)
    return 0;
  fruid_chassis->custom1 = _fruid_area_field_read(&chassis[index]);
  if (fruid_chassis->custom1 == NULL)
    return ENOMEM;
  index += FIELD_LEN(chassis[index]) + 1;
  /* Check if this field was last and there is no more custom data */
  fruid_chassis->custom2_type_len = chassis[index];
  if (chassis[index] == NO_MORE_DATA_BYTE)
    return 0;
  fruid_chassis->custom2 = _fruid_area_field_read(&chassis[index]);
  if (fruid_chassis->custom2 == NULL)
    return ENOMEM;
  index += FIELD_LEN(chassis[index]) + 1;
  /* Check if this field was last and there is no more custom data */
  fruid_chassis->custom3_type_len = chassis[index];
  if (chassis[index] == NO_MORE_DATA_BYTE)
    return 0;
  fruid_chassis->custom3 = _fruid_area_field_read(&chassis[index]);
  if (fruid_chassis->custom3 == NULL)
    return ENOMEM;
  index += FIELD_LEN(chassis[index]) + 1;
  /* Check if this field was last and there is no more custom data */
  fruid_chassis->custom4_type_len = chassis[index];
  if (chassis[index] == NO_MORE_DATA_BYTE)
    return 0;
  fruid_chassis->custom4 = _fruid_area_field_read(&chassis[index]);
  if (fruid_chassis->custom4 == NULL)
    return ENOMEM;
  index += FIELD_LEN(chassis[index]) + 1;
  /* Check if this field was last and there is no more custom data */
  fruid_chassis->custom5_type_len = chassis[index];
  if (chassis[index] == NO_MORE_DATA_BYTE)
    return 0;
  fruid_chassis->custom5 = _fruid_area_field_read(&chassis[index]);
  if (fruid_chassis->custom5 == NULL)
    return ENOMEM;
  index += FIELD_LEN(chassis[index]) + 1;
  /* Check if this field was last and there is no more custom data */
  fruid_chassis->custom6_type_len = chassis[index];
  if (chassis[index] == NO_MORE_DATA_BYTE)
    return 0;
  fruid_chassis->custom6 = _fruid_area_field_read(&chassis[index]);
  if (fruid_chassis->custom6 == NULL)
    return ENOMEM;
  return 0;
}
/* Calculate the area offsets and populate the fruid_eeprom_t struct */
void set_fruid_eeprom_offsets(const uint8_t * eeprom, fruid_header_t * header,
      fruid_eeprom_t * fruid_eeprom)
{
  fruid_eeprom->header = (uint8_t *)eeprom + 0x00;
  header->offset_area.chassis ? (fruid_eeprom->chassis = (uint8_t *)eeprom + \
    (header->offset_area.chassis * FRUID_OFFSET_MULTIPLIER)) : \
    (fruid_eeprom->chassis = NULL);
  header->offset_area.board ? (fruid_eeprom->board = (uint8_t *)eeprom + \
    (header->offset_area.board * FRUID_OFFSET_MULTIPLIER)) : \
    (fruid_eeprom->board = NULL);
  header->offset_area.product ? (fruid_eeprom->product = (uint8_t *)eeprom + \
    (header->offset_area.product * FRUID_OFFSET_MULTIPLIER)) : \
    (fruid_eeprom->product = NULL);
  header->offset_area.multirecord ? (fruid_eeprom->multirecord = (uint8_t *)eeprom + \
    (header->offset_area.multirecord * FRUID_OFFSET_MULTIPLIER)) : \
    (fruid_eeprom->multirecord = NULL);
}
/* Populate the common header struct */
int parse_fruid_header(const uint8_t * eeprom, fruid_header_t * header)
{
  int ret;
  memcpy((uint8_t *)header, (uint8_t *)eeprom, sizeof(fruid_header_t));
  ret = verify_chksum((uint8_t *) header,
          sizeof(fruid_header_t), header->chksum);
  if (ret) {
#ifdef DEBUG
    syslog(LOG_ERR, "fruid: common_header: chksum not verified.");
#endif
    return EBADF;
  }
  return ret;
}
static int check_multirecord_area(uint8_t *multirecord, int remain_len, bool *is_last_area, uint8_t *type_id, uint8_t *area_len) {
  int ret = 0;
  int index = 0;
  uint8_t format_ver = 0;
  uint8_t record_chksum = 0;
  uint8_t header_chksum = 0;
  if (multirecord == NULL || is_last_area == NULL || type_id == NULL || area_len == NULL) {
#ifdef DEBUG
    syslog(LOG_ERR, "%s Failed to parse multirecord by NULL parameter", __func__);
#endif
    return -1;
  }
  if (remain_len < sizeof(fruid_area_multirecord_header_t)) {
    return -1;
  }
  *type_id = multirecord[index++];
  format_ver = multirecord[index++];
  *area_len = multirecord[index++];
  record_chksum = multirecord[index++];
  header_chksum = multirecord[index++];
  *is_last_area = format_ver & MULTIRECORD_LAST_RECORED_BIT;
  if ((format_ver & MULTIRECORD_FORMAT_VER_MASK) != MULTIRECORD_FORMAT_VER) {
#ifdef DEBUG
    syslog(LOG_ERR, "%s: format version: %u not supported", __func__, format_ver);
#endif
    return 1;
  }
  ret = verify_chksum((multirecord + sizeof(fruid_area_multirecord_header_t)), *area_len + 1, record_chksum);
  if (ret != 0) {
    syslog(LOG_ERR, "%s: record chksum not verified.", __func__);
    return 1;
  }
  ret = verify_chksum(multirecord, sizeof(fruid_area_multirecord_header_t), header_chksum);
  if (ret != 0) {
    syslog(LOG_ERR, "%s: header chksum not verified.", __func__);
    return 1;
  }
  return 0;
}
/* Parse the eeprom dump and populate the fruid info in struct */
int populate_fruid_info(fruid_eeprom_t * fruid_eeprom, fruid_info_t * fruid, int eeprom_len)
{
  int ret;
  uint8_t *pMultirecord = NULL;
  uint8_t *pEnd = (fruid_eeprom->header + eeprom_len);
  uint8_t type_id = 0;
  uint8_t area_len = 0;
  int remain_len = 0;
  bool is_last_area = true;
  /* Initial all the required fruid structures */
  fruid_area_chassis_t fruid_chassis;
  fruid_area_board_t fruid_board;
  fruid_area_product_t fruid_product;
  fruid_area_multirecord_smart_fan_t fruid_multirecord_smart_fan;
  /* If Chassis area is present, parse and print it */
  if (fruid_eeprom->chassis) {
    ret = parse_fruid_area_chassis(fruid_eeprom->chassis, &fruid_chassis);
    if (!ret) {
      fruid->chassis.flag = 1;
      fruid->chassis.format_ver = fruid_chassis.format_ver;
      fruid->chassis.area_len = fruid_chassis.area_len;
      fruid->chassis.type = fruid_chassis.type;
      fruid->chassis.type_str = fruid_chassis.type_str;
      fruid->chassis.part_type_len = fruid_chassis.part_type_len;
      fruid->chassis.part = fruid_chassis.part;
      fruid->chassis.serial_type_len = fruid_chassis.serial_type_len;
      fruid->chassis.serial = fruid_chassis.serial;
      fruid->chassis.custom1_type_len = fruid_chassis.custom1_type_len;
      fruid->chassis.custom1 = fruid_chassis.custom1;
      fruid->chassis.custom2_type_len = fruid_chassis.custom2_type_len;
      fruid->chassis.custom2 = fruid_chassis.custom2;
      fruid->chassis.custom3_type_len = fruid_chassis.custom3_type_len;
      fruid->chassis.custom3 = fruid_chassis.custom3;
      fruid->chassis.custom4_type_len = fruid_chassis.custom4_type_len;
      fruid->chassis.custom4 = fruid_chassis.custom4;
      fruid->chassis.custom5_type_len = fruid_chassis.custom5_type_len;
      fruid->chassis.custom5 = fruid_chassis.custom5;
      fruid->chassis.custom6_type_len = fruid_chassis.custom6_type_len;
      fruid->chassis.custom6 = fruid_chassis.custom6;
      fruid->chassis.chksum = fruid_chassis.chksum;
    } else
      return ret;
  }
  /* If Board area is present, parse and print it */
  if (fruid_eeprom->board) {
    ret = parse_fruid_area_board(fruid_eeprom->board, &fruid_board);
    if (!ret) {
      fruid->board.flag = 1;
      fruid->board.format_ver = fruid_board.format_ver;
      fruid->board.area_len = fruid_board.area_len;
      fruid->board.lang_code = fruid_board.lang_code;
      fruid->board.mfg_time = fruid_board.mfg_time;
      fruid->board.mfg_time_str = fruid_board.mfg_time_str;
      fruid->board.mfg_type_len = fruid_board.mfg_type_len;
      fruid->board.mfg = fruid_board.mfg;
      fruid->board.name_type_len = fruid_board.name_type_len;
      fruid->board.name = fruid_board.name;
      fruid->board.serial_type_len = fruid_board.serial_type_len;
      fruid->board.serial = fruid_board.serial;
      fruid->board.part_type_len = fruid_board.part_type_len;
      fruid->board.part = fruid_board.part;
      fruid->board.fruid_type_len = fruid_board.fruid_type_len;
      fruid->board.fruid = fruid_board.fruid;
      fruid->board.custom1_type_len = fruid_board.custom1_type_len;
      fruid->board.custom1 = fruid_board.custom1;
      fruid->board.custom2_type_len = fruid_board.custom2_type_len;
      fruid->board.custom2 = fruid_board.custom2;
      fruid->board.custom3_type_len = fruid_board.custom3_type_len;
      fruid->board.custom3 = fruid_board.custom3;
      fruid->board.custom4_type_len = fruid_board.custom4_type_len;
      fruid->board.custom4 = fruid_board.custom4;
      fruid->board.custom5_type_len = fruid_board.custom5_type_len;
      fruid->board.custom5 = fruid_board.custom5;
      fruid->board.custom6_type_len = fruid_board.custom6_type_len;
      fruid->board.custom6 = fruid_board.custom6;
      fruid->board.chksum = fruid_board.chksum;
    } else
      return ret;
  }
  /* If Product area is present, parse and print it */
  if (fruid_eeprom->product) {
    ret = parse_fruid_area_product(fruid_eeprom->product, &fruid_product);
    if (!ret) {
      fruid->product.flag = 1;
      fruid->product.format_ver = fruid_product.format_ver;
      fruid->product.area_len = fruid_product.area_len;
      fruid->product.lang_code = fruid_product.lang_code;
      fruid->product.mfg_type_len = fruid_product.mfg_type_len;
      fruid->product.mfg = fruid_product.mfg;
      fruid->product.name_type_len = fruid_product.name_type_len;
      fruid->product.name = fruid_product.name;
      fruid->product.part_type_len = fruid_product.part_type_len;
      fruid->product.part = fruid_product.part;
      fruid->product.version_type_len = fruid_product.version_type_len;
      fruid->product.version = fruid_product.version;
      fruid->product.serial_type_len = fruid_product.serial_type_len;
      fruid->product.serial = fruid_product.serial;
      fruid->product.asset_tag_type_len = fruid_product.asset_tag_type_len;
      fruid->product.asset_tag = fruid_product.asset_tag;
      fruid->product.fruid_type_len = fruid_product.fruid_type_len;
      fruid->product.fruid = fruid_product.fruid;
      fruid->product.custom1_type_len = fruid_product.custom1_type_len;
      fruid->product.custom1 = fruid_product.custom1;
      fruid->product.custom2_type_len = fruid_product.custom2_type_len;
      fruid->product.custom2 = fruid_product.custom2;
      fruid->product.custom3_type_len = fruid_product.custom3_type_len;
      fruid->product.custom3 = fruid_product.custom3;
      fruid->product.custom4_type_len = fruid_product.custom4_type_len;
      fruid->product.custom4 = fruid_product.custom4;
      fruid->product.custom5_type_len = fruid_product.custom5_type_len;
      fruid->product.custom5 = fruid_product.custom5;
      fruid->product.custom6_type_len = fruid_product.custom6_type_len;
      fruid->product.custom6 = fruid_product.custom6;
      fruid->product.chksum = fruid_product.chksum;
    } else
      return ret;
  }
  if (fruid_eeprom->multirecord) {
    pMultirecord = fruid_eeprom->multirecord;
    // get area index and area id
    while(pMultirecord < pEnd) {
      remain_len = (pEnd - pMultirecord);
      ret = check_multirecord_area(pMultirecord, remain_len, &is_last_area, &type_id, &area_len);
      if (ret < 0) {
        break;
      }
      if (ret > 0) { // skip this record, keep parse next record
        pMultirecord += (sizeof(fruid_area_multirecord_header_t) + area_len);
        continue;
      }
      if (type_id == SMART_FAN_RECORD_ID) {
        ret = parse_fruid_area_multirecord_smart_fan(pMultirecord + sizeof(fruid_area_multirecord_header_t), &fruid_multirecord_smart_fan);
        if (ret == 0) {
          fruid->multirecord_smart_fan.flag = 1;
          fruid->multirecord_smart_fan.manufacturer_id = fruid_multirecord_smart_fan.manufacturer_id;
          fruid->multirecord_smart_fan.smart_fan_ver = fruid_multirecord_smart_fan.smart_fan_ver;
          fruid->multirecord_smart_fan.fw_ver = fruid_multirecord_smart_fan.fw_ver;
          fruid->multirecord_smart_fan.mfg_time = fruid_multirecord_smart_fan.mfg_time;
          fruid->multirecord_smart_fan.mfg_time_str = fruid_multirecord_smart_fan.mfg_time_str;
          fruid->multirecord_smart_fan.mfg_line = fruid_multirecord_smart_fan.mfg_line;
          fruid->multirecord_smart_fan.clei_code = fruid_multirecord_smart_fan.clei_code;
          fruid->multirecord_smart_fan.voltage = fruid_multirecord_smart_fan.voltage;
          fruid->multirecord_smart_fan.current = fruid_multirecord_smart_fan.current;
          fruid->multirecord_smart_fan.rpm_front = fruid_multirecord_smart_fan.rpm_front;
          fruid->multirecord_smart_fan.rpm_rear = fruid_multirecord_smart_fan.rpm_rear;
        }
      }
      // append other type here
      if (is_last_area == true) { // last one record of the list
        break;
      }
      pMultirecord += (sizeof(fruid_area_multirecord_header_t) + area_len);
    }
  }
  return 0;
}
/*
 * fruid_parse - To parse the bin file (eeprom) and populate
 *               the fruid information in the struct
 * @bin       : Eeprom binary file
 * @fruid     : ptr to the struct that holds the fruid information
 *
 * returns 0 on success
 * returns non-zero errno value on error
 */
int fruid_parse(const char * bin, fruid_info_t * fruid)
{
  int fruid_len, ret;
  FILE *fruid_fd;
  uint8_t * eeprom;
  /* Reset parser return value */
  ret = 0;
  /* Open the FRUID binary file */
  fruid_fd = fopen(bin, "rb");
  if (!fruid_fd) {
#ifdef DEBUG
    syslog(LOG_ERR, "fruid: unable to open the file");
#endif
    return ENOENT;
  }
  /* Get the size of the binary file */
  fseek(fruid_fd, 0, SEEK_END);
  fruid_len = (uint32_t) ftell(fruid_fd);
  if (fruid_len == 0) {
    fclose(fruid_fd);
    syslog(LOG_WARNING, "fruid: file %s is empty", bin);
    return -1;
  }
  fseek(fruid_fd, 0, SEEK_SET);
  eeprom = (uint8_t *) malloc(fruid_len);
  if (!eeprom) {
    fclose(fruid_fd);
#ifdef DEBUG
    syslog(LOG_WARNING, "fruid: malloc: memory allocation failed\n");
#endif
    return ENOMEM;
  }
  /* Read the binary file */
  ret = fread(eeprom, sizeof(uint8_t), fruid_len, fruid_fd);
  if (ret != fruid_len) {
    printf("Failed to read binary file, inconsistent length\n");
    return -1;
  }
  /* Close the FRUID binary file */
  fclose(fruid_fd);
  /* Parse eeprom dump*/
  ret = fruid_parse_eeprom(eeprom, fruid_len, fruid);
  /* Free the eeprom malloced memory */
  free(eeprom);
  return ret;
}
/* Populate the fruid from eeprom dump*/
int fruid_parse_eeprom(const uint8_t * eeprom, int eeprom_len, fruid_info_t * fruid)
{
  int ret = 0;
  /* Initial all the required fruid structures */
  fruid_header_t fruid_header;
  fruid_eeprom_t fruid_eeprom;
  memset(&fruid_header, 0, sizeof(fruid_header_t));
  memset(&fruid_eeprom, 0, sizeof(fruid_eeprom_t));
  memset(fruid, 0, sizeof(fruid_info_t));
  /* Parse the common header data */
  ret = parse_fruid_header(eeprom, &fruid_header);
  if (ret) {
    return ret;
  }
  /* Calculate all the area offsets */
  set_fruid_eeprom_offsets(eeprom, &fruid_header, &fruid_eeprom);
  /* Parse the eeprom and populate the fruid information */
  ret = populate_fruid_info(&fruid_eeprom, fruid, eeprom_len);
  if (ret) {
    /* Free the malloced memory for the fruid information */
    free_fruid_info(fruid);
  }
  return ret;
}
static
char *extract_content(const char *content) {
  int i = 0, j = 0;
  char *tmp_str = (char *) malloc ((strlen(content) + 1) * sizeof(char));
  while(content[i] != '\0') {
    if(content[i] != '\"') {
      tmp_str[j++] = content[i];
    }
    i++;
  }
  tmp_str[j] = '\0';
  return tmp_str;
}
static
int calculate_chksum(uint8_t * start_offset, uint8_t area_length) {
  uint8_t chksum = 0;
  int i, ret;
  for (i = 0; i < area_length - 1; i++) {
    chksum += start_offset[i];
  }
  /* Zero checksum calculation */
  chksum = ~(chksum) + 1;
  /* Update new checksum */
  start_offset[area_length - 1] = chksum;
  ret = verify_chksum((uint8_t *) start_offset, area_length, start_offset[area_length - 1]);
  return ret;
}
static
int set_mfg_time( uint8_t * time_field , char * set_time) {
  uint32_t time_value;
  uint32_t datetime;
  time_t ipmi_time;
  char *ptr = NULL;
  if (!strcmp(set_time, "")) {   //Invalid time setting
    return -1;
  }
  strtol(set_time, &ptr, 10);
  if(strcmp(ptr, "")) {       //Invalid time setting
    return -1;
  }
  time_value = (uint32_t)atoi(set_time);
  ipmi_time = UNIX_TIMESTAMP_1996;
  datetime = (time_value - ipmi_time) / 60;
  time_field[0] = (datetime & 0xFF);
  time_field[1] = ((datetime >> 8) & 0xFF);
  time_field[2] = ((datetime >> 16) & 0xFF);
  return 0;
}
static
int alter_field_content(char **fru_field , char *content) {
  if (*fru_field != NULL)
    free(*fru_field);
  *fru_field = content;
  return 0;
}
int fruid_modify(const char * cur_bin, const char * new_bin, const char * field, const char * content)
{
  int fruid_len, ret;
  FILE *fruid_fd;
  uint8_t *old_eeprom, *eeprom = NULL;
  int total_field_opt_size = sizeof(fruid_field_all_opt)/sizeof(fruid_field_all_opt[0]);
  fruid_info_t fruid;
  int i, len;
  int target = -1;
  char *tmp_content = NULL;
  int content_len;
  int new_fruid_len;
  uint8_t type_length;
  /* Reset parser return value */
  ret = 0;
  /* Open the FRUID binary file */
  fruid_fd = fopen(cur_bin, "rb");
  if (!fruid_fd) {
#ifdef DEBUG
    syslog(LOG_ERR, "fruid: unable to open the file");
#endif
    return ENOENT;
  }
  /* Get the size of the binary file */
  fseek(fruid_fd, 0, SEEK_END);
  fruid_len = (uint32_t) ftell(fruid_fd);
  fseek(fruid_fd, 0, SEEK_SET);
  old_eeprom = (uint8_t *) malloc(fruid_len);
  if (!old_eeprom) {
#ifdef DEBUG
    syslog(LOG_WARNING, "fruid: malloc: memory allocation failed\n");
#endif
    return ENOMEM;
  }
  /* Read the binary file */
  ret = fread(old_eeprom, sizeof(uint8_t), fruid_len, fruid_fd);
  if (ret != fruid_len) {
    printf("Failed to read binary file, inconsistent length\n");
    return -1;
  }
  /* Close the FRUID binary file */
  fclose(fruid_fd);
  /* Parse eeprom dump*/
  ret = fruid_parse_eeprom(old_eeprom, fruid_len, &fruid);
  /* Free the eeprom malloced memory */
  free(old_eeprom);
  if (ret) {
    printf("Failed to parse FRU!\n");
    return -1;
  }
  for(i = 0; i < total_field_opt_size; i++) {
    if (!strcmp(field, fruid_field_all_opt[i])) {
      target = i;
      break;
    }
  }
  if (target == -1) {
    printf("Parameter \"%s\" is invalid!\n", field);
    return -1;
  }
  if (target <= CCD6) {
    if (fruid.chassis.flag != 1) {
      printf("Chassis Area is invalid!\n");
      return -1;
    }
  } else if (target <= BCD6) {
    if (fruid.board.flag != 1) {
      printf("Board Area is invalid!\n");
      return -1;
    }
  } else {
    if (fruid.product.flag != 1) {
      printf("Product Area is invalid!\n");
      return -1;
    }
  }
  tmp_content = extract_content(content);
  content_len = strlen(tmp_content);
  if (content_len > MAX_FIELD_LENGTH) {
    printf("Content length \"%s\" is more than its maximum length:%d !\n", field, MAX_FIELD_LENGTH);
    return -1;
  }
  if (content_len)
    type_length = 0xc0 + content_len;
  else
    type_length = 0;
  // check custom field type length
  switch (target) {
    case CCD1:
    case CCD2:
    case CCD3:
    case CCD4:
    case CCD5:
    case CCD6:
    case BCD1:
    case BCD2:
    case BCD3:
    case BCD4:
    case BCD5:
    case BCD6:
    case PCD1:
    case PCD2:
    case PCD3:
    case PCD4:
    case PCD5:
    case PCD6:
      if (type_length == NO_MORE_DATA_BYTE) {
        printf("Content length \"%s\" should not be 1 !\n", field);
        return -1;
      }
      break;
    default:
      break;
  }
  // alter field content
  switch (target) {
    case CPN:
      fruid.chassis.part_type_len = type_length;
      alter_field_content(&fruid.chassis.part,tmp_content);
      break;
    case CSN:
      fruid.chassis.serial_type_len = type_length;
      alter_field_content(&fruid.chassis.serial,tmp_content);
      break;
    case CCD1:
      fruid.chassis.custom1_type_len = type_length;
      alter_field_content(&fruid.chassis.custom1,tmp_content);
      break;
    case CCD2:
      if (fruid.chassis.custom1_type_len == NO_MORE_DATA_BYTE)
        fruid.chassis.custom1_type_len = 0;
      fruid.chassis.custom2_type_len = type_length;
      alter_field_content(&fruid.chassis.custom2,tmp_content);
      break;
    case CCD3:
      if (fruid.chassis.custom1_type_len == NO_MORE_DATA_BYTE)
        fruid.chassis.custom1_type_len = 0;
      if (fruid.chassis.custom2_type_len == NO_MORE_DATA_BYTE)
        fruid.chassis.custom2_type_len = 0;
      fruid.chassis.custom3_type_len = type_length;
      alter_field_content(&fruid.chassis.custom3,tmp_content);
      break;
    case CCD4:
      if (fruid.chassis.custom1_type_len == NO_MORE_DATA_BYTE)
        fruid.chassis.custom1_type_len = 0;
      if (fruid.chassis.custom2_type_len == NO_MORE_DATA_BYTE)
        fruid.chassis.custom2_type_len = 0;
      if (fruid.chassis.custom3_type_len == NO_MORE_DATA_BYTE)
        fruid.chassis.custom3_type_len = 0;
      fruid.chassis.custom4_type_len = type_length;
      alter_field_content(&fruid.chassis.custom4,tmp_content);
      break;
    case CCD5:
      if (fruid.chassis.custom1_type_len == NO_MORE_DATA_BYTE)
        fruid.chassis.custom1_type_len = 0;
      if (fruid.chassis.custom2_type_len == NO_MORE_DATA_BYTE)
        fruid.chassis.custom2_type_len = 0;
      if (fruid.chassis.custom3_type_len == NO_MORE_DATA_BYTE)
        fruid.chassis.custom3_type_len = 0;
      if (fruid.chassis.custom4_type_len == NO_MORE_DATA_BYTE)
        fruid.chassis.custom4_type_len = 0;
      fruid.chassis.custom5_type_len = type_length;
      alter_field_content(&fruid.chassis.custom5,tmp_content);
      break;
    case CCD6:
      if (fruid.chassis.custom1_type_len == NO_MORE_DATA_BYTE)
        fruid.chassis.custom1_type_len = 0;
      if (fruid.chassis.custom2_type_len == NO_MORE_DATA_BYTE)
        fruid.chassis.custom2_type_len = 0;
      if (fruid.chassis.custom3_type_len == NO_MORE_DATA_BYTE)
        fruid.chassis.custom3_type_len = 0;
      if (fruid.chassis.custom4_type_len == NO_MORE_DATA_BYTE)
        fruid.chassis.custom4_type_len = 0;
      if (fruid.chassis.custom5_type_len == NO_MORE_DATA_BYTE)
        fruid.chassis.custom5_type_len = 0;
      fruid.chassis.custom6_type_len = type_length;
      alter_field_content(&fruid.chassis.custom6,tmp_content);
      break;
    case BMD:
      if(set_mfg_time(fruid.board.mfg_time, tmp_content) < 0) {
        return -1;
      }
      break;
    case BM:
      fruid.board.mfg_type_len = type_length;
      alter_field_content(&fruid.board.mfg,tmp_content);
      break;
    case BP:
      fruid.board.name_type_len = type_length;
      alter_field_content(&fruid.board.name,tmp_content);
      break;
    case BSN:
      fruid.board.serial_type_len = type_length;
      alter_field_content(&fruid.board.serial,tmp_content);
      break;
    case BPN:
      fruid.board.part_type_len = type_length;
      alter_field_content(&fruid.board.part,tmp_content);
      break;
    case BFI:
      fruid.board.fruid_type_len = type_length;
      alter_field_content(&fruid.board.fruid,tmp_content);
      break;
    case BCD1:
      fruid.board.custom1_type_len = type_length;
      alter_field_content(&fruid.board.custom1,tmp_content);
      break;
    case BCD2:
      if (fruid.board.custom1_type_len == NO_MORE_DATA_BYTE)
        fruid.board.custom1_type_len = 0;
      fruid.board.custom2_type_len = type_length;
      alter_field_content(&fruid.board.custom2,tmp_content);
      break;
    case BCD3:
      if (fruid.board.custom1_type_len == NO_MORE_DATA_BYTE)
        fruid.board.custom1_type_len = 0;
      if (fruid.board.custom2_type_len == NO_MORE_DATA_BYTE)
        fruid.board.custom2_type_len = 0;
      fruid.board.custom3_type_len = type_length;
      alter_field_content(&fruid.board.custom3,tmp_content);
      break;
    case BCD4:
      if (fruid.board.custom1_type_len == NO_MORE_DATA_BYTE)
        fruid.board.custom1_type_len = 0;
      if (fruid.board.custom2_type_len == NO_MORE_DATA_BYTE)
        fruid.board.custom2_type_len = 0;
      if (fruid.board.custom3_type_len == NO_MORE_DATA_BYTE)
        fruid.board.custom3_type_len = 0;
      fruid.board.custom4_type_len = type_length;
      alter_field_content(&fruid.board.custom4,tmp_content);
      break;
    case BCD5:
      if (fruid.board.custom1_type_len == NO_MORE_DATA_BYTE)
        fruid.board.custom1_type_len = 0;
      if (fruid.board.custom2_type_len == NO_MORE_DATA_BYTE)
        fruid.board.custom2_type_len = 0;
      if (fruid.board.custom3_type_len == NO_MORE_DATA_BYTE)
        fruid.board.custom3_type_len = 0;
      if (fruid.board.custom4_type_len == NO_MORE_DATA_BYTE)
        fruid.board.custom4_type_len = 0;
      fruid.board.custom5_type_len = type_length;
      alter_field_content(&fruid.board.custom5,tmp_content);
      break;
    case BCD6:
      if (fruid.board.custom1_type_len == NO_MORE_DATA_BYTE)
        fruid.board.custom1_type_len = 0;
      if (fruid.board.custom2_type_len == NO_MORE_DATA_BYTE)
        fruid.board.custom2_type_len = 0;
      if (fruid.board.custom3_type_len == NO_MORE_DATA_BYTE)
        fruid.board.custom3_type_len = 0;
      if (fruid.board.custom4_type_len == NO_MORE_DATA_BYTE)
        fruid.board.custom4_type_len = 0;
      if (fruid.board.custom5_type_len == NO_MORE_DATA_BYTE)
        fruid.board.custom5_type_len = 0;
      fruid.board.custom6_type_len = type_length;
      alter_field_content(&fruid.board.custom6,tmp_content);
      break;
    case PM:
      fruid.product.mfg_type_len = type_length;
      alter_field_content(&fruid.product.mfg,tmp_content);
      break;
    case PN:
      fruid.product.name_type_len = type_length;
      alter_field_content(&fruid.product.name,tmp_content);
      break;
    case PPN:
      fruid.product.part_type_len = type_length;
      alter_field_content(&fruid.product.part,tmp_content);
      break;
    case PV:
      fruid.product.version_type_len = type_length;
      alter_field_content(&fruid.product.version,tmp_content);
      break;
    case PSN:
      fruid.product.serial_type_len = type_length;
      alter_field_content(&fruid.product.serial,tmp_content);
      break;
    case PAT:
      fruid.product.asset_tag_type_len = type_length;
      alter_field_content(&fruid.product.asset_tag,tmp_content);
      break;
    case PFI:
      fruid.product.fruid_type_len = type_length;
      alter_field_content(&fruid.product.fruid,tmp_content);
      break;
    case PCD1:
      fruid.product.custom1_type_len = type_length;
      alter_field_content(&fruid.product.custom1,tmp_content);
      break;
    case PCD2:
      if (fruid.product.custom1_type_len == NO_MORE_DATA_BYTE)
        fruid.product.custom1_type_len = 0;
      fruid.product.custom2_type_len = type_length;
      alter_field_content(&fruid.product.custom2,tmp_content);
      break;
    case PCD3:
      if (fruid.product.custom1_type_len == NO_MORE_DATA_BYTE)
        fruid.product.custom1_type_len = 0;
      if (fruid.product.custom2_type_len == NO_MORE_DATA_BYTE)
        fruid.product.custom2_type_len = 0;
      fruid.product.custom3_type_len = type_length;
      alter_field_content(&fruid.product.custom3,tmp_content);
      break;
    case PCD4:
      if (fruid.product.custom1_type_len == NO_MORE_DATA_BYTE)
        fruid.product.custom1_type_len = 0;
      if (fruid.product.custom2_type_len == NO_MORE_DATA_BYTE)
        fruid.product.custom2_type_len = 0;
      if (fruid.product.custom3_type_len == NO_MORE_DATA_BYTE)
        fruid.product.custom3_type_len = 0;
      fruid.product.custom4_type_len = type_length;
      alter_field_content(&fruid.product.custom4,tmp_content);
      break;
    case PCD5:
      if (fruid.product.custom1_type_len == NO_MORE_DATA_BYTE)
        fruid.product.custom1_type_len = 0;
      if (fruid.product.custom2_type_len == NO_MORE_DATA_BYTE)
        fruid.product.custom2_type_len = 0;
      if (fruid.product.custom3_type_len == NO_MORE_DATA_BYTE)
        fruid.product.custom3_type_len = 0;
      if (fruid.product.custom4_type_len == NO_MORE_DATA_BYTE)
        fruid.product.custom4_type_len = 0;
      fruid.product.custom5_type_len = type_length;
      alter_field_content(&fruid.product.custom5,tmp_content);
      break;
    case PCD6:
      if (fruid.product.custom1_type_len == NO_MORE_DATA_BYTE)
        fruid.product.custom1_type_len = 0;
      if (fruid.product.custom2_type_len == NO_MORE_DATA_BYTE)
        fruid.product.custom2_type_len = 0;
      if (fruid.product.custom3_type_len == NO_MORE_DATA_BYTE)
        fruid.product.custom3_type_len = 0;
      if (fruid.product.custom4_type_len == NO_MORE_DATA_BYTE)
        fruid.product.custom4_type_len = 0;
      if (fruid.product.custom5_type_len == NO_MORE_DATA_BYTE)
        fruid.product.custom5_type_len = 0;
      fruid.product.custom6_type_len = type_length;
      alter_field_content(&fruid.product.custom6,tmp_content);
      break;
    default:
      return -1;
      break;
  }
  // create new FRU alloc new eeprom
  // We need the existing length + length of new content, but we can also add
  // new optional fields (for instance if custom1 is added, the others become 0
  // byte fields, but with a type/len byte).  For safety add enough bytes to
  // cover all the optional fields to prevent buffer overruns.  We track the
  // number of bytes added to the array, so only that number will be written to
  // the eeprom.
  new_fruid_len = fruid_len + ((content_len / 8) + 1) * 8 + total_field_opt_size;
  eeprom = (uint8_t *) malloc(new_fruid_len);
  memset(eeprom, 0, new_fruid_len);
  if (!eeprom) {
#ifdef DEBUG
    syslog(LOG_WARNING, "%s: malloc: memory allocation failed", __func__);
#endif
    return ENOMEM;
  }
  // chassis area
  i = 8;
  len = 0;
  int start = i;
  if (fruid.chassis.flag == 1) {
    eeprom[i++] = fruid.chassis.format_ver;
    eeprom[i++] = fruid.chassis.area_len;
    eeprom[i++] = fruid.chassis.type;
    eeprom[i++] = fruid.chassis.part_type_len;
    len = FIELD_LEN(fruid.chassis.part_type_len);
    memcpy(&eeprom[i], fruid.chassis.part, len);
    i += len;
    eeprom[i++] = fruid.chassis.serial_type_len;
    len = FIELD_LEN(fruid.chassis.serial_type_len);
    memcpy(&eeprom[i], fruid.chassis.serial, len);
    i += len;
    eeprom[i++] = fruid.chassis.custom1_type_len;
    if (fruid.chassis.custom1_type_len == NO_MORE_DATA_BYTE)
      goto chasis_chksum;
    len = FIELD_LEN(fruid.chassis.custom1_type_len);
    memcpy(&eeprom[i], fruid.chassis.custom1, len);
    i += len;
    eeprom[i++] = fruid.chassis.custom2_type_len;
    if (fruid.chassis.custom2_type_len == NO_MORE_DATA_BYTE)
      goto chasis_chksum;
    len = FIELD_LEN(fruid.chassis.custom2_type_len);
    memcpy(&eeprom[i], fruid.chassis.custom2, len);
    i += len;
    eeprom[i++] = fruid.chassis.custom3_type_len;
    if (fruid.chassis.custom3_type_len == NO_MORE_DATA_BYTE)
      goto chasis_chksum;
    len = FIELD_LEN(fruid.chassis.custom3_type_len);
    memcpy(&eeprom[i], fruid.chassis.custom3, len);
    i += len;
    eeprom[i++] = fruid.chassis.custom4_type_len;
    if (fruid.chassis.custom4_type_len == NO_MORE_DATA_BYTE)
      goto chasis_chksum;
    len = FIELD_LEN(fruid.chassis.custom4_type_len);
    memcpy(&eeprom[i], fruid.chassis.custom4, len);
    i += len;
    eeprom[i++] = fruid.chassis.custom5_type_len;
    if (fruid.chassis.custom5_type_len == NO_MORE_DATA_BYTE)
      goto chasis_chksum;
    len = FIELD_LEN(fruid.chassis.custom5_type_len);
    memcpy(&eeprom[i], fruid.chassis.custom5, len);
    i += len;
    eeprom[i++] = fruid.chassis.custom6_type_len;
    if (fruid.chassis.custom6_type_len == NO_MORE_DATA_BYTE)
      goto chasis_chksum;
    len = FIELD_LEN(fruid.chassis.custom6_type_len);
    memcpy(&eeprom[i], fruid.chassis.custom6, len);
    i += len;
  chasis_chksum:
    if (eeprom[i - 1] != NO_MORE_DATA_BYTE) {
      eeprom[i++] = NO_MORE_DATA_BYTE;
    }
    i += (7 - (i % 8));
    fruid.chassis.area_len = i - start + 1;
    eeprom[start + 1] = fruid.chassis.area_len / FRUID_AREA_LEN_MULTIPLIER;
    ret = calculate_chksum(&eeprom[start], fruid.chassis.area_len);
    if (ret < 0) {
      printf("Update chassis checksum fail!\n");
      goto error_exit;
    }
    i++;
  } else {
    fruid.chassis.area_len = 0;
  }
  // board area
  start = i;
  if (fruid.board.flag == 1) {
    eeprom[i++] = fruid.board.format_ver;
    eeprom[i++] = fruid.board.area_len;
    eeprom[i++] = fruid.board.lang_code;
    memcpy(&eeprom[i], fruid.board.mfg_time, 3);
    i += 3;
    eeprom[i++] = fruid.board.mfg_type_len;
    len = FIELD_LEN(fruid.board.mfg_type_len);
    memcpy(&eeprom[i], fruid.board.mfg, len);
    i += len;
    eeprom[i++] = fruid.board.name_type_len;
    len = FIELD_LEN(fruid.board.name_type_len);
    memcpy(&eeprom[i], fruid.board.name, len);
    i += len;
    eeprom[i++] = fruid.board.serial_type_len;
    len = FIELD_LEN(fruid.board.serial_type_len);
    memcpy(&eeprom[i], fruid.board.serial, len);
    i += len;
    eeprom[i++] = fruid.board.part_type_len;
    len = FIELD_LEN(fruid.board.part_type_len);
    memcpy(&eeprom[i], fruid.board.part, len);
    i += len;
    eeprom[i++] = fruid.board.fruid_type_len;
    len = FIELD_LEN(fruid.board.fruid_type_len);
    memcpy(&eeprom[i], fruid.board.fruid, len);
    i += len;
    eeprom[i++] = fruid.board.custom1_type_len;
    if (fruid.board.custom1_type_len == NO_MORE_DATA_BYTE)
      goto board_chksum;
    len = FIELD_LEN(fruid.board.custom1_type_len);
    memcpy(&eeprom[i], fruid.board.custom1, len);
    i += len;
    eeprom[i++] = fruid.board.custom2_type_len;
    if (fruid.board.custom2_type_len == NO_MORE_DATA_BYTE)
      goto board_chksum;
    len = FIELD_LEN(fruid.board.custom2_type_len);
    memcpy(&eeprom[i], fruid.board.custom2, len);
    i += len;
    eeprom[i++] = fruid.board.custom3_type_len;
    if (fruid.board.custom3_type_len == NO_MORE_DATA_BYTE)
      goto board_chksum;
    len = FIELD_LEN(fruid.board.custom3_type_len);
    memcpy(&eeprom[i], fruid.board.custom3, len);
    i += len;
    eeprom[i++] = fruid.board.custom4_type_len;
    if (fruid.board.custom4_type_len == NO_MORE_DATA_BYTE)
      goto board_chksum;
    len = FIELD_LEN(fruid.board.custom4_type_len);
    memcpy(&eeprom[i], fruid.board.custom4, len);
    i += len;
    eeprom[i++] = fruid.board.custom5_type_len;
    if (fruid.board.custom5_type_len == NO_MORE_DATA_BYTE)
      goto board_chksum;
    len = FIELD_LEN(fruid.board.custom5_type_len);
    memcpy(&eeprom[i], fruid.board.custom5, len);
    i += len;
    eeprom[i++] = fruid.board.custom6_type_len;
    if (fruid.board.custom6_type_len == NO_MORE_DATA_BYTE)
      goto board_chksum;
    len = FIELD_LEN(fruid.board.custom6_type_len);
    memcpy(&eeprom[i], fruid.board.custom6, len);
    i += len;
  board_chksum:
    if (eeprom[i - 1] != NO_MORE_DATA_BYTE) {
      eeprom[i++] = NO_MORE_DATA_BYTE;
    }
    i += (7 - (i % 8));
    fruid.board.area_len = i - start + 1;
    eeprom[start + 1] = fruid.board.area_len / FRUID_AREA_LEN_MULTIPLIER;
    ret = calculate_chksum(&eeprom[start], fruid.board.area_len);
    if (ret < 0) {
      printf("Update board checksum fail!\n");
      goto error_exit;
    }
    i++;
  } else {
    fruid.board.area_len = 0;
  }
  // product area
  start = i;
  if (fruid.product.flag == 1) {
    eeprom[i++] = fruid.product.format_ver;
    eeprom[i++] = fruid.product.area_len;
    eeprom[i++] = fruid.product.lang_code;
    eeprom[i++] = fruid.product.mfg_type_len;
    len = FIELD_LEN(fruid.product.mfg_type_len);
    memcpy(&eeprom[i], fruid.product.mfg, len);
    i += len;
    eeprom[i++] = fruid.product.name_type_len;
    len = FIELD_LEN(fruid.product.name_type_len);
    memcpy(&eeprom[i], fruid.product.name, len);
    i += len;
    eeprom[i++] = fruid.product.part_type_len;
    len = FIELD_LEN(fruid.product.part_type_len);
    memcpy(&eeprom[i], fruid.product.part, len);
    i += len;
    eeprom[i++] = fruid.product.version_type_len;
    len = FIELD_LEN(fruid.product.version_type_len);
    memcpy(&eeprom[i], fruid.product.version, len);
    i += len;
    eeprom[i++] = fruid.product.serial_type_len;
    len = FIELD_LEN(fruid.product.serial_type_len);
    memcpy(&eeprom[i], fruid.product.serial, len);
    i += len;
    eeprom[i++] = fruid.product.asset_tag_type_len;
    len = FIELD_LEN(fruid.product.asset_tag_type_len);
    memcpy(&eeprom[i], fruid.product.asset_tag, len);
    i += len;
    eeprom[i++] = fruid.product.fruid_type_len;
    len = FIELD_LEN(fruid.product.fruid_type_len);
    memcpy(&eeprom[i], fruid.product.fruid, len);
    i += len;
    eeprom[i++] = fruid.product.custom1_type_len;
    if (fruid.product.custom1_type_len == NO_MORE_DATA_BYTE)
      goto product_chksum;
    len = FIELD_LEN(fruid.product.custom1_type_len);
    memcpy(&eeprom[i], fruid.product.custom1, len);
    i += len;
    eeprom[i++] = fruid.product.custom2_type_len;
    if (fruid.product.custom2_type_len == NO_MORE_DATA_BYTE)
      goto product_chksum;
    len = FIELD_LEN(fruid.product.custom2_type_len);
    memcpy(&eeprom[i], fruid.product.custom2, len);
    i += len;
    eeprom[i++] = fruid.product.custom3_type_len;
    if (fruid.product.custom3_type_len == NO_MORE_DATA_BYTE)
      goto product_chksum;
    len = FIELD_LEN(fruid.product.custom3_type_len);
    memcpy(&eeprom[i], fruid.product.custom3, len);
    i += len;
    eeprom[i++] = fruid.product.custom4_type_len;
    if (fruid.product.custom4_type_len == NO_MORE_DATA_BYTE)
      goto product_chksum;
    len = FIELD_LEN(fruid.product.custom4_type_len);
    memcpy(&eeprom[i], fruid.product.custom4, len);
    i += len;
    eeprom[i++] = fruid.product.custom5_type_len;
    if (fruid.product.custom5_type_len == NO_MORE_DATA_BYTE)
      goto product_chksum;
    len = FIELD_LEN(fruid.product.custom5_type_len);
    memcpy(&eeprom[i], fruid.product.custom5, len);
    i += len;
    eeprom[i++] = fruid.product.custom6_type_len;
    if (fruid.product.custom6_type_len == NO_MORE_DATA_BYTE)
      goto product_chksum;
    len = FIELD_LEN(fruid.product.custom6_type_len);
    memcpy(&eeprom[i], fruid.product.custom6, len);
    i += len;
  product_chksum:
    if (eeprom[i - 1] != NO_MORE_DATA_BYTE) {
      eeprom[i++] = NO_MORE_DATA_BYTE;
    }
    i += (7 - (i % 8));
    fruid.product.area_len = i - start + 1;
    eeprom[start + 1] = fruid.product.area_len / FRUID_AREA_LEN_MULTIPLIER;
    ret = calculate_chksum(&eeprom[start], fruid.product.area_len);
    if (ret < 0) {
      printf("Update product checksum fail!\n");
      goto error_exit;
    }
    i++;
  } else {
    fruid.product.area_len = 0;
  }
  // update header
  int offset = 1;
  eeprom[0] = 0x01; // format version
  if (fruid.chassis.flag == 1) {
    eeprom[2] = offset; // chassis area offset
  }
  if (fruid.board.flag == 1) {
    offset += fruid.chassis.area_len / FRUID_AREA_LEN_MULTIPLIER; // board area offset
    eeprom[3] = offset;
  }
  if (fruid.product.flag == 1) {
    offset += fruid.board.area_len / FRUID_AREA_LEN_MULTIPLIER; // product area offset
    eeprom[4] = offset;
  }
  //  update header checksum
  ret = calculate_chksum(&eeprom[0], 8);
  if (ret < 0) {
    printf("Update header checksum fail!\n");
    goto error_exit;
  }
  /* Open the FRUID binary file */
  fruid_fd = fopen(new_bin, "wb");
  if (!fruid_fd) {
#ifdef DEBUG
    syslog(LOG_ERR, "%s: unable to open the file %s", __func__, new_bin);
#endif
    return ENOENT;
  }
  /* Write the binary file */
  fwrite(eeprom, sizeof(uint8_t), i, fruid_fd);
  /* Close the FRUID binary file */
  fclose(fruid_fd);
error_exit:
  /* Free the eeprom malloced memory */
  free(eeprom);
  /* Free the malloced memory for the fruid information */
  free_fruid_info(&fruid);
  return ret;
}