STYLE: stub
[vlock.git] / src / process.c
blob8ab07fa0ed8593f6a8cd0e36c0d7eaf21c7ceb4d
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>
21 #include <sys/select.h>
22 #include <errno.h>
24 #include "process.h"
26 /* Do nothing. */
27 static void ignore_sigalarm(int __attribute__((unused)) signum)
31 bool wait_for_death(pid_t pid, long sec, long usec)
33 int status;
34 struct sigaction act;
35 struct sigaction oldact;
36 struct itimerval timer;
37 struct itimerval otimer;
38 bool result;
40 /* Ignore SIGALRM. The handler must be a real function instead of SIG_IGN
41 * otherwise waitpid() would not get interrupted.
43 * There is a small window here where a previously set alarm might be
44 * ignored. */
45 sigemptyset(&act.sa_mask);
46 act.sa_handler = ignore_sigalarm;
47 act.sa_flags = 0;
48 sigaction(SIGALRM, &act, &oldact);
50 /* Initialize the timer. */
51 timer.it_value.tv_sec = sec;
52 timer.it_value.tv_usec = usec;
53 /* No repetition. */
54 timer.it_interval.tv_sec = 0;
55 timer.it_interval.tv_usec = 0;
57 /* Set the timer. */
58 setitimer(ITIMER_REAL, &timer, &otimer);
60 /* Wait until the child exits or the timer fires. */
61 result = (waitpid(pid, &status, 0) == pid);
63 /* Possible race condition. If an alarm was set before it may get ignored.
64 * This is probably better than getting killed by our own alarm. */
66 /* Restore the timer. */
67 setitimer(ITIMER_REAL, &otimer, NULL);
69 /* Restore signal handler for SIGALRM. */
70 sigaction(SIGALRM, &oldact, NULL);
72 return result;
75 /* Try hard to kill the given child process. */
76 void ensure_death(pid_t pid)
78 int status;
80 switch (waitpid(pid, &status, WNOHANG)) {
81 case -1:
82 /* Not your child? */
83 return;
84 case 0:
85 /* Not dead yet. Continue. */
86 break;
87 default:
88 /* Already dead. Nothing to do. */
89 return;
92 /* Send SIGTERM. */
93 (void) kill(pid, SIGTERM);
95 /* SIGTERM handler (if any) has 500ms to finish. */
96 if (wait_for_death(pid, 0, 500000L))
97 return;
99 // Send SIGKILL. */
100 (void) kill(pid, SIGKILL);
101 /* Child may be stopped. Send SIGCONT just to be sure. */
102 (void) kill(pid, SIGCONT);
104 /* Wait until dead. Shouldn't take long. */
105 (void) waitpid(pid, &status, 0);
108 /* Close all possibly open file descriptors except the ones specified in the
109 * given set. */
110 static void close_fds(fd_set *except_fds)
112 struct rlimit r;
113 int maxfd;
115 /* Get the maximum number of file descriptors. */
116 if (getrlimit(RLIMIT_NOFILE, &r) == 0)
117 maxfd = r.rlim_cur;
118 else
119 /* Hopefully safe default. */
120 maxfd = 1024;
122 /* Close all possibly open file descriptors except STDIN_FILENO,
123 * STDOUT_FILENO and STDERR_FILENO. */
124 for (int fd = 0; fd < maxfd; fd++)
125 if (!FD_ISSET(fd, except_fds))
126 (void) close(fd);
129 static int open_devnull(void)
131 static int devnull_fd = -1;
133 if (devnull_fd < 0)
134 devnull_fd = open("/dev/null", O_RDWR);
136 return devnull_fd;
139 bool create_child(struct child_process *child)
141 int errsv;
142 int child_errno = 0;
143 int status_pipe[2];
144 int stdin_pipe[2];
145 int stdout_pipe[2];
146 int stderr_pipe[3];
148 if (pipe(status_pipe) < 0)
149 return false;
151 (void) fcntl(status_pipe[1], F_SETFD, FD_CLOEXEC);
153 if (child->stdin_fd == REDIRECT_PIPE) {
154 if (pipe(stdin_pipe) < 0) {
155 errsv = errno;
156 goto stdin_pipe_failed;
160 if (child->stdout_fd == REDIRECT_PIPE) {
161 if (pipe(stdout_pipe) < 0) {
162 errsv = errno;
163 goto stdout_pipe_failed;
167 if (child->stderr_fd == REDIRECT_PIPE) {
168 if (pipe(stderr_pipe) < 0) {
169 errsv = errno;
170 goto stderr_pipe_failed;
174 child->pid = fork();
176 if (child->pid == 0) {
177 /* Child. */
178 fd_set except_fds;
180 if (child->stdin_fd == REDIRECT_PIPE)
181 (void) dup2(stdin_pipe[0], STDIN_FILENO);
182 else if (child->stdin_fd == REDIRECT_DEV_NULL)
183 (void) dup2(open_devnull(), STDIN_FILENO);
184 else if (child->stdin_fd != NO_REDIRECT)
185 (void) dup2(child->stdin_fd, STDIN_FILENO);
187 if (child->stdout_fd == REDIRECT_PIPE)
188 (void) dup2(stdout_pipe[1], STDOUT_FILENO);
189 else if (child->stdout_fd == REDIRECT_DEV_NULL)
190 (void) dup2(open_devnull(), STDOUT_FILENO);
191 else if (child->stdout_fd != NO_REDIRECT)
192 (void) dup2(child->stdout_fd, STDOUT_FILENO);
194 if (child->stderr_fd == REDIRECT_PIPE)
195 (void) dup2(stderr_pipe[1], STDERR_FILENO);
196 else if (child->stderr_fd == REDIRECT_DEV_NULL)
197 (void) dup2(open_devnull(), STDERR_FILENO);
198 else if (child->stderr_fd != NO_REDIRECT)
199 (void) dup2(child->stderr_fd, STDERR_FILENO);
201 FD_ZERO(&except_fds);
202 FD_SET(STDIN_FILENO, &except_fds);
203 FD_SET(STDOUT_FILENO, &except_fds);
204 FD_SET(STDERR_FILENO, &except_fds);
205 FD_SET(status_pipe[1], &except_fds);
207 (void) close_fds(&except_fds);
209 (void) setgid(getgid());
210 (void) setuid(getuid());
212 if (child->function != NULL) {
213 (void) close(status_pipe[1]);
214 _exit(child->function(child->argument));
215 } else {
216 execv(child->path, (char *const*)child->argv);
217 (void) write(status_pipe[1], &errno, sizeof errno);
220 _exit(1);
223 if (child->pid < 0) {
224 errsv = errno;
225 goto fork_failed;
228 (void) close(status_pipe[1]);
230 if (read(status_pipe[0], &child_errno, sizeof child_errno) == sizeof child_errno) {
231 errsv = child_errno;
232 goto child_failed;
235 if (child->stdin_fd == REDIRECT_PIPE) {
236 /* Write end. */
237 child->stdin_fd = stdin_pipe[1];
238 /* Read end. */
239 (void) close(stdin_pipe[0]);
242 if (child->stdout_fd == REDIRECT_PIPE) {
243 /* Read end. */
244 child->stdout_fd = stdout_pipe[0];
245 /* Write end. */
246 (void) close(stdout_pipe[1]);
249 if (child->stderr_fd == REDIRECT_PIPE) {
250 /* Read end. */
251 child->stderr_fd = stderr_pipe[0];
252 /* Write end. */
253 (void) close(stderr_pipe[1]);
256 return true;
258 child_failed:
259 fork_failed:
260 if (child->stderr_fd == REDIRECT_PIPE) {
261 (void) close(stderr_pipe[0]);
262 (void) close(stderr_pipe[1]);
265 stderr_pipe_failed:
266 if (child->stdout_fd == REDIRECT_PIPE) {
267 (void) close(stdout_pipe[0]);
268 (void) close(stdout_pipe[1]);
271 stdout_pipe_failed:
272 if (child->stdin_fd == REDIRECT_PIPE) {
273 (void) close(stdin_pipe[0]);
274 (void) close(stdin_pipe[1]);
277 stdin_pipe_failed:
278 (void) close(status_pipe[0]);
279 (void) close(status_pipe[1]);
281 errno = errsv;
283 return false;