]> git.proxmox.com Git - mirror_zfs-debian.git/blame - cmd/zed/zed_exec.c
New upstream version 0.7.2
[mirror_zfs-debian.git] / cmd / zed / zed_exec.c
CommitLineData
ea04106b 1/*
e10b0808
AX
2 * This file is part of the ZFS Event Daemon (ZED)
3 * for ZFS on Linux (ZoL) <http://zfsonlinux.org/>.
ea04106b
AX
4 * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
5 * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
e10b0808
AX
6 * Refer to the ZoL git commit log for authoritative copyright attribution.
7 *
8 * The contents of this file are subject to the terms of the
9 * Common Development and Distribution License Version 1.0 (CDDL-1.0).
10 * You can obtain a copy of the license from the top-level file
11 * "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
12 * You may not use this file except in compliance with the license.
ea04106b
AX
13 */
14
15#include <assert.h>
16#include <ctype.h>
17#include <errno.h>
18#include <fcntl.h>
19#include <stdlib.h>
20#include <string.h>
21#include <sys/stat.h>
22#include <sys/wait.h>
cae5b340 23#include <time.h>
ea04106b
AX
24#include <unistd.h>
25#include "zed_file.h"
26#include "zed_log.h"
27#include "zed_strings.h"
28
29#define ZEVENT_FILENO 3
30
31/*
32 * Create an environment string array for passing to execve() using the
33 * NAME=VALUE strings in container [zsp].
34 * Return a newly-allocated environment, or NULL on error.
35 */
36static char **
37_zed_exec_create_env(zed_strings_t *zsp)
38{
39 int num_ptrs;
40 int buflen;
41 char *buf;
42 char **pp;
43 char *p;
44 const char *q;
45 int i;
46 int len;
47
48 num_ptrs = zed_strings_count(zsp) + 1;
49 buflen = num_ptrs * sizeof (char *);
50 for (q = zed_strings_first(zsp); q; q = zed_strings_next(zsp))
51 buflen += strlen(q) + 1;
52
53 buf = calloc(1, buflen);
54 if (!buf)
55 return (NULL);
56
cae5b340 57 pp = (char **)buf;
ea04106b
AX
58 p = buf + (num_ptrs * sizeof (char *));
59 i = 0;
60 for (q = zed_strings_first(zsp); q; q = zed_strings_next(zsp)) {
61 pp[i] = p;
62 len = strlen(q) + 1;
63 memcpy(p, q, len);
64 p += len;
65 i++;
66 }
67 pp[i] = NULL;
68 assert(buf + buflen == p);
cae5b340 69 return ((char **)buf);
ea04106b
AX
70}
71
72/*
73 * Fork a child process to handle event [eid]. The program [prog]
cae5b340 74 * in directory [dir] is executed with the environment [env].
ea04106b
AX
75 *
76 * The file descriptor [zfd] is the zevent_fd used to track the
77 * current cursor location within the zevent nvlist.
78 */
79static void
80_zed_exec_fork_child(uint64_t eid, const char *dir, const char *prog,
81 char *env[], int zfd)
82{
83 char path[PATH_MAX];
84 int n;
85 pid_t pid;
86 int fd;
87 pid_t wpid;
88 int status;
89
90 assert(dir != NULL);
91 assert(prog != NULL);
92 assert(env != NULL);
93 assert(zfd >= 0);
94
95 n = snprintf(path, sizeof (path), "%s/%s", dir, prog);
96 if ((n < 0) || (n >= sizeof (path))) {
97 zed_log_msg(LOG_WARNING,
98 "Failed to fork \"%s\" for eid=%llu: %s",
99 prog, eid, strerror(ENAMETOOLONG));
100 return;
101 }
102 pid = fork();
103 if (pid < 0) {
104 zed_log_msg(LOG_WARNING,
105 "Failed to fork \"%s\" for eid=%llu: %s",
106 prog, eid, strerror(errno));
107 return;
108 } else if (pid == 0) {
109 (void) umask(022);
cae5b340
AX
110 if ((fd = open("/dev/null", O_RDWR)) != -1) {
111 (void) dup2(fd, STDIN_FILENO);
112 (void) dup2(fd, STDOUT_FILENO);
113 (void) dup2(fd, STDERR_FILENO);
114 }
ea04106b
AX
115 (void) dup2(zfd, ZEVENT_FILENO);
116 zed_file_close_from(ZEVENT_FILENO + 1);
117 execle(path, prog, NULL, env);
118 _exit(127);
cae5b340
AX
119 }
120
121 /* parent process */
122
123 zed_log_msg(LOG_INFO, "Invoking \"%s\" eid=%llu pid=%d",
124 prog, eid, pid);
125
126 /* FIXME: Timeout rogue child processes with sigalarm? */
127
128 /*
129 * Wait for child process using WNOHANG to limit
130 * the time spent waiting to 10 seconds (10,000ms).
131 */
132 for (n = 0; n < 1000; n++) {
133 wpid = waitpid(pid, &status, WNOHANG);
134 if (wpid == (pid_t)-1) {
ea04106b 135 if (errno == EINTR)
cae5b340 136 continue;
ea04106b
AX
137 zed_log_msg(LOG_WARNING,
138 "Failed to wait for \"%s\" eid=%llu pid=%d",
139 prog, eid, pid);
cae5b340
AX
140 break;
141 } else if (wpid == 0) {
142 struct timespec t;
143
144 /* child still running */
145 t.tv_sec = 0;
146 t.tv_nsec = 10000000; /* 10ms */
147 (void) nanosleep(&t, NULL);
148 continue;
149 }
150
151 if (WIFEXITED(status)) {
ea04106b
AX
152 zed_log_msg(LOG_INFO,
153 "Finished \"%s\" eid=%llu pid=%d exit=%d",
154 prog, eid, pid, WEXITSTATUS(status));
155 } else if (WIFSIGNALED(status)) {
156 zed_log_msg(LOG_INFO,
157 "Finished \"%s\" eid=%llu pid=%d sig=%d/%s",
158 prog, eid, pid, WTERMSIG(status),
159 strsignal(WTERMSIG(status)));
160 } else {
161 zed_log_msg(LOG_INFO,
162 "Finished \"%s\" eid=%llu pid=%d status=0x%X",
163 prog, eid, (unsigned int) status);
164 }
cae5b340
AX
165 break;
166 }
167
168 /*
169 * kill child process after 10 seconds
170 */
171 if (wpid == 0) {
172 zed_log_msg(LOG_WARNING, "Killing hung \"%s\" pid=%d",
173 prog, pid);
174 (void) kill(pid, SIGKILL);
ea04106b
AX
175 }
176}
177
178/*
179 * Process the event [eid] by synchronously invoking all zedlets with a
180 * matching class prefix.
181 *
182 * Each executable in [zedlets] from the directory [dir] is matched against
183 * the event's [class], [subclass], and the "all" class (which matches
184 * all events). Every zedlet with a matching class prefix is invoked.
185 * The NAME=VALUE strings in [envs] will be passed to the zedlet as
186 * environment variables.
187 *
188 * The file descriptor [zfd] is the zevent_fd used to track the
189 * current cursor location within the zevent nvlist.
190 *
191 * Return 0 on success, -1 on error.
192 */
193int
194zed_exec_process(uint64_t eid, const char *class, const char *subclass,
195 const char *dir, zed_strings_t *zedlets, zed_strings_t *envs, int zfd)
196{
197 const char *class_strings[4];
198 const char *allclass = "all";
199 const char **csp;
200 const char *z;
201 char **e;
202 int n;
203
204 if (!dir || !zedlets || !envs || zfd < 0)
205 return (-1);
206
207 csp = class_strings;
208
209 if (class)
210 *csp++ = class;
211
212 if (subclass)
213 *csp++ = subclass;
214
215 if (allclass)
216 *csp++ = allclass;
217
218 *csp = NULL;
219
220 e = _zed_exec_create_env(envs);
221
222 for (z = zed_strings_first(zedlets); z; z = zed_strings_next(zedlets)) {
223 for (csp = class_strings; *csp; csp++) {
224 n = strlen(*csp);
225 if ((strncmp(z, *csp, n) == 0) && !isalpha(z[n]))
226 _zed_exec_fork_child(eid, dir, z, e, zfd);
227 }
228 }
229 free(e);
230 return (0);
231}