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