]>
Commit | Line | Data |
---|---|---|
ea04106b AX |
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 | } |