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