xref: /freebsd/sys/contrib/openzfs/cmd/zed/zed_exec.c (revision 61145dc2b94f12f6a47344fb9aac702321880e43)
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 *
_reap_children(void * arg)200 _reap_children(void *arg)
201 {
202 	(void) arg;
203 	struct launched_process_node node, *pnode;
204 	pid_t pid;
205 	int status;
206 	struct rusage usage;
207 	struct sigaction sa = {};
208 
209 	(void) sigfillset(&sa.sa_mask);
210 	(void) sigdelset(&sa.sa_mask, SIGCHLD);
211 	(void) pthread_sigmask(SIG_SETMASK, &sa.sa_mask, NULL);
212 
213 	(void) sigemptyset(&sa.sa_mask);
214 	sa.sa_handler = _nop;
215 	sa.sa_flags = SA_NOCLDSTOP;
216 	(void) sigaction(SIGCHLD, &sa, NULL);
217 
218 	for (_reap_children_stop = B_FALSE; !_reap_children_stop; ) {
219 		(void) pthread_mutex_lock(&_launched_processes_lock);
220 		pid = wait4(0, &status, WNOHANG, &usage);
221 
222 		if (pid == 0 || pid == (pid_t)-1) {
223 			(void) pthread_mutex_unlock(&_launched_processes_lock);
224 			if (pid == 0 || errno == ECHILD)
225 				pause();
226 			else if (errno != EINTR)
227 				zed_log_msg(LOG_WARNING,
228 				    "Failed to wait for children: %s",
229 				    strerror(errno));
230 		} else {
231 			memset(&node, 0, sizeof (node));
232 			node.pid = pid;
233 			pnode = avl_find(&_launched_processes, &node, NULL);
234 			if (pnode) {
235 				memcpy(&node, pnode, sizeof (node));
236 
237 				avl_remove(&_launched_processes, pnode);
238 				free(pnode);
239 			}
240 			(void) pthread_mutex_unlock(&_launched_processes_lock);
241 			__atomic_add_fetch(&_launched_processes_limit, 1,
242 			    __ATOMIC_SEQ_CST);
243 
244 			usage.ru_utime.tv_sec += usage.ru_stime.tv_sec;
245 			usage.ru_utime.tv_usec += usage.ru_stime.tv_usec;
246 			usage.ru_utime.tv_sec +=
247 			    usage.ru_utime.tv_usec / (1000 * 1000);
248 			usage.ru_utime.tv_usec %= 1000 * 1000;
249 
250 			if (WIFEXITED(status)) {
251 				zed_log_msg(LOG_INFO,
252 				    "Finished \"%s\" eid=%llu pid=%d "
253 				    "time=%llu.%06us exit=%d",
254 				    node.name, node.eid, pid,
255 				    (unsigned long long) usage.ru_utime.tv_sec,
256 				    (unsigned int) usage.ru_utime.tv_usec,
257 				    WEXITSTATUS(status));
258 			} else if (WIFSIGNALED(status)) {
259 				zed_log_msg(LOG_INFO,
260 				    "Finished \"%s\" eid=%llu pid=%d "
261 				    "time=%llu.%06us sig=%d/%s",
262 				    node.name, node.eid, pid,
263 				    (unsigned long long) usage.ru_utime.tv_sec,
264 				    (unsigned int) usage.ru_utime.tv_usec,
265 				    WTERMSIG(status),
266 				    strsignal(WTERMSIG(status)));
267 			} else {
268 				zed_log_msg(LOG_INFO,
269 				    "Finished \"%s\" eid=%llu pid=%d "
270 				    "time=%llu.%06us status=0x%X",
271 				    node.name, node.eid, pid,
272 				    (unsigned long long) usage.ru_utime.tv_sec,
273 				    (unsigned int) usage.ru_utime.tv_usec,
274 				    (unsigned int) status);
275 			}
276 
277 			free(node.name);
278 		}
279 	}
280 
281 	return (NULL);
282 }
283 
284 void
zed_exec_fini(void)285 zed_exec_fini(void)
286 {
287 	struct launched_process_node *node;
288 	void *ck = NULL;
289 
290 	if (_reap_children_tid == (pthread_t)-1)
291 		return;
292 
293 	_reap_children_stop = B_TRUE;
294 	(void) pthread_kill(_reap_children_tid, SIGCHLD);
295 	(void) pthread_join(_reap_children_tid, NULL);
296 
297 	while ((node = avl_destroy_nodes(&_launched_processes, &ck)) != NULL) {
298 		free(node->name);
299 		free(node);
300 	}
301 	avl_destroy(&_launched_processes);
302 
303 	(void) pthread_mutex_destroy(&_launched_processes_lock);
304 	(void) pthread_mutex_init(&_launched_processes_lock, NULL);
305 
306 	_reap_children_tid = (pthread_t)-1;
307 }
308 
309 /*
310  * Process the event [eid] by synchronously invoking all zedlets with a
311  * matching class prefix.
312  *
313  * Each executable in [zcp->zedlets] from the directory [zcp->zedlet_dir]
314  * is matched against the event's [class], [subclass], and the "all" class
315  * (which matches all events).
316  * Every zedlet with a matching class prefix is invoked.
317  * The NAME=VALUE strings in [envs] will be passed to the zedlet as
318  * environment variables.
319  *
320  * The file descriptor [zcp->zevent_fd] is the zevent_fd used to track the
321  * current cursor location within the zevent nvlist.
322  *
323  * Return 0 on success, -1 on error.
324  */
325 int
zed_exec_process(uint64_t eid,const char * class,const char * subclass,struct zed_conf * zcp,zed_strings_t * envs)326 zed_exec_process(uint64_t eid, const char *class, const char *subclass,
327     struct zed_conf *zcp, zed_strings_t *envs)
328 {
329 	const char *class_strings[4];
330 	const char *allclass = "all";
331 	const char **csp;
332 	const char *z;
333 	char **e;
334 	int n;
335 
336 	if (!zcp->zedlet_dir || !zcp->zedlets || !envs || zcp->zevent_fd < 0)
337 		return (-1);
338 
339 	if (_reap_children_tid == (pthread_t)-1) {
340 		_launched_processes_limit = zcp->max_jobs;
341 
342 		if (pthread_create(&_reap_children_tid, NULL,
343 		    _reap_children, NULL) != 0)
344 			return (-1);
345 		pthread_setname_np(_reap_children_tid, "reap ZEDLETs");
346 
347 		avl_create(&_launched_processes, _launched_process_node_compare,
348 		    sizeof (struct launched_process_node),
349 		    offsetof(struct launched_process_node, node));
350 	}
351 
352 	csp = class_strings;
353 
354 	if (class)
355 		*csp++ = class;
356 
357 	if (subclass)
358 		*csp++ = subclass;
359 
360 	if (allclass)
361 		*csp++ = allclass;
362 
363 	*csp = NULL;
364 
365 	e = _zed_exec_create_env(envs);
366 
367 	for (z = zed_strings_first(zcp->zedlets); z;
368 	    z = zed_strings_next(zcp->zedlets)) {
369 		for (csp = class_strings; *csp; csp++) {
370 			n = strlen(*csp);
371 			if ((strncmp(z, *csp, n) == 0) && !isalpha(z[n]))
372 				_zed_exec_fork_child(eid, zcp->zedlet_dir,
373 				    z, e, zcp->zevent_fd, zcp->do_foreground);
374 		}
375 	}
376 	free(e);
377 	return (0);
378 }
379