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