diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | include/ipmitool/ipmi_strings.h | 4 | ||||
-rw-r--r-- | lib/Makefile.am | 6 | ||||
-rwxr-xr-x | lib/create_pen_list | 77 | ||||
-rw-r--r-- | lib/ipmi_main.c | 8 | ||||
-rw-r--r-- | lib/ipmi_strings.c | 358 |
6 files changed, 358 insertions, 96 deletions
@@ -29,6 +29,5 @@ control/prototype control/rpmmacros src/ipmievd src/ipmitool -lib/ipmi_pen_list.inc.c cscope.out tags diff --git a/include/ipmitool/ipmi_strings.h b/include/ipmitool/ipmi_strings.h index e898ce3..799f1a8 100644 --- a/include/ipmitool/ipmi_strings.h +++ b/include/ipmitool/ipmi_strings.h @@ -53,8 +53,10 @@ extern const struct valstr ipmi_chassis_power_control_vals[]; extern const struct valstr ipmi_auth_algorithms[]; extern const struct valstr ipmi_integrity_algorithms[]; extern const struct valstr ipmi_encryption_algorithms[]; -extern const struct valstr ipmi_oem_info[]; extern const struct valstr ipmi_user_enable_status_vals[]; +extern const struct valstr *ipmi_oem_info; +int ipmi_oem_info_init(); +void ipmi_oem_info_free(); extern const struct valstr picmg_frucontrol_vals[]; extern const struct valstr picmg_clk_family_vals[]; diff --git a/lib/Makefile.am b/lib/Makefile.am index fd3534b..258aa1c 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -31,7 +31,6 @@ AUTOMAKE_OPTIONS = subdir-objects AM_CPPFLAGS = -I$(top_srcdir)/include MAINTAINERCLEANFILES = Makefile.in -PEN_LIST = $(srcdir)/ipmi_pen_list.inc.c noinst_LTLIBRARIES = libipmitool.la libipmitool_la_SOURCES = helper.c ipmi_sdr.c ipmi_sel.c ipmi_sol.c ipmi_pef.c \ @@ -49,8 +48,3 @@ libipmitool_la_LDFLAGS = -export-dynamic libipmitool_la_LIBADD = -lm libipmitool_la_DEPENDENCIES = -$(PEN_LIST): - $(srcdir)/create_pen_list $(PEN_LIST) - -ipmi_strings.lo: $(PEN_LIST) - diff --git a/lib/create_pen_list b/lib/create_pen_list deleted file mode 100755 index 81864ad..0000000 --- a/lib/create_pen_list +++ /dev/null @@ -1,77 +0,0 @@ -#!/bin/bash -# vi: set ts=2 sw=2 et : -# -# IANA PEN List generator -# -# This script takes the official IANA PEN registry and generates -# a C language output for inclusion into ipmi_strings.c -# -# Copyright (c) 2018 Alexander Amelkin <alexander@amelkin.msk.ru> -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its -# contributors may be used to endorse or promote products derived from this -# software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - -OUTFILE=$1 -PENLIST_URL=https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers - -if [ -z "$OUTFILE" ]; then - echo $0: Must specify output file - exit -fi - -parse_pen_list() { - iconv -f utf8 -t ascii//TRANSLIT//IGNORE \ - | awk ' - /^[0-9]+/ { - if(PEN) { - print "{ " PEN ", \"" ENTERPRISE "\" }," - } - PEN=$1 - } - - /^ [[:alnum:]]/ { - # Remove leading spaces - sub(/^[[:space:]]+/,"") - # Remove question marks (Chinese characters after iconv) - gsub(/\?/,""); - # Remove non-printable characters - gsub(/[^[:print:]]/,""); - # Escape slashes and double quotes - gsub(/["\\]/,"\\\\&") - ENTERPRISE=$0; - } - - END { - if(PEN) { - print "{ " PEN ", \"" ENTERPRISE "\" }," - } - }' -} - -echo "Generating IANA PEN list..." -curl -# "$PENLIST_URL" | parse_pen_list > "$OUTFILE" diff --git a/lib/ipmi_main.c b/lib/ipmi_main.c index f07526c..55da967 100644 --- a/lib/ipmi_main.c +++ b/lib/ipmi_main.c @@ -844,6 +844,12 @@ ipmi_main(int argc, char ** argv, /* setup log */ log_init(progname, 0, verbose); + /* load the IANA PEN registry */ + if (ipmi_oem_info_init()) { + lprintf(LOG_ERR, "Failed to initialize the OEM info dictionary"); + goto out_free; + } + /* run OEM setup if found */ if (oemtype && ipmi_oem_setup(ipmi_main_intf, oemtype) < 0) { @@ -1063,6 +1069,8 @@ ipmi_main(int argc, char ** argv, devfile = NULL; } + ipmi_oem_info_free(); + return rc; } diff --git a/lib/ipmi_strings.c b/lib/ipmi_strings.c index ca70cef..30376c3 100644 --- a/lib/ipmi_strings.c +++ b/lib/ipmi_strings.c @@ -30,29 +30,52 @@ * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. */ +#include <ctype.h> +#include <limits.h> #include <stddef.h> + #include <ipmitool/ipmi_strings.h> #include <ipmitool/ipmi_constants.h> #include <ipmitool/ipmi_sensor.h> #include <ipmitool/ipmi_sel.h> /* for IPMI_OEM */ +#include <ipmitool/log.h> -const struct valstr ipmi_oem_info[] = { - -/* These are at the top so they are found first */ - { IPMI_OEM_UNKNOWN, "Unknown" }, - { IPMI_OEM_RESERVED, "Unspecified" }, +/* + * These are put at the head so they are found first because they + * may overlap with IANA specified numbers found in the registry. + */ +static const struct valstr ipmi_oem_info_head[] = { + { IPMI_OEM_UNKNOWN, "Unknown" }, /* IPMI Unknown */ + { IPMI_OEM_RESERVED, "Unspecified" }, /* IPMI Reserved */ + { UINT32_MAX , NULL }, +}; -/* The included file is auto-generated from official IANA PEN list */ -#include "ipmi_pen_list.inc.c" +/* + * These are our own technical values. We don't want them to take precedence + * over IANA's defined values, so they go at the very end of the array. + */ +static const struct valstr ipmi_oem_info_tail[] = { + { IPMI_OEM_DEBUG, "A Debug Assisting Company, Ltd." }, + { UINT32_MAX , NULL }, +}; /* - * This debug ID is at the bottom so that if IANA assigns it to - * any entity, that IANA's value is found first and reported. + * This is used when ipmi_oem_info couldn't be allocated. + * ipmitool would report all OEMs as unknown, but would be functional otherwise. */ - { IPMI_OEM_DEBUG, "A Debug Assisting Company, Ltd." }, - { -1 , NULL }, +static const struct valstr ipmi_oem_info_dummy[] = { + { UINT32_MAX , NULL }, }; +/* This will point to an array filled from IANA's enterprise numbers registry */ +const struct valstr *ipmi_oem_info; + +/* Single-linked list of OEM valstrs */ +typedef struct oem_valstr_list_s { + struct valstr valstr; + struct oem_valstr_list_s *next; +} oem_valstr_list_t; + const struct oemvalstr ipmi_oem_product_info[] = { /* Keep OEM grouped together */ @@ -754,3 +777,316 @@ const struct oemvalstr picmg_busres_shmc_status_vals[] = { { 0xffffff, 0x00, NULL } }; + + +/** + * A helper function to count repetitions of the same byte + * at the beginning of a string. + */ +static +size_t count_bytes(const char *s, unsigned char c) +{ + size_t count; + + for (count = 0; s && s[0] == c; ++s, ++count); + + return count; +} + +/** + * Parse the IANA PEN registry file. + * + * See https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers + * The expected entry format is: + * + * Decimal + * | Organization + * | | Contact + * | | | Email + * | | | | + * 0 + * Reserved + * Internet Assigned Numbers Authority + * iana&iana.org + * + * That is, IANA PEN at position 0, enterprise name at position 2. + */ +#define IANA_NAME_OFFSET 2 +#define IANA_PEN_REGISTRY "/usr/share/misc/enterprise-numbers" +static +int oem_info_list_load(oem_valstr_list_t **list) +{ + FILE *in = NULL; + char *home; + oem_valstr_list_t *oemlist = *list; + int count = 0; + + /* + * First try to open user's local registry if HOME is set + */ + if ((home = getenv("HOME"))) { + char path[PATH_MAX + 1] = { 0 }; + strncpy(path, home, sizeof(path)); + strncat(path, "/.local" IANA_PEN_REGISTRY, PATH_MAX); + in = fopen(path, "r"); + } + + if (!in) { + /* + * Now open the system default file + */ + in = fopen(IANA_PEN_REGISTRY, "r"); + if (!in) { + lperror(LOG_ERR, "IANA PEN registry open failed"); + return -1; + } + } + + /* + * Read the registry file line by line, fill in the linked list, + * and count the entries. No sorting is done, entries will come in + * reverse registry order. + */ + while (!feof(in)) { + oem_valstr_list_t *item; + char *line = NULL; + char *endptr = NULL; + size_t len = 0; + long iana; + + if (!getline(&line, &len, in)) { + /* Either an EOF or an empty line. Start over. */ + continue; + } + + /* + * Check if the line starts with a digit. If so, take it as IANA PEN. + * Any numbers not starting at position 0 are discarded. + */ + iana = strtol(line, &endptr, 10); + if (!isdigit(line[0]) || endptr == line) { + free_n(&line); + continue; + } + free_n(&line); + + /* + * Now as we have the enterprise number, we're expecting the + * organization name to immediately follow. + */ + len = 0; + if (!getline(&line, &len, in)) { + /* + * Either an EOF or an empty line. Neither one can happen in + * a valid registry entry. Start over. + */ + continue; + } + + if (len < IANA_NAME_OFFSET + 1 + || count_bytes(line, ' ') != IANA_NAME_OFFSET) + { + /* + * This is not a valid line, it doesn't start with + * a correct-sized indentation or is too short. + * Start over. + */ + free_n(&line); + continue; + } + + /* Adjust to real line length, don't count the indentation */ + len = strnlen(line + IANA_NAME_OFFSET, len - IANA_NAME_OFFSET); + + /* Don't count the trailing newline */ + if (line[IANA_NAME_OFFSET + len - 1] == '\n') { + --len; + } + + item = malloc(sizeof(oem_valstr_list_t)); + if (!item) { + lperror(LOG_ERR, "IANA PEN registry entry allocation failed"); + free_n(&line); + /* Just stop reading, and process what has already been read */ + break; + } + + /* + * Looks like we have a valid entry, store it in the list. + */ + item->valstr.val = iana; + item->valstr.str = malloc(len + 1); /* Add 1 for \0 terminator */ + if (!item->valstr.str) { + lperror(LOG_ERR, "IANA PEN registry string allocation failed"); + free_n(&line); + free_n(&item); + /* Just stop reading, and process what has already been read */ + break; + } + strncpy((void *)item->valstr.str, line + IANA_NAME_OFFSET, len); + ((char *)(item->valstr.str))[len] = 0; + free_n(&line); + item->next = oemlist; + oemlist = item; + ++count; + } + fclose (in); + + *list = oemlist; + return count; +} + +/** + * Free the allocated list items and, if needed, strings. + */ +static +void +oem_info_list_free(oem_valstr_list_t **list, bool free_strings) +{ + while ((*list)->next) { + oem_valstr_list_t *item = *list; + *list = item->next; + if (free_strings) { + free_n(&item->valstr.str); + } + free_n(&item); + } +} + +/** + * Initialize the ipmi_oem_info array from a list + */ +static +bool +oem_info_init_from_list(oem_valstr_list_t *oemlist, size_t count) +{ + /* Do not count terminators */ + size_t head_entries = ARRAY_SIZE(ipmi_oem_info_head) - 1; + size_t tail_entries = ARRAY_SIZE(ipmi_oem_info_tail) - 1; + static oem_valstr_list_t *item; + bool rc = false; + + /* Include static entries and the terminator */ + count += head_entries + tail_entries + 1; + + /* + * Allocate as much memory as needed to accomodata all the entries + * of the loaded linked list, plus the static head and tail, not including + * their terminating entries, plus the terminating entry for the new + * array. + */ + ipmi_oem_info = malloc(count * sizeof(*ipmi_oem_info)); + + if (!ipmi_oem_info) { + /* + * We can't identify OEMs without an allocated ipmi_oem_info. + * Report an error, set the pointer to dummy and clean up. + */ + lperror(LOG_ERR, "IANA PEN registry array allocation failed"); + ipmi_oem_info = ipmi_oem_info_dummy; + goto out; + } + + lprintf(LOG_DEBUG + 3, " Allocating %6zu entries", count); + + /* Add a terminator at the very end */ + --count; + ((struct valstr *)ipmi_oem_info)[count].val = -1; + ((struct valstr *)ipmi_oem_info)[count].str = NULL; + + /* Add tail entries from the end */ + while (count-- < SIZE_MAX && tail_entries--) { + ((struct valstr *)ipmi_oem_info)[count] = + ipmi_oem_info_tail[tail_entries]; + + lprintf(LOG_DEBUG + 3, " [%6zu] %8d | %s", count, + ipmi_oem_info[count].val, ipmi_oem_info[count].str); + } + + /* Now add the loaded entries */ + item = oemlist; + while (count < SIZE_MAX && item->next) { + ((struct valstr *)ipmi_oem_info)[count] = + item->valstr; + + lprintf(LOG_DEBUG + 3, " [%6zu] %8d | %s", count, + ipmi_oem_info[count].val, ipmi_oem_info[count].str); + + item = item->next; + --count; + } + + /* Now add head entries */ + while (count < SIZE_MAX && head_entries--) { + ((struct valstr *)ipmi_oem_info)[count] = + ipmi_oem_info_head[head_entries]; + lprintf(LOG_DEBUG + 3, " [%6zu] %8d | %s", count, + ipmi_oem_info[count].val, ipmi_oem_info[count].str); + --count; + } + + rc = true; + +out: + return rc; +} + +int ipmi_oem_info_init() +{ + oem_valstr_list_t terminator = { { -1, NULL}, NULL }; /* Terminator */ + oem_valstr_list_t *oemlist = &terminator; + bool free_strings = true; + size_t count; + int rc = -4; + + lprintf(LOG_INFO, "Loading IANA PEN Registry..."); + + if (ipmi_oem_info) { + lprintf(LOG_INFO, "IANA PEN Registry is already loaded"); + rc = 0; + goto out; + } + + if (!(count = oem_info_list_load(&oemlist))) { + /* + * We can't identify OEMs without a loaded registry. + * Set the pointer to dummy and return. + */ + ipmi_oem_info = ipmi_oem_info_dummy; + goto out; + } + + /* In the array was allocated, don't free the strings at cleanup */ + free_strings = !oem_info_init_from_list(oemlist, count); + + rc = IPMI_CC_OK; + +out: + oem_info_list_free(&oemlist, free_strings); + return rc; +} + +void ipmi_oem_info_free() +{ + /* Start with the dynamically allocated entries */ + size_t i = ARRAY_SIZE(ipmi_oem_info_head) - 1; + + if (ipmi_oem_info == ipmi_oem_info_dummy) { + return; + } + + /* + * Proceed dynamically allocated entries until we hit the first + * entry of ipmi_oem_info_tail[], which is statically allocated. + */ + while (ipmi_oem_info + && ipmi_oem_info[i].val < UINT32_MAX + && ipmi_oem_info[i].str != ipmi_oem_info_tail[0].str) + { + free_n(&((struct valstr *)ipmi_oem_info)[i].str); + ++i; + } + + /* Free the array itself */ + free_n(&ipmi_oem_info); +} |