The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/contrib/openzfs/cmd/zed/zed_exec.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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 }

Cache object: 027115b6d4a2d7ee9487b950e431b368


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.