diff --git a/cmd/zed/zed_conf.c b/cmd/zed/zed_conf.c index e6f601f3a2ac..cf68c06fe388 100644 --- a/cmd/zed/zed_conf.c +++ b/cmd/zed/zed_conf.c @@ -1,686 +1,686 @@ /* * 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 = malloc(sizeof (*zcp)); if (!zcp) goto nomem; memset(zcp, 0, sizeof (*zcp)); zcp->syslog_facility = LOG_DAEMON; zcp->min_events = ZED_MIN_EVENTS; zcp->max_events = ZED_MAX_EVENTS; zcp->scripts = 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->script_dir = strdup(ZED_SCRIPT_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)); } if (zcp->pid_file) { if ((unlink(zcp->pid_file) < 0) && (errno != ENOENT)) zed_log_msg(LOG_WARNING, - "Failed to remove pid file \"%s\": %s", + "Failed to remove PID file \"%s\": %s", zcp->pid_file, strerror(errno)); } if (zcp->conf_file) free(zcp->conf_file); if (zcp->pid_file) free(zcp->pid_file); if (zcp->script_dir) free(zcp->script_dir); if (zcp->state_file) free(zcp->state_file); if (zcp->scripts) zed_strings_destroy(zcp->scripts); 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 scripts from DIR.", ZED_SCRIPT_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->script_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] script_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 scripts, * or -1 on error with errno set. * FIXME: Check if script_dir and all parent dirs are secure. */ int zed_conf_scan_dir(struct zed_conf *zcp) { zed_strings_t *scripts; 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 script dir: %s", strerror(errno)); return (-1); } scripts = zed_strings_create(); if (!scripts) { errno = ENOMEM; zed_log_msg(LOG_WARNING, "Failed to scan dir \"%s\": %s", zcp->script_dir, strerror(errno)); return (-1); } dirp = opendir(zcp->script_dir); if (!dirp) { int errno_bak = errno; zed_log_msg(LOG_WARNING, "Failed to open dir \"%s\": %s", zcp->script_dir, strerror(errno)); zed_strings_destroy(scripts); errno = errno_bak; return (-1); } while ((direntp = readdir(dirp))) { if (direntp->d_name[0] == '.') continue; n = snprintf(pathname, sizeof (pathname), "%s/%s", zcp->script_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(scripts, 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 script \"%s\"", direntp->d_name); } if (closedir(dirp) < 0) { int errno_bak = errno; zed_log_msg(LOG_WARNING, "Failed to close dir \"%s\": %s", zcp->script_dir, strerror(errno)); zed_strings_destroy(scripts); errno = errno_bak; return (-1); } if (zcp->scripts) zed_strings_destroy(zcp->scripts); zcp->scripts = scripts; 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). * FIXME: Only update the PID file after verifying the PID previously stored * in the PID file no longer exists or belongs to a foreign process * in order to ensure the daemon cannot be started more than once. * (This check is currently done by zed_conf_open_state().) */ int zed_conf_write_pid(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; mode_t mask; FILE *fp; if (!zcp || !zcp->pid_file) { errno = EINVAL; - zed_log_msg(LOG_ERR, "Failed to write pid file: %s", + zed_log_msg(LOG_ERR, "Failed to write PID file: %s", strerror(errno)); return (-1); } n = strlcpy(dirbuf, zcp->pid_file, sizeof (dirbuf)); if (n >= sizeof (dirbuf)) { errno = ENAMETOOLONG; - zed_log_msg(LOG_WARNING, "Failed to write pid file: %s", + zed_log_msg(LOG_WARNING, "Failed to write PID 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); } (void) unlink(zcp->pid_file); mask = umask(0); umask(mask | 022); fp = fopen(zcp->pid_file, "w"); umask(mask); if (!fp) { - zed_log_msg(LOG_WARNING, "Failed to open pid file \"%s\": %s", + zed_log_msg(LOG_WARNING, "Failed to open PID file \"%s\": %s", zcp->pid_file, strerror(errno)); } else if (fprintf(fp, "%d\n", (int) getpid()) == EOF) { - zed_log_msg(LOG_WARNING, "Failed to write pid file \"%s\": %s", + zed_log_msg(LOG_WARNING, "Failed to write PID file \"%s\": %s", zcp->pid_file, strerror(errno)); } else if (fclose(fp) == EOF) { - zed_log_msg(LOG_WARNING, "Failed to close pid file \"%s\": %s", + zed_log_msg(LOG_WARNING, "Failed to close PID file \"%s\": %s", zcp->pid_file, strerror(errno)); } else { return (0); } (void) unlink(zcp->pid_file); return (-1); } /* * Open and lock the [zcp] state_file. * Return 0 on success, -1 on error. * FIXME: If state_file exists, verify ownership & permissions. * FIXME: Move lock to pid_file instead. */ 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\"", + "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_file.c b/cmd/zed/zed_file.c index 7b77345d1dd3..3d62f9fe4894 100644 --- a/cmd/zed/zed_file.c +++ b/cmd/zed/zed_file.c @@ -1,227 +1,227 @@ /* * 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 "zed_log.h" /* * Read up to [n] bytes from [fd] into [buf]. * Return the number of bytes read, 0 on EOF, or -1 on error. */ ssize_t zed_file_read_n(int fd, void *buf, size_t n) { unsigned char *p; size_t n_left; ssize_t n_read; p = buf; n_left = n; while (n_left > 0) { if ((n_read = read(fd, p, n_left)) < 0) { if (errno == EINTR) continue; else return (-1); } else if (n_read == 0) { break; } n_left -= n_read; p += n_read; } return (n - n_left); } /* * Write [n] bytes from [buf] out to [fd]. * Return the number of bytes written, or -1 on error. */ ssize_t zed_file_write_n(int fd, void *buf, size_t n) { const unsigned char *p; size_t n_left; ssize_t n_written; p = buf; n_left = n; while (n_left > 0) { if ((n_written = write(fd, p, n_left)) < 0) { if (errno == EINTR) continue; else return (-1); } n_left -= n_written; p += n_written; } return (n); } /* * Set an exclusive advisory lock on the open file descriptor [fd]. * Return 0 on success, 1 if a conflicting lock is held by another process, * or -1 on error (with errno set). */ int zed_file_lock(int fd) { struct flock lock; if (fd < 0) { errno = EBADF; return (-1); } lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; if (fcntl(fd, F_SETLK, &lock) < 0) { if ((errno == EACCES) || (errno == EAGAIN)) return (1); return (-1); } return (0); } /* * Release an advisory lock held on the open file descriptor [fd]. * Return 0 on success, or -1 on error (with errno set). */ int zed_file_unlock(int fd) { struct flock lock; if (fd < 0) { errno = EBADF; return (-1); } lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; if (fcntl(fd, F_SETLK, &lock) < 0) return (-1); return (0); } /* * Test whether an exclusive advisory lock could be obtained for the open * file descriptor [fd]. - * Return 0 if the file is not locked, >0 for the pid of another process + * Return 0 if the file is not locked, >0 for the PID of another process * holding a conflicting lock, or -1 on error (with errno set). */ pid_t zed_file_is_locked(int fd) { struct flock lock; if (fd < 0) { errno = EBADF; return (-1); } lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; if (fcntl(fd, F_GETLK, &lock) < 0) return (-1); if (lock.l_type == F_UNLCK) return (0); return (lock.l_pid); } /* * Close all open file descriptors greater than or equal to [lowfd]. * Any errors encountered while closing file descriptors are ignored. */ void zed_file_close_from(int lowfd) { const int maxfd_def = 256; int errno_bak; struct rlimit rl; int maxfd; int fd; errno_bak = errno; if (getrlimit(RLIMIT_NOFILE, &rl) < 0) { maxfd = maxfd_def; } else if (rl.rlim_max == RLIM_INFINITY) { maxfd = maxfd_def; } else { maxfd = rl.rlim_max; } for (fd = lowfd; fd < maxfd; fd++) (void) close(fd); errno = errno_bak; } /* * Set the CLOEXEC flag on file descriptor [fd] so it will be automatically * closed upon successful execution of one of the exec functions. * Return 0 on success, or -1 on error. * FIXME: No longer needed? */ int zed_file_close_on_exec(int fd) { int flags; if (fd < 0) { errno = EBADF; return (-1); } flags = fcntl(fd, F_GETFD); if (flags == -1) return (-1); flags |= FD_CLOEXEC; if (fcntl(fd, F_SETFD, flags) == -1) return (-1); return (0); } diff --git a/cmd/zed/zed_log.c b/cmd/zed/zed_log.c index 2a787e357301..c2bc25edc086 100644 --- a/cmd/zed/zed_log.c +++ b/cmd/zed/zed_log.c @@ -1,266 +1,262 @@ /* * 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_log.h" -#define ZED_LOG_MAX_ID_LEN 64 #define ZED_LOG_MAX_LOG_LEN 1024 static struct { unsigned do_stderr:1; unsigned do_syslog:1; - int level; - char id[ZED_LOG_MAX_ID_LEN]; + const char *identity; + int priority; int pipe_fd[2]; } _ctx; +/* + * Initialize the logging subsystem. + */ void zed_log_init(const char *identity) { - const char *p; - if (identity) { - p = (p = strrchr(identity, '/')) ? p + 1 : identity; - strlcpy(_ctx.id, p, sizeof (_ctx.id)); + const char *p = strrchr(identity, '/'); + _ctx.identity = (p != NULL) ? p + 1 : identity; } else { - _ctx.id[0] = '\0'; + _ctx.identity = NULL; } _ctx.pipe_fd[0] = -1; _ctx.pipe_fd[1] = -1; } +/* + * Shutdown the logging subsystem. + */ void -zed_log_fini() +zed_log_fini(void) { - if (_ctx.do_syslog) { - closelog(); - } + zed_log_stderr_close(); + zed_log_syslog_close(); } /* * Create pipe for communicating daemonization status between the parent and * child processes across the double-fork(). */ void zed_log_pipe_open(void) { if ((_ctx.pipe_fd[0] != -1) || (_ctx.pipe_fd[1] != -1)) zed_log_die("Invalid use of zed_log_pipe_open in PID %d", (int) getpid()); if (pipe(_ctx.pipe_fd) < 0) zed_log_die("Failed to create daemonize pipe in PID %d: %s", (int) getpid(), strerror(errno)); } /* * Close the read-half of the daemonize pipe. * This should be called by the child after fork()ing from the parent since * the child will never read from this pipe. */ void zed_log_pipe_close_reads(void) { if (_ctx.pipe_fd[0] < 0) zed_log_die( "Invalid use of zed_log_pipe_close_reads in PID %d", (int) getpid()); if (close(_ctx.pipe_fd[0]) < 0) zed_log_die( "Failed to close reads on daemonize pipe in PID %d: %s", (int) getpid(), strerror(errno)); _ctx.pipe_fd[0] = -1; } /* * Close the write-half of the daemonize pipe. * This should be called by the parent after fork()ing its child since the * parent will never write to this pipe. * This should also be called by the child once initialization is complete * in order to signal the parent that it can safely exit. */ void zed_log_pipe_close_writes(void) { if (_ctx.pipe_fd[1] < 0) zed_log_die( "Invalid use of zed_log_pipe_close_writes in PID %d", (int) getpid()); if (close(_ctx.pipe_fd[1]) < 0) zed_log_die( "Failed to close writes on daemonize pipe in PID %d: %s", (int) getpid(), strerror(errno)); _ctx.pipe_fd[1] = -1; } /* * Block on reading from the daemonize pipe until signaled by the child * (via zed_log_pipe_close_writes()) that initialization is complete. * This should only be called by the parent while waiting to exit after * fork()ing the child. */ void zed_log_pipe_wait(void) { ssize_t n; char c; if (_ctx.pipe_fd[0] < 0) zed_log_die("Invalid use of zed_log_pipe_wait in PID %d", (int) getpid()); for (;;) { n = read(_ctx.pipe_fd[0], &c, sizeof (c)); if (n < 0) { if (errno == EINTR) continue; zed_log_die( "Failed to read from daemonize pipe in PID %d: %s", (int) getpid(), strerror(errno)); } if (n == 0) { break; } } } +/* + * Start logging messages at the syslog [priority] level or higher to stderr. + * Refer to syslog(3) for valid priority values. + */ void -zed_log_stderr_open(int level) +zed_log_stderr_open(int priority) { _ctx.do_stderr = 1; - _ctx.level = level; + _ctx.priority = priority; } +/* + * Stop logging messages to stderr. + */ void zed_log_stderr_close(void) { - _ctx.do_stderr = 0; + if (_ctx.do_stderr) + _ctx.do_stderr = 0; } +/* + * Start logging messages to syslog. + * Refer to syslog(3) for valid option/facility values. + */ void zed_log_syslog_open(int facility) { - const char *identity; - _ctx.do_syslog = 1; - identity = (_ctx.id[0] == '\0') ? NULL : _ctx.id; - openlog(identity, LOG_NDELAY, facility); + openlog(_ctx.identity, LOG_NDELAY | LOG_PID, facility); } +/* + * Stop logging messages to syslog. + */ void zed_log_syslog_close(void) { - _ctx.do_syslog = 0; - closelog(); + if (_ctx.do_syslog) { + _ctx.do_syslog = 0; + closelog(); + } } +/* + * Auxiliary function to log a message to syslog and/or stderr. + */ static void _zed_log_aux(int priority, const char *fmt, va_list vargs) { char buf[ZED_LOG_MAX_LOG_LEN]; - char *syslogp; - char *p; - int len; int n; - assert(fmt != NULL); - - syslogp = NULL; - p = buf; - len = sizeof (buf); + if (!fmt) + return; - if (_ctx.id[0] != '\0') { - n = snprintf(p, len, "%s: ", _ctx.id); - if ((n < 0) || (n >= len)) { - p += len - 1; - len = 0; - } else { - p += n; - len -= n; - } - } - if ((len > 0) && fmt) { - syslogp = p; - n = vsnprintf(p, len, fmt, vargs); - if ((n < 0) || (n >= len)) { - p += len - 1; - len = 0; - } else { - p += n; - len -= n; - } + n = vsnprintf(buf, sizeof (buf), fmt, vargs); + if ((n < 0) || (n >= sizeof (buf))) { + buf[sizeof (buf) - 2] = '+'; + buf[sizeof (buf) - 1] = '\0'; } - *p = '\0'; - if (_ctx.do_syslog && syslogp) - syslog(priority, "%s", syslogp); + if (_ctx.do_syslog) + syslog(priority, "%s", buf); - if (_ctx.do_stderr && priority <= _ctx.level) + if (_ctx.do_stderr && (priority <= _ctx.priority)) fprintf(stderr, "%s\n", buf); } /* * Log a message at the given [priority] level specified by the printf-style * format string [fmt]. */ void zed_log_msg(int priority, const char *fmt, ...) { va_list vargs; if (fmt) { va_start(vargs, fmt); _zed_log_aux(priority, fmt, vargs); va_end(vargs); } } /* * Log a fatal error message specified by the printf-style format string [fmt]. */ void zed_log_die(const char *fmt, ...) { va_list vargs; if (fmt) { va_start(vargs, fmt); _zed_log_aux(LOG_ERR, fmt, vargs); va_end(vargs); } exit(EXIT_FAILURE); } diff --git a/cmd/zed/zed_log.h b/cmd/zed/zed_log.h index 767c4c7dba0f..d963089d03a2 100644 --- a/cmd/zed/zed_log.h +++ b/cmd/zed/zed_log.h @@ -1,56 +1,56 @@ /* * 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_LOG_H #define ZED_LOG_H #include void zed_log_init(const char *identity); void zed_log_fini(void); void zed_log_pipe_open(void); void zed_log_pipe_close_reads(void); void zed_log_pipe_close_writes(void); void zed_log_pipe_wait(void); -void zed_log_stderr_open(int level); +void zed_log_stderr_open(int priority); void zed_log_stderr_close(void); void zed_log_syslog_open(int facility); void zed_log_syslog_close(void); void zed_log_msg(int priority, const char *fmt, ...); void zed_log_die(const char *fmt, ...); #endif /* !ZED_LOG_H */