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 if (node->name == NULL) { 179 perror("strdup"); 180 exit(EXIT_FAILURE); 181 } 182 183 avl_add(&_launched_processes, node); 184 } 185 (void) pthread_mutex_unlock(&_launched_processes_lock); 186 187 __atomic_sub_fetch(&_launched_processes_limit, 1, __ATOMIC_SEQ_CST); 188 zed_log_msg(LOG_INFO, "Invoking \"%s\" eid=%llu pid=%d", 189 prog, eid, pid); 190 } 191 192 static void 193 _nop(int sig) 194 { 195 (void) sig; 196 } 197 198 static void * 199 _reap_children(void *arg) 200 { 201 (void) arg; 202 struct launched_process_node node, *pnode; 203 pid_t pid; 204 int status; 205 struct rusage usage; 206 struct sigaction sa = {}; 207 208 (void) sigfillset(&sa.sa_mask); 209 (void) sigdelset(&sa.sa_mask, SIGCHLD); 210 (void) pthread_sigmask(SIG_SETMASK, &sa.sa_mask, NULL); 211 212 (void) sigemptyset(&sa.sa_mask); 213 sa.sa_handler = _nop; 214 sa.sa_flags = SA_NOCLDSTOP; 215 (void) sigaction(SIGCHLD, &sa, NULL); 216 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); 220 221 if (pid == 0 || pid == (pid_t)-1) { 222 (void) pthread_mutex_unlock(&_launched_processes_lock); 223 if (pid == 0 || errno == ECHILD) 224 pause(); 225 else if (errno != EINTR) 226 zed_log_msg(LOG_WARNING, 227 "Failed to wait for children: %s", 228 strerror(errno)); 229 } else { 230 memset(&node, 0, sizeof (node)); 231 node.pid = pid; 232 pnode = avl_find(&_launched_processes, &node, NULL); 233 if (pnode) { 234 memcpy(&node, pnode, sizeof (node)); 235 236 avl_remove(&_launched_processes, pnode); 237 free(pnode); 238 } 239 (void) pthread_mutex_unlock(&_launched_processes_lock); 240 __atomic_add_fetch(&_launched_processes_limit, 1, 241 __ATOMIC_SEQ_CST); 242 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; 248 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, 264 WTERMSIG(status), 265 strsignal(WTERMSIG(status))); 266 } else { 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); 274 } 275 276 free(node.name); 277 } 278 } 279 280 return (NULL); 281 } 282 283 void 284 zed_exec_fini(void) 285 { 286 struct launched_process_node *node; 287 void *ck = NULL; 288 289 if (_reap_children_tid == (pthread_t)-1) 290 return; 291 292 _reap_children_stop = B_TRUE; 293 (void) pthread_kill(_reap_children_tid, SIGCHLD); 294 (void) pthread_join(_reap_children_tid, NULL); 295 296 while ((node = avl_destroy_nodes(&_launched_processes, &ck)) != NULL) { 297 free(node->name); 298 free(node); 299 } 300 avl_destroy(&_launched_processes); 301 302 (void) pthread_mutex_destroy(&_launched_processes_lock); 303 (void) pthread_mutex_init(&_launched_processes_lock, NULL); 304 305 _reap_children_tid = (pthread_t)-1; 306 } 307 308 /* 309 * Process the event [eid] by synchronously invoking all zedlets with a 310 * matching class prefix. 311 * 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. 318 * 319 * The file descriptor [zcp->zevent_fd] is the zevent_fd used to track the 320 * current cursor location within the zevent nvlist. 321 * 322 * Return 0 on success, -1 on error. 323 */ 324 int 325 zed_exec_process(uint64_t eid, const char *class, const char *subclass, 326 struct zed_conf *zcp, zed_strings_t *envs) 327 { 328 const char *class_strings[4]; 329 const char *allclass = "all"; 330 const char **csp; 331 const char *z; 332 char **e; 333 int n; 334 335 if (!zcp->zedlet_dir || !zcp->zedlets || !envs || zcp->zevent_fd < 0) 336 return (-1); 337 338 if (_reap_children_tid == (pthread_t)-1) { 339 _launched_processes_limit = zcp->max_jobs; 340 341 if (pthread_create(&_reap_children_tid, NULL, 342 _reap_children, NULL) != 0) 343 return (-1); 344 pthread_setname_np(_reap_children_tid, "reap ZEDLETs"); 345 346 avl_create(&_launched_processes, _launched_process_node_compare, 347 sizeof (struct launched_process_node), 348 offsetof(struct launched_process_node, node)); 349 } 350 351 csp = class_strings; 352 353 if (class) 354 *csp++ = class; 355 356 if (subclass) 357 *csp++ = subclass; 358 359 if (allclass) 360 *csp++ = allclass; 361 362 *csp = NULL; 363 364 e = _zed_exec_create_env(envs); 365 366 for (z = zed_strings_first(zcp->zedlets); z; 367 z = zed_strings_next(zcp->zedlets)) { 368 for (csp = class_strings; *csp; csp++) { 369 n = strlen(*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); 373 } 374 } 375 free(e); 376 return (0); 377 } 378