diff options
Diffstat (limited to 'lib/helper.c')
-rw-r--r-- | lib/helper.c | 398 |
1 files changed, 347 insertions, 51 deletions
diff --git a/lib/helper.c b/lib/helper.c index 022a9c9..b547123 100644 --- a/lib/helper.c +++ b/lib/helper.c @@ -29,12 +29,6 @@ * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. */ -#define _POSIX_SOURCE -#define /* glibc 2.19 and earlier */ _BSD_SOURCE || \ - /* Since glibc 2.20 */_DEFAULT_SOURCE || \ - _XOPEN_SOURCE >= 500 || \ - _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED || \ - /* Since glibc 2.10: */ _POSIX_C_SOURCE >= 200112L \ #include <sys/types.h> #include <sys/stat.h> @@ -51,6 +45,7 @@ #include <fcntl.h> #include <errno.h> #include <assert.h> +#include <ctype.h> #if HAVE_CONFIG_H # include <config.h> @@ -59,7 +54,7 @@ #ifdef HAVE_PATHS_H # include <paths.h> #else -# define _PATH_VARRUN "/var/run/" +# define _PATH_RUN "/run/" #endif #include <ipmitool/ipmi.h> @@ -79,22 +74,139 @@ uint16_t buf2short(uint8_t * buf) return (uint16_t)(buf[1] << 8 | buf[0]); } -const char * buf2str(uint8_t * buf, int len) +/* buf2str_extended - convert sequence of bytes to hexadecimal string with + * optional separator + * + * @param buf - data to convert + * @param len - size of data + * @param sep - optional separator (can be NULL) + * + * @returns buf representation in hex, possibly truncated to fit + * allocated static memory + */ +const char * +buf2str_extended(const uint8_t *buf, int len, const char *sep) { - static char str[2049]; + static char str[BUF2STR_MAXIMUM_OUTPUT_SIZE]; + char *cur; int i; + int sz; + int left; + int sep_len; - if (len <= 0 || len > 1024) - return NULL; + if (!buf) { + snprintf(str, sizeof(str), "<NULL>"); + return (const char *)str; + } + cur = str; + left = sizeof(str); + if (sep) { + sep_len = strlen(sep); + } else { + sep_len = 0; + } + for (i = 0; i < len; i++) { + /* may return more than 2, depending on locale */ + sz = snprintf(cur, left, "%2.2x", buf[i]); + if (sz >= left) { + /* buffer overflow, truncate */ + break; + } + cur += sz; + left -= sz; + /* do not write separator after last byte */ + if (sep && i != (len - 1)) { + if (sep_len >= left) { + break; + } + strncpy(cur, sep, left - sz); + cur += sep_len; + left -= sep_len; + } + } + *cur = '\0'; + + return (const char *)str; +} - memset(str, 0, 2049); +const char * +buf2str(const uint8_t *buf, int len) +{ + return buf2str_extended(buf, len, NULL); +} - for (i=0; i<len; i++) - sprintf(str+i+i, "%2.2x", buf[i]); +/* ipmi_parse_hex - convert hexadecimal numbers to ascii string + * Input string must be composed of two-characer + * hexadecimal numbers. + * There is no separator between the numbers. Each number + * results in one byte of the converted string. + * + * Example: ipmi_parse_hex("50415353574F5244") + * returns 'PASSWORD' + * + * @param str: input string. It must contain only even number + * of '0'-'9','a'-'f' and 'A-F' characters. + * @param out: pointer to output data + * @param size: size of the output buffer + * @returns 0 for empty input string + * -1 for string with odd length + * -2 if out is NULL + * -3 if there is non-hexadecimal char in string + * >0 length of resulting binary data even if it is > size + */ +int +ipmi_parse_hex(const char *str, uint8_t *out, int size) +{ + const char *p; + uint8_t *q; + uint8_t d = 0; + uint8_t b = 0; + int shift = 4; + int len; + + len = strlen(str); + if (len == 0) { + return 0; + } - str[len*2] = '\0'; + if (len % 2 != 0) { + return -1; + } - return (const char *)str; + len /= 2; /* out bytes */ + if (!out) { + return -2; + } + + for (p = str, q = out; *p; p++) { + if (!isxdigit(*p)) { + return -3; + } + + if (*p < 'A') { + /* it must be 0-9 */ + d = *p - '0'; + } else { + /* it's A-F or a-f */ + /* convert to lowercase and to 10-15 */ + d = (*p | 0x20) - 'a' + 10; + } + + if (q < (out + size)) { + /* there is space, store */ + b += d << shift; + if (shift) { + shift = 0; + } else { + shift = 4; + *q = b; + b = 0; + q++; + } + } + } + + return len; } void printbuf(const uint8_t * buf, int len, const char * desc) @@ -116,29 +228,168 @@ void printbuf(const uint8_t * buf, int len, const char * desc) fprintf(stderr, "\n"); } -const char * val2str(uint16_t val, const struct valstr *vs) +/* + * Unconditionally reverse the order of arbitrarily long strings of bytes + */ +uint8_t *array_byteswap(uint8_t *buffer, size_t length) { - static char un_str[32]; - int i; + size_t i; + uint8_t temp; + size_t max = length - 1; + + for (i = 0; i < length / 2; ++i) { + temp = buffer[i]; + buffer[i] = buffer[max - i]; + buffer[max - i] = temp; + } - for (i = 0; vs[i].str != NULL; i++) { - if (vs[i].val == val) - return vs[i].str; + return buffer; +} + +/* Convert data array from network (big-endian) to host byte order */ +uint8_t *array_ntoh(uint8_t *buffer, size_t length) +{ +#if WORDS_BIGENDIAN + /* Big-endian host doesn't need conversion from big-endian network */ + (void)length; /* Silence the compiler */ + return buffer; +#else + /* Little-endian host needs conversion from big-endian network */ + return array_byteswap(buffer, length); +#endif +} + +/* Convert data array from little-endian to host byte order */ +uint8_t *array_letoh(uint8_t *buffer, size_t length) +{ +#if WORDS_BIGENDIAN + /* Big-endian host needs conversion from little-endian IPMI */ + return array_byteswap(buffer, length); +#else + /* Little-endian host doesn't need conversion from little-endian IPMI */ + (void)length; /* Silence the compiler */ + return buffer; +#endif +} + +/* str2mac - parse-out MAC address from given string and store it + * into buffer. + * + * @arg: string to be parsed. + * @buf: buffer of 6 to hold parsed MAC address. + * + * returns zero on success, (-1) on error and error message is printed-out. + */ +int +str2mac(const char *arg, uint8_t *buf) +{ + unsigned int m1 = 0; + unsigned int m2 = 0; + unsigned int m3 = 0; + unsigned int m4 = 0; + unsigned int m5 = 0; + unsigned int m6 = 0; + if (sscanf(arg, "%02x:%02x:%02x:%02x:%02x:%02x", + &m1, &m2, &m3, &m4, &m5, &m6) != 6) { + lprintf(LOG_ERR, "Invalid MAC address: %s", arg); + return -1; + } + if (m1 > UINT8_MAX || m2 > UINT8_MAX + || m3 > UINT8_MAX || m4 > UINT8_MAX + || m5 > UINT8_MAX || m6 > UINT8_MAX) { + lprintf(LOG_ERR, "Invalid MAC address: %s", arg); + return -1; + } + buf[0] = (uint8_t)m1; + buf[1] = (uint8_t)m2; + buf[2] = (uint8_t)m3; + buf[3] = (uint8_t)m4; + buf[4] = (uint8_t)m5; + buf[5] = (uint8_t)m6; + return 0; +} + +/* mac2str -- return MAC address as a string + * + * @buf: buffer of 6 to hold parsed MAC address. + */ +const char * +mac2str(const uint8_t *buf) +{ + return buf2str_extended(buf, 6, ":"); +} + +/** + * Find the index of value in a valstr array + * + * @param[in] val The value to search for + * @param[in] vs The valstr array to search in + * @return >=0 The index into \p vs + * @return -1 Error: value \p val was not found in \p vs + */ +static +inline +off_t find_val_idx(uint32_t val, const struct valstr *vs) +{ + if (vs) { + for (off_t i = 0; vs[i].str; ++i) { + if (vs[i].val == val) { + return i; + } + } } + return -1; +} + +/** + * Generate a statically allocated 'Unknown' string for the provided value. + * The function is not thread-safe (as most of ipmitool). + * + * @param[in] val The value to put into the string + * @returns A pointer to a statically allocated string + */ +static +inline +const char *unknown_val_str(uint32_t val) +{ + static char un_str[32]; memset(un_str, 0, 32); snprintf(un_str, 32, "Unknown (0x%02X)", val); return un_str; } -const char * oemval2str(uint32_t oem, uint16_t val, - const struct oemvalstr *vs) +const char * +specific_val2str(uint32_t val, + const struct valstr *specific, + const struct valstr *generic) { - static char un_str[32]; int i; - for (i = 0; vs[i].oem != 0xffffff && vs[i].str != NULL; i++) { + if (0 <= (i = find_val_idx(val, specific))) { + return specific[i].str; + } + + if (0 <= (i = find_val_idx(val, generic))) { + return generic[i].str; + } + + return unknown_val_str(val); +} + +const char *val2str(uint32_t val, const struct valstr *vs) +{ + return specific_val2str(val, NULL, vs); +} + + +const char *oemval2str(uint32_t oem, uint32_t val, + const struct oemvalstr *vs) +{ + int i; + + for (i = 0; vs[i].oem != 0xffffff && vs[i].str; i++) { /* FIXME: for now on we assume PICMG capability on all IANAs */ if ( (vs[i].oem == oem || vs[i].oem == IPMI_OEM_PICMG) && vs[i].val == val ) { @@ -146,10 +397,7 @@ const char * oemval2str(uint32_t oem, uint16_t val, } } - memset(un_str, 0, 32); - snprintf(un_str, 32, "Unknown (0x%X)", val); - - return un_str; + return unknown_val_str(val); } /* str2double - safely convert string to double @@ -394,12 +642,12 @@ int str2uchar(const char * str, uint8_t * uchr_ptr) return 0; } /* str2uchar(...) */ -uint16_t str2val(const char *str, const struct valstr *vs) +uint32_t str2val32(const char *str, const struct valstr *vs) { int i; - for (i = 0; vs[i].str != NULL; i++) { - if (strncasecmp(vs[i].str, str, __maxlen(str, vs[i].str)) == 0) + for (i = 0; vs[i].str; i++) { + if (strcasecmp(vs[i].str, str) == 0) return vs[i].val; } @@ -417,10 +665,10 @@ print_valstr(const struct valstr * vs, const char * title, int loglevel) { int i; - if (vs == NULL) + if (!vs) return; - if (title != NULL) { + if (title) { if (loglevel < 0) printf("\n%s:\n\n", title); else @@ -435,7 +683,7 @@ print_valstr(const struct valstr * vs, const char * title, int loglevel) lprintf(loglevel, "=============================================="); } - for (i = 0; vs[i].str != NULL; i++) { + for (i = 0; vs[i].str; i++) { if (loglevel < 0) { if (vs[i].val < 256) printf(" %d\t0x%02x\t%s\n", vs[i].val, vs[i].val, vs[i].str); @@ -466,18 +714,18 @@ print_valstr_2col(const struct valstr * vs, const char * title, int loglevel) { int i; - if (vs == NULL) + if (!vs) return; - if (title != NULL) { + if (title) { if (loglevel < 0) printf("\n%s:\n\n", title); else lprintf(loglevel, "\n%s:\n", title); } - for (i = 0; vs[i].str != NULL; i++) { - if (vs[i+1].str == NULL) { + for (i = 0; vs[i].str; i++) { + if (!vs[i+1].str) { /* last one */ if (loglevel < 0) { printf(" %4d %-32s\n", vs[i].val, vs[i].str); @@ -531,12 +779,12 @@ ipmi_open_file(const char * file, int rw) struct stat st1, st2; FILE * fp; - /* verify existance */ + /* verify existence */ if (lstat(file, &st1) < 0) { if (rw) { /* does not exist, ok to create */ fp = fopen(file, "w"); - if (fp == NULL) { + if (!fp) { lperror(LOG_ERR, "Unable to open file %s " "for write", file); return NULL; @@ -553,7 +801,7 @@ ipmi_open_file(const char * file, int rw) if (!rw) { /* on read skip the extra checks */ fp = fopen(file, "r"); - if (fp == NULL) { + if (!fp) { lperror(LOG_ERR, "Unable to open file %s", file); return NULL; } @@ -576,7 +824,7 @@ ipmi_open_file(const char * file, int rw) } fp = fopen(file, rw ? "w+" : "r"); - if (fp == NULL) { + if (!fp) { lperror(LOG_ERR, "Unable to open file %s", file); return NULL; } @@ -620,6 +868,7 @@ ipmi_start_daemon(struct ipmi_intf *intf) { pid_t pid; int fd; + int ret; #ifdef SIGHUP sigset_t sighup; #endif @@ -663,7 +912,11 @@ ipmi_start_daemon(struct ipmi_intf *intf) exit(0); #endif - chdir("/"); + ret = chdir("/"); + if (ret) { + lprintf(LOG_ERR, "chdir failed: %s (%d)", strerror(errno), errno); + exit(1); + } umask(0); for (fd=0; fd<64; fd++) { @@ -672,9 +925,20 @@ ipmi_start_daemon(struct ipmi_intf *intf) } fd = open("/dev/null", O_RDWR); - assert(0 == fd); - dup(fd); - dup(fd); + if (fd != STDIN_FILENO) { + lprintf(LOG_ERR, "failed to reset stdin: %s (%d)", strerror(errno), errno); + exit(1); + } + ret = dup(fd); + if (ret != STDOUT_FILENO) { + lprintf(LOG_ERR, "failed to reset stdout: %s (%d)", strerror(errno), errno); + exit(1); + } + ret = dup(fd); + if (ret != STDERR_FILENO) { + lprintf(LOG_ERR, "failed to reset stderr: %s (%d)", strerror(errno), errno); + exit(1); + } } /* eval_ccode - evaluate return value of _ipmi_* functions and print error error @@ -687,7 +951,7 @@ ipmi_start_daemon(struct ipmi_intf *intf) int eval_ccode(const int ccode) { - if (ccode == 0) { + if (!ccode) { return 0; } else if (ccode < 0) { switch (ccode) { @@ -847,11 +1111,11 @@ ipmi_get_oem_id(struct ipmi_intf *intf) req.msg.data_len = 0; rsp = intf->sendrecv(intf, &req); - if (rsp == NULL) { + if (!rsp) { lprintf(LOG_ERR, "Get Board ID command failed"); return 0; } - if (rsp->ccode > 0) { + if (rsp->ccode) { lprintf(LOG_ERR, "Get Board ID command failed: %#x %s", rsp->ccode, val2str(rsp->ccode, completion_code_vals)); return 0; @@ -861,3 +1125,35 @@ ipmi_get_oem_id(struct ipmi_intf *intf) return oem_id; } + +/** Parse command line arguments as numeric byte values (dec or hex) + * and store them in a \p len sized buffer \p out. + * + * @param[in] argc Number of arguments + * @param[in] argv Array of arguments + * @param[out] out The output buffer + * @param[in] len Length of the output buffer in bytes (no null-termination + * is assumed, the input data is treated as raw byte values, + * not as a string. + * + * @returns A success status indicator + * @return false Error + * @return true Success + */ +bool +args2buf(int argc, char *argv[], uint8_t *out, size_t len) +{ + size_t i; + + for (i = 0; i < len && i < (size_t)argc; ++i) { + uint8_t byte; + + if (str2uchar(argv[i], &byte)) { + lprintf(LOG_ERR, "Bad byte value: %s", argv[i]); + return false; + } + + out[i] = byte; + } + return true; +} |