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. 616038816SMartin 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> 2116038816SMartin Matuska #include <stddef.h> 2216038816SMartin Matuska #include <sys/avl.h> 2316038816SMartin 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> 2816038816SMartin Matuska #include <pthread.h> 29*53b70c86SMartin Matuska #include <signal.h> 30*53b70c86SMartin Matuska 31eda14cbcSMatt Macy #include "zed_exec.h" 32eda14cbcSMatt Macy #include "zed_log.h" 33eda14cbcSMatt Macy #include "zed_strings.h" 34eda14cbcSMatt Macy 35eda14cbcSMatt Macy #define ZEVENT_FILENO 3 36eda14cbcSMatt Macy 3716038816SMartin Matuska struct launched_process_node { 3816038816SMartin Matuska avl_node_t node; 3916038816SMartin Matuska pid_t pid; 4016038816SMartin Matuska uint64_t eid; 4116038816SMartin Matuska char *name; 4216038816SMartin Matuska }; 4316038816SMartin Matuska 4416038816SMartin Matuska static int 4516038816SMartin Matuska _launched_process_node_compare(const void *x1, const void *x2) 4616038816SMartin Matuska { 4716038816SMartin Matuska pid_t p1; 4816038816SMartin Matuska pid_t p2; 4916038816SMartin Matuska 5016038816SMartin Matuska assert(x1 != NULL); 5116038816SMartin Matuska assert(x2 != NULL); 5216038816SMartin Matuska 5316038816SMartin Matuska p1 = ((const struct launched_process_node *) x1)->pid; 5416038816SMartin Matuska p2 = ((const struct launched_process_node *) x2)->pid; 5516038816SMartin Matuska 5616038816SMartin Matuska if (p1 < p2) 5716038816SMartin Matuska return (-1); 5816038816SMartin Matuska else if (p1 == p2) 5916038816SMartin Matuska return (0); 6016038816SMartin Matuska else 6116038816SMartin Matuska return (1); 6216038816SMartin Matuska } 6316038816SMartin Matuska 6416038816SMartin Matuska static pthread_t _reap_children_tid = (pthread_t)-1; 6516038816SMartin Matuska static volatile boolean_t _reap_children_stop; 6616038816SMartin Matuska static avl_tree_t _launched_processes; 6716038816SMartin Matuska static pthread_mutex_t _launched_processes_lock = PTHREAD_MUTEX_INITIALIZER; 6816038816SMartin Matuska static int16_t _launched_processes_limit; 6916038816SMartin Matuska 70eda14cbcSMatt Macy /* 71eda14cbcSMatt Macy * Create an environment string array for passing to execve() using the 72eda14cbcSMatt Macy * NAME=VALUE strings in container [zsp]. 73eda14cbcSMatt Macy * Return a newly-allocated environment, or NULL on error. 74eda14cbcSMatt Macy */ 75eda14cbcSMatt Macy static char ** 76eda14cbcSMatt Macy _zed_exec_create_env(zed_strings_t *zsp) 77eda14cbcSMatt Macy { 78eda14cbcSMatt Macy int num_ptrs; 79eda14cbcSMatt Macy int buflen; 80eda14cbcSMatt Macy char *buf; 81eda14cbcSMatt Macy char **pp; 82eda14cbcSMatt Macy char *p; 83eda14cbcSMatt Macy const char *q; 84eda14cbcSMatt Macy int i; 85eda14cbcSMatt Macy int len; 86eda14cbcSMatt Macy 87eda14cbcSMatt Macy num_ptrs = zed_strings_count(zsp) + 1; 88eda14cbcSMatt Macy buflen = num_ptrs * sizeof (char *); 89eda14cbcSMatt Macy for (q = zed_strings_first(zsp); q; q = zed_strings_next(zsp)) 90eda14cbcSMatt Macy buflen += strlen(q) + 1; 91eda14cbcSMatt Macy 92eda14cbcSMatt Macy buf = calloc(1, buflen); 93eda14cbcSMatt Macy if (!buf) 94eda14cbcSMatt Macy return (NULL); 95eda14cbcSMatt Macy 96eda14cbcSMatt Macy pp = (char **)buf; 97eda14cbcSMatt Macy p = buf + (num_ptrs * sizeof (char *)); 98eda14cbcSMatt Macy i = 0; 99eda14cbcSMatt Macy for (q = zed_strings_first(zsp); q; q = zed_strings_next(zsp)) { 100eda14cbcSMatt Macy pp[i] = p; 101eda14cbcSMatt Macy len = strlen(q) + 1; 102eda14cbcSMatt Macy memcpy(p, q, len); 103eda14cbcSMatt Macy p += len; 104eda14cbcSMatt Macy i++; 105eda14cbcSMatt Macy } 106eda14cbcSMatt Macy pp[i] = NULL; 107eda14cbcSMatt Macy assert(buf + buflen == p); 108eda14cbcSMatt Macy return ((char **)buf); 109eda14cbcSMatt Macy } 110eda14cbcSMatt Macy 111eda14cbcSMatt Macy /* 112eda14cbcSMatt Macy * Fork a child process to handle event [eid]. The program [prog] 113eda14cbcSMatt Macy * in directory [dir] is executed with the environment [env]. 114eda14cbcSMatt Macy * 115eda14cbcSMatt Macy * The file descriptor [zfd] is the zevent_fd used to track the 116eda14cbcSMatt Macy * current cursor location within the zevent nvlist. 117eda14cbcSMatt Macy */ 118eda14cbcSMatt Macy static void 119eda14cbcSMatt Macy _zed_exec_fork_child(uint64_t eid, const char *dir, const char *prog, 12016038816SMartin Matuska char *env[], int zfd, boolean_t in_foreground) 121eda14cbcSMatt Macy { 122eda14cbcSMatt Macy char path[PATH_MAX]; 123eda14cbcSMatt Macy int n; 124eda14cbcSMatt Macy pid_t pid; 125eda14cbcSMatt Macy int fd; 12616038816SMartin Matuska struct launched_process_node *node; 12716038816SMartin Matuska sigset_t mask; 12816038816SMartin Matuska struct timespec launch_timeout = 12916038816SMartin Matuska { .tv_sec = 0, .tv_nsec = 200 * 1000 * 1000, }; 130eda14cbcSMatt Macy 131eda14cbcSMatt Macy assert(dir != NULL); 132eda14cbcSMatt Macy assert(prog != NULL); 133eda14cbcSMatt Macy assert(env != NULL); 134eda14cbcSMatt Macy assert(zfd >= 0); 135eda14cbcSMatt Macy 13616038816SMartin Matuska while (__atomic_load_n(&_launched_processes_limit, 13716038816SMartin Matuska __ATOMIC_SEQ_CST) <= 0) 13816038816SMartin Matuska (void) nanosleep(&launch_timeout, NULL); 13916038816SMartin Matuska 140eda14cbcSMatt Macy n = snprintf(path, sizeof (path), "%s/%s", dir, prog); 141eda14cbcSMatt Macy if ((n < 0) || (n >= sizeof (path))) { 142eda14cbcSMatt Macy zed_log_msg(LOG_WARNING, 143eda14cbcSMatt Macy "Failed to fork \"%s\" for eid=%llu: %s", 144eda14cbcSMatt Macy prog, eid, strerror(ENAMETOOLONG)); 145eda14cbcSMatt Macy return; 146eda14cbcSMatt Macy } 14716038816SMartin Matuska (void) pthread_mutex_lock(&_launched_processes_lock); 148eda14cbcSMatt Macy pid = fork(); 149eda14cbcSMatt Macy if (pid < 0) { 15016038816SMartin Matuska (void) pthread_mutex_unlock(&_launched_processes_lock); 151eda14cbcSMatt Macy zed_log_msg(LOG_WARNING, 152eda14cbcSMatt Macy "Failed to fork \"%s\" for eid=%llu: %s", 153eda14cbcSMatt Macy prog, eid, strerror(errno)); 154eda14cbcSMatt Macy return; 155eda14cbcSMatt Macy } else if (pid == 0) { 15616038816SMartin Matuska (void) sigemptyset(&mask); 15716038816SMartin Matuska (void) sigprocmask(SIG_SETMASK, &mask, NULL); 15816038816SMartin Matuska 159eda14cbcSMatt Macy (void) umask(022); 16016038816SMartin Matuska if (in_foreground && /* we're already devnulled if daemonised */ 16116038816SMartin Matuska (fd = open("/dev/null", O_RDWR | O_CLOEXEC)) != -1) { 162eda14cbcSMatt Macy (void) dup2(fd, STDIN_FILENO); 163eda14cbcSMatt Macy (void) dup2(fd, STDOUT_FILENO); 164eda14cbcSMatt Macy (void) dup2(fd, STDERR_FILENO); 165eda14cbcSMatt Macy } 166eda14cbcSMatt Macy (void) dup2(zfd, ZEVENT_FILENO); 167eda14cbcSMatt Macy execle(path, prog, NULL, env); 168eda14cbcSMatt Macy _exit(127); 169eda14cbcSMatt Macy } 170eda14cbcSMatt Macy 171eda14cbcSMatt Macy /* parent process */ 172eda14cbcSMatt Macy 17316038816SMartin Matuska node = calloc(1, sizeof (*node)); 17416038816SMartin Matuska if (node) { 17516038816SMartin Matuska node->pid = pid; 17616038816SMartin Matuska node->eid = eid; 17716038816SMartin Matuska node->name = strdup(prog); 17816038816SMartin Matuska 17916038816SMartin Matuska avl_add(&_launched_processes, node); 18016038816SMartin Matuska } 18116038816SMartin Matuska (void) pthread_mutex_unlock(&_launched_processes_lock); 18216038816SMartin Matuska 18316038816SMartin Matuska __atomic_sub_fetch(&_launched_processes_limit, 1, __ATOMIC_SEQ_CST); 184eda14cbcSMatt Macy zed_log_msg(LOG_INFO, "Invoking \"%s\" eid=%llu pid=%d", 185eda14cbcSMatt Macy prog, eid, pid); 186eda14cbcSMatt Macy } 187eda14cbcSMatt Macy 18816038816SMartin Matuska static void 18916038816SMartin Matuska _nop(int sig) 19016038816SMartin Matuska {} 19116038816SMartin Matuska 19216038816SMartin Matuska static void * 19316038816SMartin Matuska _reap_children(void *arg) 19416038816SMartin Matuska { 19516038816SMartin Matuska struct launched_process_node node, *pnode; 19616038816SMartin Matuska pid_t pid; 19716038816SMartin Matuska int status; 19816038816SMartin Matuska struct rusage usage; 19916038816SMartin Matuska struct sigaction sa = {}; 20016038816SMartin Matuska 20116038816SMartin Matuska (void) sigfillset(&sa.sa_mask); 20216038816SMartin Matuska (void) sigdelset(&sa.sa_mask, SIGCHLD); 20316038816SMartin Matuska (void) pthread_sigmask(SIG_SETMASK, &sa.sa_mask, NULL); 20416038816SMartin Matuska 20516038816SMartin Matuska (void) sigemptyset(&sa.sa_mask); 20616038816SMartin Matuska sa.sa_handler = _nop; 20716038816SMartin Matuska sa.sa_flags = SA_NOCLDSTOP; 20816038816SMartin Matuska (void) sigaction(SIGCHLD, &sa, NULL); 20916038816SMartin Matuska 21016038816SMartin Matuska for (_reap_children_stop = B_FALSE; !_reap_children_stop; ) { 21116038816SMartin Matuska (void) pthread_mutex_lock(&_launched_processes_lock); 21216038816SMartin Matuska pid = wait4(0, &status, WNOHANG, &usage); 21316038816SMartin Matuska 21416038816SMartin Matuska if (pid == 0 || pid == (pid_t)-1) { 21516038816SMartin Matuska (void) pthread_mutex_unlock(&_launched_processes_lock); 21616038816SMartin Matuska if (pid == 0 || errno == ECHILD) 21716038816SMartin Matuska pause(); 21816038816SMartin Matuska else if (errno != EINTR) 21916038816SMartin Matuska zed_log_msg(LOG_WARNING, 22016038816SMartin Matuska "Failed to wait for children: %s", 22116038816SMartin Matuska strerror(errno)); 22216038816SMartin Matuska } else { 22316038816SMartin Matuska memset(&node, 0, sizeof (node)); 22416038816SMartin Matuska node.pid = pid; 22516038816SMartin Matuska pnode = avl_find(&_launched_processes, &node, NULL); 22616038816SMartin Matuska if (pnode) { 22716038816SMartin Matuska memcpy(&node, pnode, sizeof (node)); 22816038816SMartin Matuska 22916038816SMartin Matuska avl_remove(&_launched_processes, pnode); 23016038816SMartin Matuska free(pnode); 23116038816SMartin Matuska } 23216038816SMartin Matuska (void) pthread_mutex_unlock(&_launched_processes_lock); 23316038816SMartin Matuska __atomic_add_fetch(&_launched_processes_limit, 1, 23416038816SMartin Matuska __ATOMIC_SEQ_CST); 23516038816SMartin Matuska 23616038816SMartin Matuska usage.ru_utime.tv_sec += usage.ru_stime.tv_sec; 23716038816SMartin Matuska usage.ru_utime.tv_usec += usage.ru_stime.tv_usec; 23816038816SMartin Matuska usage.ru_utime.tv_sec += 23916038816SMartin Matuska usage.ru_utime.tv_usec / (1000 * 1000); 24016038816SMartin Matuska usage.ru_utime.tv_usec %= 1000 * 1000; 24116038816SMartin Matuska 242eda14cbcSMatt Macy if (WIFEXITED(status)) { 243eda14cbcSMatt Macy zed_log_msg(LOG_INFO, 24416038816SMartin Matuska "Finished \"%s\" eid=%llu pid=%d " 24516038816SMartin Matuska "time=%llu.%06us exit=%d", 24616038816SMartin Matuska node.name, node.eid, pid, 24716038816SMartin Matuska (unsigned long long) usage.ru_utime.tv_sec, 24816038816SMartin Matuska (unsigned int) usage.ru_utime.tv_usec, 24916038816SMartin Matuska WEXITSTATUS(status)); 250eda14cbcSMatt Macy } else if (WIFSIGNALED(status)) { 251eda14cbcSMatt Macy zed_log_msg(LOG_INFO, 25216038816SMartin Matuska "Finished \"%s\" eid=%llu pid=%d " 25316038816SMartin Matuska "time=%llu.%06us sig=%d/%s", 25416038816SMartin Matuska node.name, node.eid, pid, 25516038816SMartin Matuska (unsigned long long) usage.ru_utime.tv_sec, 25616038816SMartin Matuska (unsigned int) usage.ru_utime.tv_usec, 25716038816SMartin Matuska WTERMSIG(status), 258eda14cbcSMatt Macy strsignal(WTERMSIG(status))); 259eda14cbcSMatt Macy } else { 260eda14cbcSMatt Macy zed_log_msg(LOG_INFO, 26116038816SMartin Matuska "Finished \"%s\" eid=%llu pid=%d " 26216038816SMartin Matuska "time=%llu.%06us status=0x%X", 26316038816SMartin Matuska node.name, node.eid, 26416038816SMartin Matuska (unsigned long long) usage.ru_utime.tv_sec, 26516038816SMartin Matuska (unsigned int) usage.ru_utime.tv_usec, 26616038816SMartin Matuska (unsigned int) status); 267eda14cbcSMatt Macy } 268eda14cbcSMatt Macy 26916038816SMartin Matuska free(node.name); 270eda14cbcSMatt Macy } 271eda14cbcSMatt Macy } 272eda14cbcSMatt Macy 27316038816SMartin Matuska return (NULL); 27416038816SMartin Matuska } 27516038816SMartin Matuska 27616038816SMartin Matuska void 27716038816SMartin Matuska zed_exec_fini(void) 27816038816SMartin Matuska { 27916038816SMartin Matuska struct launched_process_node *node; 28016038816SMartin Matuska void *ck = NULL; 28116038816SMartin Matuska 28216038816SMartin Matuska if (_reap_children_tid == (pthread_t)-1) 28316038816SMartin Matuska return; 28416038816SMartin Matuska 28516038816SMartin Matuska _reap_children_stop = B_TRUE; 28616038816SMartin Matuska (void) pthread_kill(_reap_children_tid, SIGCHLD); 28716038816SMartin Matuska (void) pthread_join(_reap_children_tid, NULL); 28816038816SMartin Matuska 28916038816SMartin Matuska while ((node = avl_destroy_nodes(&_launched_processes, &ck)) != NULL) { 29016038816SMartin Matuska free(node->name); 29116038816SMartin Matuska free(node); 29216038816SMartin Matuska } 29316038816SMartin Matuska avl_destroy(&_launched_processes); 29416038816SMartin Matuska 29516038816SMartin Matuska (void) pthread_mutex_destroy(&_launched_processes_lock); 29616038816SMartin Matuska (void) pthread_mutex_init(&_launched_processes_lock, NULL); 29716038816SMartin Matuska 29816038816SMartin Matuska _reap_children_tid = (pthread_t)-1; 29916038816SMartin Matuska } 30016038816SMartin Matuska 301eda14cbcSMatt Macy /* 302eda14cbcSMatt Macy * Process the event [eid] by synchronously invoking all zedlets with a 303eda14cbcSMatt Macy * matching class prefix. 304eda14cbcSMatt Macy * 30516038816SMartin Matuska * Each executable in [zcp->zedlets] from the directory [zcp->zedlet_dir] 30616038816SMartin Matuska * is matched against the event's [class], [subclass], and the "all" class 30716038816SMartin Matuska * (which matches all events). 30816038816SMartin Matuska * Every zedlet with a matching class prefix is invoked. 309eda14cbcSMatt Macy * The NAME=VALUE strings in [envs] will be passed to the zedlet as 310eda14cbcSMatt Macy * environment variables. 311eda14cbcSMatt Macy * 31216038816SMartin Matuska * The file descriptor [zcp->zevent_fd] is the zevent_fd used to track the 313eda14cbcSMatt Macy * current cursor location within the zevent nvlist. 314eda14cbcSMatt Macy * 315eda14cbcSMatt Macy * Return 0 on success, -1 on error. 316eda14cbcSMatt Macy */ 317eda14cbcSMatt Macy int 318eda14cbcSMatt Macy zed_exec_process(uint64_t eid, const char *class, const char *subclass, 31916038816SMartin Matuska struct zed_conf *zcp, zed_strings_t *envs) 320eda14cbcSMatt Macy { 321eda14cbcSMatt Macy const char *class_strings[4]; 322eda14cbcSMatt Macy const char *allclass = "all"; 323eda14cbcSMatt Macy const char **csp; 324eda14cbcSMatt Macy const char *z; 325eda14cbcSMatt Macy char **e; 326eda14cbcSMatt Macy int n; 327eda14cbcSMatt Macy 32816038816SMartin Matuska if (!zcp->zedlet_dir || !zcp->zedlets || !envs || zcp->zevent_fd < 0) 329eda14cbcSMatt Macy return (-1); 330eda14cbcSMatt Macy 33116038816SMartin Matuska if (_reap_children_tid == (pthread_t)-1) { 33216038816SMartin Matuska _launched_processes_limit = zcp->max_jobs; 33316038816SMartin Matuska 33416038816SMartin Matuska if (pthread_create(&_reap_children_tid, NULL, 33516038816SMartin Matuska _reap_children, NULL) != 0) 33616038816SMartin Matuska return (-1); 33716038816SMartin Matuska pthread_setname_np(_reap_children_tid, "reap ZEDLETs"); 33816038816SMartin Matuska 33916038816SMartin Matuska avl_create(&_launched_processes, _launched_process_node_compare, 34016038816SMartin Matuska sizeof (struct launched_process_node), 34116038816SMartin Matuska offsetof(struct launched_process_node, node)); 34216038816SMartin Matuska } 34316038816SMartin Matuska 344eda14cbcSMatt Macy csp = class_strings; 345eda14cbcSMatt Macy 346eda14cbcSMatt Macy if (class) 347eda14cbcSMatt Macy *csp++ = class; 348eda14cbcSMatt Macy 349eda14cbcSMatt Macy if (subclass) 350eda14cbcSMatt Macy *csp++ = subclass; 351eda14cbcSMatt Macy 352eda14cbcSMatt Macy if (allclass) 353eda14cbcSMatt Macy *csp++ = allclass; 354eda14cbcSMatt Macy 355eda14cbcSMatt Macy *csp = NULL; 356eda14cbcSMatt Macy 357eda14cbcSMatt Macy e = _zed_exec_create_env(envs); 358eda14cbcSMatt Macy 35916038816SMartin Matuska for (z = zed_strings_first(zcp->zedlets); z; 36016038816SMartin Matuska z = zed_strings_next(zcp->zedlets)) { 361eda14cbcSMatt Macy for (csp = class_strings; *csp; csp++) { 362eda14cbcSMatt Macy n = strlen(*csp); 363eda14cbcSMatt Macy if ((strncmp(z, *csp, n) == 0) && !isalpha(z[n])) 36416038816SMartin Matuska _zed_exec_fork_child(eid, zcp->zedlet_dir, 36516038816SMartin Matuska z, e, zcp->zevent_fd, zcp->do_foreground); 366eda14cbcSMatt Macy } 367eda14cbcSMatt Macy } 368eda14cbcSMatt Macy free(e); 369eda14cbcSMatt Macy return (0); 370eda14cbcSMatt Macy } 371