Share table manipulation and sysfs scanning code in libutil. It will be needed by libopenfcoe.a in a later patch.
Note that there was some confusion around the sa_enum_decode function. This patch ensures that the found string is returned in the 'buf' argument and not just as the return value from the function. Callers were not relying on the return value. Signed-off-by: Robert Love <robert.w.l...@intel.com> Tested-by: Ross Brattain <ross.b.bratt...@intel.com> --- Makefile.am | 2 fcoeadm_display.c | 114 ------------------- include/fcoe_utils.h | 2 include/fcoemon_utils.h | 132 +++++++++++++++++++++- lib/fcoe_utils.c | 2 lib/sa_log.c | 24 ++++ lib/sa_sys.c | 286 +++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 442 insertions(+), 120 deletions(-) create mode 100644 lib/sa_sys.c diff --git a/Makefile.am b/Makefile.am index c5c99e5..8f01599 100644 --- a/Makefile.am +++ b/Makefile.am @@ -32,7 +32,7 @@ fipvlan_LDADD = lib/libutil.a AUTOMAKE_OPTIONS=subdir-objects noinst_LIBRARIES = lib/libutil.a lib_libutil_a_SOURCES = lib/fcoe_utils.c lib/sa_log.c lib/sa_select.c \ - lib/sa_timer.c lib/sa_other.c lib/fip.c lib/rtnetlink.c + lib/sa_timer.c lib/sa_other.c lib/fip.c lib/rtnetlink.c lib/sa_sys.c ## header files that need to be distributed noinst_HEADERS = fcoeadm_display.h fcoe_clif.h fcoemon.h \ diff --git a/fcoeadm_display.c b/fcoeadm_display.c index 906689c..75d20d0 100644 --- a/fcoeadm_display.c +++ b/fcoeadm_display.c @@ -41,6 +41,7 @@ #include "hbaapi.h" #include "fcoeadm_display.h" #include "fcoe_utils.h" +#include "fcoemon_utils.h" /* #define TEST_HBAAPI_V1 */ #ifdef TEST_HBAAPI_V1 @@ -65,11 +66,6 @@ #define FCP_TARG_STR "FCP Target" -struct sa_nameval { - char *nv_name; - u_int32_t nv_val; -}; - /* * HBA and port objects are one-to-one since there * is one host created per Ethernet port (vlan). @@ -116,28 +112,6 @@ struct sa_nameval port_speeds[] = { { NULL, 0 } }; -/** sa_enum_decode(buf, len, tp, val) - * - * @param buf buffer for result (may be used or not). - * @param len size of buffer (at least 32 bytes recommended). - * @param tp pointer to table of names and values, struct sa_nameval. - * @param val value to be decoded into a name. - * @returns pointer to name string. Unknown values are put into buffer in hex. - */ -static const char * -sa_enum_decode(char *buf, size_t len, - const struct sa_nameval *tp, u_int32_t val) -{ - snprintf(buf, len, "Unknown"); - for (; tp->nv_name != NULL; tp++) { - if (tp->nv_val == val) { - strncpy(buf, tp->nv_name, len); - break; - } - } - return buf; -} - /** sa_enum_decode_speed(buf, len, val) * * @param buf buffer for result (may be used or not). @@ -183,92 +157,6 @@ sa_dump_wwn(void *Data, int Length, int Break) } } -/* - * Make a printable NUL-terminated copy of the string. - * The source buffer might not be NUL-terminated. - */ -static char * -sa_strncpy_safe(char *dest, size_t len, const char *src, size_t src_len) -{ - char *dp = dest; - const char *sp = src; - - while (len-- > 1 && src_len-- > 0 && *sp != '\0') { - *dp++ = isprint(*sp) ? *sp : (isspace(*sp) ? ' ' : '.'); - sp++; - } - *dp = '\0'; - - /* - * Take off trailing blanks. - */ - while (--dp >= dest && isspace(*dp)) - *dp = '\0'; - - return dest; -} - -/* - * Read a line from the specified file in the specified directory - * into the buffer. The file is opened and closed. - * Any trailing white space is trimmed off. - * This is useful for accessing /sys files. - * Returns 0 or an error number. - */ -static int -sa_sys_read_line(const char *dir, const char *file, char *buf, size_t len) -{ - FILE *fp; - char file_name[256]; - char *cp; - int rc = 0; - - snprintf(file_name, sizeof(file_name), "%s/%s", dir, file); - fp = fopen(file_name, "r"); - if (fp == NULL) - rc = -1; - else { - cp = fgets(buf, len, fp); - if (cp == NULL) { - fprintf(stderr, "read error or empty file %s," - " errno=0x%x\n", file_name, errno); - rc = -1; - } else { - - /* - * Trim off trailing newline or other white space. - */ - cp = buf + strlen(buf); - while (--cp >= buf && isspace(*cp)) - *cp = '\0'; - } - fclose(fp); - } - return rc; -} - -static int -sa_sys_read_u32(const char *dir, const char *file, u_int32_t *vp) -{ - char buf[256]; - int rc; - u_int32_t val; - char *endptr; - - rc = sa_sys_read_line(dir, file, buf, sizeof(buf)); - if (rc == 0) { - val = strtoul(buf, &endptr, 0); - if (*endptr != '\0') { - fprintf(stderr, - "parse error. file %s/%s line '%s'\n", - dir, file, buf); - rc = -1; - } else - *vp = val; - } - return rc; -} - static int is_fcp_target(HBA_PORTATTRIBUTES *rp_info) { char buf[MAX_STR_LEN]; diff --git a/include/fcoe_utils.h b/include/fcoe_utils.h index 46085be..11e5fdd 100644 --- a/include/fcoe_utils.h +++ b/include/fcoe_utils.h @@ -76,5 +76,5 @@ int fcoe_checkdir(char *dir); int check_symbolic_name_for_interface(const char *symbolic_name, const char *ifname); char *get_ifname_from_symbolic_name(const char *symbolic_name); - +int fcoe_sysfs_read(char *buf, int size, const char *path); #endif /* _FCOE_UTILS_H_ */ diff --git a/include/fcoemon_utils.h b/include/fcoemon_utils.h index 9e31902..3bdb377 100644 --- a/include/fcoemon_utils.h +++ b/include/fcoemon_utils.h @@ -36,6 +36,7 @@ #include <signal.h> #include <errno.h> #include <syslog.h> +#include <dirent.h> #include "fc_types.h" @@ -138,8 +139,8 @@ char *sa_hex_format(char *buf, size_t buflen, * Structure for tables encoding and decoding name-value pairs such as enums. */ struct sa_nameval { - char *nv_name; - u_int nv_val; + char *nv_name; + u_int32_t nv_val; }; const char *sa_enum_decode(char *, size_t, const struct sa_nameval *, u_int); @@ -269,4 +270,131 @@ extern int use_syslog; void enable_syslog(int); void enable_debug_log(int); +/* + * Table and sysfs helpers + */ + +/* + * Structure for integer-indexed tables that can grow. + */ +struct sa_table { + u_int32_t st_size; /* number of entries in table */ + u_int32_t st_limit; /* end of valid entries in table (public) */ + void **st_table; /* re-allocatable array of pointers */ +}; + +/* + * Function prototypes + */ +extern int sa_sys_read_line(const char *, const char *, char *, size_t); +extern int sa_sys_write_line(const char *, const char *, const char *); +extern int sa_sys_read_u32(const char *, const char *, u_int32_t *); +extern int sa_sys_read_u64(const char *, const char *, u_int64_t *); +extern int sa_dir_read(char *, int (*)(struct dirent *, void *), void *); +extern char *sa_strncpy_safe(char *dest, size_t len, + const char *src, size_t src_len); +extern const char *sa_enum_decode(char *, size_t, + const struct sa_nameval *, u_int32_t); +extern int sa_enum_encode(const struct sa_nameval *tp, + const char *, u_int32_t *); +extern const char *sa_flags_decode(char *, size_t, + const struct sa_nameval *, u_int32_t); +extern int sa_table_grow(struct sa_table *, u_int32_t index); +extern void sa_table_destroy_all(struct sa_table *); +extern void sa_table_destroy(struct sa_table *); +extern void sa_table_iterate(struct sa_table *tp, + void (*handler)(void *ep, void *arg), void *arg); +extern void *sa_table_search(struct sa_table *tp, + void *(*match)(void *ep, void *arg), void *arg); + +/** sa_table_init(tp) - initialize a table. + * @param tp table pointer. + * + * This just clears a table structure that was allocated by the caller. + */ +static inline void sa_table_init(struct sa_table *tp) +{ + memset(tp, 0, sizeof(*tp)); +} + +/** sa_table_lookup(tp, index) - lookup an entry in the table. + * @param tp table pointer. + * @param index the index in the table to access + * @returns the entry, or NULL if the index wasn't valid. + */ +static inline void *sa_table_lookup(const struct sa_table *tp, u_int32_t index) +{ + void *ep = NULL; + + if (index < tp->st_limit) + ep = tp->st_table[index]; + return ep; +} + +/** sa_table_lookup_n(tp, n) - find Nth non-empty entry in a table. + * @param tp table pointer. + * @param n is the entry number, the first non-empty entry is 0. + * @returns the entry, or NULL if the end of the table reached first. + */ +static inline void *sa_table_lookup_n(const struct sa_table *tp, u_int32_t n) +{ + void *ep = NULL; + u_int32_t i; + + for (i = 0; i < tp->st_limit; i++) { + ep = tp->st_table[i]; + if (ep != NULL && n-- == 0) + return ep; + } + return NULL; +} + +/** sa_table_insert(tp, index, ep) - Replace or insert an entry in the table. + * @param tp table pointer. + * @param index the index for the new entry. + * @param ep entry pointer. + * @returns index on success, or -1 if the insert failed. + * + * Note: if the table has never been used, and is still all zero, this works. + * + * Note: perhaps not safe for multithreading. Caller can lock the table + * externally, but reallocation can take a while, during which time the + * caller may not wish to hold the lock. + */ +static inline int sa_table_insert(struct sa_table *tp, + u_int32_t index, void *ep) +{ + if (index >= tp->st_limit && sa_table_grow(tp, index) < 0) + return -1; + tp->st_table[index] = ep; + return index; +} + +/** sa_table_append(tp, ep) - add entry to table and return index. + * + * @param tp pointer to sa_table structure. + * @param ep pointer to new entry, to be added at the end of the table. + * @returns new index, or -1 if table couldn't be grown. + * + * See notes on sa_table_insert(). + */ +static inline int +sa_table_append(struct sa_table *tp, void *ep) +{ + return sa_table_insert(tp, tp->st_limit, ep); +} + +/** sa_table_sort(tp, compare) - sort table in place + * + * @param tp pointer to sa_table structure. + * @param compare function to compare two entries. It is called with pointers + * to the pointers to the entries to be compared. See qsort(3). + */ +static inline void +sa_table_sort(struct sa_table *tp, int (*compare)(const void **, const void **)) +{ + qsort(tp->st_table, tp->st_limit, sizeof(void *), + (int (*)(const void *, const void *)) compare); +} + #endif /* _FCOEMON_UTILS_H_ */ diff --git a/lib/fcoe_utils.c b/lib/fcoe_utils.c index d54cd4d..0ebc854 100644 --- a/lib/fcoe_utils.c +++ b/lib/fcoe_utils.c @@ -19,7 +19,7 @@ #include "fcoe_utils.h" -static int fcoe_sysfs_read(char *buf, int size, const char *path) +int fcoe_sysfs_read(char *buf, int size, const char *path) { FILE *fp; int i, rc = -EINVAL; diff --git a/lib/sa_log.c b/lib/sa_log.c index 498b08b..65f7e40 100644 --- a/lib/sa_log.c +++ b/lib/sa_log.c @@ -243,15 +243,35 @@ sa_strncpy_safe(char *dest, size_t len, const char *src, size_t src_len) * @param len size of buffer (at least 32 bytes recommended). * @param tp pointer to table of names and values, struct sa_nameval. * @param val value to be decoded into a name. - * @returns pointer to name string. Unknown values are put into buffer in hex. + * @returns pointer to name string and in buf. Unknown values are put into buffer in hex. */ const char * sa_enum_decode(char *buf, size_t len, const struct sa_nameval *tp, u_int val) { for (; tp->nv_name != NULL; tp++) { - if (tp->nv_val == val) + if (tp->nv_val == val) { + snprintf(buf, len, "%s", tp->nv_name); return tp->nv_name; + } } snprintf(buf, len, "Unknown (code 0x%X)", val); return buf; } + +/** sa_enum_encode(tp, name, valp) + * + * @param tp pointer to table of names and values, struct sa_nameval. + * @param name string to be encoded into a value + * @returns zero on success, non-zero if no matching string found. + */ +int +sa_enum_encode(const struct sa_nameval *tp, const char *name, u_int32_t *valp) +{ + for (; tp->nv_name != NULL; tp++) { + if (strcasecmp(tp->nv_name, name) == 0) { + *valp = tp->nv_val; + return 0; + } + } + return -1; +} diff --git a/lib/sa_sys.c b/lib/sa_sys.c new file mode 100644 index 0000000..bb35083 --- /dev/null +++ b/lib/sa_sys.c @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2012-2013, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include "fcoemon_utils.h" + +/* + * Read a line from the specified file in the specified directory + * into the buffer. The file is opened and closed. + * Any trailing white space is trimmed off. + * This is useful for accessing /sys files. + * Returns 0 or an error number. + */ +int +sa_sys_read_line(const char *dir, const char *file, char *buf, size_t len) +{ + FILE *fp; + char file_name[256]; + char *cp; + int rc = 0; + + snprintf(file_name, sizeof(file_name), "%s/%s", dir, file); + fp = fopen(file_name, "r"); + if (fp == NULL) + rc = -1; + else { + cp = fgets(buf, len, fp); + if (cp == NULL) { + fprintf(stderr, + "%s: read error or empty file %s," + " errno=0x%x\n", __func__, + file_name, errno); + rc = -1; + } else { + + /* + * Trim off trailing newline or other white space. + */ + cp = buf + strlen(buf); + while (--cp >= buf && isspace(*cp)) + *cp = '\0'; + } + fclose(fp); + } + return rc; +} + +/* + * Write a string to the specified file in the specified directory. + * The file is opened and closed. + * The string has a new-line appended to it. + * This is useful for accessing /sys files. + * Returns 0 or an error number. + */ +int +sa_sys_write_line(const char *dir, const char *file, const char *string) +{ + FILE *fp; + char file_name[256]; + int rc = 0; + + snprintf(file_name, sizeof(file_name), "%s/%s", dir, file); + fp = fopen(file_name, "w"); + if (fp == NULL) { + fprintf(stderr, "%s: fopen of %s failed, errno=0x%x\n", + __func__, file_name, errno); + rc = -1; + } else { + rc = fprintf(fp, "%s\n", string); + if (rc < 0) + fprintf(stderr, + "%s: write to %s of %s failed, errno=0x%x\n", + __func__, file_name, string, errno); + fclose(fp); + } + return rc; +} + +int +sa_sys_read_u32(const char *dir, const char *file, u_int32_t *vp) +{ + char buf[256]; + int rc; + u_int32_t val; + char *endptr; + + rc = sa_sys_read_line(dir, file, buf, sizeof(buf)); + if (rc == 0) { + val = strtoul(buf, &endptr, 0); + if (*endptr != '\0') { + fprintf(stderr, + "%s: parse error. file %s/%s line '%s'\n", + __func__, dir, file, buf); + rc = -1; + } else + *vp = val; + } + return rc; +} + +int +sa_sys_read_u64(const char *dir, const char *file, u_int64_t *vp) +{ + char buf[256]; + int rc; + u_int64_t val; + char *endptr; + + rc = sa_sys_read_line(dir, file, buf, sizeof(buf)); + if (rc == 0) { + val = strtoull(buf, &endptr, 0); + if (*endptr != '\0') { + fprintf(stderr, + "%s: parse error. file %s/%s line '%s'\n", + __func__, dir, file, buf); + rc = -1; + } else + *vp = val; + } + return rc; +} + +/* + * Read through a directory and call a function for each entry. + */ +int +sa_dir_read(char *dir_name, int (*func)(struct dirent *dp, void *), void *arg) +{ + DIR *dir; + struct dirent *dp; + int error = 0; + + dir = opendir(dir_name); + if (dir == NULL) + error = errno; + else { + while ((dp = readdir(dir)) != NULL && error == 0) { + if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || + (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) + continue; + error = (*func)(dp, arg); + } + closedir(dir); + } + return error; +} + +/* + * Size of on-stack line buffers. + * These shouldn't be to large for a kernel stack frame. + */ +#define SA_LOG_BUF_LEN 200 /* on-stack line buffer size */ + +static const u_int32_t sa_table_growth = 16; /* entries to grow by */ + +/** sa_table_grow(tp, index) - add space to a table for index. + * + * @param tp pointer to sa_table structure. + * @param index - new index past the end of the current table. + * @returns new index, or -1 if table couldn't be grown. + * + * Note: if the table has never been used, and is still all zero, this works. + * + * Note: perhaps not safe for multithreading. Caller can lock the table + * externally, but reallocation can take a while, during which time the + * caller may not wish to hold the lock. + */ +int +sa_table_grow(struct sa_table *tp, u_int32_t index) +{ + u_int32_t new_size; + void **ap; + + if (index >= tp->st_size) { + new_size = index + sa_table_growth; + ap = realloc(tp->st_table, new_size * sizeof(*ap)); + if (ap == NULL) + return -1; + memset(ap + tp->st_size, 0, + (new_size - tp->st_size) * sizeof(*ap)); + tp->st_table = ap; + tp->st_size = new_size; + } + tp->st_limit = index + 1; + return index; +} + +/** sa_table_destroy(tp) - free memory used by table. + * + * @param tp pointer to sa_table structure. + */ +void +sa_table_destroy(struct sa_table *tp) +{ + if (tp->st_table) { + free(tp->st_table); + tp->st_table = NULL; + } + tp->st_limit = 0; + tp->st_size = 0; +} + +/** sa_table_destroy_all(tp) - free memory used by table, including entries. + * + * @param tp pointer to sa_table structure. + */ +void +sa_table_destroy_all(struct sa_table *tp) +{ + int i; + + if (tp->st_table) { + for (i = 0; i < tp->st_limit; i++) { + if (tp->st_table[i]) { + free(tp->st_table[i]); + tp->st_table[i] = NULL; + } + } + } + sa_table_destroy(tp); +} + +/** sa_table_iterate(tp, handler, arg) + * + * @param tp pointer to sa_table structure. + * @param handler function to be called for each non-NULL entry. + * @param arg argument for function. + */ +void +sa_table_iterate(struct sa_table *tp, + void (*handler)(void *ep, void *arg), + void *arg) +{ + int i; + void *ep; + + for (i = 0; i < tp->st_limit; i++) { + ep = tp->st_table[i]; + if (ep != NULL) + (*handler)(ep, arg); + } +} + +/** sa_table_search(tp, match, arg) + * + * @param tp pointer to sa_table structure. + * @param match function to compare entries with arg and + * return non-NULL if match. + * @param arg argument for match function. + * + * Note that the value found could actually be something not in the table + * if the match function is doing something clever, like returning a + * sub-structure of the table entry. + */ +void * +sa_table_search(struct sa_table *tp, void *(*match)(void *ep, void *arg), + void *arg) +{ + int i; + void *found = NULL; + void *ep; + + for (i = 0; i < tp->st_limit; i++) { + ep = tp->st_table[i]; + if (ep != NULL) { + found = (*match)(ep, arg); + if (found != NULL) + break; + } + } + return found; +} _______________________________________________ devel mailing list devel@open-fcoe.org https://lists.open-fcoe.org/mailman/listinfo/devel