src/process.{c,h}: first implementation of create_child
[vlock.git] / src / process.c
blob94c6fafc166c81593a1bee037bb294ed2912296d
1 /* process.c -- child process routines for vlock,
2 * the VT locking program for linux
4 * This program is copyright (C) 2007 Frank Benkstein, and is free
5 * software which is freely distributable under the terms of the
6 * GNU General Public License version 2, included as the file COPYING in this
7 * distribution. It is NOT public domain software, and any
8 * redistribution not permitted by the GNU General Public License is
9 * expressly forbidden without prior written permission from
10 * the author.
14 #include <stdio.h>
15 #include <unistd.h>
16 #include <signal.h>
17 #include <sys/wait.h>
18 #include <sys/resource.h>
19 #include <sys/time.h>
20 #include <fcntl.h>
22 #include "process.h"
24 /* Do nothing. */
25 static void ignore_sigalarm(int __attribute__((unused)) signum)
29 bool wait_for_death(pid_t pid, long sec, long usec)
31 int status;
32 struct sigaction act;
33 struct sigaction oldact;
34 struct itimerval timer;
35 struct itimerval otimer;
36 bool result;
38 /* Ignore SIGALRM. The handler must be a real function instead of SIG_IGN
39 * otherwise waitpid() would not get interrupted.
41 * There is a small window here where a previously set alarm might be
42 * ignored. */
43 sigemptyset(&act.sa_mask);
44 act.sa_handler = ignore_sigalarm;
45 act.sa_flags = 0;
46 sigaction(SIGALRM, &act, &oldact);
48 /* Initialize the timer. */
49 timer.it_value.tv_sec = sec;
50 timer.it_value.tv_usec = usec;
51 /* No repetition. */
52 timer.it_interval.tv_sec = 0;
53 timer.it_interval.tv_usec = 0;
55 /* Set the timer. */
56 setitimer(ITIMER_REAL, &timer, &otimer);
58 /* Wait until the child exits or the timer fires. */
59 result = (waitpid(pid, &status, 0) == pid);
61 /* Possible race condition. If an alarm was set before it may get ignored.
62 * This is probably better than getting killed by our own alarm. */
64 /* Restore the timer. */
65 setitimer(ITIMER_REAL, &otimer, NULL);
67 /* Restore signal handler for SIGALRM. */
68 sigaction(SIGALRM, &oldact, NULL);
70 return result;
73 /* Try hard to kill the given child process. */
74 void ensure_death(pid_t pid)
76 int status;
78 switch (waitpid(pid, &status, WNOHANG)) {
79 case -1:
80 /* Not your child? */
81 return;
82 case 0:
83 /* Not dead yet. Continue. */
84 break;
85 default:
86 /* Already dead. Nothing to do. */
87 return;
90 /* Send SIGTERM. */
91 (void) kill(pid, SIGTERM);
93 /* SIGTERM handler (if any) has 500ms to finish. */
94 if (wait_for_death(pid, 0, 500000L))
95 return;
97 // Send SIGKILL. */
98 (void) kill(pid, SIGKILL);
99 /* Child may be stopped. Send SIGCONT just to be sure. */
100 (void) kill(pid, SIGCONT);
102 /* Wait until dead. Shouldn't take long. */
103 (void) waitpid(pid, &status, 0);
106 /* Close all possibly open file descriptors except STDIN_FILENO, STDOUT_FILENO
107 * and STDERR_FILENO. */
108 void close_all_fds(void)
110 struct rlimit r;
111 int maxfd;
113 /* Get the maximum number of file descriptors. */
114 if (getrlimit(RLIMIT_NOFILE, &r) == 0)
115 maxfd = r.rlim_cur;
116 else
117 /* Hopefully safe default. */
118 maxfd = 1024;
120 /* Close all possibly open file descriptors except STDIN_FILENO,
121 * STDOUT_FILENO and STDERR_FILENO. */
122 for (int i = 0; i < maxfd; i++) {
123 switch (i) {
124 case STDIN_FILENO:
125 case STDOUT_FILENO:
126 case STDERR_FILENO:
127 break;
128 default:
129 (void) close(i);
130 break;
135 static int open_devnull(void)
137 static int devnull_fd = -1;
139 if (devnull_fd < 0)
140 devnull_fd = open("/dev/null", O_RDWR);
142 return devnull_fd;
145 bool create_child(struct child_process *child)
147 int stdin_pipe[2];
148 int stdout_pipe[2];
149 int stderr_pipe[3];
151 if (child->stdin_fd == REDIRECT_PIPE) {
152 if (pipe(stdin_pipe) < 0)
153 return false;
156 if (child->stdout_fd == REDIRECT_PIPE) {
157 if (pipe(stdout_pipe) < 0)
158 goto stdout_pipe_failed;
161 if (child->stderr_fd == REDIRECT_PIPE) {
162 if (pipe(stderr_pipe) < 0)
163 goto stderr_pipe_failed;
166 child->pid = fork();
168 if (child->pid == 0) {
169 /* Child. */
170 if (child->stdin_fd == REDIRECT_PIPE)
171 (void) dup2(STDIN_FILENO, stdin_pipe[0]);
172 else if (child->stdin_fd == REDIRECT_DEV_NULL)
173 (void) dup2(STDIN_FILENO, open_devnull());
174 else if (child->stdin_fd != NO_REDIRECT)
175 (void) dup2(STDIN_FILENO, child->stdin_fd);
177 if (child->stdout_fd == REDIRECT_PIPE)
178 (void) dup2(STDIN_FILENO, stdout_pipe[1]);
179 else if (child->stdout_fd == REDIRECT_DEV_NULL)
180 (void) dup2(STDIN_FILENO, open_devnull());
181 else if (child->stdout_fd != NO_REDIRECT)
182 (void) dup2(STDIN_FILENO, child->stdout_fd);
184 if (child->stderr_fd == REDIRECT_PIPE)
185 (void) dup2(STDIN_FILENO, stderr_pipe[1]);
186 else if (child->stderr_fd == REDIRECT_DEV_NULL)
187 (void) dup2(STDIN_FILENO, open_devnull());
188 else if (child->stderr_fd != NO_REDIRECT)
189 (void) dup2(STDIN_FILENO, child->stderr_fd);
191 (void) close_all_fds();
193 (void) setgid(getgid());
194 (void) setuid(getuid());
196 if (child->function != NULL) {
197 _exit(child->function(child->argument));
198 } else {
199 execv(child->path, (char *const*)child->argv);
202 _exit(1);
205 if (child->pid < 0)
206 goto fork_failed;
208 if (child->stdin_fd == REDIRECT_PIPE) {
209 /* Write end. */
210 child->stdin_fd = stdin_pipe[1];
211 /* Read end. */
212 (void) close(stdin_pipe[0]);
215 if (child->stdout_fd == REDIRECT_PIPE) {
216 /* Read end. */
217 child->stdout_fd = stdout_pipe[0];
218 /* Write end. */
219 (void) close(stdout_pipe[1]);
222 if (child->stderr_fd == REDIRECT_PIPE) {
223 /* Read end. */
224 child->stderr_fd = stderr_pipe[0];
225 /* Write end. */
226 (void) close(stderr_pipe[1]);
229 return true;
231 fork_failed:
232 if (child->stderr_fd == REDIRECT_PIPE) {
233 (void) close(stderr_pipe[0]);
234 (void) close(stderr_pipe[1]);
237 stderr_pipe_failed:
238 if (child->stdout_fd == REDIRECT_PIPE) {
239 (void) close(stdout_pipe[0]);
240 (void) close(stdout_pipe[1]);
243 stdout_pipe_failed:
244 if (child->stdin_fd == REDIRECT_PIPE) {
245 (void) close(stdin_pipe[0]);
246 (void) close(stdin_pipe[1]);
249 return false;