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 OpenZFS 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>
33 #include "zed_strings.h"
35 #define ZEVENT_FILENO 3
37 struct launched_process_node
{
45 _launched_process_node_compare(const void *x1
, const void *x2
)
53 p1
= ((const struct launched_process_node
*) x1
)->pid
;
54 p2
= ((const struct launched_process_node
*) x2
)->pid
;
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
;
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.
76 _zed_exec_create_env(zed_strings_t
*zsp
)
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;
92 buf
= calloc(1, buflen
);
97 p
= buf
+ (num_ptrs
* sizeof (char *));
99 for (q
= zed_strings_first(zsp
); q
; q
= zed_strings_next(zsp
)) {
107 assert(buf
+ buflen
== p
);
108 return ((char **)buf
);
112 * Fork a child process to handle event [eid]. The program [prog]
113 * in directory [dir] is executed with the environment [env].
115 * The file descriptor [zfd] is the zevent_fd used to track the
116 * current cursor location within the zevent nvlist.
119 _zed_exec_fork_child(uint64_t eid
, const char *dir
, const char *prog
,
120 char *env
[], int zfd
, boolean_t in_foreground
)
126 struct launched_process_node
*node
;
128 struct timespec launch_timeout
=
129 { .tv_sec
= 0, .tv_nsec
= 200 * 1000 * 1000, };
132 assert(prog
!= NULL
);
136 while (__atomic_load_n(&_launched_processes_limit
,
137 __ATOMIC_SEQ_CST
) <= 0)
138 (void) nanosleep(&launch_timeout
, NULL
);
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
));
147 (void) pthread_mutex_lock(&_launched_processes_lock
);
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
));
155 } else if (pid
== 0) {
156 (void) sigemptyset(&mask
);
157 (void) sigprocmask(SIG_SETMASK
, &mask
, NULL
);
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
);
166 (void) dup2(zfd
, ZEVENT_FILENO
);
167 execle(path
, prog
, NULL
, env
);
173 node
= calloc(1, sizeof (*node
));
177 node
->name
= strdup(prog
);
178 if (node
->name
== NULL
) {
183 avl_add(&_launched_processes
, node
);
185 (void) pthread_mutex_unlock(&_launched_processes_lock
);
187 __atomic_sub_fetch(&_launched_processes_limit
, 1, __ATOMIC_SEQ_CST
);
188 zed_log_msg(LOG_INFO
, "Invoking \"%s\" eid=%llu pid=%d",
199 _reap_children(void *arg
)
202 struct launched_process_node node
, *pnode
;
206 struct sigaction sa
= {};
208 (void) sigfillset(&sa
.sa_mask
);
209 (void) sigdelset(&sa
.sa_mask
, SIGCHLD
);
210 (void) pthread_sigmask(SIG_SETMASK
, &sa
.sa_mask
, NULL
);
212 (void) sigemptyset(&sa
.sa_mask
);
213 sa
.sa_handler
= _nop
;
214 sa
.sa_flags
= SA_NOCLDSTOP
;
215 (void) sigaction(SIGCHLD
, &sa
, NULL
);
217 for (_reap_children_stop
= B_FALSE
; !_reap_children_stop
; ) {
218 (void) pthread_mutex_lock(&_launched_processes_lock
);
219 pid
= wait4(0, &status
, WNOHANG
, &usage
);
221 if (pid
== 0 || pid
== (pid_t
)-1) {
222 (void) pthread_mutex_unlock(&_launched_processes_lock
);
223 if (pid
== 0 || errno
== ECHILD
)
225 else if (errno
!= EINTR
)
226 zed_log_msg(LOG_WARNING
,
227 "Failed to wait for children: %s",
230 memset(&node
, 0, sizeof (node
));
232 pnode
= avl_find(&_launched_processes
, &node
, NULL
);
234 memcpy(&node
, pnode
, sizeof (node
));
236 avl_remove(&_launched_processes
, pnode
);
239 (void) pthread_mutex_unlock(&_launched_processes_lock
);
240 __atomic_add_fetch(&_launched_processes_limit
, 1,
243 usage
.ru_utime
.tv_sec
+= usage
.ru_stime
.tv_sec
;
244 usage
.ru_utime
.tv_usec
+= usage
.ru_stime
.tv_usec
;
245 usage
.ru_utime
.tv_sec
+=
246 usage
.ru_utime
.tv_usec
/ (1000 * 1000);
247 usage
.ru_utime
.tv_usec
%= 1000 * 1000;
249 if (WIFEXITED(status
)) {
250 zed_log_msg(LOG_INFO
,
251 "Finished \"%s\" eid=%llu pid=%d "
252 "time=%llu.%06us exit=%d",
253 node
.name
, node
.eid
, pid
,
254 (unsigned long long) usage
.ru_utime
.tv_sec
,
255 (unsigned int) usage
.ru_utime
.tv_usec
,
256 WEXITSTATUS(status
));
257 } else if (WIFSIGNALED(status
)) {
258 zed_log_msg(LOG_INFO
,
259 "Finished \"%s\" eid=%llu pid=%d "
260 "time=%llu.%06us sig=%d/%s",
261 node
.name
, node
.eid
, pid
,
262 (unsigned long long) usage
.ru_utime
.tv_sec
,
263 (unsigned int) usage
.ru_utime
.tv_usec
,
265 strsignal(WTERMSIG(status
)));
267 zed_log_msg(LOG_INFO
,
268 "Finished \"%s\" eid=%llu pid=%d "
269 "time=%llu.%06us status=0x%X",
270 node
.name
, node
.eid
, pid
,
271 (unsigned long long) usage
.ru_utime
.tv_sec
,
272 (unsigned int) usage
.ru_utime
.tv_usec
,
273 (unsigned int) status
);
286 struct launched_process_node
*node
;
289 if (_reap_children_tid
== (pthread_t
)-1)
292 _reap_children_stop
= B_TRUE
;
293 (void) pthread_kill(_reap_children_tid
, SIGCHLD
);
294 (void) pthread_join(_reap_children_tid
, NULL
);
296 while ((node
= avl_destroy_nodes(&_launched_processes
, &ck
)) != NULL
) {
300 avl_destroy(&_launched_processes
);
302 (void) pthread_mutex_destroy(&_launched_processes_lock
);
303 (void) pthread_mutex_init(&_launched_processes_lock
, NULL
);
305 _reap_children_tid
= (pthread_t
)-1;
309 * Process the event [eid] by synchronously invoking all zedlets with a
310 * matching class prefix.
312 * Each executable in [zcp->zedlets] from the directory [zcp->zedlet_dir]
313 * is matched against the event's [class], [subclass], and the "all" class
314 * (which matches all events).
315 * Every zedlet with a matching class prefix is invoked.
316 * The NAME=VALUE strings in [envs] will be passed to the zedlet as
317 * environment variables.
319 * The file descriptor [zcp->zevent_fd] is the zevent_fd used to track the
320 * current cursor location within the zevent nvlist.
322 * Return 0 on success, -1 on error.
325 zed_exec_process(uint64_t eid
, const char *class, const char *subclass
,
326 struct zed_conf
*zcp
, zed_strings_t
*envs
)
328 const char *class_strings
[4];
329 const char *allclass
= "all";
335 if (!zcp
->zedlet_dir
|| !zcp
->zedlets
|| !envs
|| zcp
->zevent_fd
< 0)
338 if (_reap_children_tid
== (pthread_t
)-1) {
339 _launched_processes_limit
= zcp
->max_jobs
;
341 if (pthread_create(&_reap_children_tid
, NULL
,
342 _reap_children
, NULL
) != 0)
344 pthread_setname_np(_reap_children_tid
, "reap ZEDLETs");
346 avl_create(&_launched_processes
, _launched_process_node_compare
,
347 sizeof (struct launched_process_node
),
348 offsetof(struct launched_process_node
, node
));
364 e
= _zed_exec_create_env(envs
);
366 for (z
= zed_strings_first(zcp
->zedlets
); z
;
367 z
= zed_strings_next(zcp
->zedlets
)) {
368 for (csp
= class_strings
; *csp
; csp
++) {
370 if ((strncmp(z
, *csp
, n
) == 0) && !isalpha(z
[n
]))
371 _zed_exec_fork_child(eid
, zcp
->zedlet_dir
,
372 z
, e
, zcp
->zevent_fd
, zcp
->do_foreground
);