1*61145dc2SMartin Matuska // SPDX-License-Identifier: CDDL-1.0
2eda14cbcSMatt Macy /*
3180f8225SMatt Macy * This file is part of the ZFS Event Daemon (ZED).
4180f8225SMatt Macy *
5eda14cbcSMatt Macy * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
6eda14cbcSMatt Macy * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
716038816SMartin Matuska * Refer to the OpenZFS git commit log for authoritative copyright attribution.
8eda14cbcSMatt Macy *
9eda14cbcSMatt Macy * The contents of this file are subject to the terms of the
10eda14cbcSMatt Macy * Common Development and Distribution License Version 1.0 (CDDL-1.0).
11eda14cbcSMatt Macy * You can obtain a copy of the license from the top-level file
12eda14cbcSMatt Macy * "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
13eda14cbcSMatt Macy * You may not use this file except in compliance with the license.
14eda14cbcSMatt Macy */
15eda14cbcSMatt Macy
16eda14cbcSMatt Macy #include <assert.h>
17eda14cbcSMatt Macy #include <ctype.h>
18eda14cbcSMatt Macy #include <errno.h>
19eda14cbcSMatt Macy #include <fcntl.h>
20eda14cbcSMatt Macy #include <stdlib.h>
21eda14cbcSMatt Macy #include <string.h>
2216038816SMartin Matuska #include <stddef.h>
2316038816SMartin Matuska #include <sys/avl.h>
2416038816SMartin Matuska #include <sys/resource.h>
25eda14cbcSMatt Macy #include <sys/stat.h>
26eda14cbcSMatt Macy #include <sys/wait.h>
27eda14cbcSMatt Macy #include <time.h>
28eda14cbcSMatt Macy #include <unistd.h>
2916038816SMartin Matuska #include <pthread.h>
3053b70c86SMartin Matuska #include <signal.h>
3153b70c86SMartin Matuska
32eda14cbcSMatt Macy #include "zed_exec.h"
33eda14cbcSMatt Macy #include "zed_log.h"
34eda14cbcSMatt Macy #include "zed_strings.h"
35eda14cbcSMatt Macy
36eda14cbcSMatt Macy #define ZEVENT_FILENO 3
37eda14cbcSMatt Macy
3816038816SMartin Matuska struct launched_process_node {
3916038816SMartin Matuska avl_node_t node;
4016038816SMartin Matuska pid_t pid;
4116038816SMartin Matuska uint64_t eid;
4216038816SMartin Matuska char *name;
4316038816SMartin Matuska };
4416038816SMartin Matuska
4516038816SMartin Matuska static int
_launched_process_node_compare(const void * x1,const void * x2)4616038816SMartin Matuska _launched_process_node_compare(const void *x1, const void *x2)
4716038816SMartin Matuska {
4816038816SMartin Matuska pid_t p1;
4916038816SMartin Matuska pid_t p2;
5016038816SMartin Matuska
5116038816SMartin Matuska assert(x1 != NULL);
5216038816SMartin Matuska assert(x2 != NULL);
5316038816SMartin Matuska
5416038816SMartin Matuska p1 = ((const struct launched_process_node *) x1)->pid;
5516038816SMartin Matuska p2 = ((const struct launched_process_node *) x2)->pid;
5616038816SMartin Matuska
5716038816SMartin Matuska if (p1 < p2)
5816038816SMartin Matuska return (-1);
5916038816SMartin Matuska else if (p1 == p2)
6016038816SMartin Matuska return (0);
6116038816SMartin Matuska else
6216038816SMartin Matuska return (1);
6316038816SMartin Matuska }
6416038816SMartin Matuska
6516038816SMartin Matuska static pthread_t _reap_children_tid = (pthread_t)-1;
6616038816SMartin Matuska static volatile boolean_t _reap_children_stop;
6716038816SMartin Matuska static avl_tree_t _launched_processes;
6816038816SMartin Matuska static pthread_mutex_t _launched_processes_lock = PTHREAD_MUTEX_INITIALIZER;
6916038816SMartin Matuska static int16_t _launched_processes_limit;
7016038816SMartin Matuska
71eda14cbcSMatt Macy /*
72eda14cbcSMatt Macy * Create an environment string array for passing to execve() using the
73eda14cbcSMatt Macy * NAME=VALUE strings in container [zsp].
74eda14cbcSMatt Macy * Return a newly-allocated environment, or NULL on error.
75eda14cbcSMatt Macy */
76eda14cbcSMatt Macy static char **
_zed_exec_create_env(zed_strings_t * zsp)77eda14cbcSMatt Macy _zed_exec_create_env(zed_strings_t *zsp)
78eda14cbcSMatt Macy {
79eda14cbcSMatt Macy int num_ptrs;
80eda14cbcSMatt Macy int buflen;
81eda14cbcSMatt Macy char *buf;
82eda14cbcSMatt Macy char **pp;
83eda14cbcSMatt Macy char *p;
84eda14cbcSMatt Macy const char *q;
85eda14cbcSMatt Macy int i;
86eda14cbcSMatt Macy int len;
87eda14cbcSMatt Macy
88eda14cbcSMatt Macy num_ptrs = zed_strings_count(zsp) + 1;
89eda14cbcSMatt Macy buflen = num_ptrs * sizeof (char *);
90eda14cbcSMatt Macy for (q = zed_strings_first(zsp); q; q = zed_strings_next(zsp))
91eda14cbcSMatt Macy buflen += strlen(q) + 1;
92eda14cbcSMatt Macy
93eda14cbcSMatt Macy buf = calloc(1, buflen);
94eda14cbcSMatt Macy if (!buf)
95eda14cbcSMatt Macy return (NULL);
96eda14cbcSMatt Macy
97eda14cbcSMatt Macy pp = (char **)buf;
98eda14cbcSMatt Macy p = buf + (num_ptrs * sizeof (char *));
99eda14cbcSMatt Macy i = 0;
100eda14cbcSMatt Macy for (q = zed_strings_first(zsp); q; q = zed_strings_next(zsp)) {
101eda14cbcSMatt Macy pp[i] = p;
102eda14cbcSMatt Macy len = strlen(q) + 1;
103eda14cbcSMatt Macy memcpy(p, q, len);
104eda14cbcSMatt Macy p += len;
105eda14cbcSMatt Macy i++;
106eda14cbcSMatt Macy }
107eda14cbcSMatt Macy pp[i] = NULL;
108eda14cbcSMatt Macy assert(buf + buflen == p);
109eda14cbcSMatt Macy return ((char **)buf);
110eda14cbcSMatt Macy }
111eda14cbcSMatt Macy
112eda14cbcSMatt Macy /*
113eda14cbcSMatt Macy * Fork a child process to handle event [eid]. The program [prog]
114eda14cbcSMatt Macy * in directory [dir] is executed with the environment [env].
115eda14cbcSMatt Macy *
116eda14cbcSMatt Macy * The file descriptor [zfd] is the zevent_fd used to track the
117eda14cbcSMatt Macy * current cursor location within the zevent nvlist.
118eda14cbcSMatt Macy */
119eda14cbcSMatt Macy static void
_zed_exec_fork_child(uint64_t eid,const char * dir,const char * prog,char * env[],int zfd,boolean_t in_foreground)120eda14cbcSMatt Macy _zed_exec_fork_child(uint64_t eid, const char *dir, const char *prog,
12116038816SMartin Matuska char *env[], int zfd, boolean_t in_foreground)
122eda14cbcSMatt Macy {
123eda14cbcSMatt Macy char path[PATH_MAX];
124eda14cbcSMatt Macy int n;
125eda14cbcSMatt Macy pid_t pid;
126eda14cbcSMatt Macy int fd;
12716038816SMartin Matuska struct launched_process_node *node;
12816038816SMartin Matuska sigset_t mask;
12916038816SMartin Matuska struct timespec launch_timeout =
13016038816SMartin Matuska { .tv_sec = 0, .tv_nsec = 200 * 1000 * 1000, };
131eda14cbcSMatt Macy
132eda14cbcSMatt Macy assert(dir != NULL);
133eda14cbcSMatt Macy assert(prog != NULL);
134eda14cbcSMatt Macy assert(env != NULL);
135eda14cbcSMatt Macy assert(zfd >= 0);
136eda14cbcSMatt Macy
13716038816SMartin Matuska while (__atomic_load_n(&_launched_processes_limit,
13816038816SMartin Matuska __ATOMIC_SEQ_CST) <= 0)
13916038816SMartin Matuska (void) nanosleep(&launch_timeout, NULL);
14016038816SMartin Matuska
141eda14cbcSMatt Macy n = snprintf(path, sizeof (path), "%s/%s", dir, prog);
142eda14cbcSMatt Macy if ((n < 0) || (n >= sizeof (path))) {
143eda14cbcSMatt Macy zed_log_msg(LOG_WARNING,
144eda14cbcSMatt Macy "Failed to fork \"%s\" for eid=%llu: %s",
145eda14cbcSMatt Macy prog, eid, strerror(ENAMETOOLONG));
146eda14cbcSMatt Macy return;
147eda14cbcSMatt Macy }
14816038816SMartin Matuska (void) pthread_mutex_lock(&_launched_processes_lock);
149eda14cbcSMatt Macy pid = fork();
150eda14cbcSMatt Macy if (pid < 0) {
15116038816SMartin Matuska (void) pthread_mutex_unlock(&_launched_processes_lock);
152eda14cbcSMatt Macy zed_log_msg(LOG_WARNING,
153eda14cbcSMatt Macy "Failed to fork \"%s\" for eid=%llu: %s",
154eda14cbcSMatt Macy prog, eid, strerror(errno));
155eda14cbcSMatt Macy return;
156eda14cbcSMatt Macy } else if (pid == 0) {
15716038816SMartin Matuska (void) sigemptyset(&mask);
15816038816SMartin Matuska (void) sigprocmask(SIG_SETMASK, &mask, NULL);
15916038816SMartin Matuska
160eda14cbcSMatt Macy (void) umask(022);
16116038816SMartin Matuska if (in_foreground && /* we're already devnulled if daemonised */
16216038816SMartin Matuska (fd = open("/dev/null", O_RDWR | O_CLOEXEC)) != -1) {
163eda14cbcSMatt Macy (void) dup2(fd, STDIN_FILENO);
164eda14cbcSMatt Macy (void) dup2(fd, STDOUT_FILENO);
165eda14cbcSMatt Macy (void) dup2(fd, STDERR_FILENO);
166eda14cbcSMatt Macy }
167eda14cbcSMatt Macy (void) dup2(zfd, ZEVENT_FILENO);
168eda14cbcSMatt Macy execle(path, prog, NULL, env);
169eda14cbcSMatt Macy _exit(127);
170eda14cbcSMatt Macy }
171eda14cbcSMatt Macy
172eda14cbcSMatt Macy /* parent process */
173eda14cbcSMatt Macy
17416038816SMartin Matuska node = calloc(1, sizeof (*node));
17516038816SMartin Matuska if (node) {
17616038816SMartin Matuska node->pid = pid;
17716038816SMartin Matuska node->eid = eid;
17816038816SMartin Matuska node->name = strdup(prog);
179dbd5678dSMartin Matuska if (node->name == NULL) {
180dbd5678dSMartin Matuska perror("strdup");
181dbd5678dSMartin Matuska exit(EXIT_FAILURE);
182dbd5678dSMartin Matuska }
18316038816SMartin Matuska
18416038816SMartin Matuska avl_add(&_launched_processes, node);
18516038816SMartin Matuska }
18616038816SMartin Matuska (void) pthread_mutex_unlock(&_launched_processes_lock);
18716038816SMartin Matuska
18816038816SMartin Matuska __atomic_sub_fetch(&_launched_processes_limit, 1, __ATOMIC_SEQ_CST);
189eda14cbcSMatt Macy zed_log_msg(LOG_INFO, "Invoking \"%s\" eid=%llu pid=%d",
190eda14cbcSMatt Macy prog, eid, pid);
191eda14cbcSMatt Macy }
192eda14cbcSMatt Macy
19316038816SMartin Matuska static void
_nop(int sig)19416038816SMartin Matuska _nop(int sig)
195e92ffd9bSMartin Matuska {
196e92ffd9bSMartin Matuska (void) sig;
197e92ffd9bSMartin Matuska }
19816038816SMartin Matuska
19916038816SMartin Matuska static void *
_reap_children(void * arg)20016038816SMartin Matuska _reap_children(void *arg)
20116038816SMartin Matuska {
202e92ffd9bSMartin Matuska (void) arg;
20316038816SMartin Matuska struct launched_process_node node, *pnode;
20416038816SMartin Matuska pid_t pid;
20516038816SMartin Matuska int status;
20616038816SMartin Matuska struct rusage usage;
20716038816SMartin Matuska struct sigaction sa = {};
20816038816SMartin Matuska
20916038816SMartin Matuska (void) sigfillset(&sa.sa_mask);
21016038816SMartin Matuska (void) sigdelset(&sa.sa_mask, SIGCHLD);
21116038816SMartin Matuska (void) pthread_sigmask(SIG_SETMASK, &sa.sa_mask, NULL);
21216038816SMartin Matuska
21316038816SMartin Matuska (void) sigemptyset(&sa.sa_mask);
21416038816SMartin Matuska sa.sa_handler = _nop;
21516038816SMartin Matuska sa.sa_flags = SA_NOCLDSTOP;
21616038816SMartin Matuska (void) sigaction(SIGCHLD, &sa, NULL);
21716038816SMartin Matuska
21816038816SMartin Matuska for (_reap_children_stop = B_FALSE; !_reap_children_stop; ) {
21916038816SMartin Matuska (void) pthread_mutex_lock(&_launched_processes_lock);
22016038816SMartin Matuska pid = wait4(0, &status, WNOHANG, &usage);
22116038816SMartin Matuska
22216038816SMartin Matuska if (pid == 0 || pid == (pid_t)-1) {
22316038816SMartin Matuska (void) pthread_mutex_unlock(&_launched_processes_lock);
22416038816SMartin Matuska if (pid == 0 || errno == ECHILD)
22516038816SMartin Matuska pause();
22616038816SMartin Matuska else if (errno != EINTR)
22716038816SMartin Matuska zed_log_msg(LOG_WARNING,
22816038816SMartin Matuska "Failed to wait for children: %s",
22916038816SMartin Matuska strerror(errno));
23016038816SMartin Matuska } else {
23116038816SMartin Matuska memset(&node, 0, sizeof (node));
23216038816SMartin Matuska node.pid = pid;
23316038816SMartin Matuska pnode = avl_find(&_launched_processes, &node, NULL);
23416038816SMartin Matuska if (pnode) {
23516038816SMartin Matuska memcpy(&node, pnode, sizeof (node));
23616038816SMartin Matuska
23716038816SMartin Matuska avl_remove(&_launched_processes, pnode);
23816038816SMartin Matuska free(pnode);
23916038816SMartin Matuska }
24016038816SMartin Matuska (void) pthread_mutex_unlock(&_launched_processes_lock);
24116038816SMartin Matuska __atomic_add_fetch(&_launched_processes_limit, 1,
24216038816SMartin Matuska __ATOMIC_SEQ_CST);
24316038816SMartin Matuska
24416038816SMartin Matuska usage.ru_utime.tv_sec += usage.ru_stime.tv_sec;
24516038816SMartin Matuska usage.ru_utime.tv_usec += usage.ru_stime.tv_usec;
24616038816SMartin Matuska usage.ru_utime.tv_sec +=
24716038816SMartin Matuska usage.ru_utime.tv_usec / (1000 * 1000);
24816038816SMartin Matuska usage.ru_utime.tv_usec %= 1000 * 1000;
24916038816SMartin Matuska
250eda14cbcSMatt Macy if (WIFEXITED(status)) {
251eda14cbcSMatt Macy zed_log_msg(LOG_INFO,
25216038816SMartin Matuska "Finished \"%s\" eid=%llu pid=%d "
25316038816SMartin Matuska "time=%llu.%06us exit=%d",
25416038816SMartin Matuska node.name, node.eid, pid,
25516038816SMartin Matuska (unsigned long long) usage.ru_utime.tv_sec,
25616038816SMartin Matuska (unsigned int) usage.ru_utime.tv_usec,
25716038816SMartin Matuska WEXITSTATUS(status));
258eda14cbcSMatt Macy } else if (WIFSIGNALED(status)) {
259eda14cbcSMatt Macy zed_log_msg(LOG_INFO,
26016038816SMartin Matuska "Finished \"%s\" eid=%llu pid=%d "
26116038816SMartin Matuska "time=%llu.%06us sig=%d/%s",
26216038816SMartin Matuska node.name, node.eid, pid,
26316038816SMartin Matuska (unsigned long long) usage.ru_utime.tv_sec,
26416038816SMartin Matuska (unsigned int) usage.ru_utime.tv_usec,
26516038816SMartin Matuska WTERMSIG(status),
266eda14cbcSMatt Macy strsignal(WTERMSIG(status)));
267eda14cbcSMatt Macy } else {
268eda14cbcSMatt Macy zed_log_msg(LOG_INFO,
26916038816SMartin Matuska "Finished \"%s\" eid=%llu pid=%d "
27016038816SMartin Matuska "time=%llu.%06us status=0x%X",
271c7046f76SMartin Matuska node.name, node.eid, pid,
27216038816SMartin Matuska (unsigned long long) usage.ru_utime.tv_sec,
27316038816SMartin Matuska (unsigned int) usage.ru_utime.tv_usec,
27416038816SMartin Matuska (unsigned int) status);
275eda14cbcSMatt Macy }
276eda14cbcSMatt Macy
27716038816SMartin Matuska free(node.name);
278eda14cbcSMatt Macy }
279eda14cbcSMatt Macy }
280eda14cbcSMatt Macy
28116038816SMartin Matuska return (NULL);
28216038816SMartin Matuska }
28316038816SMartin Matuska
28416038816SMartin Matuska void
zed_exec_fini(void)28516038816SMartin Matuska zed_exec_fini(void)
28616038816SMartin Matuska {
28716038816SMartin Matuska struct launched_process_node *node;
28816038816SMartin Matuska void *ck = NULL;
28916038816SMartin Matuska
29016038816SMartin Matuska if (_reap_children_tid == (pthread_t)-1)
29116038816SMartin Matuska return;
29216038816SMartin Matuska
29316038816SMartin Matuska _reap_children_stop = B_TRUE;
29416038816SMartin Matuska (void) pthread_kill(_reap_children_tid, SIGCHLD);
29516038816SMartin Matuska (void) pthread_join(_reap_children_tid, NULL);
29616038816SMartin Matuska
29716038816SMartin Matuska while ((node = avl_destroy_nodes(&_launched_processes, &ck)) != NULL) {
29816038816SMartin Matuska free(node->name);
29916038816SMartin Matuska free(node);
30016038816SMartin Matuska }
30116038816SMartin Matuska avl_destroy(&_launched_processes);
30216038816SMartin Matuska
30316038816SMartin Matuska (void) pthread_mutex_destroy(&_launched_processes_lock);
30416038816SMartin Matuska (void) pthread_mutex_init(&_launched_processes_lock, NULL);
30516038816SMartin Matuska
30616038816SMartin Matuska _reap_children_tid = (pthread_t)-1;
30716038816SMartin Matuska }
30816038816SMartin Matuska
309eda14cbcSMatt Macy /*
310eda14cbcSMatt Macy * Process the event [eid] by synchronously invoking all zedlets with a
311eda14cbcSMatt Macy * matching class prefix.
312eda14cbcSMatt Macy *
31316038816SMartin Matuska * Each executable in [zcp->zedlets] from the directory [zcp->zedlet_dir]
31416038816SMartin Matuska * is matched against the event's [class], [subclass], and the "all" class
31516038816SMartin Matuska * (which matches all events).
31616038816SMartin Matuska * Every zedlet with a matching class prefix is invoked.
317eda14cbcSMatt Macy * The NAME=VALUE strings in [envs] will be passed to the zedlet as
318eda14cbcSMatt Macy * environment variables.
319eda14cbcSMatt Macy *
32016038816SMartin Matuska * The file descriptor [zcp->zevent_fd] is the zevent_fd used to track the
321eda14cbcSMatt Macy * current cursor location within the zevent nvlist.
322eda14cbcSMatt Macy *
323eda14cbcSMatt Macy * Return 0 on success, -1 on error.
324eda14cbcSMatt Macy */
325eda14cbcSMatt Macy int
zed_exec_process(uint64_t eid,const char * class,const char * subclass,struct zed_conf * zcp,zed_strings_t * envs)326eda14cbcSMatt Macy zed_exec_process(uint64_t eid, const char *class, const char *subclass,
32716038816SMartin Matuska struct zed_conf *zcp, zed_strings_t *envs)
328eda14cbcSMatt Macy {
329eda14cbcSMatt Macy const char *class_strings[4];
330eda14cbcSMatt Macy const char *allclass = "all";
331eda14cbcSMatt Macy const char **csp;
332eda14cbcSMatt Macy const char *z;
333eda14cbcSMatt Macy char **e;
334eda14cbcSMatt Macy int n;
335eda14cbcSMatt Macy
33616038816SMartin Matuska if (!zcp->zedlet_dir || !zcp->zedlets || !envs || zcp->zevent_fd < 0)
337eda14cbcSMatt Macy return (-1);
338eda14cbcSMatt Macy
33916038816SMartin Matuska if (_reap_children_tid == (pthread_t)-1) {
34016038816SMartin Matuska _launched_processes_limit = zcp->max_jobs;
34116038816SMartin Matuska
34216038816SMartin Matuska if (pthread_create(&_reap_children_tid, NULL,
34316038816SMartin Matuska _reap_children, NULL) != 0)
34416038816SMartin Matuska return (-1);
34516038816SMartin Matuska pthread_setname_np(_reap_children_tid, "reap ZEDLETs");
34616038816SMartin Matuska
34716038816SMartin Matuska avl_create(&_launched_processes, _launched_process_node_compare,
34816038816SMartin Matuska sizeof (struct launched_process_node),
34916038816SMartin Matuska offsetof(struct launched_process_node, node));
35016038816SMartin Matuska }
35116038816SMartin Matuska
352eda14cbcSMatt Macy csp = class_strings;
353eda14cbcSMatt Macy
354eda14cbcSMatt Macy if (class)
355eda14cbcSMatt Macy *csp++ = class;
356eda14cbcSMatt Macy
357eda14cbcSMatt Macy if (subclass)
358eda14cbcSMatt Macy *csp++ = subclass;
359eda14cbcSMatt Macy
360eda14cbcSMatt Macy if (allclass)
361eda14cbcSMatt Macy *csp++ = allclass;
362eda14cbcSMatt Macy
363eda14cbcSMatt Macy *csp = NULL;
364eda14cbcSMatt Macy
365eda14cbcSMatt Macy e = _zed_exec_create_env(envs);
366eda14cbcSMatt Macy
36716038816SMartin Matuska for (z = zed_strings_first(zcp->zedlets); z;
36816038816SMartin Matuska z = zed_strings_next(zcp->zedlets)) {
369eda14cbcSMatt Macy for (csp = class_strings; *csp; csp++) {
370eda14cbcSMatt Macy n = strlen(*csp);
371eda14cbcSMatt Macy if ((strncmp(z, *csp, n) == 0) && !isalpha(z[n]))
37216038816SMartin Matuska _zed_exec_fork_child(eid, zcp->zedlet_dir,
37316038816SMartin Matuska z, e, zcp->zevent_fd, zcp->do_foreground);
374eda14cbcSMatt Macy }
375eda14cbcSMatt Macy }
376eda14cbcSMatt Macy free(e);
377eda14cbcSMatt Macy return (0);
378eda14cbcSMatt Macy }
379