/* * Copyright (c) 2008, 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 "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; } /* * Make a printable NUL-terminated copy of the string. * The source buffer might not be NUL-terminated. */ 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; } /** 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; } /** 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. */ const char * sa_enum_decode(char *buf, size_t len, const struct sa_nameval *tp, u_int32_t val) { for (; tp->nv_name != NULL; tp++) { if (tp->nv_val == val) return tp->nv_name; } snprintf(buf, len, "Unknown (code 0x%X)", val); return buf; } /* * 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; }