]> git.proxmox.com Git - mirror_zfs-debian.git/blob - cmd/zed/zed_exec.c
Imported Upstream version 0.6.4.2
[mirror_zfs-debian.git] / cmd / zed / zed_exec.c
1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license from the top-level
9 * OPENSOLARIS.LICENSE or <http://opensource.org/licenses/CDDL-1.0>.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each file
14 * and include the License file from the top-level OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
24 * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
25 */
26
27 #include <assert.h>
28 #include <ctype.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/stat.h>
34 #include <sys/wait.h>
35 #include <unistd.h>
36 #include "zed_file.h"
37 #include "zed_log.h"
38 #include "zed_strings.h"
39
40 #define ZEVENT_FILENO 3
41
42 /*
43 * Create an environment string array for passing to execve() using the
44 * NAME=VALUE strings in container [zsp].
45 * Return a newly-allocated environment, or NULL on error.
46 */
47 static char **
48 _zed_exec_create_env(zed_strings_t *zsp)
49 {
50 int num_ptrs;
51 int buflen;
52 char *buf;
53 char **pp;
54 char *p;
55 const char *q;
56 int i;
57 int len;
58
59 num_ptrs = zed_strings_count(zsp) + 1;
60 buflen = num_ptrs * sizeof (char *);
61 for (q = zed_strings_first(zsp); q; q = zed_strings_next(zsp))
62 buflen += strlen(q) + 1;
63
64 buf = calloc(1, buflen);
65 if (!buf)
66 return (NULL);
67
68 pp = (char **) buf;
69 p = buf + (num_ptrs * sizeof (char *));
70 i = 0;
71 for (q = zed_strings_first(zsp); q; q = zed_strings_next(zsp)) {
72 pp[i] = p;
73 len = strlen(q) + 1;
74 memcpy(p, q, len);
75 p += len;
76 i++;
77 }
78 pp[i] = NULL;
79 assert(buf + buflen == p);
80 return ((char **) buf);
81 }
82
83 /*
84 * Fork a child process to handle event [eid]. The program [prog]
85 * in directory [dir] is executed with the envionment [env].
86 *
87 * The file descriptor [zfd] is the zevent_fd used to track the
88 * current cursor location within the zevent nvlist.
89 */
90 static void
91 _zed_exec_fork_child(uint64_t eid, const char *dir, const char *prog,
92 char *env[], int zfd)
93 {
94 char path[PATH_MAX];
95 int n;
96 pid_t pid;
97 int fd;
98 pid_t wpid;
99 int status;
100
101 assert(dir != NULL);
102 assert(prog != NULL);
103 assert(env != NULL);
104 assert(zfd >= 0);
105
106 n = snprintf(path, sizeof (path), "%s/%s", dir, prog);
107 if ((n < 0) || (n >= sizeof (path))) {
108 zed_log_msg(LOG_WARNING,
109 "Failed to fork \"%s\" for eid=%llu: %s",
110 prog, eid, strerror(ENAMETOOLONG));
111 return;
112 }
113 pid = fork();
114 if (pid < 0) {
115 zed_log_msg(LOG_WARNING,
116 "Failed to fork \"%s\" for eid=%llu: %s",
117 prog, eid, strerror(errno));
118 return;
119 } else if (pid == 0) {
120 (void) umask(022);
121 fd = open("/dev/null", O_RDWR);
122 (void) dup2(fd, STDIN_FILENO);
123 (void) dup2(fd, STDOUT_FILENO);
124 (void) dup2(fd, STDERR_FILENO);
125 (void) dup2(zfd, ZEVENT_FILENO);
126 zed_file_close_from(ZEVENT_FILENO + 1);
127 execle(path, prog, NULL, env);
128 _exit(127);
129 } else {
130 zed_log_msg(LOG_INFO, "Invoking \"%s\" eid=%llu pid=%d",
131 prog, eid, pid);
132 /* FIXME: Timeout rogue child processes with sigalarm? */
133 restart:
134 wpid = waitpid(pid, &status, 0);
135 if (wpid == (pid_t) -1) {
136 if (errno == EINTR)
137 goto restart;
138 zed_log_msg(LOG_WARNING,
139 "Failed to wait for \"%s\" eid=%llu pid=%d",
140 prog, eid, pid);
141 } else if (WIFEXITED(status)) {
142 zed_log_msg(LOG_INFO,
143 "Finished \"%s\" eid=%llu pid=%d exit=%d",
144 prog, eid, pid, WEXITSTATUS(status));
145 } else if (WIFSIGNALED(status)) {
146 zed_log_msg(LOG_INFO,
147 "Finished \"%s\" eid=%llu pid=%d sig=%d/%s",
148 prog, eid, pid, WTERMSIG(status),
149 strsignal(WTERMSIG(status)));
150 } else {
151 zed_log_msg(LOG_INFO,
152 "Finished \"%s\" eid=%llu pid=%d status=0x%X",
153 prog, eid, (unsigned int) status);
154 }
155 }
156 }
157
158 /*
159 * Process the event [eid] by synchronously invoking all zedlets with a
160 * matching class prefix.
161 *
162 * Each executable in [zedlets] from the directory [dir] is matched against
163 * the event's [class], [subclass], and the "all" class (which matches
164 * all events). Every zedlet with a matching class prefix is invoked.
165 * The NAME=VALUE strings in [envs] will be passed to the zedlet as
166 * environment variables.
167 *
168 * The file descriptor [zfd] is the zevent_fd used to track the
169 * current cursor location within the zevent nvlist.
170 *
171 * Return 0 on success, -1 on error.
172 */
173 int
174 zed_exec_process(uint64_t eid, const char *class, const char *subclass,
175 const char *dir, zed_strings_t *zedlets, zed_strings_t *envs, int zfd)
176 {
177 const char *class_strings[4];
178 const char *allclass = "all";
179 const char **csp;
180 const char *z;
181 char **e;
182 int n;
183
184 if (!dir || !zedlets || !envs || zfd < 0)
185 return (-1);
186
187 csp = class_strings;
188
189 if (class)
190 *csp++ = class;
191
192 if (subclass)
193 *csp++ = subclass;
194
195 if (allclass)
196 *csp++ = allclass;
197
198 *csp = NULL;
199
200 e = _zed_exec_create_env(envs);
201
202 for (z = zed_strings_first(zedlets); z; z = zed_strings_next(zedlets)) {
203 for (csp = class_strings; *csp; csp++) {
204 n = strlen(*csp);
205 if ((strncmp(z, *csp, n) == 0) && !isalpha(z[n]))
206 _zed_exec_fork_child(eid, dir, z, e, zfd);
207 }
208 }
209 free(e);
210 return (0);
211 }