2 * This file is part of the ZFS Event Daemon (ZED).
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.
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.
23 #include <sys/resource.h>
31 #include "zed_strings.h"
33 #define ZEVENT_FILENO 3
35 struct launched_process_node {
43 _launched_process_node_compare(const void *x1, const void *x2)
51 p1 = ((const struct launched_process_node *) x1)->pid;
52 p2 = ((const struct launched_process_node *) x2)->pid;
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;
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.
74 _zed_exec_create_env(zed_strings_t *zsp)
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;
90 buf = calloc(1, buflen);
95 p = buf + (num_ptrs * sizeof (char *));
97 for (q = zed_strings_first(zsp); q; q = zed_strings_next(zsp)) {
105 assert(buf + buflen == p);
106 return ((char **)buf);
110 * Fork a child process to handle event [eid]. The program [prog]
111 * in directory [dir] is executed with the environment [env].
113 * The file descriptor [zfd] is the zevent_fd used to track the
114 * current cursor location within the zevent nvlist.
117 _zed_exec_fork_child(uint64_t eid, const char *dir, const char *prog,
118 char *env[], int zfd, boolean_t in_foreground)
124 struct launched_process_node *node;
126 struct timespec launch_timeout =
127 { .tv_sec = 0, .tv_nsec = 200 * 1000 * 1000, };
130 assert(prog != NULL);
134 while (__atomic_load_n(&_launched_processes_limit,
135 __ATOMIC_SEQ_CST) <= 0)
136 (void) nanosleep(&launch_timeout, NULL);
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));
147 zed_log_msg(LOG_WARNING,
148 "Failed to fork \"%s\" for eid=%llu: %s",
149 prog, eid, strerror(errno));
151 } else if (pid == 0) {
152 (void) sigemptyset(&mask);
153 (void) sigprocmask(SIG_SETMASK, &mask, NULL);
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);
162 (void) dup2(zfd, ZEVENT_FILENO);
163 execle(path, prog, NULL, env);
169 __atomic_sub_fetch(&_launched_processes_limit, 1, __ATOMIC_SEQ_CST);
170 zed_log_msg(LOG_INFO, "Invoking \"%s\" eid=%llu pid=%d",
173 node = calloc(1, sizeof (*node));
177 node->name = strdup(prog);
179 (void) pthread_mutex_lock(&_launched_processes_lock);
180 avl_add(&_launched_processes, node);
181 (void) pthread_mutex_unlock(&_launched_processes_lock);
190 _reap_children(void *arg)
192 struct launched_process_node node, *pnode;
196 struct sigaction sa = {};
198 (void) sigfillset(&sa.sa_mask);
199 (void) sigdelset(&sa.sa_mask, SIGCHLD);
200 (void) pthread_sigmask(SIG_SETMASK, &sa.sa_mask, NULL);
202 (void) sigemptyset(&sa.sa_mask);
203 sa.sa_handler = _nop;
204 sa.sa_flags = SA_NOCLDSTOP;
205 (void) sigaction(SIGCHLD, &sa, NULL);
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);
211 if (pid == 0 || pid == (pid_t)-1) {
212 (void) pthread_mutex_unlock(&_launched_processes_lock);
213 if (pid == 0 || errno == ECHILD)
215 else if (errno != EINTR)
216 zed_log_msg(LOG_WARNING,
217 "Failed to wait for children: %s",
220 memset(&node, 0, sizeof (node));
222 pnode = avl_find(&_launched_processes, &node, NULL);
224 memcpy(&node, pnode, sizeof (node));
226 avl_remove(&_launched_processes, pnode);
229 (void) pthread_mutex_unlock(&_launched_processes_lock);
230 __atomic_add_fetch(&_launched_processes_limit, 1,
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;
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,
255 strsignal(WTERMSIG(status)));
257 zed_log_msg(LOG_INFO,
258 "Finished \"%s\" eid=%llu pid=%d "
259 "time=%llu.%06us status=0x%X",
261 (unsigned long long) usage.ru_utime.tv_sec,
262 (unsigned int) usage.ru_utime.tv_usec,
263 (unsigned int) status);
276 struct launched_process_node *node;
279 if (_reap_children_tid == (pthread_t)-1)
282 _reap_children_stop = B_TRUE;
283 (void) pthread_kill(_reap_children_tid, SIGCHLD);
284 (void) pthread_join(_reap_children_tid, NULL);
286 while ((node = avl_destroy_nodes(&_launched_processes, &ck)) != NULL) {
290 avl_destroy(&_launched_processes);
292 (void) pthread_mutex_destroy(&_launched_processes_lock);
293 (void) pthread_mutex_init(&_launched_processes_lock, NULL);
295 _reap_children_tid = (pthread_t)-1;
299 * Process the event [eid] by synchronously invoking all zedlets with a
300 * matching class prefix.
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.
309 * The file descriptor [zcp->zevent_fd] is the zevent_fd used to track the
310 * current cursor location within the zevent nvlist.
312 * Return 0 on success, -1 on error.
315 zed_exec_process(uint64_t eid, const char *class, const char *subclass,
316 struct zed_conf *zcp, zed_strings_t *envs)
318 const char *class_strings[4];
319 const char *allclass = "all";
325 if (!zcp->zedlet_dir || !zcp->zedlets || !envs || zcp->zevent_fd < 0)
328 if (_reap_children_tid == (pthread_t)-1) {
329 _launched_processes_limit = zcp->max_jobs;
331 if (pthread_create(&_reap_children_tid, NULL,
332 _reap_children, NULL) != 0)
334 pthread_setname_np(_reap_children_tid, "reap ZEDLETs");
336 avl_create(&_launched_processes, _launched_process_node_compare,
337 sizeof (struct launched_process_node),
338 offsetof(struct launched_process_node, node));
354 e = _zed_exec_create_env(envs);
356 for (z = zed_strings_first(zcp->zedlets); z;
357 z = zed_strings_next(zcp->zedlets)) {
358 for (csp = class_strings; *csp; 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);