aboutsummaryrefslogtreecommitdiff
path: root/cmd/zed/zed_exec.c
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/zed/zed_exec.c')
-rw-r--r--cmd/zed/zed_exec.c232
1 files changed, 232 insertions, 0 deletions
diff --git a/cmd/zed/zed_exec.c b/cmd/zed/zed_exec.c
new file mode 100644
index 000000000000..08b7b5568362
--- /dev/null
+++ b/cmd/zed/zed_exec.c
@@ -0,0 +1,232 @@
+/*
+ * This file is part of the ZFS Event Daemon (ZED)
+ * for ZFS on Linux (ZoL) <http://zfsonlinux.org/>.
+ * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
+ * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
+ * Refer to the ZoL git commit log for authoritative copyright attribution.
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License Version 1.0 (CDDL-1.0).
+ * You can obtain a copy of the license from the top-level file
+ * "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
+ * You may not use this file except in compliance with the license.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+#include "zed_exec.h"
+#include "zed_file.h"
+#include "zed_log.h"
+#include "zed_strings.h"
+
+#define ZEVENT_FILENO 3
+
+/*
+ * Create an environment string array for passing to execve() using the
+ * NAME=VALUE strings in container [zsp].
+ * Return a newly-allocated environment, or NULL on error.
+ */
+static char **
+_zed_exec_create_env(zed_strings_t *zsp)
+{
+ int num_ptrs;
+ int buflen;
+ char *buf;
+ char **pp;
+ char *p;
+ const char *q;
+ int i;
+ int len;
+
+ num_ptrs = zed_strings_count(zsp) + 1;
+ buflen = num_ptrs * sizeof (char *);
+ for (q = zed_strings_first(zsp); q; q = zed_strings_next(zsp))
+ buflen += strlen(q) + 1;
+
+ buf = calloc(1, buflen);
+ if (!buf)
+ return (NULL);
+
+ pp = (char **)buf;
+ p = buf + (num_ptrs * sizeof (char *));
+ i = 0;
+ for (q = zed_strings_first(zsp); q; q = zed_strings_next(zsp)) {
+ pp[i] = p;
+ len = strlen(q) + 1;
+ memcpy(p, q, len);
+ p += len;
+ i++;
+ }
+ pp[i] = NULL;
+ assert(buf + buflen == p);
+ return ((char **)buf);
+}
+
+/*
+ * Fork a child process to handle event [eid]. The program [prog]
+ * in directory [dir] is executed with the environment [env].
+ *
+ * The file descriptor [zfd] is the zevent_fd used to track the
+ * current cursor location within the zevent nvlist.
+ */
+static void
+_zed_exec_fork_child(uint64_t eid, const char *dir, const char *prog,
+ char *env[], int zfd)
+{
+ char path[PATH_MAX];
+ int n;
+ pid_t pid;
+ int fd;
+ pid_t wpid;
+ int status;
+
+ assert(dir != NULL);
+ assert(prog != NULL);
+ assert(env != NULL);
+ assert(zfd >= 0);
+
+ n = snprintf(path, sizeof (path), "%s/%s", dir, prog);
+ if ((n < 0) || (n >= sizeof (path))) {
+ zed_log_msg(LOG_WARNING,
+ "Failed to fork \"%s\" for eid=%llu: %s",
+ prog, eid, strerror(ENAMETOOLONG));
+ return;
+ }
+ pid = fork();
+ if (pid < 0) {
+ zed_log_msg(LOG_WARNING,
+ "Failed to fork \"%s\" for eid=%llu: %s",
+ prog, eid, strerror(errno));
+ return;
+ } else if (pid == 0) {
+ (void) umask(022);
+ if ((fd = open("/dev/null", O_RDWR)) != -1) {
+ (void) dup2(fd, STDIN_FILENO);
+ (void) dup2(fd, STDOUT_FILENO);
+ (void) dup2(fd, STDERR_FILENO);
+ }
+ (void) dup2(zfd, ZEVENT_FILENO);
+ zed_file_close_from(ZEVENT_FILENO + 1);
+ execle(path, prog, NULL, env);
+ _exit(127);
+ }
+
+ /* parent process */
+
+ zed_log_msg(LOG_INFO, "Invoking \"%s\" eid=%llu pid=%d",
+ prog, eid, pid);
+
+ /* FIXME: Timeout rogue child processes with sigalarm? */
+
+ /*
+ * Wait for child process using WNOHANG to limit
+ * the time spent waiting to 10 seconds (10,000ms).
+ */
+ for (n = 0; n < 1000; n++) {
+ wpid = waitpid(pid, &status, WNOHANG);
+ if (wpid == (pid_t)-1) {
+ if (errno == EINTR)
+ continue;
+ zed_log_msg(LOG_WARNING,
+ "Failed to wait for \"%s\" eid=%llu pid=%d",
+ prog, eid, pid);
+ break;
+ } else if (wpid == 0) {
+ struct timespec t;
+
+ /* child still running */
+ t.tv_sec = 0;
+ t.tv_nsec = 10000000; /* 10ms */
+ (void) nanosleep(&t, NULL);
+ continue;
+ }
+
+ if (WIFEXITED(status)) {
+ zed_log_msg(LOG_INFO,
+ "Finished \"%s\" eid=%llu pid=%d exit=%d",
+ prog, eid, pid, WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ zed_log_msg(LOG_INFO,
+ "Finished \"%s\" eid=%llu pid=%d sig=%d/%s",
+ prog, eid, pid, WTERMSIG(status),
+ strsignal(WTERMSIG(status)));
+ } else {
+ zed_log_msg(LOG_INFO,
+ "Finished \"%s\" eid=%llu pid=%d status=0x%X",
+ prog, eid, (unsigned int) status);
+ }
+ break;
+ }
+
+ /*
+ * kill child process after 10 seconds
+ */
+ if (wpid == 0) {
+ zed_log_msg(LOG_WARNING, "Killing hung \"%s\" pid=%d",
+ prog, pid);
+ (void) kill(pid, SIGKILL);
+ }
+}
+
+/*
+ * Process the event [eid] by synchronously invoking all zedlets with a
+ * matching class prefix.
+ *
+ * Each executable in [zedlets] from the directory [dir] is matched against
+ * the event's [class], [subclass], and the "all" class (which matches
+ * all events). Every zedlet with a matching class prefix is invoked.
+ * The NAME=VALUE strings in [envs] will be passed to the zedlet as
+ * environment variables.
+ *
+ * The file descriptor [zfd] is the zevent_fd used to track the
+ * current cursor location within the zevent nvlist.
+ *
+ * Return 0 on success, -1 on error.
+ */
+int
+zed_exec_process(uint64_t eid, const char *class, const char *subclass,
+ const char *dir, zed_strings_t *zedlets, zed_strings_t *envs, int zfd)
+{
+ const char *class_strings[4];
+ const char *allclass = "all";
+ const char **csp;
+ const char *z;
+ char **e;
+ int n;
+
+ if (!dir || !zedlets || !envs || zfd < 0)
+ return (-1);
+
+ csp = class_strings;
+
+ if (class)
+ *csp++ = class;
+
+ if (subclass)
+ *csp++ = subclass;
+
+ if (allclass)
+ *csp++ = allclass;
+
+ *csp = NULL;
+
+ e = _zed_exec_create_env(envs);
+
+ for (z = zed_strings_first(zedlets); z; z = zed_strings_next(zedlets)) {
+ for (csp = class_strings; *csp; csp++) {
+ n = strlen(*csp);
+ if ((strncmp(z, *csp, n) == 0) && !isalpha(z[n]))
+ _zed_exec_fork_child(eid, dir, z, e, zfd);
+ }
+ }
+ free(e);
+ return (0);
+}