1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3 * This file and its contents are supplied under the terms of the
4 * Common Development and Distribution License ("CDDL"), version 1.0.
5 * You may only use this file in accordance with the terms of version
6 * 1.0 of the CDDL.
7 *
8 * A full copy of the text of the CDDL should have accompanied this
9 * source. A copy of the CDDL is also available via the Internet at
10 * https://opensource.org/license/CDDL-1.0.
11 */
12 /*
13 * This file is part of the ZFS Event Daemon (ZED).
14 *
15 * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
16 * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
17 */
18
19 #include <assert.h>
20 #include <ctype.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stddef.h>
26 #include <sys/avl.h>
27 #include <sys/resource.h>
28 #include <sys/stat.h>
29 #include <sys/wait.h>
30 #include <time.h>
31 #include <unistd.h>
32 #include <pthread.h>
33 #include <signal.h>
34
35 #include "zed_exec.h"
36 #include "zed_log.h"
37 #include "zed_strings.h"
38
39 #define ZEVENT_FILENO 3
40
41 struct launched_process_node {
42 avl_node_t node;
43 pid_t pid;
44 uint64_t eid;
45 char *name;
46 };
47
48 static int
_launched_process_node_compare(const void * x1,const void * x2)49 _launched_process_node_compare(const void *x1, const void *x2)
50 {
51 const struct launched_process_node *node1 = x1;
52 const struct launched_process_node *node2 = x2;
53
54 return (TREE_CMP(node1->pid, node2->pid));
55 }
56
57 static pthread_t _reap_children_tid = (pthread_t)-1;
58 static volatile boolean_t _reap_children_stop;
59 static avl_tree_t _launched_processes;
60 static pthread_mutex_t _launched_processes_lock = PTHREAD_MUTEX_INITIALIZER;
61 static int16_t _launched_processes_limit;
62
63 /*
64 * Create an environment string array for passing to execve() using the
65 * NAME=VALUE strings in container [zsp].
66 * Return a newly-allocated environment, or NULL on error.
67 */
68 static char **
_zed_exec_create_env(zed_strings_t * zsp)69 _zed_exec_create_env(zed_strings_t *zsp)
70 {
71 int num_ptrs;
72 int buflen;
73 char *buf;
74 char **pp;
75 char *p;
76 const char *q;
77 int i;
78 int len;
79
80 num_ptrs = zed_strings_count(zsp) + 1;
81 buflen = num_ptrs * sizeof (char *);
82 for (q = zed_strings_first(zsp); q; q = zed_strings_next(zsp))
83 buflen += strlen(q) + 1;
84
85 buf = calloc(1, buflen);
86 if (!buf)
87 return (NULL);
88
89 pp = (char **)buf;
90 p = buf + (num_ptrs * sizeof (char *));
91 i = 0;
92 for (q = zed_strings_first(zsp); q; q = zed_strings_next(zsp)) {
93 pp[i] = p;
94 len = strlen(q) + 1;
95 memcpy(p, q, len);
96 p += len;
97 i++;
98 }
99 pp[i] = NULL;
100 assert(buf + buflen == p);
101 return ((char **)buf);
102 }
103
104 /*
105 * Fork a child process to handle event [eid]. The program [prog]
106 * in directory [dir] is executed with the environment [env].
107 *
108 * The file descriptor [zfd] is the zevent_fd used to track the
109 * current cursor location within the zevent nvlist.
110 */
111 static void
_zed_exec_fork_child(uint64_t eid,const char * dir,const char * prog,char * env[],int zfd,boolean_t in_foreground)112 _zed_exec_fork_child(uint64_t eid, const char *dir, const char *prog,
113 char *env[], int zfd, boolean_t in_foreground)
114 {
115 char path[PATH_MAX];
116 int n;
117 pid_t pid;
118 int fd;
119 struct launched_process_node *node;
120 sigset_t mask;
121 struct timespec launch_timeout =
122 { .tv_sec = 0, .tv_nsec = 200 * 1000 * 1000, };
123
124 assert(dir != NULL);
125 assert(prog != NULL);
126 assert(env != NULL);
127 assert(zfd >= 0);
128
129 while (__atomic_load_n(&_launched_processes_limit,
130 __ATOMIC_SEQ_CST) <= 0)
131 (void) nanosleep(&launch_timeout, NULL);
132
133 n = snprintf(path, sizeof (path), "%s/%s", dir, prog);
134 if ((n < 0) || (n >= sizeof (path))) {
135 zed_log_msg(LOG_WARNING,
136 "Failed to fork \"%s\" for eid=%llu: %s",
137 prog, eid, strerror(ENAMETOOLONG));
138 return;
139 }
140 (void) pthread_mutex_lock(&_launched_processes_lock);
141 pid = fork();
142 if (pid < 0) {
143 (void) pthread_mutex_unlock(&_launched_processes_lock);
144 zed_log_msg(LOG_WARNING,
145 "Failed to fork \"%s\" for eid=%llu: %s",
146 prog, eid, strerror(errno));
147 return;
148 } else if (pid == 0) {
149 (void) sigemptyset(&mask);
150 (void) sigprocmask(SIG_SETMASK, &mask, NULL);
151
152 (void) umask(022);
153 if (in_foreground && /* we're already devnulled if daemonised */
154 (fd = open("/dev/null", O_RDWR | O_CLOEXEC)) != -1) {
155 (void) dup2(fd, STDIN_FILENO);
156 (void) dup2(fd, STDOUT_FILENO);
157 (void) dup2(fd, STDERR_FILENO);
158 }
159 (void) dup2(zfd, ZEVENT_FILENO);
160 execle(path, prog, NULL, env);
161 _exit(127);
162 }
163
164 /* parent process */
165
166 node = calloc(1, sizeof (*node));
167 if (node) {
168 node->pid = pid;
169 node->eid = eid;
170 node->name = strdup(prog);
171 if (node->name == NULL) {
172 perror("strdup");
173 exit(EXIT_FAILURE);
174 }
175
176 avl_add(&_launched_processes, node);
177 }
178 (void) pthread_mutex_unlock(&_launched_processes_lock);
179
180 __atomic_sub_fetch(&_launched_processes_limit, 1, __ATOMIC_SEQ_CST);
181 zed_log_msg(LOG_INFO, "Invoking \"%s\" eid=%llu pid=%d",
182 prog, eid, pid);
183 }
184
185 static void
_nop(int sig)186 _nop(int sig)
187 {
188 (void) sig;
189 }
190
191 static void
wait_for_children(boolean_t do_pause,boolean_t wait)192 wait_for_children(boolean_t do_pause, boolean_t wait)
193 {
194 pid_t pid;
195 struct rusage usage;
196 int status;
197 struct launched_process_node node, *pnode;
198
199 for (_reap_children_stop = B_FALSE; !_reap_children_stop; ) {
200 (void) pthread_mutex_lock(&_launched_processes_lock);
201 pid = wait4(0, &status, wait ? 0 : WNOHANG, &usage);
202 if (pid == 0 || pid == (pid_t)-1) {
203 (void) pthread_mutex_unlock(&_launched_processes_lock);
204 if ((pid == 0) || (errno == ECHILD)) {
205 if (do_pause)
206 pause();
207 } else if (errno != EINTR)
208 zed_log_msg(LOG_WARNING,
209 "Failed to wait for children: %s",
210 strerror(errno));
211 if (!do_pause)
212 return;
213
214 } else {
215 memset(&node, 0, sizeof (node));
216 node.pid = pid;
217 pnode = avl_find(&_launched_processes, &node, NULL);
218 if (pnode) {
219 memcpy(&node, pnode, sizeof (node));
220
221 avl_remove(&_launched_processes, pnode);
222 free(pnode);
223 }
224 (void) pthread_mutex_unlock(&_launched_processes_lock);
225 __atomic_add_fetch(&_launched_processes_limit, 1,
226 __ATOMIC_SEQ_CST);
227
228 usage.ru_utime.tv_sec += usage.ru_stime.tv_sec;
229 usage.ru_utime.tv_usec += usage.ru_stime.tv_usec;
230 usage.ru_utime.tv_sec +=
231 usage.ru_utime.tv_usec / (1000 * 1000);
232 usage.ru_utime.tv_usec %= 1000 * 1000;
233
234 if (WIFEXITED(status)) {
235 zed_log_msg(LOG_INFO,
236 "Finished \"%s\" eid=%llu pid=%d "
237 "time=%llu.%06us exit=%d",
238 node.name, node.eid, pid,
239 (unsigned long long) usage.ru_utime.tv_sec,
240 (unsigned int) usage.ru_utime.tv_usec,
241 WEXITSTATUS(status));
242 } else if (WIFSIGNALED(status)) {
243 zed_log_msg(LOG_INFO,
244 "Finished \"%s\" eid=%llu pid=%d "
245 "time=%llu.%06us sig=%d/%s",
246 node.name, node.eid, pid,
247 (unsigned long long) usage.ru_utime.tv_sec,
248 (unsigned int) usage.ru_utime.tv_usec,
249 WTERMSIG(status),
250 strsignal(WTERMSIG(status)));
251 } else {
252 zed_log_msg(LOG_INFO,
253 "Finished \"%s\" eid=%llu pid=%d "
254 "time=%llu.%06us status=0x%X",
255 node.name, node.eid, pid,
256 (unsigned long long) usage.ru_utime.tv_sec,
257 (unsigned int) usage.ru_utime.tv_usec,
258 (unsigned int) status);
259 }
260
261 free(node.name);
262 }
263 }
264
265 }
266
267 static void *
_reap_children(void * arg)268 _reap_children(void *arg)
269 {
270 (void) arg;
271 struct sigaction sa = {};
272
273 (void) sigfillset(&sa.sa_mask);
274 (void) sigdelset(&sa.sa_mask, SIGCHLD);
275 (void) pthread_sigmask(SIG_SETMASK, &sa.sa_mask, NULL);
276
277 (void) sigemptyset(&sa.sa_mask);
278 sa.sa_handler = _nop;
279 sa.sa_flags = SA_NOCLDSTOP;
280 (void) sigaction(SIGCHLD, &sa, NULL);
281
282 wait_for_children(B_TRUE, B_FALSE);
283
284 return (NULL);
285 }
286
287 void
zed_exec_fini(void)288 zed_exec_fini(void)
289 {
290 struct launched_process_node *node;
291 void *ck = NULL;
292
293 if (_reap_children_tid == (pthread_t)-1)
294 return;
295
296 _reap_children_stop = B_TRUE;
297 (void) pthread_kill(_reap_children_tid, SIGCHLD);
298 (void) pthread_join(_reap_children_tid, NULL);
299
300 while ((node = avl_destroy_nodes(&_launched_processes, &ck)) != NULL) {
301 free(node->name);
302 free(node);
303 }
304 avl_destroy(&_launched_processes);
305
306 (void) pthread_mutex_destroy(&_launched_processes_lock);
307 (void) pthread_mutex_init(&_launched_processes_lock, NULL);
308
309 _reap_children_tid = (pthread_t)-1;
310 }
311
312 /*
313 * Check if the zedlet name indicates if it is a synchronous zedlet
314 *
315 * Synchronous zedlets have a "-sync-" immediately following the event name in
316 * their zedlet filename, like:
317 *
318 * EVENT_NAME-sync-ZEDLETNAME.sh
319 *
320 * For example, if you wanted a synchronous statechange script:
321 *
322 * statechange-sync-myzedlet.sh
323 *
324 * Synchronous zedlets are guaranteed to be the only zedlet running. No other
325 * zedlets may run in parallel with a synchronous zedlet. A synchronous
326 * zedlet will wait for all previously spawned zedlets to finish before running.
327 * Users should be careful to only use synchronous zedlets when needed, since
328 * they decrease parallelism.
329 */
330 static boolean_t
zedlet_is_sync(const char * zedlet,const char * event)331 zedlet_is_sync(const char *zedlet, const char *event)
332 {
333 const char *sync_str = "-sync-";
334 size_t sync_str_len;
335 size_t zedlet_len;
336 size_t event_len;
337
338 sync_str_len = strlen(sync_str);
339 zedlet_len = strlen(zedlet);
340 event_len = strlen(event);
341
342 if (event_len + sync_str_len >= zedlet_len)
343 return (B_FALSE);
344
345 if (strncmp(&zedlet[event_len], sync_str, sync_str_len) == 0)
346 return (B_TRUE);
347
348 return (B_FALSE);
349 }
350
351 /*
352 * Process the event [eid] by synchronously invoking all zedlets with a
353 * matching class prefix.
354 *
355 * Each executable in [zcp->zedlets] from the directory [zcp->zedlet_dir]
356 * is matched against the event's [class], [subclass], and the "all" class
357 * (which matches all events).
358 * Every zedlet with a matching class prefix is invoked.
359 * The NAME=VALUE strings in [envs] will be passed to the zedlet as
360 * environment variables.
361 *
362 * The file descriptor [zcp->zevent_fd] is the zevent_fd used to track the
363 * current cursor location within the zevent nvlist.
364 *
365 * Return 0 on success, -1 on error.
366 */
367 int
zed_exec_process(uint64_t eid,const char * class,const char * subclass,struct zed_conf * zcp,zed_strings_t * envs)368 zed_exec_process(uint64_t eid, const char *class, const char *subclass,
369 struct zed_conf *zcp, zed_strings_t *envs)
370 {
371 const char *class_strings[4];
372 const char *allclass = "all";
373 const char **csp;
374 const char *z;
375 char **e;
376 int n;
377
378 if (!zcp->zedlet_dir || !zcp->zedlets || !envs || zcp->zevent_fd < 0)
379 return (-1);
380
381 if (_reap_children_tid == (pthread_t)-1) {
382 _launched_processes_limit = zcp->max_jobs;
383
384 if (pthread_create(&_reap_children_tid, NULL,
385 _reap_children, NULL) != 0)
386 return (-1);
387 pthread_setname_np(_reap_children_tid, "reap ZEDLETs");
388
389 avl_create(&_launched_processes, _launched_process_node_compare,
390 sizeof (struct launched_process_node),
391 offsetof(struct launched_process_node, node));
392 }
393
394 csp = class_strings;
395
396 if (class)
397 *csp++ = class;
398
399 if (subclass)
400 *csp++ = subclass;
401
402 if (allclass)
403 *csp++ = allclass;
404
405 *csp = NULL;
406
407 e = _zed_exec_create_env(envs);
408
409 for (z = zed_strings_first(zcp->zedlets); z;
410 z = zed_strings_next(zcp->zedlets)) {
411 for (csp = class_strings; *csp; csp++) {
412 n = strlen(*csp);
413 if ((strncmp(z, *csp, n) == 0) && !isalpha(z[n])) {
414 boolean_t is_sync = zedlet_is_sync(z, *csp);
415
416 if (is_sync) {
417 /*
418 * Wait for previous zedlets to
419 * finish
420 */
421 wait_for_children(B_FALSE, B_TRUE);
422 }
423
424 _zed_exec_fork_child(eid, zcp->zedlet_dir,
425 z, e, zcp->zevent_fd, zcp->do_foreground);
426
427 if (is_sync) {
428 /*
429 * Wait for sync zedlet we just launched
430 * to finish.
431 */
432 wait_for_children(B_FALSE, B_TRUE);
433 }
434 }
435 }
436 }
437 free(e);
438 return (0);
439 }
440