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