mirror of
https://github.com/ipmitool/ipmitool.git
synced 2025-05-10 18:47:22 +00:00
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>
This commit is contained in:
parent
b397a07e9e
commit
bd0475ce4a
1
.gitignore
vendored
1
.gitignore
vendored
@ -29,6 +29,5 @@ control/prototype
|
||||
control/rpmmacros
|
||||
src/ipmievd
|
||||
src/ipmitool
|
||||
lib/ipmi_pen_list.inc.c
|
||||
cscope.out
|
||||
tags
|
||||
|
@ -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[];
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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"
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
const struct valstr ipmi_oem_info[] = {
|
||||
|
||||
/* These are at the top so they are found first */
|
||||
{ IPMI_OEM_UNKNOWN, "Unknown" },
|
||||
{ IPMI_OEM_RESERVED, "Unspecified" },
|
||||
|
||||
/* The included file is auto-generated from official IANA PEN list */
|
||||
#include "ipmi_pen_list.inc.c"
|
||||
#include <ipmitool/log.h>
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* These are put at the head so they are found first because they
|
||||
* may overlap with IANA specified numbers found in the registry.
|
||||
*/
|
||||
{ IPMI_OEM_DEBUG, "A Debug Assisting Company, Ltd." },
|
||||
{ -1 , NULL },
|
||||
static const struct valstr ipmi_oem_info_head[] = {
|
||||
{ IPMI_OEM_UNKNOWN, "Unknown" }, /* IPMI Unknown */
|
||||
{ IPMI_OEM_RESERVED, "Unspecified" }, /* IPMI Reserved */
|
||||
{ UINT32_MAX , NULL },
|
||||
};
|
||||
|
||||
/*
|
||||
* 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 is used when ipmi_oem_info couldn't be allocated.
|
||||
* ipmitool would report all OEMs as unknown, but would be functional otherwise.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user