BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /**
2 * @file BProcess.c
3 * @author Ambroz Bizjak <ambrop7@gmail.com>
4 *
5 * @section LICENSE
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the author nor the
15 * names of its contributors may be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29  
30 #include <stddef.h>
31 #include <string.h>
32 #include <inttypes.h>
33 #include <stdlib.h>
34 #include <sys/types.h>
35 #include <sys/wait.h>
36 #include <unistd.h>
37 #include <signal.h>
38 #include <grp.h>
39 #include <pwd.h>
40 #include <errno.h>
41 #include <sys/stat.h>
42 #include <fcntl.h>
43  
44 #include <misc/offset.h>
45 #include <misc/open_standard_streams.h>
46 #include <base/BLog.h>
47  
48 #include "BProcess.h"
49  
50 #include <generated/blog_channel_BProcess.h>
51  
52 #define INITIAL_NUM_GROUPS 24
53  
54 static void call_handler (BProcess *o, int normally, uint8_t normally_exit_status)
55 {
56 DEBUGERROR(&o->d_err, o->handler(o->user, normally, normally_exit_status))
57 }
58  
59 static BProcess * find_process (BProcessManager *o, pid_t pid)
60 {
61 for (LinkedList1Node *node = LinkedList1_GetFirst(&o->processes); node; node = LinkedList1Node_Next(node)) {
62 BProcess *p = UPPER_OBJECT(node, BProcess, list_node);
63 if (p->pid == pid) {
64 return p;
65 }
66 }
67  
68 return NULL;
69 }
70  
71 static void work_signals (BProcessManager *o)
72 {
73 // read exit status with waitpid()
74 int status;
75 pid_t pid = waitpid(-1, &status, WNOHANG);
76 if (pid <= 0) {
77 return;
78 }
79  
80 // schedule next waitpid
81 BPending_Set(&o->wait_job);
82  
83 // find process
84 BProcess *p = find_process(o, pid);
85 if (!p) {
86 BLog(BLOG_DEBUG, "unknown child %p");
87 }
88  
89 if (WIFEXITED(status)) {
90 uint8_t exit_status = WEXITSTATUS(status);
91  
92 BLog(BLOG_INFO, "child %"PRIiMAX" exited with status %"PRIu8, (intmax_t)pid, exit_status);
93  
94 if (p) {
95 call_handler(p, 1, exit_status);
96 return;
97 }
98 }
99 else if (WIFSIGNALED(status)) {
100 int signo = WTERMSIG(status);
101  
102 BLog(BLOG_INFO, "child %"PRIiMAX" exited with signal %d", (intmax_t)pid, signo);
103  
104 if (p) {
105 call_handler(p, 0, 0);
106 return;
107 }
108 }
109 else {
110 BLog(BLOG_ERROR, "unknown wait status type for pid %"PRIiMAX" (%d)", (intmax_t)pid, status);
111 }
112 }
113  
114 static void wait_job_handler (BProcessManager *o)
115 {
116 DebugObject_Access(&o->d_obj);
117  
118 work_signals(o);
119 return;
120 }
121  
122 static void signal_handler (BProcessManager *o, int signo)
123 {
124 ASSERT(signo == SIGCHLD)
125 DebugObject_Access(&o->d_obj);
126  
127 work_signals(o);
128 return;
129 }
130  
131 int BProcessManager_Init (BProcessManager *o, BReactor *reactor)
132 {
133 // init arguments
134 o->reactor = reactor;
135  
136 // init signal handling
137 sigset_t sset;
138 ASSERT_FORCE(sigemptyset(&sset) == 0)
139 ASSERT_FORCE(sigaddset(&sset, SIGCHLD) == 0)
140 if (!BUnixSignal_Init(&o->signal, o->reactor, sset, (BUnixSignal_handler)signal_handler, o)) {
141 BLog(BLOG_ERROR, "BUnixSignal_Init failed");
142 goto fail0;
143 }
144  
145 // init processes list
146 LinkedList1_Init(&o->processes);
147  
148 // init wait job
149 BPending_Init(&o->wait_job, BReactor_PendingGroup(o->reactor), (BPending_handler)wait_job_handler, o);
150  
151 DebugObject_Init(&o->d_obj);
152  
153 return 1;
154  
155 fail0:
156 return 0;
157 }
158  
159 void BProcessManager_Free (BProcessManager *o)
160 {
161 ASSERT(LinkedList1_IsEmpty(&o->processes))
162 DebugObject_Free(&o->d_obj);
163  
164 // free wait job
165 BPending_Free(&o->wait_job);
166  
167 // free signal handling
168 BUnixSignal_Free(&o->signal, 1);
169 }
170  
171 static int fds_contains (const int *fds, int fd, size_t *pos)
172 {
173 for (size_t i = 0; fds[i] >= 0; i++) {
174 if (fds[i] == fd) {
175 if (pos) {
176 *pos = i;
177 }
178  
179 return 1;
180 }
181 }
182  
183 return 0;
184 }
185  
186 int BProcess_Init2 (BProcess *o, BProcessManager *m, BProcess_handler handler, void *user, const char *file, char *const argv[], struct BProcess_params params)
187 {
188 int res = 0;
189  
190 // init arguments
191 o->m = m;
192 o->handler = handler;
193 o->user = user;
194  
195 // count fds
196 size_t num_fds;
197 for (num_fds = 0; params.fds[num_fds] >= 0; num_fds++);
198  
199 // We retrieve all the needed info and allocate all needed memory
200 // before the fork, because doing these things in the child after
201 // the fork may be unsafe.
202  
203 // Get the max FD number.
204 int max_fd = sysconf(_SC_OPEN_MAX);
205 if (max_fd < 0) {
206 BLog(BLOG_ERROR, "sysconf(_SC_OPEN_MAX)");
207 goto fail0;
208 }
209  
210 char *pwnam_buf = NULL;
211 struct passwd pwd;
212 gid_t groups_static[INITIAL_NUM_GROUPS];
213 gid_t *groups = groups_static;
214 int num_groups = INITIAL_NUM_GROUPS;
215  
216 if (params.username) {
217 // Get the max getpwnam_r buffer size.
218 long pwnam_bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
219 if (pwnam_bufsize < 0) {
220 pwnam_bufsize = 16384;
221 }
222  
223 // Allocate memory for getpwnam_r.
224 pwnam_buf = malloc(pwnam_bufsize);
225 if (!pwnam_buf) {
226 BLog(BLOG_ERROR, "malloc failed");
227 goto fail0;
228 }
229  
230 // Get information about the user.
231 struct passwd *getpwnam_res;
232 getpwnam_r(params.username, &pwd, pwnam_buf, pwnam_bufsize, &getpwnam_res);
233 if (!getpwnam_res) {
234 BLog(BLOG_ERROR, "getpwnam_r failed");
235 goto fail1;
236 }
237  
238 // Retrieve the group list.
239 // Start with a small auto-allocated list and only if it turns out
240 // to be too small resort to malloc.
241 while (1) {
242 int groups_ret = getgrouplist(params.username, pwd.pw_gid, groups, &num_groups);
243 if ((groups_ret < 0) ? (num_groups <= 0) : (num_groups != groups_ret)) {
244 BLog(BLOG_ERROR, "getgrouplist behaved inconsistently");
245 goto fail2;
246 }
247  
248 if (groups_ret >= 0) {
249 break;
250 }
251  
252 if (groups != groups_static) {
253 free(groups);
254 }
255 groups = malloc(num_groups * sizeof(gid_t));
256 if (!groups) {
257 BLog(BLOG_ERROR, "malloc failed");
258 goto fail2;
259 }
260 }
261 }
262  
263 // Make a copy of the file descriptors array.
264 int *fds2 = malloc((num_fds + 1) * sizeof(fds2[0]));
265 if (!fds2) {
266 BLog(BLOG_ERROR, "malloc failed");
267 goto fail2;
268 }
269 memcpy(fds2, params.fds, (num_fds + 1) * sizeof(fds2[0]));
270  
271 // block signals
272 // needed to prevent parent's signal handlers from being called
273 // in the child
274 sigset_t sset_all;
275 sigfillset(&sset_all);
276 sigset_t sset_old;
277 if (pthread_sigmask(SIG_SETMASK, &sset_all, &sset_old) != 0) {
278 BLog(BLOG_ERROR, "pthread_sigmask failed");
279 goto fail3;
280 }
281  
282 // fork
283 pid_t pid = fork();
284  
285 if (pid == 0) {
286 // this is child
287  
288 // restore signal dispositions
289 for (int i = 1; i < NSIG; i++) {
290 struct sigaction sa;
291 memset(&sa, 0, sizeof(sa));
292 sa.sa_handler = SIG_DFL;
293 sa.sa_flags = 0;
294 sigaction(i, &sa, NULL);
295 }
296  
297 // unblock signals
298 sigset_t sset_none;
299 sigemptyset(&sset_none);
300 if (pthread_sigmask(SIG_SETMASK, &sset_none, NULL) != 0) {
301 abort();
302 }
303  
304 // close file descriptors, except the given fds
305 for (int i = 0; i < max_fd; i++) {
306 if (!fds_contains(fds2, i, NULL)) {
307 close(i);
308 }
309 }
310  
311 // map fds to requested fd numbers
312 while (*fds2 >= 0) {
313 // resolve possible conflict
314 size_t cpos;
315 if (fds_contains(fds2 + 1, *params.fds_map, &cpos)) {
316 // dup() the fd to a new number; the old one will be closed
317 // in the following dup2()
318 if ((fds2[1 + cpos] = dup(fds2[1 + cpos])) < 0) {
319 abort();
320 }
321 }
322  
323 if (*fds2 != *params.fds_map) {
324 // dup fd
325 if (dup2(*fds2, *params.fds_map) < 0) {
326 abort();
327 }
328  
329 // close original fd
330 close(*fds2);
331 }
332  
333 fds2++;
334 params.fds_map++;
335 }
336  
337 // make sure standard streams are open
338 open_standard_streams();
339  
340 // make session leader if requested
341 if (params.do_setsid) {
342 setsid();
343 }
344  
345 // assume identity of username, if requested
346 if (params.username) {
347 if (setgroups(num_groups, groups) < 0) {
348 abort();
349 }
350  
351 if (setgid(pwd.pw_gid) < 0) {
352 abort();
353 }
354  
355 if (setuid(pwd.pw_uid) < 0) {
356 abort();
357 }
358 }
359  
360 // do the exec
361 execv(file, argv);
362  
363 // if we're still here, something went wrong
364 abort();
365 }
366  
367 // restore original signal mask
368 ASSERT_FORCE(pthread_sigmask(SIG_SETMASK, &sset_old, NULL) == 0)
369  
370 if (pid < 0) {
371 BLog(BLOG_ERROR, "fork failed");
372 goto fail3;
373 }
374  
375 // remember pid
376 o->pid = pid;
377  
378 // add to processes list
379 LinkedList1_Append(&o->m->processes, &o->list_node);
380  
381 DebugObject_Init(&o->d_obj);
382 DebugError_Init(&o->d_err, BReactor_PendingGroup(m->reactor));
383  
384 // Returning success, but cleanup first.
385 res = 1;
386  
387 fail3:
388 free(fds2);
389 fail2:
390 if (groups != groups_static) {
391 free(groups);
392 }
393 fail1:
394 free(pwnam_buf);
395 fail0:
396 return res;
397 }
398  
399 int BProcess_InitWithFds (BProcess *o, BProcessManager *m, BProcess_handler handler, void *user, const char *file, char *const argv[], const char *username, const int *fds, const int *fds_map)
400 {
401 struct BProcess_params params;
402 params.username = username;
403 params.fds = fds;
404 params.fds_map = fds_map;
405 params.do_setsid = 0;
406  
407 return BProcess_Init2(o, m, handler, user, file, argv, params);
408 }
409  
410 int BProcess_Init (BProcess *o, BProcessManager *m, BProcess_handler handler, void *user, const char *file, char *const argv[], const char *username)
411 {
412 int fds[] = {-1};
413  
414 return BProcess_InitWithFds(o, m, handler, user, file, argv, username, fds, NULL);
415 }
416  
417 void BProcess_Free (BProcess *o)
418 {
419 DebugError_Free(&o->d_err);
420 DebugObject_Free(&o->d_obj);
421  
422 // remove from processes list
423 LinkedList1_Remove(&o->m->processes, &o->list_node);
424 }
425  
426 int BProcess_Terminate (BProcess *o)
427 {
428 DebugObject_Access(&o->d_obj);
429 DebugError_AssertNoError(&o->d_err);
430  
431 ASSERT(o->pid > 0)
432  
433 if (kill(o->pid, SIGTERM) < 0) {
434 BLog(BLOG_ERROR, "kill(%"PRIiMAX", SIGTERM) failed", (intmax_t)o->pid);
435 return 0;
436 }
437  
438 return 1;
439 }
440  
441 int BProcess_Kill (BProcess *o)
442 {
443 DebugObject_Access(&o->d_obj);
444 DebugError_AssertNoError(&o->d_err);
445  
446 ASSERT(o->pid > 0)
447  
448 if (kill(o->pid, SIGKILL) < 0) {
449 BLog(BLOG_ERROR, "kill(%"PRIiMAX", SIGKILL) failed", (intmax_t)o->pid);
450 return 0;
451 }
452  
453 return 1;
454 }