From 450c26b81abb8be1faabbb50ee3bc79a963baa82 Mon Sep 17 00:00:00 2001 From: "Matsievskiy S.V" Date: Thu, 12 Jan 2023 14:48:38 +0300 Subject: [PATCH 1/4] Fix wrong arguments to fatal macro --- frugen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frugen.c b/frugen.c index 5e8b5dd..6cc2f98 100644 --- a/frugen.c +++ b/frugen.c @@ -192,7 +192,7 @@ bool json_fill_fru_area_fields(json_object *jso, int count, const char *type = json_object_get_string(typefield); const char *val = json_object_get_string(valfield); if (!strcmp("binary", type)) { - fatal(1, "Binary format not yet implemented"); + fatal("Binary format not yet implemented"); } else if (!strcmp("bcdplus", type)) { fields[i]->type = FIELD_TYPE_BCDPLUS; } else if (!strcmp("6bitascii", type)) { From ad885f0636387dba640248806106a86667d50ae3 Mon Sep 17 00:00:00 2001 From: "Matsievskiy S.V" Date: Thu, 12 Jan 2023 14:48:06 +0300 Subject: [PATCH 2/4] Fix field comment line --- fru.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fru.h b/fru.h index 20a6278..a8d62bf 100644 --- a/fru.h +++ b/fru.h @@ -199,7 +199,7 @@ typedef fru_info_area_t fru_chassis_area_t; typedef struct fru_board_area_s { FRU_INFO_AREA_HEADER; - uint8_t mfgdate[3]; ///< Manufacturing date/time in seconds since 1996/1/1 0:00 + uint8_t mfgdate[3]; ///< Manufacturing date/time in minutes since 1996/1/1 0:00 uint8_t data[]; ///< Variable size (multiple of 8 bytes) data with tail padding and checksum } fru_board_area_t; From f57e093d269e12670828d00d7a6b3a2773f5733d Mon Sep 17 00:00:00 2001 From: "Matsievskiy S.V" Date: Thu, 12 Jan 2023 14:45:06 +0300 Subject: [PATCH 3/4] Add reader --- fru.c | 450 ++++++++++++++++++++++++++++++++++++++++++++++--------- fru.h | 152 ++++++++++++++++++- frugen.c | 450 ++++++++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 881 insertions(+), 171 deletions(-) diff --git a/fru.c b/fru.c index db08a47..bb624a4 100644 --- a/fru.c +++ b/fru.c @@ -39,11 +39,39 @@ static bool autodetect = true; +const char* enc_names[TOTAL_FIELD_TYPES] = { + [FIELD_TYPE_AUTO] = "auto", + [FIELD_TYPE_BINARY] = "binary", + [FIELD_TYPE_BCDPLUS] = "bcdplus", + [FIELD_TYPE_SIXBITASCII] = "6bitascii", + [FIELD_TYPE_TEXT] = "text" +}; + void fru_set_autodetect(bool enable) { autodetect = enable; } +/** + * Get the FRU date/time base in seconds since UNIX Epoch + * + * According to IPMI FRU Information Storage Definition v1.0, rev 1.3, + * the date/time encoded as zero designates "0:00 hrs 1/1/96", + * see Table 11-1 "BOARD INFO AREA" + * + * @returns The number of seconds from UNIX Epoch to the FRU date/time base + */ +static time_t fru_datetime_base() { + struct tm tm_1996 = { + .tm_year = 96, + .tm_mon = 0, + .tm_mday = 1 + }; + // The argument to mktime is zoneless + return mktime(&tm_1996); +} + + /** * Strip trailing spaces */ @@ -219,25 +247,32 @@ static fru_field_t *fru_encode_6bit(const unsigned char *s /**< [in] Input strin } /** - * Allocate a buffer and decode a 6-bit ASCII string from it + * @brief Decode a 6-bit ASCII string. + * + * @param[in] field Field to decode. + * @param[out] out Buffer to decode into. + * @param[in] out_len Length of output buffer. + * @retval true Success. + * @retval false Failure. */ -static unsigned char *fru_decode_6bit(const fru_field_t *field) +static +bool fru_decode_6bit(const fru_field_t *field, + uint8_t *out, + size_t out_len) { - unsigned char *out = NULL; const unsigned char *s6; int len, len6bit; int i, i6; - if (!field) return out; + if (!field) return false; len6bit = FRU_FIELDDATALEN(field->typelen); s6 = field->data; len = FRU_6BIT_FULLLENGTH(len6bit); - if (!(out = calloc(1, len + 1))) { - return out; + if (out_len < (len + 1)) { + return false; } - DEBUG("Allocated a destination buffer at %p\n", out); for(i = 0, i6 = 0; i6 <= len6bit && i < len && s6[i6]; i++) { int base = i / 4; @@ -272,11 +307,96 @@ static unsigned char *fru_decode_6bit(const fru_field_t *field) // string that was a byte shorter than a multiple of 4. cut_tail(out); - return out; + return true; +} + +/** + * @brief Decode BCDPLUS string. + * + * @param[in] field Field to decode. + * @param[out] out Buffer to decode into. + * @param[in] out_len Length of output buffer. + * @retval true Success. + * @retval false Failure. + */ +static +bool fru_decode_bcdplus(const fru_field_t *field, + uint8_t *out, + size_t out_len) +{ + int i; + uint8_t c; + if (out_len < 2 * FRU_FIELDDATALEN(field->typelen) + 1) + return false; + /* Copy the data and pack it as BCD */ + for (i = 0; i < 2 * FRU_FIELDDATALEN(field->typelen); i++) { + c = (field->data[i / 2] >> ((i % 2) ? 0 : 4)) & 0x0F; + switch (c) { + case 0xA: + out[i] = ' '; + break; + case 0xB: + out[i] = '-'; + break; + case 0xC: + out[i] = '.'; + break; + case 0xD: + case 0xE: + case 0xF: + out[i] = '?'; + break; + default: // Digits + out[i] = c + '0'; + } + } + out[2 * FRU_FIELDDATALEN(field->typelen)] = 0; // Terminate the string + // Strip trailing spaces that may have emerged when a string of odd + // length was BCD-encoded. + cut_tail(out); + + return true; +} + +/** + * @brief Get a hex string representation of the supplied binary field. + * + * @param[in] field Field to decode. + * @param[out] out Buffer to decode into. + * @param[in] out_len Length of output buffer. + * @retval true Success. + * @retval false Failure. + */ +static +bool fru_decode_binary(const fru_field_t *field, + uint8_t *out, + size_t out_len) +{ + int i; + uint8_t c; + + if ((FRU_FIELDDATALEN(field->typelen) * 2 + 1) > out_len) + return false; + + for (i = 0; i < FRU_FIELDDATALEN(field->typelen); i++) { + c = (field->data[i] & 0xf0) >> 4; + out[2 * i] = c > 9? c - 10 + 'A': c + '0'; + c = field->data[i] & 0xf; + out[2 * i + 1] = c > 9? c - 10 + 'A': c + '0'; + } + out[i * 2 + 1] = '0'; + + return true; } /** - * Allocate a buffer and encode that data as per FRU specification + * @brief Allocate a buffer and encode that data as per FRU specification + * + * @param[in] len Buffer length. + * @param[in] data Binary buffer. + * @param[in] out_len Length of output buffer. + * @retval NULL Failure. + * @return Encoded field. */ fru_field_t * fru_encode_data(int len, const uint8_t *data) { @@ -327,58 +447,29 @@ fru_field_t * fru_encode_data(int len, const uint8_t *data) return out; } -/** - * Allocate a buffer and decode the data from it. - * - * For binary data use FRU_FIELDDATALEN(field->typelen) to find - * out the size of the returned buffer. - */ -static -unsigned char * fru_decode_data(const fru_field_t *field) +bool fru_decode_data(fru_field_t *field, + typed_field_t *out, + size_t out_len) { - unsigned char * out; - - if (!field) return NULL; + if (!field) return false; if (FRU_ISTYPE(field->typelen, ASCII_6BIT)) { - out = fru_decode_6bit(field); - } - else { - out = malloc(FRU_FIELDDATALEN(field->typelen) + 1); - if (!out) return NULL; + out->type = FIELD_TYPE_SIXBITASCII; + return fru_decode_6bit(field, out->val, out_len); + } else { + if (out_len < (FRU_FIELDDATALEN(field->typelen) + 1)) + return false; if (FRU_ISTYPE(field->typelen, BCDPLUS)) { - int i; - uint8_t c; - /* Copy the data and pack it as BCD */ - for (i = 0; i < 2 * FRU_FIELDDATALEN(field->typelen); i++) { - c = (field->data[i / 2] >> ((i % 2) ? 0 : 4)) & 0x0F; - switch(c) { - case 0xA: - out[i] = ' '; - break; - case 0xB: - out[i] = '-'; - break; - case 0xC: - out[i] = '.'; - break; - default: // Digits - out[i] = c + '0'; - } - } - out[2 * FRU_FIELDDATALEN(field->typelen)] = 0; // Terminate the string - // Strip trailing spaces that may have emerged when a string of odd - // length was BCD-encoded. - cut_tail(out); - } - else { - memcpy(out, field->data, FRU_FIELDDATALEN(field->typelen)); - out[FRU_FIELDDATALEN(field->typelen)] = 0; // Terminate the string + out->type = FIELD_TYPE_BCDPLUS; + return fru_decode_bcdplus(field, out->val, out_len); + } else { + out->type = FIELD_TYPE_TEXT; + memcpy(out->val, field->data, FRU_FIELDDATALEN(field->typelen)); + out->val[FRU_FIELDDATALEN(field->typelen)] = 0; // Terminate the string + return true; } } - - return out; } #if 0 @@ -450,11 +541,11 @@ uint8_t fru_area_checksum(fru_info_area_t *area) */ static fru_info_area_t *fru_create_info_area(fru_area_type_t atype, ///< [in] Area type (FRU_[CHASSIS|BOARD|PRODUCT]_INFO) - uint8_t langtype, ///< [in] Language code for areas that use it (board, product) or Chassis Type for chassis info area - const struct timeval *tv, ///< [in] Manufacturing time since the Epoch (1970/01/01 00:00:00 +0000 UTC) for areas that use it (board) - fru_reclist_t *fields, ///< [in] Single-linked list of data fields - size_t nstrings, ///< [in] Number of strings for mandatory fields - const typed_field_t strings[]) ///<[in] Array of typed strings for mandatory fields + uint8_t langtype, ///< [in] Language code for areas that use it (board, product) or Chassis Type for chassis info area + const struct timeval *tv, ///< [in] Manufacturing time since the Epoch (1970/01/01 00:00:00 +0000 UTC) for areas that use it (board) + fru_reclist_t *fields, ///< [in] Single-linked list of data fields + size_t nstrings, ///< [in] Number of strings for mandatory fields + const typed_field_t strings[]) ///<[in] Array of typed strings for mandatory fields { int i = 0; int field_count; @@ -478,13 +569,7 @@ fru_info_area_t *fru_create_info_area(fru_area_type_t atype, ///< [in] Area t if (FRU_AREA_HAS_DATE(atype)) { uint32_t fru_time; - struct tm tm_1996 = { - .tm_year = 96, - .tm_mon = 0, - .tm_mday = 1 - }; const struct timeval tv_unspecified = { 0 }; - struct timeval tv_1996 = { 0 }; if (!tv) { errno = EFAULT; @@ -499,10 +584,8 @@ fru_info_area_t *fru_create_info_area(fru_area_type_t atype, ///< [in] Area t printf("Using FRU_DATE_UNSPECIFIED\n"); fru_time = FRU_DATE_UNSPECIFIED; } else { - // The argument to mktime is zoneless - tv_1996.tv_sec = mktime(&tm_1996); // FRU time is in minutes and we don't care about microseconds - fru_time = (tv->tv_sec - tv_1996.tv_sec) / 60; + fru_time = (tv->tv_sec - fru_datetime_base()) / 60; } header.mfgdate[0] = fru_time & 0xFF; header.mfgdate[1] = (fru_time >> 8) & 0xFF; @@ -590,6 +673,44 @@ fru_info_area_t *fru_create_info_area(fru_area_type_t atype, ///< [in] Area t return out; } +static bool fru_decode_custom_fields(const uint8_t *data, fru_reclist_t **reclist) { + fru_field_t *field = NULL; + + while (true) { + field = (fru_field_t*)data; + + // end of fields + if (field->typelen == FRU_TYPE_EOF) + break; + + fru_reclist_t *custom_field = add_reclist(reclist); + if (custom_field == NULL) + return false; + + size_t length = FRU_FIELDDATALEN(field->typelen); + custom_field->rec = calloc(1, FRU_FIELDMAXARRAY); + custom_field->rec->typelen = field->typelen; + switch (FRU_TYPE(field->typelen)) { + case __TYPE_BINARY: + fru_decode_binary(field, custom_field->rec->data, FRU_FIELDMAXLEN); + break; + case __TYPE_ASCII_6BIT: + fru_decode_6bit(field, custom_field->rec->data, FRU_FIELDMAXLEN); + break; + case __TYPE_BCDPLUS: + fru_decode_bcdplus(field, custom_field->rec->data, FRU_FIELDMAXLEN); + break; + default: + memcpy(custom_field->rec->data, field->data, length); + custom_field->rec->data[length] = 0; // Terminate the string + break; + } + data += length + 1; + } + + return true; +} + /** * Allocate and build a Chassis Information Area block. * @@ -608,7 +729,7 @@ fru_info_area_t *fru_create_info_area(fru_area_type_t atype, ///< [in] Area t * @returns fru_info_area_t *area A newly allocated buffer containing the created area * */ -fru_chassis_area_t * fru_chassis_info(const fru_exploded_chassis_t *chassis) ///< [in] Exploded chassis info area +fru_chassis_area_t * fru_encode_chassis_info(const fru_exploded_chassis_t *chassis) ///< [in] Exploded chassis info area { int i; @@ -637,6 +758,31 @@ fru_chassis_area_t * fru_chassis_info(const fru_exploded_chassis_t *chassis) /// return out; } +bool fru_decode_chassis_info( + const fru_chassis_area_t *area, + fru_exploded_chassis_t *chassis_out +) +{ + chassis_out->type = area->langtype; + const uint8_t *data = area->data; + fru_field_t *field = (fru_field_t*)data; + + if(!fru_decode_data(field, &chassis_out->pn, + sizeof(chassis_out->pn.val))) + return false; + + data += FRU_FIELDSIZE(field->typelen); + field = (fru_field_t*)data; + if (!fru_decode_data(field, &chassis_out->serial, + sizeof(chassis_out->serial.val))) + return false; + data += FRU_FIELDSIZE(field->typelen); + + fru_decode_custom_fields(data, &chassis_out->cust); + + return true; +} + /** * Allocate and build a Board Information Area block. * @@ -655,7 +801,7 @@ fru_chassis_area_t * fru_chassis_info(const fru_exploded_chassis_t *chassis) /// * @returns fru_info_area_t *area A newly allocated buffer containing the created area * */ -fru_board_area_t * fru_board_info(const fru_exploded_board_t *board) ///< [in] Exploded board information area +fru_board_area_t * fru_encode_board_info(const fru_exploded_board_t *board) ///< [in] Exploded board information area { int i; @@ -682,6 +828,61 @@ fru_board_area_t * fru_board_info(const fru_exploded_board_t *board) ///< [in] E return out; } +bool fru_decode_board_info(const fru_board_area_t *area, + fru_exploded_board_t *board_out) +{ + fru_field_t *field; + const uint8_t *data = area->data; + + board_out->lang = area->langtype; + + // NOTE: host is not always little endian! // + union { + uint32_t val; + uint8_t arr[4]; + } min_since_1996_big_endian = { 0 }; + min_since_1996_big_endian.arr[1] = area->mfgdate[2]; + min_since_1996_big_endian.arr[2] = area->mfgdate[1]; + min_since_1996_big_endian.arr[3] = area->mfgdate[0]; + uint32_t min_since_1996 = be32toh(min_since_1996_big_endian.val); + // The argument to mktime is zoneless + board_out->tv.tv_sec = fru_datetime_base() + 60 * min_since_1996; + + field = (fru_field_t*)data; + if (!fru_decode_data(field, &board_out->mfg, + sizeof(board_out->mfg.val))) + return false; + data += FRU_FIELDSIZE(field->typelen); + + field = (fru_field_t*)data; + if (!fru_decode_data(field, &board_out->pname, + sizeof(board_out->pname.val))) + return false; + data += FRU_FIELDSIZE(field->typelen); + + field = (fru_field_t*)data; + if (!fru_decode_data(field, &board_out->serial, + sizeof(board_out->serial.val))) + return false; + data += FRU_FIELDSIZE(field->typelen); + + field = (fru_field_t*)data; + if (!fru_decode_data(field, &board_out->pn, + sizeof(board_out->pn.val))) + return false; + data += FRU_FIELDSIZE(field->typelen); + + field = (fru_field_t*)data; + if (!fru_decode_data(field, &board_out->file, + sizeof(board_out->file.val))) + return false; + data += FRU_FIELDSIZE(field->typelen); + + fru_decode_custom_fields(data, &board_out->cust); + + return true; +} + /** * Allocate and build a Product Information Area block. * @@ -911,6 +1112,63 @@ fru_mr_area_t *fru_mr_area(fru_mr_reclist_t *reclist, size_t *total) return area; } +bool fru_decode_product_info( + const fru_product_area_t *area, + fru_exploded_product_t *product_out +) +{ + fru_field_t *field; + const uint8_t *data = area->data; + + product_out->lang = area->langtype; + + field = (fru_field_t*)data; + if (!fru_decode_data(field, &product_out->mfg, + sizeof(product_out->mfg.val))) + return false; + data += FRU_FIELDSIZE(field->typelen); + + field = (fru_field_t*)data; + if (!fru_decode_data(field, &product_out->pname, + sizeof(product_out->pname.val))) + return false; + data += FRU_FIELDSIZE(field->typelen); + + field = (fru_field_t*)data; + if (!fru_decode_data(field, &product_out->pn, + sizeof(product_out->pn.val))) + return false; + data += FRU_FIELDSIZE(field->typelen); + + field = (fru_field_t*)data; + if (!fru_decode_data(field, &product_out->ver, + sizeof(product_out->ver.val))) + return false; + data += FRU_FIELDSIZE(field->typelen); + + field = (fru_field_t*)data; + if (!fru_decode_data(field, &product_out->serial, + sizeof(product_out->serial.val))) + return false; + data += FRU_FIELDSIZE(field->typelen); + + field = (fru_field_t*)data; + if (!fru_decode_data(field, &product_out->atag, + sizeof(product_out->atag.val))) + return false; + data += FRU_FIELDSIZE(field->typelen); + + field = (fru_field_t*)data; + if (!fru_decode_data(field, &product_out->file, + sizeof(product_out->file.val))) + return false; + data += FRU_FIELDSIZE(field->typelen); + + fru_decode_custom_fields(data, &product_out->cust); + + return true; +} + /** * Create a FRU information file. * @@ -999,6 +1257,53 @@ fru_t * fru_create(fru_area_t area[FRU_MAX_AREAS], size_t *size) return out; } +fru_t *find_fru_header(uint8_t *buffer, size_t size) { + if (size < 8) { + errno = ENOBUFS; + return NULL; + } + fru_t *header = (fru_t *) buffer; + if ((header->ver != FRU_VER_1) || (header->rsvd != 0) || (header->pad != 0)) { + errno = EPROTO; + return NULL; + } + if (header->hchecksum != calc_checksum(header, sizeof(fru_t) - 1)) { + errno = EPROTO; + return NULL; + } + return header; +} + +#define AREA(NAME) \ +fru_##NAME##_area_t *find_fru_##NAME##_area(uint8_t *buffer, size_t size) { \ + fru_t *header = find_fru_header(buffer, size); \ + if ((header == NULL) || (header->NAME == 0)) { \ + return NULL; \ + } \ + if ((header->NAME + 3) > size) { \ + errno = ENOBUFS; \ + return NULL; \ + } \ + fru_##NAME##_area_t *area = \ + (fru_##NAME##_area_t *)(buffer + FRU_BYTES(header->NAME)); \ + if (area->ver != 1) { \ + errno = EPROTO; \ + return NULL; \ + } \ + if (FRU_BYTES(header->NAME) + FRU_BYTES(area->blocks) > size) { \ + errno = ENOBUFS; \ + return NULL; \ + } \ + if (*(((uint8_t *)area) + FRU_BYTES(area->blocks) - 1) != \ + calc_checksum(((uint8_t *)area), FRU_BYTES(area->blocks) - 1)) { \ + errno = EPROTO; \ + return NULL; \ + } \ + return area; \ +} +AREA(chassis); +AREA(board); +AREA(product); #ifdef __STANDALONE__ @@ -1073,8 +1378,8 @@ void test_encodings(void) dump(FRU_FIELDSIZE(field->typelen), (uint8_t *)field); printf("Decoding... "); - out = fru_decode_data(field); - if (!out) { + if (!fru_decode_data(field->&typelen, &field->data,, + FRU_FIELDMAXARRAY)) { printf("FAIL!"); goto next; } @@ -1097,7 +1402,6 @@ void test_encodings(void) printf("FAIL!"); } - free((void *)out); next: free((void *)field); printf("\n\n"); diff --git a/fru.h b/fru.h index a8d62bf..42d86e9 100644 --- a/fru.h +++ b/fru.h @@ -182,6 +182,7 @@ static inline fru_reclist_t *add_reclist(fru_reclist_t **reclist) #define LANG_DEFAULT 0 #define LANG_ENGLISH 25 +#define FRU_TYPE_EOF 0xc1 typedef struct fru_info_area_s { // The generic info area structure FRU_INFO_AREA_HEADER; @@ -273,14 +274,23 @@ typedef fru_mr_rec_t fru_mr_area_t; /// Intended for use as a pointer only #define FRU_BYTES(blocks) ((blocks) * FRU_BLOCK_SZ) #define FRU_BLOCKS(bytes) (((bytes) + FRU_BLOCK_SZ - 1) / FRU_BLOCK_SZ) +typedef enum { + FIELD_TYPE_AUTO, + FIELD_TYPE_BINARY = (__TYPE_BINARY + 1), + BASE_FIELD_TYPE = FIELD_TYPE_BINARY, + FIELD_TYPE_BCDPLUS = (__TYPE_BCDPLUS + 1), + FIELD_TYPE_SIXBITASCII = (__TYPE_ASCII_6BIT + 1), + FIELD_TYPE_TEXT = (__TYPE_TEXT + 1), + TOTAL_FIELD_TYPES +} field_type_t; + +extern const char* enc_names[TOTAL_FIELD_TYPES]; + +/// Extract FRU field type as field_type_t +#define FIELD_TYPE_T(t) (FRU_TYPE(t) + BASE_FIELD_TYPE) + typedef struct { - enum { - FIELD_TYPE_AUTO, - FIELD_TYPE_BINARY, - FIELD_TYPE_BCDPLUS, - FIELD_TYPE_SIXBITASCII, - FIELD_TYPE_TEXT - } type; + field_type_t type; unsigned char val[FRU_FIELDMAXARRAY]; } typed_field_t; @@ -326,7 +336,135 @@ int fru_mr_uuid2rec(fru_mr_rec_t **rec, const unsigned char *str); fru_mr_reclist_t * add_mr_reclist(fru_mr_reclist_t **reclist); fru_mr_area_t * fru_mr_area(fru_mr_reclist_t *reclist, size_t *total); +/** + * @brief Encode chassis info into binary buffer. + * + * Binary buffer needs to be freed after use. + * + * @param[in] chassis Area info. + * @return Encoded area buffer. + * @retval NULL Encoding failed. \p errno is set accordingly. + */ +fru_chassis_area_t * fru_encode_chassis_info(const fru_exploded_chassis_t *chassis); + +/** + * @brief Encode board info into binary buffer. + * + * Binary buffer needs to be freed after use. + * + * @param[in] board Area info. + * @return Encoded area buffer. + * @retval NULL Encoding failed. \p errno is set accordingly. + */ +fru_board_area_t * fru_encode_board_info(const fru_exploded_board_t *board); + +/** + * @brief Encode product info into binary buffer. + * + * Binary buffer needs to be freed after use. + * + * @param[in] product Area info. + * @return Encoded area buffer. + * @retval NULL Encoding failed. \p errno is set accordingly. + */ +fru_product_area_t * fru_encode_product_info(const fru_exploded_product_t *product); + +/** + * @brief Encode data field. + * + * Binary buffer needs to be freed after use. + * + * @param[in] len Binary buffer length. + * @param[in] data Binary buffer. + * @return Encoded field buffer. + * @retval NULL Encoding error. \p errno is set accordingly. + */ fru_field_t * fru_encode_data(int len, const uint8_t *data); + fru_t * fru_create(fru_area_t area[FRU_MAX_AREAS], size_t *size); +/** + * @brief Find and validate FRU header in the byte buffer. + * + * @param[in] buffer Byte buffer. + * @param[in] size Byte buffer size. + * @return Pointer to the FRU header in the buffer. + * @retval NULL FRU header not found. \p errno is set accordingly. + */ +fru_t *find_fru_header(uint8_t *buffer, size_t size); + +/** + * @brief Find and validate FRU chassis area in the byte buffer. + * + * @param[in] buffer Byte buffer. + * @param[in] size Byte buffer size. + * @return Pointer to the FRU chassis area in the buffer. + * @retval NULL FRU chassis area not found. \p errno is set accordingly. + */ +fru_chassis_area_t *find_fru_chassis_area(uint8_t *buffer, size_t size); + +/** + * @brief Find and validate FRU board area in the byte buffer. + * + * @param[in] buffer Byte buffer. + * @param[in] size Byte buffer size. + * @return Pointer to the FRU board area in the buffer. + * @retval NULL FRU board area not found. \p errno is set accordingly. + */ +fru_board_area_t *find_fru_board_area(uint8_t *buffer, size_t size); + +/** + * @brief Find and validate FRU product area in the byte buffer. + * + * @param[in] buffer Byte buffer. + * @param[in] size Byte buffer size. + * @return Pointer to the FRU product area in the buffer. + * @retval NULL FRU product area not found. \p errno is set accordingly. + */ +fru_product_area_t *find_fru_product_area(uint8_t *buffer, size_t size); + +/** + * @brief Decode chassis area into \p fru_exploded_chassis_t. + * + * @param[in] area Encoded area. + * @param[out] chassis_out Decoded structure. + * @retval true Success. + * @retval false Failure. + */ +bool fru_decode_chassis_info(const fru_chassis_area_t *area, fru_exploded_chassis_t *chassis_out); + +/** + * @brief Decode board area into \p fru_exploded_board_t. + * + * @param[in] area Encoded area. + * @param[out] chassis_out Decoded structure. + * @retval true Success. + * @retval false Failure. + */ +bool fru_decode_board_info(const fru_board_area_t *area, fru_exploded_board_t *board_out); + +/** + * @brief Decode product area into \p fru_product_board_t. + * + * @param[in] area Encoded area. + * @param[out] chassis_out Decoded structure. + * @retval true Success. + * @retval false Failure. + */ +bool fru_decode_product_info(const fru_product_area_t *area, fru_exploded_product_t *product_out); + +/** + * Decode data from a buffer into another buffer. + * + * For binary data use FRU_FIELDDATALEN(field->typelen) to find + * out the size of valid bytes in the returned buffer. + * + * @param[in] field Encoded data field. + * @param[out] out Decoded field. + * @param[in] out_len Size of the decoded field region. + * @retval true Success. + * @retval false Failure. + */ +bool fru_decode_data(fru_field_t *field, typed_field_t *out, size_t out_len); + #endif // __FRULIB_FRU_H__ diff --git a/frugen.c b/frugen.c index 6cc2f98..cd2bd83 100644 --- a/frugen.c +++ b/frugen.c @@ -9,6 +9,7 @@ #endif #define COPYRIGHT_YEARS "2016-2021" +#define MAX_FILE_SIZE 1L * 1024L * 1024L #define _GNU_SOURCE #include @@ -22,6 +23,7 @@ #include #include #include +#include #include "fru.h" #include "smbios.h" @@ -47,6 +49,14 @@ volatile int debug_level = 0; } \ } while(0) +static +int typelen2ind(uint8_t field) { + if (FIELD_TYPE_T(field) < TOTAL_FIELD_TYPES) + return FIELD_TYPE_T(field); + else + return FIELD_TYPE_AUTO; +} + static void hexdump(const void *data, size_t len) { @@ -174,6 +184,35 @@ fru_field_t * fru_encode_custom_binary_field(const char *hexstr) } #ifdef __HAS_JSON__ + +#if (JSON_C_MAJOR_VERSION == 0 && JSON_C_MINOR_VERSION < 13) +int +json_object_to_fd(int fd, struct json_object *obj, int flags) +{ + // implementation is copied from json-c v0.13 + int ret; + const char *json_str; + unsigned int wpos, wsize; + + if (!(json_str = json_object_to_json_string_ext(obj,flags))) { + return -1; + } + + wsize = (unsigned int)(strlen(json_str) & UINT_MAX); /* CAW: probably unnecessary, but the most 64bit safe */ + wpos = 0; + while(wpos < wsize) { + if((ret = write(fd, json_str + wpos, wsize-wpos)) < 0) { + return -1; + } + + /* because of the above check for ret < 0, we can safely cast and add */ + wpos += (unsigned int)ret; + } + + return 0; +} +#endif + static bool json_fill_fru_area_fields(json_object *jso, int count, const char *fieldnames[], @@ -379,6 +418,39 @@ bool json_fill_fru_mr_reclist(json_object *jso, fru_mr_reclist_t **mr_reclist) return has_multirec; } +static +int json_object_add_with_type(struct json_object* obj, + const char* key, + const unsigned char* val, + int type) { + struct json_object *string, *type_string, *entry; + if ((string = json_object_new_string(val)) == NULL) + goto STRING_ERR; + + if (type == FIELD_TYPE_AUTO) { + entry = string; + } else { + if ((type_string = json_object_new_string(enc_names[type])) == NULL) + goto TYPE_STRING_ERR; + if ((entry = json_object_new_object()) == NULL) + goto ENTRY_ERR; + json_object_object_add(entry, "type", type_string); + json_object_object_add(entry, "data", string); + } + if (key == NULL) { + return json_object_array_add(obj, entry); + } else { + json_object_object_add(obj, key, entry); + return 0; + } + +ENTRY_ERR: + json_object_put(type_string); +TYPE_STRING_ERR: + json_object_put(string); +STRING_ERR: + return -1; +} #endif /* __HAS_JSON__ */ @@ -431,6 +503,9 @@ int main(int argc, char *argv[]) /* Set input file format to JSON */ { .name = "json", .val = 'j', .has_arg = false }, + /* Set input file format to raw binary */ + { .name = "raw", .val = 'r', .has_arg = false }, + /* Set file to load the data from */ { .name = "from", .val = 'z', .has_arg = true }, @@ -473,6 +548,7 @@ int main(int argc, char *argv[]) ['I'] = "Disable auto-encoding on all fields, force ASCII.\n\t\t" "Out of ASCII range data will still result in binary encoding", ['j'] = "Set input text file format to JSON (default). Specify before '--from'", + ['r'] = "Set input file format to raw binary. Specify before '--from'", ['z'] = "Load FRU information from a text file", /* Chassis info area related options */ ['t'] = "Set chassis type (hex). Defaults to 0x02 ('Unknown')", @@ -510,7 +586,8 @@ int main(int argc, char *argv[]) has_multirec = false; fru_mr_reclist_t *mr_reclist = NULL; - bool use_json = true; /* TODO: Add more input formats, consider libconfig */ + bool use_json = false; /* TODO: Add more input formats, consider libconfig */ + bool use_binary = false; unsigned char optstring[ARRAY_SZ(options) * 2 + 1] = {0}; @@ -567,6 +644,16 @@ int main(int argc, char *argv[]) case 'j': // json use_json = true; + if (use_binary) { + fatal("Can't specify --json and --raw together"); + } + break; + + case 'r': // binary + use_binary = true; + if (use_json) { + fatal("Can't specify --json and --raw together"); + } break; case 'z': // from @@ -674,8 +761,57 @@ int main(int argc, char *argv[]) fatal("JSON support was disabled at compile time"); #endif } + else if (use_binary) { + int fd = open(optarg, O_RDONLY); + if (fd < 0) { + fatal("Failed to open file: %s", strerror(errno)); + } + + struct stat statbuf = {0}; + if (fstat(fd, &statbuf)) { + fatal("Failed to get file properties: %s", strerror(errno)); + } + if (statbuf.st_size > MAX_FILE_SIZE) { + fatal("File too large"); + } + + uint8_t *buffer = calloc(1, statbuf.st_size); + if (buffer == NULL) { + fatal("Cannot allocate buffer"); + } + + if (read(fd, buffer, statbuf.st_size) != statbuf.st_size) { + fatal("Cannot read file"); + } + close(fd); + + fru_chassis_area_t *chassis_area = + find_fru_chassis_area(buffer, statbuf.st_size); + if (chassis_area) { + if (!fru_decode_chassis_info(chassis_area, &chassis)) + fatal("Failed to decode chassis"); + has_chassis = true; + } + fru_board_area_t *board_area = + find_fru_board_area(buffer, statbuf.st_size); + if (board_area) { + if (!fru_decode_board_info(board_area, &board)) + fatal("Failed to decode board"); + has_board = true; + } + + fru_product_area_t *product_area = + find_fru_product_area(buffer, statbuf.st_size); + if (product_area) { + if (!fru_decode_product_info(product_area, &product)) + fatal("Failed to decode product"); + has_product = true; + } + + free(buffer); + } else { - fatal("The requested input file format is not supported"); + fatal("Please specify the input file format"); } break; @@ -816,120 +952,252 @@ int main(int argc, char *argv[]) } } while (opt != -1); - if (optind >= argc) - fatal("Filename must be specified"); - - fname = argv[optind]; - debug(1, "FRU info data will be stored in %s", fname); + if (use_binary) { + char timebuf[20] = {0}; + struct tm* bdtime = gmtime(&board.tv.tv_sec); + strftime(timebuf, 20, "%d/%m/%Y %H:%M:%S", bdtime); +#ifdef __HAS_JSON__ + struct json_object *json_root = json_object_new_object(); + struct json_object *section = NULL, *temp_obj = NULL; + + if (has_chassis) { + section = json_object_new_object(); + temp_obj = json_object_new_int(chassis.type); + json_object_object_add(section, "type", temp_obj); + json_object_add_with_type(section, "pn", chassis.pn.val, chassis.pn.type); + json_object_add_with_type(section, "serial", chassis.serial.val, chassis.serial.type); + temp_obj = json_object_new_array(); + fru_reclist_t *next = chassis.cust; + while (next != NULL) { + json_object_add_with_type(temp_obj, NULL, next->rec->data, + typelen2ind(next->rec->typelen)); + next = next->next; + } + json_object_object_add(section, "custom", temp_obj); + json_object_object_add(json_root, "chassis", section); + } - if (has_internal) { - debug(1, "FRU file will have an internal use area"); - /* Nothing to do here, added for uniformity, the actual - * internal use area can only be initialized from file */ - } + if (has_product) { + section = json_object_new_object(); + temp_obj = json_object_new_int(product.lang); + json_object_object_add(section, "lang", temp_obj); + json_object_add_with_type(section, "mfg", product.mfg.val, product.mfg.type); + json_object_add_with_type(section, "pname", product.pname.val, product.pname.type); + json_object_add_with_type(section, "serial", product.serial.val, product.serial.type); + json_object_add_with_type(section, "pn", product.pn.val, product.pn.type); + json_object_add_with_type(section, "ver", product.ver.val, product.ver.type); + json_object_add_with_type(section, "atag", product.atag.val, product.atag.type); + json_object_add_with_type(section, "file", product.file.val, product.file.type); + temp_obj = json_object_new_array(); + fru_reclist_t *next = product.cust; + while (next != NULL) { + json_object_add_with_type(temp_obj, NULL, next->rec->data, + typelen2ind(next->rec->typelen)); + next = next->next; + } + json_object_object_add(section, "custom", temp_obj); + json_object_object_add(json_root, "product", section); + } - if (has_chassis) { - int e; - fru_chassis_area_t *ci = NULL; - debug(1, "FRU file will have a chassis information area"); - debug(3, "Chassis information area's custom field list is %p", chassis.cust); - ci = fru_chassis_info(&chassis); - e = errno; - free_reclist(chassis.cust); - - if (ci) - areas[FRU_CHASSIS_INFO].data = ci; - else { - errno = e; - fatal("Error allocating a chassis info area: %m"); + if (has_board) { + section = json_object_new_object(); + temp_obj = json_object_new_int(board.lang); + json_object_object_add(section, "lang", temp_obj); + json_object_add_with_type(section, "time", (unsigned char*) timebuf, 0); + json_object_add_with_type(section, "mfg", board.mfg.val, board.mfg.type); + json_object_add_with_type(section, "pname", board.pname.val, board.pname.type); + json_object_add_with_type(section, "serial", board.serial.val, board.serial.type); + json_object_add_with_type(section, "pn", board.pn.val, board.pn.type); + json_object_add_with_type(section, "file", board.file.val, board.file.type); + temp_obj = json_object_new_array(); + fru_reclist_t *next = board.cust; + while (next != NULL) { + json_object_add_with_type(temp_obj, NULL, next->rec->data, + typelen2ind(next->rec->typelen)); + next = next->next; + } + json_object_object_add(section, "custom", temp_obj); + json_object_object_add(json_root, "board", section); + } + if (has_multirec) { + // TODO: add multirec dump + } + // dump to stdout + json_object_to_fd(fileno(stdout), json_root, 0); + json_object_put(json_root); +#else + if (has_chassis) { + puts("Chassis"); + printf("\ttype: %u\n", chassis.type); + printf("\tpn(%s): %s\n", enc_names[chassis.pn.type], chassis.pn.val); + printf("\tserial(%s): %s\n", enc_names[chassis.serial.type], chassis.serial.val); + fru_reclist_t *next = chassis.cust; + while (next != NULL) { + printf("\tcustom(%s): %s\n", + enc_names[typelen2ind(next->rec->typelen)], + next->rec->data); + next = next->next; + } } - } - if (has_board) { - int e; - fru_board_area_t *bi = NULL; - debug(1, "FRU file will have a board information area"); - debug(3, "Board information area's custom field list is %p", board.cust); - debug(3, "Board date is specified? = %d", has_bdate); - debug(3, "Board date use unspec? = %d", no_curr_date); - if (!has_bdate && no_curr_date) { - debug(1, "Using 'unspecified' board mfg. date"); - board.tv = (struct timeval){0}; + if (has_product) { + puts("Product"); + printf("\tlang: %u\n", product.lang); + printf("\tmfg(%s): %s\n", enc_names[product.mfg.type], product.mfg.val); + printf("\tpname(%s): %s\n", enc_names[product.pname.type], product.pname.val); + printf("\tserial(%s): %s\n", enc_names[product.serial.type], product.serial.val); + printf("\tpn(%s): %s\n", enc_names[product.pn.type], product.pn.val); + printf("\tver(%s): %s\n", enc_names[product.ver.type], product.ver.val); + printf("\tatag(%s): %s\n", enc_names[product.atag.type], product.atag.val); + printf("\tfile(%s): %s\n", enc_names[product.file.type], product.file.val); + fru_reclist_t *next = product.cust; + while (next != NULL) { + printf("\tcustom(%s): %s\n", + enc_names[typelen2ind(next->rec->typelen)], + next->rec->data); + next = next->next; + } } - bi = fru_board_info(&board); - e = errno; - free_reclist(board.cust); + if (has_board) { + puts("Board"); + printf("\tlang: %u\n", board.lang); + printf("\ttime: %s\n", timebuf); + printf("\tmfg(%s): %s\n", enc_names[board.mfg.type], board.mfg.val); + printf("\tpname(%s): %s\n", enc_names[board.pname.type], board.pname.val); + printf("\tserial(%s): %s\n", enc_names[board.serial.type], board.serial.val); + printf("\tpn(%s): %s\n", enc_names[board.pn.type], board.pn.val); + printf("\tfile(%s): %s\n", enc_names[board.file.type], board.file.val); + fru_reclist_t *next = board.cust; + while (next != NULL) { + printf("\tcustom(%s): %s\n", + enc_names[typelen2ind(next->rec->typelen)], + next->rec->data); + next = next->next; + } + } - if (bi) - areas[FRU_BOARD_INFO].data = bi; - else { - errno = e; - fatal("Error allocating a board info area: %m"); + if (has_multirec) { + // TODO: add multirec dump } - } +#endif + } else { + if (optind >= argc) + fatal("Filename must be specified"); - if (has_product) { - int e; - fru_product_area_t *pi = NULL; - debug(1, "FRU file will have a product information area"); - debug(3, "Product information area's custom field list is %p", product.cust); - pi = fru_product_info(&product); + fname = argv[optind]; + debug(1, "FRU info data will be stored in %s", fname); - e = errno; - free_reclist(product.cust); + if (has_internal) { + debug(1, "FRU file will have an internal use area"); + /* Nothing to do here, added for uniformity, the actual + * internal use area can only be initialized from file */ + } - if (pi) - areas[FRU_PRODUCT_INFO].data = pi; - else { - errno = e; - fatal("Error allocating a product info area: %m"); + if (has_chassis) { + int e; + fru_chassis_area_t *ci = NULL; + debug(1, "FRU file will have a chassis information area"); + debug(3, "Chassis information area's custom field list is %p", chassis.cust); + ci = fru_encode_chassis_info(&chassis); + e = errno; + free_reclist(chassis.cust); + + if (ci) + areas[FRU_CHASSIS_INFO].data = ci; + else { + errno = e; + fatal("Error allocating a chassis info area: %m"); + } } - } - if (has_multirec) { - int e; - fru_mr_area_t *mr = NULL; - size_t totalbytes = 0; - debug(1, "FRU file will have a multirecord area"); - debug(3, "Multirecord area record list is %p", mr_reclist); - mr = fru_mr_area(mr_reclist, &totalbytes); + if (has_board) { + int e; + fru_board_area_t *bi = NULL; + debug(1, "FRU file will have a board information area"); + debug(3, "Board information area's custom field list is %p", board.cust); + debug(3, "Board date is specified? = %d", has_bdate); + debug(3, "Board date use unspec? = %d", no_curr_date); + if (!has_bdate && no_curr_date) { + debug(1, "Using 'unspecified' board mfg. date"); + board.tv = (struct timeval){0}; + } + + bi = fru_encode_board_info(&board); + e = errno; + free_reclist(board.cust); - e = errno; - free_reclist(mr_reclist); + if (bi) + areas[FRU_BOARD_INFO].data = bi; + else { + errno = e; + fatal("Error allocating a board info area: %m"); + } + } - if (mr) { - areas[FRU_MULTIRECORD].data = mr; - areas[FRU_MULTIRECORD].blocks = FRU_BLOCKS(totalbytes); + if (has_product) { + int e; + fru_product_area_t *pi = NULL; + debug(1, "FRU file will have a product information area"); + debug(3, "Product information area's custom field list is %p", product.cust); + pi = fru_encode_product_info(&product); - debug_dump(3, mr, totalbytes, "Multirecord data:"); + e = errno; + free_reclist(product.cust); + + if (pi) + areas[FRU_PRODUCT_INFO].data = pi; + else { + errno = e; + fatal("Error allocating a product info area: %m"); + } } - else { - errno = e; - fatal("Error allocating a multirecord area: %m"); + + if (has_multirec) { + int e; + fru_mr_area_t *mr = NULL; + size_t totalbytes = 0; + debug(1, "FRU file will have a multirecord area"); + debug(3, "Multirecord area record list is %p", mr_reclist); + mr = fru_mr_area(mr_reclist, &totalbytes); + + e = errno; + free_reclist(mr_reclist); + + if (mr) { + areas[FRU_MULTIRECORD].data = mr; + areas[FRU_MULTIRECORD].blocks = FRU_BLOCKS(totalbytes); + + debug_dump(3, mr, totalbytes, "Multirecord data:"); + } + else { + errno = e; + fatal("Error allocating a multirecord area: %m"); + } } - } - fru = fru_create(areas, &size); - if (!fru) { - fatal("Error allocating a FRU file buffer: %m"); - } + fru = fru_create(areas, &size); + if (!fru) { + fatal("Error allocating a FRU file buffer: %m"); + } - debug(1, "Writing %lu bytes of FRU data", (long unsigned int)FRU_BYTES(size)); + debug(1, "Writing %lu bytes of FRU data", (long unsigned int)FRU_BYTES(size)); - fd = open(fname, + fd = open(fname, #if __WIN32__ || __WIN64__ - O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, + O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, #else - O_CREAT | O_TRUNC | O_WRONLY, + O_CREAT | O_TRUNC | O_WRONLY, #endif - 0644); + 0644); - if (fd < 0) - fatal("Couldn't create file %s: %m", fname); + if (fd < 0) + fatal("Couldn't create file %s: %m", fname); - if (0 > write(fd, fru, FRU_BYTES(size))) - fatal("Couldn't write to %s: %m", fname); + if (0 > write(fd, fru, FRU_BYTES(size))) + fatal("Couldn't write to %s: %m", fname); - free(fru); + free(fru); + close(fd); + } } From 9abaf7067f3c5430a5c7963203d9c7f3680b677e Mon Sep 17 00:00:00 2001 From: "Matsievskiy S.V" Date: Thu, 12 Jan 2023 14:46:52 +0300 Subject: [PATCH 4/4] Reindent files --- fru.c | 99 ++++++++++++++++++++++++++++---------------------------- frugen.c | 30 ++++++++--------- 2 files changed, 64 insertions(+), 65 deletions(-) diff --git a/fru.c b/fru.c index bb624a4..6cf8841 100644 --- a/fru.c +++ b/fru.c @@ -96,11 +96,11 @@ static inline uint8_t fru_field_copy(void *dest, const fru_field_t *fieldp) * @retval FRU_FIELD_TERMINATOR The data exceeded the maximum length (63 bytes) * */ -static -uint8_t fru_get_typelen(int len, /**< [in] Length of the data, - LEN_AUTO for pure text zero-terminated data or - one of LEN_BCDPLUS, LEN_6BITASCII, LEN_TEXT for explicit text type */ - const uint8_t *data) /**< [in] The input data */ +static uint8_t +fru_get_typelen(int len, /**< [in] Length of the data, + LEN_AUTO for pure text zero-terminated data or + one of LEN_BCDPLUS, LEN_6BITASCII, LEN_TEXT for explicit text type */ + const uint8_t *data) /**< [in] The input data */ { uint8_t typelen = len; int i; @@ -111,16 +111,16 @@ uint8_t fru_get_typelen(int len, /**< [in] Length of the data, if (len < 0) { DEBUG("Forcing string '%s' to ...\n", (char *)data); // Explicit text type - if (len == LEN_BCDPLUS) { + if (len == LEN_BCDPLUS) { DEBUG("BCDPLUS type\n"); return FRU_TYPELEN(BCDPLUS, (strlen(data) + 1) / 2); - } else if (len == LEN_6BITASCII) { + } else if (len == LEN_6BITASCII) { DEBUG("6BIT ASCII type\n"); return FRU_TYPELEN(ASCII_6BIT, FRU_6BIT_LENGTH(strlen(data))); - } else if (len == LEN_TEXT) { + } else if (len == LEN_TEXT) { DEBUG("ASCII type\n"); return FRU_TYPELEN(TEXT, strlen(data)); - } else { + } else { DEBUG("Nothing... Unknown text type\n"); return FRU_FIELD_TERMINATOR; } @@ -162,9 +162,9 @@ uint8_t fru_get_typelen(int len, /**< [in] Length of the data, // Go through the data and expand charset as needed for (i = 0; i < len; i++) { if (data[i] < ' ' - && data[i] != '\t' - && data[i] != '\r' - && data[i] != '\n') + && data[i] != '\t' + && data[i] != '\r' + && data[i] != '\n') { // They lied! The data is binary! // That's the widest range type. @@ -176,7 +176,7 @@ uint8_t fru_get_typelen(int len, /**< [in] Length of the data, if (autodetect) { if (typelen < FRU_MAKETYPE(TEXT) - && (data[i] > '_' || data[i] < ' ')) + && (data[i] > '_' || data[i] < ' ')) { // Do not reduce the range // The data doesn't fit into 6-bit ASCII, expand to simple text. DEBUG("[%c] Data is simple text!\n", data[i]); @@ -211,8 +211,7 @@ static fru_field_t *fru_encode_6bit(const unsigned char *s /**< [in] Input strin fru_field_t *out = NULL; size_t outlen = sizeof(fru_field_t) + len6bit + 1; // 1 extra for null-byte - if (len6bit > FRU_FIELDDATALEN(len6bit) || - !(out = calloc(1, outlen))) + if (len6bit > FRU_FIELDDATALEN(len6bit) || !(out = calloc(1, outlen))) { return out; } @@ -321,8 +320,8 @@ bool fru_decode_6bit(const fru_field_t *field, */ static bool fru_decode_bcdplus(const fru_field_t *field, - uint8_t *out, - size_t out_len) + uint8_t *out, + size_t out_len) { int i; uint8_t c; @@ -369,8 +368,8 @@ bool fru_decode_bcdplus(const fru_field_t *field, */ static bool fru_decode_binary(const fru_field_t *field, - uint8_t *out, - size_t out_len) + uint8_t *out, + size_t out_len) { int i; uint8_t c; @@ -448,8 +447,8 @@ fru_field_t * fru_encode_data(int len, const uint8_t *data) } bool fru_decode_data(fru_field_t *field, - typed_field_t *out, - size_t out_len) + typed_field_t *out, + size_t out_len) { if (!field) return false; @@ -541,11 +540,11 @@ uint8_t fru_area_checksum(fru_info_area_t *area) */ static fru_info_area_t *fru_create_info_area(fru_area_type_t atype, ///< [in] Area type (FRU_[CHASSIS|BOARD|PRODUCT]_INFO) - uint8_t langtype, ///< [in] Language code for areas that use it (board, product) or Chassis Type for chassis info area - const struct timeval *tv, ///< [in] Manufacturing time since the Epoch (1970/01/01 00:00:00 +0000 UTC) for areas that use it (board) - fru_reclist_t *fields, ///< [in] Single-linked list of data fields - size_t nstrings, ///< [in] Number of strings for mandatory fields - const typed_field_t strings[]) ///<[in] Array of typed strings for mandatory fields + uint8_t langtype, ///< [in] Language code for areas that use it (board, product) or Chassis Type for chassis info area + const struct timeval *tv, ///< [in] Manufacturing time since the Epoch (1970/01/01 00:00:00 +0000 UTC) for areas that use it (board) + fru_reclist_t *fields, ///< [in] Single-linked list of data fields + size_t nstrings, ///< [in] Number of strings for mandatory fields + const typed_field_t strings[]) ///<[in] Array of typed strings for mandatory fields { int i = 0; int field_count; @@ -768,13 +767,13 @@ bool fru_decode_chassis_info( fru_field_t *field = (fru_field_t*)data; if(!fru_decode_data(field, &chassis_out->pn, - sizeof(chassis_out->pn.val))) + sizeof(chassis_out->pn.val))) return false; data += FRU_FIELDSIZE(field->typelen); field = (fru_field_t*)data; if (!fru_decode_data(field, &chassis_out->serial, - sizeof(chassis_out->serial.val))) + sizeof(chassis_out->serial.val))) return false; data += FRU_FIELDSIZE(field->typelen); @@ -829,7 +828,7 @@ fru_board_area_t * fru_encode_board_info(const fru_exploded_board_t *board) ///< } bool fru_decode_board_info(const fru_board_area_t *area, - fru_exploded_board_t *board_out) + fru_exploded_board_t *board_out) { fru_field_t *field; const uint8_t *data = area->data; @@ -850,31 +849,31 @@ bool fru_decode_board_info(const fru_board_area_t *area, field = (fru_field_t*)data; if (!fru_decode_data(field, &board_out->mfg, - sizeof(board_out->mfg.val))) + sizeof(board_out->mfg.val))) return false; data += FRU_FIELDSIZE(field->typelen); field = (fru_field_t*)data; if (!fru_decode_data(field, &board_out->pname, - sizeof(board_out->pname.val))) + sizeof(board_out->pname.val))) return false; data += FRU_FIELDSIZE(field->typelen); field = (fru_field_t*)data; if (!fru_decode_data(field, &board_out->serial, - sizeof(board_out->serial.val))) + sizeof(board_out->serial.val))) return false; data += FRU_FIELDSIZE(field->typelen); field = (fru_field_t*)data; if (!fru_decode_data(field, &board_out->pn, - sizeof(board_out->pn.val))) + sizeof(board_out->pn.val))) return false; data += FRU_FIELDSIZE(field->typelen); field = (fru_field_t*)data; if (!fru_decode_data(field, &board_out->file, - sizeof(board_out->file.val))) + sizeof(board_out->file.val))) return false; data += FRU_FIELDSIZE(field->typelen); @@ -901,7 +900,7 @@ bool fru_decode_board_info(const fru_board_area_t *area, * @returns fru_info_area_t *area A newly allocated buffer containing the created area * */ -fru_product_area_t * fru_product_info(const fru_exploded_product_t *product) ///< [in] Exploded product information area +fru_product_area_t * fru_encode_product_info(const fru_exploded_product_t *product) ///< [in] Exploded product information area { int i; @@ -920,10 +919,11 @@ fru_product_area_t * fru_product_info(const fru_exploded_product_t *product) /// [FRU_PROD_FILE] = { NULL, product->cust }, }; - const typed_field_t strings[] = { product->mfg, product->pname, - product->pn, product->ver, - product->serial, product->atag, - product->file }; + const typed_field_t strings[] = { + product->mfg, product->pname, + product->pn, product->ver, + product->serial, product->atag, + product->file }; fru_product_area_t *out = NULL; out = fru_create_info_area(FRU_PRODUCT_INFO, @@ -1124,43 +1124,43 @@ bool fru_decode_product_info( field = (fru_field_t*)data; if (!fru_decode_data(field, &product_out->mfg, - sizeof(product_out->mfg.val))) + sizeof(product_out->mfg.val))) return false; data += FRU_FIELDSIZE(field->typelen); field = (fru_field_t*)data; if (!fru_decode_data(field, &product_out->pname, - sizeof(product_out->pname.val))) + sizeof(product_out->pname.val))) return false; data += FRU_FIELDSIZE(field->typelen); field = (fru_field_t*)data; if (!fru_decode_data(field, &product_out->pn, - sizeof(product_out->pn.val))) + sizeof(product_out->pn.val))) return false; data += FRU_FIELDSIZE(field->typelen); field = (fru_field_t*)data; if (!fru_decode_data(field, &product_out->ver, - sizeof(product_out->ver.val))) + sizeof(product_out->ver.val))) return false; data += FRU_FIELDSIZE(field->typelen); field = (fru_field_t*)data; if (!fru_decode_data(field, &product_out->serial, - sizeof(product_out->serial.val))) + sizeof(product_out->serial.val))) return false; data += FRU_FIELDSIZE(field->typelen); field = (fru_field_t*)data; if (!fru_decode_data(field, &product_out->atag, - sizeof(product_out->atag.val))) + sizeof(product_out->atag.val))) return false; data += FRU_FIELDSIZE(field->typelen); field = (fru_field_t*)data; if (!fru_decode_data(field, &product_out->file, - sizeof(product_out->file.val))) + sizeof(product_out->file.val))) return false; data += FRU_FIELDSIZE(field->typelen); @@ -1211,7 +1211,7 @@ fru_t * fru_create(fru_area_t area[FRU_MAX_AREAS], size_t *size) if(!data || // No data is provided or !FRU_AREA_HAS_SIZE(atype) && !blocks || // no size is given for a non-sized area or !((fru_info_area_t *)data)->blocks // the sized area contains a zero size - ) { + ) { // Mark the area as *offset = 0; continue; @@ -1248,8 +1248,7 @@ fru_t * fru_create(fru_area_t area[FRU_MAX_AREAS], size_t *size) if (!blocks) continue; DEBUG("copying %d bytes of area of type %d to offset 0x%03X (0x%03lX)\n", - FRU_BYTES(blocks), atype, FRU_BYTES(*offset), dst - (uint8_t *)out - ); + FRU_BYTES(blocks), atype, FRU_BYTES(*offset), dst - (uint8_t *)out); memcpy(dst, data, FRU_BYTES(blocks)); } @@ -1342,7 +1341,7 @@ void test_encodings(void) for(i = 0; i < ARRAY_SZ(test_strings); i++) { fru_field_t *field; - const unsigned char *out; + const unsigned char out[FRU_FIELDMAXARRAY]; printf("Data set %d.\n", i); printf("Original data "); diff --git a/frugen.c b/frugen.c index cd2bd83..362a2f4 100644 --- a/frugen.c +++ b/frugen.c @@ -202,7 +202,7 @@ json_object_to_fd(int fd, struct json_object *obj, int flags) wpos = 0; while(wpos < wsize) { if((ret = write(fd, json_str + wpos, wsize-wpos)) < 0) { - return -1; + return -1; } /* because of the above check for ret < 0, we can safely cast and add */ @@ -247,16 +247,16 @@ bool json_fill_fru_area_fields(json_object *jso, int count, debug(2, "Field %s '%s' (%s) loaded from JSON", fieldnames[i], val, type); data_in_this_area = true; - } else { + } else { const char *s = json_object_get_string(jsfield); debug(2, "Field %s '%s' loaded from JSON", fieldnames[i], s); fru_loadfield(fields[i]->val, s); fields[i]->type = FIELD_TYPE_AUTO; data_in_this_area = true; - } - } - } + } + } + } return data_in_this_area; } @@ -420,9 +420,9 @@ bool json_fill_fru_mr_reclist(json_object *jso, fru_mr_reclist_t **mr_reclist) static int json_object_add_with_type(struct json_object* obj, - const char* key, - const unsigned char* val, - int type) { + const char* key, + const unsigned char* val, + int type) { struct json_object *string, *type_string, *entry; if ((string = json_object_new_string(val)) == NULL) goto STRING_ERR; @@ -918,7 +918,7 @@ int main(int argc, char *argv[]) } has_multirec = true; - switch(opt) { + switch(opt) { case 'U': // UUID errno = fru_mr_uuid2rec(&mr_reclist_tail->rec, optarg); if (errno) { @@ -970,7 +970,7 @@ int main(int argc, char *argv[]) fru_reclist_t *next = chassis.cust; while (next != NULL) { json_object_add_with_type(temp_obj, NULL, next->rec->data, - typelen2ind(next->rec->typelen)); + typelen2ind(next->rec->typelen)); next = next->next; } json_object_object_add(section, "custom", temp_obj); @@ -992,7 +992,7 @@ int main(int argc, char *argv[]) fru_reclist_t *next = product.cust; while (next != NULL) { json_object_add_with_type(temp_obj, NULL, next->rec->data, - typelen2ind(next->rec->typelen)); + typelen2ind(next->rec->typelen)); next = next->next; } json_object_object_add(section, "custom", temp_obj); @@ -1013,7 +1013,7 @@ int main(int argc, char *argv[]) fru_reclist_t *next = board.cust; while (next != NULL) { json_object_add_with_type(temp_obj, NULL, next->rec->data, - typelen2ind(next->rec->typelen)); + typelen2ind(next->rec->typelen)); next = next->next; } json_object_object_add(section, "custom", temp_obj); @@ -1185,11 +1185,11 @@ int main(int argc, char *argv[]) fd = open(fname, #if __WIN32__ || __WIN64__ - O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, + O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, #else - O_CREAT | O_TRUNC | O_WRONLY, + O_CREAT | O_TRUNC | O_WRONLY, #endif - 0644); + 0644); if (fd < 0) fatal("Couldn't create file %s: %m", fname);