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