diff --git a/cmd/zed/zed_conf.c b/cmd/zed/zed_conf.c index 638f728e1567..61529bd8f953 100644 --- a/cmd/zed/zed_conf.c +++ b/cmd/zed/zed_conf.c @@ -1,738 +1,738 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license from the top-level * OPENSOLARIS.LICENSE or . * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each file * and include the License file from the top-level OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049). * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "zed.h" #include "zed_conf.h" #include "zed_file.h" #include "zed_log.h" #include "zed_strings.h" /* * Return a new configuration with default values. */ struct zed_conf * zed_conf_create(void) { struct zed_conf *zcp; zcp = calloc(1, sizeof (*zcp)); if (!zcp) goto nomem; zcp->syslog_facility = LOG_DAEMON; zcp->min_events = ZED_MIN_EVENTS; zcp->max_events = ZED_MAX_EVENTS; zcp->pid_fd = -1; zcp->zedlets = NULL; /* created via zed_conf_scan_dir() */ zcp->state_fd = -1; /* opened via zed_conf_open_state() */ zcp->zfs_hdl = NULL; /* opened via zed_event_init() */ zcp->zevent_fd = -1; /* opened via zed_event_init() */ if (!(zcp->conf_file = strdup(ZED_CONF_FILE))) goto nomem; if (!(zcp->pid_file = strdup(ZED_PID_FILE))) goto nomem; if (!(zcp->zedlet_dir = strdup(ZED_ZEDLET_DIR))) goto nomem; if (!(zcp->state_file = strdup(ZED_STATE_FILE))) goto nomem; return (zcp); nomem: zed_log_die("Failed to create conf: %s", strerror(errno)); return (NULL); } /* * Destroy the configuration [zcp]. * * Note: zfs_hdl & zevent_fd are destroyed via zed_event_fini(). */ void zed_conf_destroy(struct zed_conf *zcp) { if (!zcp) return; if (zcp->state_fd >= 0) { if (close(zcp->state_fd) < 0) zed_log_msg(LOG_WARNING, "Failed to close state file \"%s\": %s", zcp->state_file, strerror(errno)); zcp->state_fd = -1; } if (zcp->pid_file) { if ((unlink(zcp->pid_file) < 0) && (errno != ENOENT)) zed_log_msg(LOG_WARNING, "Failed to remove PID file \"%s\": %s", zcp->pid_file, strerror(errno)); } if (zcp->pid_fd >= 0) { if (close(zcp->pid_fd) < 0) zed_log_msg(LOG_WARNING, "Failed to close PID file \"%s\": %s", zcp->pid_file, strerror(errno)); zcp->pid_fd = -1; } if (zcp->conf_file) { free(zcp->conf_file); zcp->conf_file = NULL; } if (zcp->pid_file) { free(zcp->pid_file); zcp->pid_file = NULL; } if (zcp->zedlet_dir) { free(zcp->zedlet_dir); zcp->zedlet_dir = NULL; } if (zcp->state_file) { free(zcp->state_file); zcp->state_file = NULL; } if (zcp->zedlets) { zed_strings_destroy(zcp->zedlets); zcp->zedlets = NULL; } free(zcp); } /* * Display command-line help and exit. * * If [got_err] is 0, output to stdout and exit normally; * otherwise, output to stderr and exit with a failure status. */ static void _zed_conf_display_help(const char *prog, int got_err) { FILE *fp = got_err ? stderr : stdout; int w1 = 4; /* width of leading whitespace */ int w2 = 8; /* width of L-justified option field */ fprintf(fp, "Usage: %s [OPTION]...\n", (prog ? prog : "zed")); fprintf(fp, "\n"); fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-h", "Display help."); fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-L", "Display license information."); fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-V", "Display version information."); fprintf(fp, "\n"); fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-v", "Be verbose."); fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-f", "Force daemon to run."); fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-F", "Run daemon in the foreground."); fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-M", "Lock all pages in memory."); fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-Z", "Zero state file."); fprintf(fp, "\n"); #if 0 fprintf(fp, "%*c%*s %s [%s]\n", w1, 0x20, -w2, "-c FILE", "Read configuration from FILE.", ZED_CONF_FILE); #endif fprintf(fp, "%*c%*s %s [%s]\n", w1, 0x20, -w2, "-d DIR", "Read enabled ZEDLETs from DIR.", ZED_ZEDLET_DIR); fprintf(fp, "%*c%*s %s [%s]\n", w1, 0x20, -w2, "-p FILE", "Write daemon's PID to FILE.", ZED_PID_FILE); fprintf(fp, "%*c%*s %s [%s]\n", w1, 0x20, -w2, "-s FILE", "Write daemon's state to FILE.", ZED_STATE_FILE); fprintf(fp, "\n"); exit(got_err ? EXIT_FAILURE : EXIT_SUCCESS); } /* * Display license information to stdout and exit. */ static void _zed_conf_display_license(void) { const char **pp; const char *text[] = { "The ZFS Event Daemon (ZED) is distributed under the terms of the", " Common Development and Distribution License (CDDL-1.0)", " .", "Developed at Lawrence Livermore National Laboratory" " (LLNL-CODE-403049).", "Copyright (C) 2013-2014" " Lawrence Livermore National Security, LLC.", "", NULL }; for (pp = text; *pp; pp++) printf("%s\n", *pp); exit(EXIT_SUCCESS); } /* * Display version information to stdout and exit. */ static void _zed_conf_display_version(void) { printf("%s-%s-%s\n", ZFS_META_NAME, ZFS_META_VERSION, ZFS_META_RELEASE); exit(EXIT_SUCCESS); } /* * Copy the [path] string to the [resultp] ptr. * If [path] is not an absolute path, prefix it with the current working dir. * If [resultp] is non-null, free its existing string before assignment. */ static void _zed_conf_parse_path(char **resultp, const char *path) { char buf[PATH_MAX]; assert(resultp != NULL); assert(path != NULL); if (*resultp) free(*resultp); if (path[0] == '/') { *resultp = strdup(path); } else if (!getcwd(buf, sizeof (buf))) { zed_log_die("Failed to get current working dir: %s", strerror(errno)); } else if (strlcat(buf, "/", sizeof (buf)) >= sizeof (buf)) { zed_log_die("Failed to copy path: %s", strerror(ENAMETOOLONG)); } else if (strlcat(buf, path, sizeof (buf)) >= sizeof (buf)) { zed_log_die("Failed to copy path: %s", strerror(ENAMETOOLONG)); } else { *resultp = strdup(buf); } if (!*resultp) zed_log_die("Failed to copy path: %s", strerror(ENOMEM)); } /* * Parse the command-line options into the configuration [zcp]. */ void zed_conf_parse_opts(struct zed_conf *zcp, int argc, char **argv) { const char * const opts = ":hLVc:d:p:s:vfFMZ"; int opt; if (!zcp || !argv || !argv[0]) zed_log_die("Failed to parse options: Internal error"); opterr = 0; /* suppress default getopt err msgs */ while ((opt = getopt(argc, argv, opts)) != -1) { switch (opt) { case 'h': _zed_conf_display_help(argv[0], EXIT_SUCCESS); break; case 'L': _zed_conf_display_license(); break; case 'V': _zed_conf_display_version(); break; case 'c': _zed_conf_parse_path(&zcp->conf_file, optarg); break; case 'd': _zed_conf_parse_path(&zcp->zedlet_dir, optarg); break; case 'p': _zed_conf_parse_path(&zcp->pid_file, optarg); break; case 's': _zed_conf_parse_path(&zcp->state_file, optarg); break; case 'v': zcp->do_verbose = 1; break; case 'f': zcp->do_force = 1; break; case 'F': zcp->do_foreground = 1; break; case 'M': zcp->do_memlock = 1; break; case 'Z': zcp->do_zero = 1; break; case '?': default: if (optopt == '?') _zed_conf_display_help(argv[0], EXIT_SUCCESS); fprintf(stderr, "%s: %s '-%c'\n\n", argv[0], "Invalid option", optopt); _zed_conf_display_help(argv[0], EXIT_FAILURE); break; } } } /* * Parse the configuration file into the configuration [zcp]. * * FIXME: Not yet implemented. */ void zed_conf_parse_file(struct zed_conf *zcp) { if (!zcp) zed_log_die("Failed to parse config: %s", strerror(EINVAL)); } /* * Scan the [zcp] zedlet_dir for files to exec based on the event class. * Files must be executable by user, but not writable by group or other. * Dotfiles are ignored. * * Return 0 on success with an updated set of zedlets, * or -1 on error with errno set. * * FIXME: Check if zedlet_dir and all parent dirs are secure. */ int zed_conf_scan_dir(struct zed_conf *zcp) { zed_strings_t *zedlets; DIR *dirp; struct dirent *direntp; char pathname[PATH_MAX]; struct stat st; int n; if (!zcp) { errno = EINVAL; zed_log_msg(LOG_ERR, "Failed to scan zedlet dir: %s", strerror(errno)); return (-1); } zedlets = zed_strings_create(); if (!zedlets) { errno = ENOMEM; zed_log_msg(LOG_WARNING, "Failed to scan dir \"%s\": %s", zcp->zedlet_dir, strerror(errno)); return (-1); } dirp = opendir(zcp->zedlet_dir); if (!dirp) { int errno_bak = errno; zed_log_msg(LOG_WARNING, "Failed to open dir \"%s\": %s", zcp->zedlet_dir, strerror(errno)); zed_strings_destroy(zedlets); errno = errno_bak; return (-1); } while ((direntp = readdir(dirp))) { if (direntp->d_name[0] == '.') continue; n = snprintf(pathname, sizeof (pathname), "%s/%s", zcp->zedlet_dir, direntp->d_name); if ((n < 0) || (n >= sizeof (pathname))) { zed_log_msg(LOG_WARNING, "Failed to stat \"%s\": %s", direntp->d_name, strerror(ENAMETOOLONG)); continue; } if (stat(pathname, &st) < 0) { zed_log_msg(LOG_WARNING, "Failed to stat \"%s\": %s", pathname, strerror(errno)); continue; } if (!S_ISREG(st.st_mode)) { zed_log_msg(LOG_INFO, "Ignoring \"%s\": not a regular file", direntp->d_name); continue; } if ((st.st_uid != 0) && !zcp->do_force) { zed_log_msg(LOG_NOTICE, "Ignoring \"%s\": not owned by root", direntp->d_name); continue; } if (!(st.st_mode & S_IXUSR)) { zed_log_msg(LOG_INFO, "Ignoring \"%s\": not executable by user", direntp->d_name); continue; } if ((st.st_mode & S_IWGRP) & !zcp->do_force) { zed_log_msg(LOG_NOTICE, "Ignoring \"%s\": writable by group", direntp->d_name); continue; } if ((st.st_mode & S_IWOTH) & !zcp->do_force) { zed_log_msg(LOG_NOTICE, "Ignoring \"%s\": writable by other", direntp->d_name); continue; } - if (zed_strings_add(zedlets, direntp->d_name) < 0) { + if (zed_strings_add(zedlets, NULL, direntp->d_name) < 0) { zed_log_msg(LOG_WARNING, "Failed to register \"%s\": %s", direntp->d_name, strerror(errno)); continue; } if (zcp->do_verbose) zed_log_msg(LOG_INFO, "Registered zedlet \"%s\"", direntp->d_name); } if (closedir(dirp) < 0) { int errno_bak = errno; zed_log_msg(LOG_WARNING, "Failed to close dir \"%s\": %s", zcp->zedlet_dir, strerror(errno)); zed_strings_destroy(zedlets); errno = errno_bak; return (-1); } if (zcp->zedlets) zed_strings_destroy(zcp->zedlets); zcp->zedlets = zedlets; return (0); } /* * Write the PID file specified in [zcp]. * Return 0 on success, -1 on error. * * This must be called after fork()ing to become a daemon (so the correct PID * is recorded), but before daemonization is complete and the parent process * exits (for synchronization with systemd). */ int zed_conf_write_pid(struct zed_conf *zcp) { const mode_t dirmode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; const mode_t filemode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; char buf[PATH_MAX]; int n; char *p; mode_t mask; int rv; if (!zcp || !zcp->pid_file) { errno = EINVAL; zed_log_msg(LOG_ERR, "Failed to create PID file: %s", strerror(errno)); return (-1); } assert(zcp->pid_fd == -1); /* * Create PID file directory if needed. */ n = strlcpy(buf, zcp->pid_file, sizeof (buf)); if (n >= sizeof (buf)) { errno = ENAMETOOLONG; zed_log_msg(LOG_ERR, "Failed to create PID file: %s", strerror(errno)); goto err; } p = strrchr(buf, '/'); if (p) *p = '\0'; if ((mkdirp(buf, dirmode) < 0) && (errno != EEXIST)) { zed_log_msg(LOG_ERR, "Failed to create directory \"%s\": %s", buf, strerror(errno)); goto err; } /* * Obtain PID file lock. */ mask = umask(0); umask(mask | 022); zcp->pid_fd = open(zcp->pid_file, (O_RDWR | O_CREAT), filemode); umask(mask); if (zcp->pid_fd < 0) { zed_log_msg(LOG_ERR, "Failed to open PID file \"%s\": %s", zcp->pid_file, strerror(errno)); goto err; } rv = zed_file_lock(zcp->pid_fd); if (rv < 0) { zed_log_msg(LOG_ERR, "Failed to lock PID file \"%s\": %s", zcp->pid_file, strerror(errno)); goto err; } else if (rv > 0) { pid_t pid = zed_file_is_locked(zcp->pid_fd); if (pid < 0) { zed_log_msg(LOG_ERR, "Failed to test lock on PID file \"%s\"", zcp->pid_file); } else if (pid > 0) { zed_log_msg(LOG_ERR, "Found PID %d bound to PID file \"%s\"", pid, zcp->pid_file); } else { zed_log_msg(LOG_ERR, "Inconsistent lock state on PID file \"%s\"", zcp->pid_file); } goto err; } /* * Write PID file. */ n = snprintf(buf, sizeof (buf), "%d\n", (int) getpid()); if ((n < 0) || (n >= sizeof (buf))) { errno = ERANGE; zed_log_msg(LOG_ERR, "Failed to write PID file \"%s\": %s", zcp->pid_file, strerror(errno)); } else if (zed_file_write_n(zcp->pid_fd, buf, n) != n) { zed_log_msg(LOG_ERR, "Failed to write PID file \"%s\": %s", zcp->pid_file, strerror(errno)); } else if (fdatasync(zcp->pid_fd) < 0) { zed_log_msg(LOG_ERR, "Failed to sync PID file \"%s\": %s", zcp->pid_file, strerror(errno)); } else { return (0); } err: if (zcp->pid_fd >= 0) { (void) close(zcp->pid_fd); zcp->pid_fd = -1; } return (-1); } /* * Open and lock the [zcp] state_file. * Return 0 on success, -1 on error. * * FIXME: Move state information into kernel. */ int zed_conf_open_state(struct zed_conf *zcp) { char dirbuf[PATH_MAX]; mode_t dirmode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; int n; char *p; int rv; if (!zcp || !zcp->state_file) { errno = EINVAL; zed_log_msg(LOG_ERR, "Failed to open state file: %s", strerror(errno)); return (-1); } n = strlcpy(dirbuf, zcp->state_file, sizeof (dirbuf)); if (n >= sizeof (dirbuf)) { errno = ENAMETOOLONG; zed_log_msg(LOG_WARNING, "Failed to open state file: %s", strerror(errno)); return (-1); } p = strrchr(dirbuf, '/'); if (p) *p = '\0'; if ((mkdirp(dirbuf, dirmode) < 0) && (errno != EEXIST)) { zed_log_msg(LOG_WARNING, "Failed to create directory \"%s\": %s", dirbuf, strerror(errno)); return (-1); } if (zcp->state_fd >= 0) { if (close(zcp->state_fd) < 0) { zed_log_msg(LOG_WARNING, "Failed to close state file \"%s\": %s", zcp->state_file, strerror(errno)); return (-1); } } if (zcp->do_zero) (void) unlink(zcp->state_file); zcp->state_fd = open(zcp->state_file, (O_RDWR | O_CREAT), (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); if (zcp->state_fd < 0) { zed_log_msg(LOG_WARNING, "Failed to open state file \"%s\": %s", zcp->state_file, strerror(errno)); return (-1); } rv = zed_file_lock(zcp->state_fd); if (rv < 0) { zed_log_msg(LOG_WARNING, "Failed to lock state file \"%s\": %s", zcp->state_file, strerror(errno)); return (-1); } if (rv > 0) { pid_t pid = zed_file_is_locked(zcp->state_fd); if (pid < 0) { zed_log_msg(LOG_WARNING, "Failed to test lock on state file \"%s\"", zcp->state_file); } else if (pid > 0) { zed_log_msg(LOG_WARNING, "Found PID %d bound to state file \"%s\"", pid, zcp->state_file); } else { zed_log_msg(LOG_WARNING, "Inconsistent lock state on state file \"%s\"", zcp->state_file); } return (-1); } return (0); } /* * Read the opened [zcp] state_file to obtain the eid & etime of the last event * processed. Write the state from the last event to the [eidp] & [etime] args * passed by reference. Note that etime[] is an array of size 2. * Return 0 on success, -1 on error. */ int zed_conf_read_state(struct zed_conf *zcp, uint64_t *eidp, int64_t etime[]) { ssize_t len; struct iovec iov[3]; ssize_t n; if (!zcp || !eidp || !etime) { errno = EINVAL; zed_log_msg(LOG_ERR, "Failed to read state file: %s", strerror(errno)); return (-1); } if (lseek(zcp->state_fd, 0, SEEK_SET) == (off_t) -1) { zed_log_msg(LOG_WARNING, "Failed to reposition state file offset: %s", strerror(errno)); return (-1); } len = 0; iov[0].iov_base = eidp; len += iov[0].iov_len = sizeof (*eidp); iov[1].iov_base = &etime[0]; len += iov[1].iov_len = sizeof (etime[0]); iov[2].iov_base = &etime[1]; len += iov[2].iov_len = sizeof (etime[1]); n = readv(zcp->state_fd, iov, 3); if (n == 0) { *eidp = 0; } else if (n < 0) { zed_log_msg(LOG_WARNING, "Failed to read state file \"%s\": %s", zcp->state_file, strerror(errno)); return (-1); } else if (n != len) { errno = EIO; zed_log_msg(LOG_WARNING, "Failed to read state file \"%s\": Read %d of %d bytes", zcp->state_file, n, len); return (-1); } return (0); } /* * Write the [eid] & [etime] of the last processed event to the opened * [zcp] state_file. Note that etime[] is an array of size 2. * Return 0 on success, -1 on error. */ int zed_conf_write_state(struct zed_conf *zcp, uint64_t eid, int64_t etime[]) { ssize_t len; struct iovec iov[3]; ssize_t n; if (!zcp) { errno = EINVAL; zed_log_msg(LOG_ERR, "Failed to write state file: %s", strerror(errno)); return (-1); } if (lseek(zcp->state_fd, 0, SEEK_SET) == (off_t) -1) { zed_log_msg(LOG_WARNING, "Failed to reposition state file offset: %s", strerror(errno)); return (-1); } len = 0; iov[0].iov_base = &eid; len += iov[0].iov_len = sizeof (eid); iov[1].iov_base = &etime[0]; len += iov[1].iov_len = sizeof (etime[0]); iov[2].iov_base = &etime[1]; len += iov[2].iov_len = sizeof (etime[1]); n = writev(zcp->state_fd, iov, 3); if (n < 0) { zed_log_msg(LOG_WARNING, "Failed to write state file \"%s\": %s", zcp->state_file, strerror(errno)); return (-1); } if (n != len) { errno = EIO; zed_log_msg(LOG_WARNING, "Failed to write state file \"%s\": Wrote %d of %d bytes", zcp->state_file, n, len); return (-1); } if (fdatasync(zcp->state_fd) < 0) { zed_log_msg(LOG_WARNING, "Failed to sync state file \"%s\": %s", zcp->state_file, strerror(errno)); return (-1); } return (0); } diff --git a/cmd/zed/zed_event.c b/cmd/zed/zed_event.c index f3f201800c81..6f6c5ba42c5a 100644 --- a/cmd/zed/zed_event.c +++ b/cmd/zed/zed_event.c @@ -1,833 +1,864 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license from the top-level * OPENSOLARIS.LICENSE or . * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each file * and include the License file from the top-level OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049). * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. */ #include #include #include #include /* FIXME: Replace with libzfs_core. */ #include #include #include #include #include #include #include #include #include "zed.h" #include "zed_conf.h" #include "zed_exec.h" #include "zed_file.h" #include "zed_log.h" #include "zed_strings.h" +#define MAXBUF 4096 + /* * Open the libzfs interface. */ void zed_event_init(struct zed_conf *zcp) { if (!zcp) zed_log_die("Failed zed_event_init: %s", strerror(EINVAL)); zcp->zfs_hdl = libzfs_init(); if (!zcp->zfs_hdl) zed_log_die("Failed to initialize libzfs"); zcp->zevent_fd = open(ZFS_DEV, O_RDWR); if (zcp->zevent_fd < 0) zed_log_die("Failed to open \"%s\": %s", ZFS_DEV, strerror(errno)); } /* * Close the libzfs interface. */ void zed_event_fini(struct zed_conf *zcp) { if (!zcp) zed_log_die("Failed zed_event_fini: %s", strerror(EINVAL)); if (zcp->zevent_fd >= 0) { if (close(zcp->zevent_fd) < 0) zed_log_msg(LOG_WARNING, "Failed to close \"%s\": %s", ZFS_DEV, strerror(errno)); zcp->zevent_fd = -1; } if (zcp->zfs_hdl) { libzfs_fini(zcp->zfs_hdl); zcp->zfs_hdl = NULL; } } /* * Seek to the event specified by [saved_eid] and [saved_etime]. * This protects against processing a given event more than once. * Return 0 upon a successful seek to the specified event, or -1 otherwise. * * A zevent is considered to be uniquely specified by its (eid,time) tuple. * The unsigned 64b eid is set to 1 when the kernel module is loaded, and * incremented by 1 for each new event. Since the state file can persist * across a kernel module reload, the time must be checked to ensure a match. */ int zed_event_seek(struct zed_conf *zcp, uint64_t saved_eid, int64_t saved_etime[]) { uint64_t eid; int found; nvlist_t *nvl; int n_dropped; int64_t *etime; uint_t nelem; int rv; if (!zcp) { errno = EINVAL; zed_log_msg(LOG_ERR, "Failed to seek zevent: %s", strerror(errno)); return (-1); } eid = 0; found = 0; while ((eid < saved_eid) && !found) { rv = zpool_events_next(zcp->zfs_hdl, &nvl, &n_dropped, ZEVENT_NONBLOCK, zcp->zevent_fd); if ((rv != 0) || !nvl) break; if (n_dropped > 0) { zed_log_msg(LOG_WARNING, "Missed %d events", n_dropped); /* * FIXME: Increase max size of event nvlist in * /sys/module/zfs/parameters/zfs_zevent_len_max ? */ } if (nvlist_lookup_uint64(nvl, "eid", &eid) != 0) { zed_log_msg(LOG_WARNING, "Failed to lookup zevent eid"); } else if (nvlist_lookup_int64_array(nvl, "time", &etime, &nelem) != 0) { zed_log_msg(LOG_WARNING, "Failed to lookup zevent time (eid=%llu)", eid); } else if (nelem != 2) { zed_log_msg(LOG_WARNING, "Failed to lookup zevent time (eid=%llu, nelem=%u)", eid, nelem); } else if ((eid != saved_eid) || (etime[0] != saved_etime[0]) || (etime[1] != saved_etime[1])) { /* no-op */ } else { found = 1; } free(nvl); } if (!found && (saved_eid > 0)) { if (zpool_events_seek(zcp->zfs_hdl, ZEVENT_SEEK_START, zcp->zevent_fd) < 0) zed_log_msg(LOG_WARNING, "Failed to seek to eid=0"); else eid = 0; } zed_log_msg(LOG_NOTICE, "Processing events since eid=%llu", eid); return (found ? 0 : -1); } static int _zed_event_convert_int8_array(char *buf, int buflen, nvpair_t *nvp) { int8_t *i8p; uint_t nelem; uint_t i; char *p; int n; assert(buf != NULL); (void) nvpair_value_int8_array(nvp, &i8p, &nelem); for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) { n = snprintf(p, buflen, "%d ", i8p[i]); if ((n < 0) || (n >= buflen)) { *buf = '\0'; return (-1); } p += n; buflen -= n; } if (nelem > 0) *--p = '\0'; return (p - buf); } static int _zed_event_convert_uint8_array(char *buf, int buflen, nvpair_t *nvp) { uint8_t *u8p; uint_t nelem; uint_t i; char *p; int n; assert(buf != NULL); (void) nvpair_value_uint8_array(nvp, &u8p, &nelem); for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) { n = snprintf(p, buflen, "%u ", u8p[i]); if ((n < 0) || (n >= buflen)) { *buf = '\0'; return (-1); } p += n; buflen -= n; } if (nelem > 0) *--p = '\0'; return (p - buf); } static int _zed_event_convert_int16_array(char *buf, int buflen, nvpair_t *nvp) { int16_t *i16p; uint_t nelem; uint_t i; char *p; int n; assert(buf != NULL); (void) nvpair_value_int16_array(nvp, &i16p, &nelem); for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) { n = snprintf(p, buflen, "%d ", i16p[i]); if ((n < 0) || (n >= buflen)) { *buf = '\0'; return (-1); } p += n; buflen -= n; } if (nelem > 0) *--p = '\0'; return (p - buf); } static int _zed_event_convert_uint16_array(char *buf, int buflen, nvpair_t *nvp) { uint16_t *u16p; uint_t nelem; uint_t i; char *p; int n; assert(buf != NULL); (void) nvpair_value_uint16_array(nvp, &u16p, &nelem); for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) { n = snprintf(p, buflen, "%u ", u16p[i]); if ((n < 0) || (n >= buflen)) { *buf = '\0'; return (-1); } p += n; buflen -= n; } if (nelem > 0) *--p = '\0'; return (p - buf); } static int _zed_event_convert_int32_array(char *buf, int buflen, nvpair_t *nvp) { int32_t *i32p; uint_t nelem; uint_t i; char *p; int n; assert(buf != NULL); (void) nvpair_value_int32_array(nvp, &i32p, &nelem); for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) { n = snprintf(p, buflen, "%d ", i32p[i]); if ((n < 0) || (n >= buflen)) { *buf = '\0'; return (-1); } p += n; buflen -= n; } if (nelem > 0) *--p = '\0'; return (p - buf); } static int _zed_event_convert_uint32_array(char *buf, int buflen, nvpair_t *nvp) { uint32_t *u32p; uint_t nelem; uint_t i; char *p; int n; assert(buf != NULL); (void) nvpair_value_uint32_array(nvp, &u32p, &nelem); for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) { n = snprintf(p, buflen, "%u ", u32p[i]); if ((n < 0) || (n >= buflen)) { *buf = '\0'; return (-1); } p += n; buflen -= n; } if (nelem > 0) *--p = '\0'; return (p - buf); } static int _zed_event_convert_int64_array(char *buf, int buflen, nvpair_t *nvp) { int64_t *i64p; uint_t nelem; uint_t i; char *p; int n; assert(buf != NULL); (void) nvpair_value_int64_array(nvp, &i64p, &nelem); for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) { n = snprintf(p, buflen, "%lld ", (u_longlong_t) i64p[i]); if ((n < 0) || (n >= buflen)) { *buf = '\0'; return (-1); } p += n; buflen -= n; } if (nelem > 0) *--p = '\0'; return (p - buf); } static int _zed_event_convert_uint64_array(char *buf, int buflen, nvpair_t *nvp, const char *fmt) { uint64_t *u64p; uint_t nelem; uint_t i; char *p; int n; assert(buf != NULL); (void) nvpair_value_uint64_array(nvp, &u64p, &nelem); for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) { n = snprintf(p, buflen, fmt, (u_longlong_t) u64p[i]); if ((n < 0) || (n >= buflen)) { *buf = '\0'; return (-1); } p += n; buflen -= n; } if (nelem > 0) *--p = '\0'; return (p - buf); } static int _zed_event_convert_string_array(char *buf, int buflen, nvpair_t *nvp) { char **strp; uint_t nelem; uint_t i; char *p; int n; assert(buf != NULL); (void) nvpair_value_string_array(nvp, &strp, &nelem); for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) { n = snprintf(p, buflen, "%s ", strp[i] ? strp[i] : ""); if ((n < 0) || (n >= buflen)) { *buf = '\0'; return (-1); } p += n; buflen -= n; } if (nelem > 0) *--p = '\0'; return (p - buf); } /* * Return non-zero if nvpair [name] should be formatted in hex; o/w, return 0. */ static int _zed_event_value_is_hex(const char *name) { const char *hex_suffix[] = { "_guid", "_guids", NULL }; const char **pp; char *p; if (!name) return (0); for (pp = hex_suffix; *pp; pp++) { p = strstr(name, *pp); if (p && strlen(p) == strlen(*pp)) return (1); } return (0); } +/* + * Add an environment variable for [eid] to the container [zsp]. + * + * The variable name is the concatenation of [prefix] and [name] converted to + * uppercase with non-alphanumeric characters converted to underscores; + * [prefix] is optional, and [name] must begin with an alphabetic character. + * If the converted variable name already exists within the container [zsp], + * its existing value will be replaced with the new value. + * + * The variable value is specified by the format string [fmt]. + * + * Returns 0 on success, and -1 on error (with errno set). + * + * All environment variables in [zsp] should be added through this function. + */ +static int +_zed_event_add_var(uint64_t eid, zed_strings_t *zsp, + const char *prefix, const char *name, const char *fmt, ...) +{ + char keybuf[MAXBUF]; + char valbuf[MAXBUF]; + char *dstp; + const char *srcp; + const char *lastp; + int n; + int buflen; + va_list vargs; + + assert(zsp != NULL); + assert(fmt != NULL); + + if (!name) { + errno = EINVAL; + zed_log_msg(LOG_WARNING, + "Failed to add variable for eid=%llu: Name is empty", eid); + return (-1); + } else if (!isalpha(name[0])) { + errno = EINVAL; + zed_log_msg(LOG_WARNING, + "Failed to add variable for eid=%llu: " + "Name \"%s\" is invalid", eid, name); + return (-1); + } + /* + * Construct the string key by converting PREFIX (if present) and NAME. + */ + dstp = keybuf; + lastp = keybuf + sizeof (keybuf); + if (prefix) { + for (srcp = prefix; *srcp && (dstp < lastp); srcp++) + *dstp++ = isalnum(*srcp) ? toupper(*srcp) : '_'; + } + for (srcp = name; *srcp && (dstp < lastp); srcp++) + *dstp++ = isalnum(*srcp) ? toupper(*srcp) : '_'; + + if (dstp == lastp) { + errno = ENAMETOOLONG; + zed_log_msg(LOG_WARNING, + "Failed to add variable for eid=%llu: Name too long", eid); + return (-1); + } + *dstp = '\0'; + /* + * Construct the string specified by "[PREFIX][NAME]=[FMT]". + */ + dstp = valbuf; + buflen = sizeof (valbuf); + n = strlcpy(dstp, keybuf, buflen); + if (n >= sizeof (valbuf)) { + errno = EMSGSIZE; + zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s", + keybuf, eid, "Exceeded buffer size"); + return (-1); + } + dstp += n; + buflen -= n; + + *dstp++ = '='; + buflen--; + + va_start(vargs, fmt); + n = vsnprintf(dstp, buflen, fmt, vargs); + va_end(vargs); + + if ((n < 0) || (n >= buflen)) { + errno = EMSGSIZE; + zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s", + keybuf, eid, "Exceeded buffer size"); + return (-1); + } else if (zed_strings_add(zsp, keybuf, valbuf) < 0) { + zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s", + keybuf, eid, strerror(errno)); + return (-1); + } + return (0); +} + /* * Convert the nvpair [nvp] to a string which is added to the environment * of the child process. * Return 0 on success, -1 on error. * * FIXME: Refactor with cmd/zpool/zpool_main.c:zpool_do_events_nvprint()? */ static void _zed_event_add_nvpair(uint64_t eid, zed_strings_t *zsp, nvpair_t *nvp) { const char *name; data_type_t type; - char buf[4096]; - int buflen; + char buf[MAXBUF]; int n; - char *p; - const char *q; const char *fmt; boolean_t b; double d; uint8_t i8; uint16_t i16; uint32_t i32; uint64_t i64; char *str; assert(zsp != NULL); assert(nvp != NULL); name = nvpair_name(nvp); type = nvpair_type(nvp); - buflen = sizeof (buf); - /* Copy NAME prefix for ZED zevent namespace. */ - n = strlcpy(buf, ZEVENT_VAR_PREFIX, sizeof (buf)); - if (n >= sizeof (buf)) { - zed_log_msg(LOG_WARNING, - "Failed to convert nvpair \"%s\" for eid=%llu: %s", - name, eid, "Exceeded buffer size"); - return; - } - buflen -= n; - p = buf + n; - - /* Convert NAME to alphanumeric uppercase. */ - for (q = name; *q && (buflen > 0); q++) { - *p++ = isalnum(*q) ? toupper(*q) : '_'; - buflen--; - } - - /* Separate NAME from VALUE. */ - if (buflen > 0) { - *p++ = '='; - buflen--; - } - *p = '\0'; - - /* Convert VALUE. */ switch (type) { case DATA_TYPE_BOOLEAN: - n = snprintf(p, buflen, "%s", "1"); + n = snprintf(buf, sizeof (buf), "%s", "1"); break; case DATA_TYPE_BOOLEAN_VALUE: (void) nvpair_value_boolean_value(nvp, &b); - n = snprintf(p, buflen, "%s", b ? "1" : "0"); + n = snprintf(buf, sizeof (buf), "%s", b ? "1" : "0"); break; case DATA_TYPE_BYTE: (void) nvpair_value_byte(nvp, &i8); - n = snprintf(p, buflen, "%d", i8); + n = snprintf(buf, sizeof (buf), "%d", i8); break; case DATA_TYPE_INT8: (void) nvpair_value_int8(nvp, (int8_t *) &i8); - n = snprintf(p, buflen, "%d", i8); + n = snprintf(buf, sizeof (buf), "%d", i8); break; case DATA_TYPE_UINT8: (void) nvpair_value_uint8(nvp, &i8); - n = snprintf(p, buflen, "%u", i8); + n = snprintf(buf, sizeof (buf), "%u", i8); break; case DATA_TYPE_INT16: (void) nvpair_value_int16(nvp, (int16_t *) &i16); - n = snprintf(p, buflen, "%d", i16); + n = snprintf(buf, sizeof (buf), "%d", i16); break; case DATA_TYPE_UINT16: (void) nvpair_value_uint16(nvp, &i16); - n = snprintf(p, buflen, "%u", i16); + n = snprintf(buf, sizeof (buf), "%u", i16); break; case DATA_TYPE_INT32: (void) nvpair_value_int32(nvp, (int32_t *) &i32); - n = snprintf(p, buflen, "%d", i32); + n = snprintf(buf, sizeof (buf), "%d", i32); break; case DATA_TYPE_UINT32: (void) nvpair_value_uint32(nvp, &i32); - n = snprintf(p, buflen, "%u", i32); + n = snprintf(buf, sizeof (buf), "%u", i32); break; case DATA_TYPE_INT64: (void) nvpair_value_int64(nvp, (int64_t *) &i64); - n = snprintf(p, buflen, "%lld", (longlong_t) i64); + n = snprintf(buf, sizeof (buf), "%lld", (longlong_t) i64); break; case DATA_TYPE_UINT64: (void) nvpair_value_uint64(nvp, &i64); fmt = _zed_event_value_is_hex(name) ? "0x%.16llX" : "%llu"; - n = snprintf(p, buflen, fmt, (u_longlong_t) i64); + n = snprintf(buf, sizeof (buf), fmt, (u_longlong_t) i64); break; case DATA_TYPE_DOUBLE: (void) nvpair_value_double(nvp, &d); - n = snprintf(p, buflen, "%g", d); + n = snprintf(buf, sizeof (buf), "%g", d); break; case DATA_TYPE_HRTIME: (void) nvpair_value_hrtime(nvp, (hrtime_t *) &i64); - n = snprintf(p, buflen, "%llu", (u_longlong_t) i64); + n = snprintf(buf, sizeof (buf), "%llu", (u_longlong_t) i64); break; case DATA_TYPE_NVLIST: /* FIXME */ - n = snprintf(p, buflen, "%s", "_NOT_IMPLEMENTED_"); + n = snprintf(buf, sizeof (buf), "%s", "_NOT_IMPLEMENTED_"); break; case DATA_TYPE_STRING: (void) nvpair_value_string(nvp, &str); - n = snprintf(p, buflen, "%s", (str ? str : "")); + n = snprintf(buf, sizeof (buf), "%s", (str ? str : "")); break; case DATA_TYPE_BOOLEAN_ARRAY: /* FIXME */ - n = snprintf(p, buflen, "%s", "_NOT_IMPLEMENTED_"); + n = snprintf(buf, sizeof (buf), "%s", "_NOT_IMPLEMENTED_"); break; case DATA_TYPE_BYTE_ARRAY: /* FIXME */ - n = snprintf(p, buflen, "%s", "_NOT_IMPLEMENTED_"); + n = snprintf(buf, sizeof (buf), "%s", "_NOT_IMPLEMENTED_"); break; case DATA_TYPE_INT8_ARRAY: - n = _zed_event_convert_int8_array(p, buflen, nvp); + n = _zed_event_convert_int8_array(buf, sizeof (buf), nvp); break; case DATA_TYPE_UINT8_ARRAY: - n = _zed_event_convert_uint8_array(p, buflen, nvp); + n = _zed_event_convert_uint8_array(buf, sizeof (buf), nvp); break; case DATA_TYPE_INT16_ARRAY: - n = _zed_event_convert_int16_array(p, buflen, nvp); + n = _zed_event_convert_int16_array(buf, sizeof (buf), nvp); break; case DATA_TYPE_UINT16_ARRAY: - n = _zed_event_convert_uint16_array(p, buflen, nvp); + n = _zed_event_convert_uint16_array(buf, sizeof (buf), nvp); break; case DATA_TYPE_INT32_ARRAY: - n = _zed_event_convert_int32_array(p, buflen, nvp); + n = _zed_event_convert_int32_array(buf, sizeof (buf), nvp); break; case DATA_TYPE_UINT32_ARRAY: - n = _zed_event_convert_uint32_array(p, buflen, nvp); + n = _zed_event_convert_uint32_array(buf, sizeof (buf), nvp); break; case DATA_TYPE_INT64_ARRAY: - n = _zed_event_convert_int64_array(p, buflen, nvp); + n = _zed_event_convert_int64_array(buf, sizeof (buf), nvp); break; case DATA_TYPE_UINT64_ARRAY: fmt = _zed_event_value_is_hex(name) ? "0x%.16llX " : "%llu "; - n = _zed_event_convert_uint64_array(p, buflen, nvp, fmt); + n = _zed_event_convert_uint64_array(buf, sizeof (buf), + nvp, fmt); break; case DATA_TYPE_STRING_ARRAY: - n = _zed_event_convert_string_array(p, buflen, nvp); + n = _zed_event_convert_string_array(buf, sizeof (buf), nvp); break; case DATA_TYPE_NVLIST_ARRAY: /* FIXME */ - n = snprintf(p, buflen, "%s", "_NOT_IMPLEMENTED_"); + n = snprintf(buf, sizeof (buf), "%s", "_NOT_IMPLEMENTED_"); break; default: zed_log_msg(LOG_WARNING, "Failed to convert nvpair \"%s\" for eid=%llu: " "Unrecognized type=%u", name, eid, (unsigned int) type); return; } if ((n < 0) || (n >= sizeof (buf))) { zed_log_msg(LOG_WARNING, "Failed to convert nvpair \"%s\" for eid=%llu: %s", name, eid, "Exceeded buffer size"); return; } - if (zed_strings_add(zsp, buf) < 0) { - zed_log_msg(LOG_WARNING, - "Failed to convert nvpair \"%s\" for eid=%llu: %s", - name, eid, strerror(ENOMEM)); - return; - } -} - -/* - * Add the environment variable specified by the format string [fmt]. - */ -static void -_zed_event_add_var(uint64_t eid, zed_strings_t *zsp, const char *fmt, ...) -{ - char buf[4096]; - va_list vargs; - int n; - const char *p; - size_t namelen; - - assert(zsp != NULL); - assert(fmt != NULL); - - va_start(vargs, fmt); - n = vsnprintf(buf, sizeof (buf), fmt, vargs); - va_end(vargs); - p = strchr(buf, '='); - namelen = (p) ? p - buf : strlen(buf); - - if ((n < 0) || (n >= sizeof (buf))) { - zed_log_msg(LOG_WARNING, "Failed to add %.*s for eid=%llu: %s", - namelen, buf, eid, "Exceeded buffer size"); - } else if (!p) { - zed_log_msg(LOG_WARNING, "Failed to add %.*s for eid=%llu: %s", - namelen, buf, eid, "Missing assignment"); - } else if (zed_strings_add(zsp, buf) < 0) { - zed_log_msg(LOG_WARNING, "Failed to add %.*s for eid=%llu: %s", - namelen, buf, eid, strerror(ENOMEM)); - } + _zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, name, "%s", buf); } /* * Restrict various environment variables to safe and sane values * when constructing the environment for the child process. * * Reference: Secure Programming Cookbook by Viega & Messier, Section 1.1. */ static void _zed_event_add_env_restrict(uint64_t eid, zed_strings_t *zsp) { - const char *env_restrict[] = { - "IFS= \t\n", - "PATH=" _PATH_STDPATH, - "ZDB=" SBINDIR "/zdb", - "ZED=" SBINDIR "/zed", - "ZFS=" SBINDIR "/zfs", - "ZINJECT=" SBINDIR "/zinject", - "ZPOOL=" SBINDIR "/zpool", - "ZFS_ALIAS=" ZFS_META_ALIAS, - "ZFS_VERSION=" ZFS_META_VERSION, - "ZFS_RELEASE=" ZFS_META_RELEASE, - NULL + const char *env_restrict[][2] = { + { "IFS", " \t\n" }, + { "PATH", _PATH_STDPATH }, + { "ZDB", SBINDIR "/zdb" }, + { "ZED", SBINDIR "/zed" }, + { "ZFS", SBINDIR "/zfs" }, + { "ZINJECT", SBINDIR "/zinject" }, + { "ZPOOL", SBINDIR "/zpool" }, + { "ZFS_ALIAS", ZFS_META_ALIAS }, + { "ZFS_VERSION", ZFS_META_VERSION }, + { "ZFS_RELEASE", ZFS_META_RELEASE }, + { NULL, NULL } }; - const char **pp; + const char *(*pa)[2]; assert(zsp != NULL); - for (pp = env_restrict; *pp; pp++) { - _zed_event_add_var(eid, zsp, "%s", *pp); + for (pa = env_restrict; *(*pa); pa++) { + _zed_event_add_var(eid, zsp, NULL, (*pa)[0], "%s", (*pa)[1]); } } /* * Preserve specified variables from the parent environment * when constructing the environment for the child process. * * Reference: Secure Programming Cookbook by Viega & Messier, Section 1.1. */ static void _zed_event_add_env_preserve(uint64_t eid, zed_strings_t *zsp) { const char *env_preserve[] = { "TZ", NULL }; - const char **pp; - const char *p; + const char **keyp; + const char *val; assert(zsp != NULL); - for (pp = env_preserve; *pp; pp++) { - if ((p = getenv(*pp))) - _zed_event_add_var(eid, zsp, "%s=%s", *pp, p); + for (keyp = env_preserve; *keyp; keyp++) { + if ((val = getenv(*keyp))) + _zed_event_add_var(eid, zsp, NULL, *keyp, "%s", val); } } /* * Compute the "subclass" by removing the first 3 components of [class] * (which seem to always be either "ereport.fs.zfs" or "resource.fs.zfs"). * Return a pointer inside the string [class], or NULL if insufficient * components exist. */ static const char * _zed_event_get_subclass(const char *class) { const char *p; int i; if (!class) return (NULL); p = class; for (i = 0; i < 3; i++) { p = strchr(p, '.'); if (!p) break; p++; } return (p); } /* * Convert the zevent time from a 2-element array of 64b integers * into a more convenient form: * - TIME_SECS is the second component of the time. * - TIME_NSECS is the nanosecond component of the time. * - TIME_STRING is an almost-RFC3339-compliant string representation. */ static void _zed_event_add_time_strings(uint64_t eid, zed_strings_t *zsp, int64_t etime[]) { struct tm *stp; char buf[32]; assert(zsp != NULL); assert(etime != NULL); - _zed_event_add_var(eid, zsp, "%s%s=%lld", - ZEVENT_VAR_PREFIX, "TIME_SECS", (long long int) etime[0]); - _zed_event_add_var(eid, zsp, "%s%s=%lld", - ZEVENT_VAR_PREFIX, "TIME_NSECS", (long long int) etime[1]); + _zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "TIME_SECS", + "%lld", (long long int) etime[0]); + _zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "TIME_NSECS", + "%lld", (long long int) etime[1]); if (!(stp = localtime((const time_t *) &etime[0]))) { zed_log_msg(LOG_WARNING, "Failed to add %s%s for eid=%llu: %s", ZEVENT_VAR_PREFIX, "TIME_STRING", eid, "localtime error"); } else if (!strftime(buf, sizeof (buf), "%Y-%m-%d %H:%M:%S%z", stp)) { zed_log_msg(LOG_WARNING, "Failed to add %s%s for eid=%llu: %s", ZEVENT_VAR_PREFIX, "TIME_STRING", eid, "strftime error"); } else { - _zed_event_add_var(eid, zsp, "%s%s=%s", - ZEVENT_VAR_PREFIX, "TIME_STRING", buf); + _zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "TIME_STRING", + "%s", buf); } } /* * Service the next zevent, blocking until one is available. */ void zed_event_service(struct zed_conf *zcp) { nvlist_t *nvl; nvpair_t *nvp; int n_dropped; zed_strings_t *zsp; uint64_t eid; int64_t *etime; uint_t nelem; char *class; const char *subclass; int rv; if (!zcp) { errno = EINVAL; zed_log_msg(LOG_ERR, "Failed to service zevent: %s", strerror(errno)); return; } rv = zpool_events_next(zcp->zfs_hdl, &nvl, &n_dropped, ZEVENT_NONE, zcp->zevent_fd); if ((rv != 0) || !nvl) return; if (n_dropped > 0) { zed_log_msg(LOG_WARNING, "Missed %d events", n_dropped); /* * FIXME: Increase max size of event nvlist in * /sys/module/zfs/parameters/zfs_zevent_len_max ? */ } if (nvlist_lookup_uint64(nvl, "eid", &eid) != 0) { zed_log_msg(LOG_WARNING, "Failed to lookup zevent eid"); } else if (nvlist_lookup_int64_array( nvl, "time", &etime, &nelem) != 0) { zed_log_msg(LOG_WARNING, "Failed to lookup zevent time (eid=%llu)", eid); } else if (nelem != 2) { zed_log_msg(LOG_WARNING, "Failed to lookup zevent time (eid=%llu, nelem=%u)", eid, nelem); } else if (nvlist_lookup_string(nvl, "class", &class) != 0) { zed_log_msg(LOG_WARNING, "Failed to lookup zevent class (eid=%llu)", eid); } else { zsp = zed_strings_create(); nvp = NULL; while ((nvp = nvlist_next_nvpair(nvl, nvp))) _zed_event_add_nvpair(eid, zsp, nvp); _zed_event_add_env_restrict(eid, zsp); _zed_event_add_env_preserve(eid, zsp); - _zed_event_add_var(eid, zsp, "%s%s=%d", - ZED_VAR_PREFIX, "PID", (int) getpid()); - _zed_event_add_var(eid, zsp, "%s%s=%s", - ZED_VAR_PREFIX, "ZEDLET_DIR", zcp->zedlet_dir); - + _zed_event_add_var(eid, zsp, ZED_VAR_PREFIX, "PID", + "%d", (int) getpid()); + _zed_event_add_var(eid, zsp, ZED_VAR_PREFIX, "ZEDLET_DIR", + "%s", zcp->zedlet_dir); subclass = _zed_event_get_subclass(class); - _zed_event_add_var(eid, zsp, "%s%s=%s", - ZEVENT_VAR_PREFIX, "SUBCLASS", - (subclass ? subclass : class)); + _zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "SUBCLASS", + "%s", (subclass ? subclass : class)); _zed_event_add_time_strings(eid, zsp, etime); zed_exec_process(eid, class, subclass, zcp->zedlet_dir, zcp->zedlets, zsp, zcp->zevent_fd); zed_conf_write_state(zcp, eid, etime); zed_strings_destroy(zsp); } nvlist_free(nvl); } diff --git a/cmd/zed/zed_strings.c b/cmd/zed/zed_strings.c index 8e0f58079798..01736e3881cf 100644 --- a/cmd/zed/zed_strings.c +++ b/cmd/zed/zed_strings.c @@ -1,198 +1,259 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license from the top-level * OPENSOLARIS.LICENSE or . * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each file * and include the License file from the top-level OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049). * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. */ #include #include #include #include #include #include #include #include "zed_strings.h" struct zed_strings { avl_tree_t tree; avl_node_t *iteratorp; }; struct zed_strings_node { avl_node_t node; - char string[]; + char *key; + char *val; }; typedef struct zed_strings_node zed_strings_node_t; /* * Compare zed_strings_node_t nodes [x1] and [x2]. * As required for the AVL tree, return -1 for <, 0 for ==, and +1 for >. */ static int _zed_strings_node_compare(const void *x1, const void *x2) { const char *s1; const char *s2; int rv; assert(x1 != NULL); assert(x2 != NULL); - s1 = ((const zed_strings_node_t *) x1)->string; + s1 = ((const zed_strings_node_t *) x1)->key; assert(s1 != NULL); - s2 = ((const zed_strings_node_t *) x2)->string; + s2 = ((const zed_strings_node_t *) x2)->key; assert(s2 != NULL); rv = strcmp(s1, s2); if (rv < 0) return (-1); if (rv > 0) return (1); return (0); } /* * Return a new string container, or NULL on error. */ zed_strings_t * zed_strings_create(void) { zed_strings_t *zsp; zsp = calloc(1, sizeof (*zsp)); if (!zsp) return (NULL); avl_create(&zsp->tree, _zed_strings_node_compare, sizeof (zed_strings_node_t), offsetof(zed_strings_node_t, node)); zsp->iteratorp = NULL; return (zsp); } /* - * Destroy the string container [zsp] and all strings within. + * Destroy the string node [np]. + */ +static void +_zed_strings_node_destroy(zed_strings_node_t *np) +{ + if (!np) + return; + + if (np->key) { + if (np->key != np->val) + free(np->key); + np->key = NULL; + } + if (np->val) { + free(np->val); + np->val = NULL; + } + free(np); +} + +/* + * Return a new string node for storing the string [val], or NULL on error. + * If [key] is specified, it will be used to index the node; otherwise, + * the string [val] will be used. + */ +zed_strings_node_t * +_zed_strings_node_create(const char *key, const char *val) +{ + zed_strings_node_t *np; + + assert(val != NULL); + + np = calloc(1, sizeof (*np)); + if (!np) + return (NULL); + + np->val = strdup(val); + if (!np->val) + goto nomem; + + if (key) { + np->key = strdup(key); + if (!np->key) + goto nomem; + } else { + np->key = np->val; + } + return (np); + +nomem: + _zed_strings_node_destroy(np); + return (NULL); +} + +/* + * Destroy the string container [zsp] and all nodes within. */ void zed_strings_destroy(zed_strings_t *zsp) { void *cookie; zed_strings_node_t *np; if (!zsp) return; cookie = NULL; while ((np = avl_destroy_nodes(&zsp->tree, &cookie))) - free(np); + _zed_strings_node_destroy(np); avl_destroy(&zsp->tree); free(zsp); } /* - * Add a copy of the string [s] to the container [zsp]. + * Add a copy of the string [s] indexed by [key] to the container [zsp]. + * If [key] already exists within the container [zsp], it will be replaced + * with the new string [s]. + * If [key] is NULL, the string [s] will be used as the key. * Return 0 on success, or -1 on error. - * - * FIXME: Handle dup strings. */ int -zed_strings_add(zed_strings_t *zsp, const char *s) +zed_strings_add(zed_strings_t *zsp, const char *key, const char *s) { - size_t len; - zed_strings_node_t *np; + zed_strings_node_t *newp, *oldp; if (!zsp || !s) { errno = EINVAL; return (-1); } - len = sizeof (zed_strings_node_t) + strlen(s) + 1; - np = calloc(1, len); - if (!np) + if (key == s) + key = NULL; + + newp = _zed_strings_node_create(key, s); + if (!newp) return (-1); - assert((char *) np->string + strlen(s) < (char *) np + len); - (void) strcpy(np->string, s); - avl_add(&zsp->tree, np); + oldp = avl_find(&zsp->tree, newp, NULL); + if (oldp) { + avl_remove(&zsp->tree, oldp); + _zed_strings_node_destroy(oldp); + } + avl_add(&zsp->tree, newp); return (0); } /* * Return the first string in container [zsp]. * Return NULL if there are no strings, or on error. * This can be called multiple times to re-traverse [zsp]. * XXX: Not thread-safe. */ const char * zed_strings_first(zed_strings_t *zsp) { if (!zsp) { errno = EINVAL; return (NULL); } zsp->iteratorp = avl_first(&zsp->tree); if (!zsp->iteratorp) return (NULL); - return (((zed_strings_node_t *) zsp->iteratorp)->string); + return (((zed_strings_node_t *) zsp->iteratorp)->val); } /* * Return the next string in container [zsp]. * Return NULL after the last string, or on error. * This must be called after zed_strings_first(). * XXX: Not thread-safe. */ const char * zed_strings_next(zed_strings_t *zsp) { if (!zsp) { errno = EINVAL; return (NULL); } if (!zsp->iteratorp) return (NULL); zsp->iteratorp = AVL_NEXT(&zsp->tree, zsp->iteratorp); if (!zsp->iteratorp) return (NULL); - return (((zed_strings_node_t *)zsp->iteratorp)->string); + return (((zed_strings_node_t *)zsp->iteratorp)->val); } /* * Return the number of strings in container [zsp], or -1 on error. */ int zed_strings_count(zed_strings_t *zsp) { if (!zsp) { errno = EINVAL; return (-1); } return (avl_numnodes(&zsp->tree)); } diff --git a/cmd/zed/zed_strings.h b/cmd/zed/zed_strings.h index c1ea804bbe5b..07e85e23f4c1 100644 --- a/cmd/zed/zed_strings.h +++ b/cmd/zed/zed_strings.h @@ -1,44 +1,44 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license from the top-level * OPENSOLARIS.LICENSE or . * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each file * and include the License file from the top-level OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049). * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. */ #ifndef ZED_STRINGS_H #define ZED_STRINGS_H typedef struct zed_strings zed_strings_t; zed_strings_t * zed_strings_create(void); void zed_strings_destroy(zed_strings_t *zsp); -int zed_strings_add(zed_strings_t *zsp, const char *s); +int zed_strings_add(zed_strings_t *zsp, const char *key, const char *s); const char * zed_strings_first(zed_strings_t *zsp); const char * zed_strings_next(zed_strings_t *zsp); int zed_strings_count(zed_strings_t *zsp); #endif /* !ZED_STRINGS_H */