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 ZoL 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 <sys/stat.h> 22 #include <sys/wait.h> 23 #include <time.h> 24 #include <unistd.h> 25 #include "zed_exec.h" 26 #include "zed_file.h" 27 #include "zed_log.h" 28 #include "zed_strings.h" 29 30 #define ZEVENT_FILENO 3 31 32 /* 33 * Create an environment string array for passing to execve() using the 34 * NAME=VALUE strings in container [zsp]. 35 * Return a newly-allocated environment, or NULL on error. 36 */ 37 static char ** 38 _zed_exec_create_env(zed_strings_t *zsp) 39 { 40 int num_ptrs; 41 int buflen; 42 char *buf; 43 char **pp; 44 char *p; 45 const char *q; 46 int i; 47 int len; 48 49 num_ptrs = zed_strings_count(zsp) + 1; 50 buflen = num_ptrs * sizeof (char *); 51 for (q = zed_strings_first(zsp); q; q = zed_strings_next(zsp)) 52 buflen += strlen(q) + 1; 53 54 buf = calloc(1, buflen); 55 if (!buf) 56 return (NULL); 57 58 pp = (char **)buf; 59 p = buf + (num_ptrs * sizeof (char *)); 60 i = 0; 61 for (q = zed_strings_first(zsp); q; q = zed_strings_next(zsp)) { 62 pp[i] = p; 63 len = strlen(q) + 1; 64 memcpy(p, q, len); 65 p += len; 66 i++; 67 } 68 pp[i] = NULL; 69 assert(buf + buflen == p); 70 return ((char **)buf); 71 } 72 73 /* 74 * Fork a child process to handle event [eid]. The program [prog] 75 * in directory [dir] is executed with the environment [env]. 76 * 77 * The file descriptor [zfd] is the zevent_fd used to track the 78 * current cursor location within the zevent nvlist. 79 */ 80 static void 81 _zed_exec_fork_child(uint64_t eid, const char *dir, const char *prog, 82 char *env[], int zfd) 83 { 84 char path[PATH_MAX]; 85 int n; 86 pid_t pid; 87 int fd; 88 pid_t wpid; 89 int status; 90 91 assert(dir != NULL); 92 assert(prog != NULL); 93 assert(env != NULL); 94 assert(zfd >= 0); 95 96 n = snprintf(path, sizeof (path), "%s/%s", dir, prog); 97 if ((n < 0) || (n >= sizeof (path))) { 98 zed_log_msg(LOG_WARNING, 99 "Failed to fork \"%s\" for eid=%llu: %s", 100 prog, eid, strerror(ENAMETOOLONG)); 101 return; 102 } 103 pid = fork(); 104 if (pid < 0) { 105 zed_log_msg(LOG_WARNING, 106 "Failed to fork \"%s\" for eid=%llu: %s", 107 prog, eid, strerror(errno)); 108 return; 109 } else if (pid == 0) { 110 (void) umask(022); 111 if ((fd = open("/dev/null", O_RDWR)) != -1) { 112 (void) dup2(fd, STDIN_FILENO); 113 (void) dup2(fd, STDOUT_FILENO); 114 (void) dup2(fd, STDERR_FILENO); 115 } 116 (void) dup2(zfd, ZEVENT_FILENO); 117 zed_file_close_from(ZEVENT_FILENO + 1); 118 execle(path, prog, NULL, env); 119 _exit(127); 120 } 121 122 /* parent process */ 123 124 zed_log_msg(LOG_INFO, "Invoking \"%s\" eid=%llu pid=%d", 125 prog, eid, pid); 126 127 /* FIXME: Timeout rogue child processes with sigalarm? */ 128 129 /* 130 * Wait for child process using WNOHANG to limit 131 * the time spent waiting to 10 seconds (10,000ms). 132 */ 133 for (n = 0; n < 1000; n++) { 134 wpid = waitpid(pid, &status, WNOHANG); 135 if (wpid == (pid_t)-1) { 136 if (errno == EINTR) 137 continue; 138 zed_log_msg(LOG_WARNING, 139 "Failed to wait for \"%s\" eid=%llu pid=%d", 140 prog, eid, pid); 141 break; 142 } else if (wpid == 0) { 143 struct timespec t; 144 145 /* child still running */ 146 t.tv_sec = 0; 147 t.tv_nsec = 10000000; /* 10ms */ 148 (void) nanosleep(&t, NULL); 149 continue; 150 } 151 152 if (WIFEXITED(status)) { 153 zed_log_msg(LOG_INFO, 154 "Finished \"%s\" eid=%llu pid=%d exit=%d", 155 prog, eid, pid, WEXITSTATUS(status)); 156 } else if (WIFSIGNALED(status)) { 157 zed_log_msg(LOG_INFO, 158 "Finished \"%s\" eid=%llu pid=%d sig=%d/%s", 159 prog, eid, pid, WTERMSIG(status), 160 strsignal(WTERMSIG(status))); 161 } else { 162 zed_log_msg(LOG_INFO, 163 "Finished \"%s\" eid=%llu pid=%d status=0x%X", 164 prog, eid, (unsigned int) status); 165 } 166 break; 167 } 168 169 /* 170 * kill child process after 10 seconds 171 */ 172 if (wpid == 0) { 173 zed_log_msg(LOG_WARNING, "Killing hung \"%s\" pid=%d", 174 prog, pid); 175 (void) kill(pid, SIGKILL); 176 } 177 } 178 179 /* 180 * Process the event [eid] by synchronously invoking all zedlets with a 181 * matching class prefix. 182 * 183 * Each executable in [zedlets] from the directory [dir] is matched against 184 * the event's [class], [subclass], and the "all" class (which matches 185 * all events). Every zedlet with a matching class prefix is invoked. 186 * The NAME=VALUE strings in [envs] will be passed to the zedlet as 187 * environment variables. 188 * 189 * The file descriptor [zfd] is the zevent_fd used to track the 190 * current cursor location within the zevent nvlist. 191 * 192 * Return 0 on success, -1 on error. 193 */ 194 int 195 zed_exec_process(uint64_t eid, const char *class, const char *subclass, 196 const char *dir, zed_strings_t *zedlets, zed_strings_t *envs, int zfd) 197 { 198 const char *class_strings[4]; 199 const char *allclass = "all"; 200 const char **csp; 201 const char *z; 202 char **e; 203 int n; 204 205 if (!dir || !zedlets || !envs || zfd < 0) 206 return (-1); 207 208 csp = class_strings; 209 210 if (class) 211 *csp++ = class; 212 213 if (subclass) 214 *csp++ = subclass; 215 216 if (allclass) 217 *csp++ = allclass; 218 219 *csp = NULL; 220 221 e = _zed_exec_create_env(envs); 222 223 for (z = zed_strings_first(zedlets); z; z = zed_strings_next(zedlets)) { 224 for (csp = class_strings; *csp; csp++) { 225 n = strlen(*csp); 226 if ((strncmp(z, *csp, n) == 0) && !isalpha(z[n])) 227 _zed_exec_fork_child(eid, dir, z, e, zfd); 228 } 229 } 230 free(e); 231 return (0); 232 } 233