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