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