Super User's BSD Cross Reference: /FreeBSD/sys/contrib/openzfs/cmd/zed/zed_exec.c

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 

AltStyle によって変換されたページ (->オリジナル) /