/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2019 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, 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 General Public License for * more details. * * You should have received a copy of the GNU 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if _HAVE_DMALLOC_H #include #endif #include "config.h" #include "list.h" #include "status.h" #include "utils.h" /** */ #define TIMESTAMP_PATTERN "%b %d %T " /** * Name of the executable. It is the last section of invocation path. */ char *progname = NULL; /** */ static FILE *s_log = NULL; struct log_level_info log_level_infos[] = { [LOG_LEVEL_DEBUG] = {PREFIX_DEBUG, LOG_DEBUG}, [LOG_LEVEL_WARNING] = {PREFIX_WARNING, LOG_WARNING}, [LOG_LEVEL_INFO] = {PREFIX_INFO, LOG_INFO}, [LOG_LEVEL_ERROR] = {PREFIX_ERROR, LOG_ERR} }; /* * Function returns a content of a text file. See utils.h for details. */ char *get_text(const char *path, const char *name) { char temp[PATH_MAX]; snprintf(temp, sizeof(temp), "%s/%s", path, name); return buf_read(temp); } /* * Function returns integer value (1 or 0) based on a boolean value ('Y' or 'N') * read from a text file. See utils.h for details. */ int get_bool(const char *path, int defval, const char *name) { char *p = get_text(path, name); if (p) { if (*p == 'Y') defval = 1; else if (*p == 'N') defval = 0; free(p); } return defval; } /* * Function returns 64-bit unsigned integer value read from a text file. See * utils.h for details. */ uint64_t get_uint64(const char *path, uint64_t defval, const char *name) { char *p = get_text(path, name); uint64_t retval = defval; if (p) { if (sscanf(p, "%" SCNx64, &defval) == 1) retval = defval; free(p); } return retval; } /* * Function returns integer value read from a text file. * See utils.h for details. */ int get_int(const char *path, int defval, const char *name) { char *p = get_text(path, name); if (p) { defval = atoi(p); free(p); } return defval; } /** */ int scan_dir(const char *path, struct list *result) { struct dirent *dirent; int ret = 0; DIR *dir = opendir(path); if (!dir) return -1; list_init(result, NULL); while ((dirent = readdir(dir)) != NULL) { char *str; size_t len; if ((strcmp(dirent->d_name, ".") == 0) || (strcmp(dirent->d_name, "..")) == 0) continue; len = strlen(path) + strlen(dirent->d_name) + 2; str = malloc(len); if (!str) { ret = -1; list_erase(result); break; } snprintf(str, len, "%s/%s", path, dirent->d_name); list_append(result, str); } closedir(dir); return ret; } /** */ static int _is_virtual(int dev_type) { switch (dev_type) { case 0: /* sysfs */ case 3: /* procfs */ return 1; } return 0; } /** */ ssize_t buf_write(const char *path, const char *buf) { int fd; ssize_t size = -1; if (path == NULL) __set_errno_and_return(EINVAL); if ((buf == NULL) || (strlen(buf) == 0)) __set_errno_and_return(ENODATA); fd = open(path, O_WRONLY); if (fd >= 0) { size = write(fd, buf, strlen(buf)); close(fd); } return size; } /** */ char *buf_read(const char *path) { struct stat st; int fd, size; char *buf, *t; if (stat(path, &st) < 0) return NULL; if (st.st_size == 0) { if (!_is_virtual(st.st_dev)) return NULL; st.st_size = st.st_blksize; } if (_is_virtual(st.st_dev)) st.st_size = st.st_blksize; t = buf = malloc(st.st_size); if (buf) { fd = open(path, O_RDONLY); if (fd >= 0) { size = read(fd, buf, st.st_size); close(fd); if (size > 0) t = strchrnul(buf, '\n'); } *t = '\0'; } return buf; } /** */ void get_id(const char *path, struct device_id *did) { char *t, *p; if (did && path) { did->major = did->minor = -1; p = buf_read(path); if (p) { t = strchr(p, ':'); if (t) { *(t++) = '\0'; did->major = atoi(p); did->minor = atoi(t); } free(p); } } } /** */ static void _log_timestamp(void) { time_t timestamp; struct tm *t; char buf[30]; timestamp = time(NULL); t = localtime(×tamp); if (t) { strftime(buf, sizeof(buf), TIMESTAMP_PATTERN, t); fprintf(s_log, "%s", buf); } } /** */ int log_open(const char *path) { if (s_log) log_close(); s_log = fopen(path, "a"); if (s_log == NULL) return -1; return 0; } /** */ void log_close(void) { if (s_log) { fflush(s_log); fclose(s_log); s_log = NULL; } } /** */ void _log(enum log_level_enum loglevel, const char *buf, ...) { va_list vl; struct log_level_info *lli = &log_level_infos[loglevel]; if (s_log == NULL) log_open(conf.log_path); if (conf.log_level >= loglevel) { char msg[4096]; va_start(vl, buf); vsnprintf(msg, sizeof(msg), buf, vl); va_end(vl); if (s_log) { _log_timestamp(); fprintf(s_log, "%s", lli->prefix); fprintf(s_log, "%s\n", msg); fflush(s_log); } syslog(lli->priority, "%s", msg); } } /** * @brief Sets program's short name. * * This is internal function of monitor service. It is used to extract the name * of executable file from command line argument. * * @param[in] invocation_name - the pointer to command line argument * with the invocation name. * * @return The function does not return a value. */ void set_invocation_name(char *invocation_name) { #ifdef program_invocation_short_name (void)invocation_name; progname = program_invocation_short_name; #else char *t = strrchr(invocation_name, PATH_DELIM); if (t) progname = t + 1; else progname = invocation_name; #endif /* program_invocation_short_name */ } /** */ char *str_cpy(char *dest, const char *src, size_t size) { strncpy(dest, src, size - 1); dest[size - 1] = '\0'; return dest; } /** */ char *str_dup(const char *src) { char *ret; if (!src) return NULL; ret = strdup(src); if (!ret) { log_error("Cannot duplicate string"); exit(EXIT_FAILURE); } return ret; } char *get_path_hostN(const char *path) { char *c = NULL, *s = NULL, *p = str_dup(path); if (!p) return NULL; c = strstr(p, "host"); if (!c) goto end; s = strchr(c, '/'); if (!s) goto end; *s = 0; s = str_dup(c); end: free(p); return s; } char *get_path_component_rev(const char *path, int index) { int i; char *c = NULL, *p = str_dup(path); char *result = NULL; for (i = 0; i <= index; i++) { if (c) *c = '\0'; c = strrchr(p, '/'); } if (c) result = str_dup(c + 1); free(p); return result; } char *truncate_path_component_rev(const char *path, int index) { int i; char *c = NULL, *p = str_dup(path); if (!p) return NULL; for (i = 0; i <= index; i++) { if (c) *c = '\0'; c = strrchr(p, '/'); } c = str_dup(p); free(p); return c; } int match_string(const char *string, const char *pattern) { int status; regex_t regex; if (!string || !pattern) return 0; if (strcmp(string, pattern) == 0) return 1; status = regcomp(®ex, pattern, REG_EXTENDED); if (status != 0) { log_debug("regecomp failed, ret=%d", status); return 0; } status = regexec(®ex, string, 0, NULL, 0); if (status != 0) return 0; return 1; } int get_log_fd(void) { if (s_log) return fileno(s_log); return -1; } void print_opt(const char *long_opt, const char *short_opt, const char *desc) { printf("%-20s%-10s%s\n", long_opt, short_opt, desc); } /** * @brief Sets the path to local log file. * * This function sets the path and * file name of log file. The function checks if the specified path is valid. In * case of incorrect path the function does nothing. * * @param[in] path new location and name of log file. * * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. * The following status code are returned: * * STATUS_INVALID_PATH the given path is invalid. * STATUS_FILE_OPEN_ERROR unable to open a log file i.e. because of * insufficient privileges. */ status_t set_log_path(const char *path) { char temp[PATH_MAX]; char log_file[PATH_MAX]; char *resolved, *logdir, *logfile, *cpath; /* * Extract directory from path */ cpath = str_dup(path); logdir = dirname(cpath); /* * Check if directory exists */ resolved = realpath(logdir, temp); if (resolved == NULL) { printf("%s: %s\n", strerror(errno), logdir); free(cpath); return STATUS_INVALID_PATH; } free(cpath); cpath = str_dup(path); logfile = basename(cpath); snprintf(log_file, sizeof(log_file), "%s/%s", resolved, logfile); free(cpath); if (conf.log_path) free(conf.log_path); conf.log_path = str_dup(log_file); return STATUS_SUCCESS; } /** * Internal array with option tokens. It is used to help parse command line * long options. */ struct option longopt_all[] = { [OPT_ALL] = {"all", no_argument, NULL, '\0'}, [OPT_CONFIG] = {"config", required_argument, NULL, 'c'}, [OPT_DEBUG] = {"debug", no_argument, NULL, '\0'}, [OPT_ERROR] = {"error", no_argument, NULL, '\0'}, [OPT_HELP] = {"help", no_argument, NULL, 'h'}, [OPT_INFO] = {"info", no_argument, NULL, '\0'}, [OPT_INTERVAL] = {"interval", required_argument, NULL, 't'}, [OPT_LOG] = {"log", required_argument, NULL, 'l'}, [OPT_QUIET] = {"quiet", no_argument, NULL, '\0'}, [OPT_VERSION] = {"version", no_argument, NULL, 'v'}, [OPT_WARNING] = {"warning", no_argument, NULL, '\0'}, [OPT_LOG_LEVEL] = {"log-level", required_argument, NULL, '\0'}, [OPT_LIST_CTRL] = {"list-controllers", no_argument, NULL, 'L'}, [OPT_LISTED_ONLY] = {"listed-only", no_argument, NULL, 'x'}, [OPT_FOREGROUND] = {"foreground", no_argument, NULL, '\0'}, [OPT_NULL_ELEMENT] = {NULL, no_argument, NULL, '\0'} }; void setup_options(struct option **_longopt, char **_shortopt, int *options, int options_nr) { struct option *longopt; char *shortopt; int i, j = 0; struct option *opt; longopt = malloc(sizeof(struct option) * (options_nr + 1)); shortopt = calloc(options_nr * 2 + 1, sizeof(char)); if (!longopt || !shortopt) { fprintf(stderr, "Out of memory\n"); exit(STATUS_OUT_OF_MEMORY); } for (i = 0; i < options_nr; i++) { opt = &longopt_all[options[i]]; longopt[i] = *opt; if (opt->val != '\0') { shortopt[j++] = (char) opt->val; if (opt->has_arg) shortopt[j++] = ':'; } } longopt[i] = longopt_all[OPT_NULL_ELEMENT]; shortopt[j] = '\0'; *_longopt = longopt; *_shortopt = shortopt; } /** * @brief Gets id for given CLI option which corresponds to value from longopt * table. * * This is internal function of monitor service. The function maps given string * to the value from longopt enum and returns id of matched element. Generic * parameters allow to use this function for any CLI options-table which bases * on option struct. * * @param[in] optarg String containing value given by user in CLI. * * @return integer id if successful, otherwise a -1. */ int get_option_id(const char *optarg) { int i = 0; while (longopt_all[i].name != NULL) { if (strcmp(longopt_all[i].name, optarg) == 0) return i; i++; } return -1; } /** * @brief Sets verbose variable to given level. * * This is internal function of monitor service. The function maps given level * to the value from verbose_level enum and sets verbose value to ledmon * configuration. * * @param[in] log_level required new log_level. * * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. */ status_t set_verbose_level(int log_level) { int new_verbose = -1; switch (log_level) { case OPT_ALL: new_verbose = LOG_LEVEL_ALL; break; case OPT_DEBUG: new_verbose = LOG_LEVEL_DEBUG; break; case OPT_ERROR: new_verbose = LOG_LEVEL_ERROR; break; case OPT_INFO: new_verbose = LOG_LEVEL_INFO; break; case OPT_QUIET: new_verbose = LOG_LEVEL_QUIET; break; case OPT_WARNING: new_verbose = LOG_LEVEL_WARNING; break; } if (new_verbose != -1) { conf.log_level = new_verbose; return STATUS_SUCCESS; } return STATUS_CMDLINE_ERROR; } const char *ibpi2str(enum ibpi_pattern ibpi) { #ifdef _TEST_CONFIG return NULL; #else static char buf[20]; const char *ret; if (ibpi >= 0 && ibpi < ibpi_pattern_count) ret = ibpi_str[ibpi]; else ret = NULL; if (!ret) { snprintf(buf, sizeof(buf), "(unknown: %u)", ibpi); ret = buf; } return ret; #endif }