summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Amelkin <alexander@amelkin.msk.ru>2019-06-11 19:33:35 +0300
committerAlexander Amelkin <mocbuhtig@amelkin.msk.ru>2019-06-18 16:43:41 +0300
commitbd0475ce4ad85645f893010423b265ada1514cc5 (patch)
tree644bf07d62719f7607e40981e16b0e993f0cb13d
parentb397a07e9e6b491217b588da0e22f49400a523b0 (diff)
downloadipmitool-bd0475ce4ad85645f893010423b265ada1514cc5.tar.gz
Load IANA PEN registry from a file
Previously, the OEM names dictionary was compiled in and updating it required rebuilding of `ipmitool`, thus taking a long time for newly registered OEMs to get supported by the tool. Building also required a direct internet connection to succeed. With this commit, the OEM enterprise dictionary is now loaded from either ${HOME}/.local/usr/share/misc/enterprise-numbers or from /usr/share/misc/enterprise-numbers (in that precedence). Those files can be downloaded from iana.org at http://www.iana.org/assignments/enterprise-numbers Partially resolves ipmitool/ipmitool#11 Fixes: 9d41136c9b7c7d392f1a3f3adeb6d7fe3bd3135e Signed-off-by: Alexander Amelkin <alexander@amelkin.msk.ru>
-rw-r--r--.gitignore1
-rw-r--r--include/ipmitool/ipmi_strings.h4
-rw-r--r--lib/Makefile.am6
-rwxr-xr-xlib/create_pen_list77
-rw-r--r--lib/ipmi_main.c8
-rw-r--r--lib/ipmi_strings.c358
6 files changed, 358 insertions, 96 deletions
diff --git a/.gitignore b/.gitignore
index ad2144a..ee72220 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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);
+}