]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/contrib/openzfs/cmd/zed/zed_exec.c
Update OpenZFS to 2.0.0-rc3-gfc5966
[FreeBSD/FreeBSD.git] / 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 }