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
18 #include <sys/resource.h>
21 #include <sys/select.h>
27 static void ignore_sigalarm(int __attribute__((unused
)) signum
)
31 bool wait_for_death(pid_t pid
, long sec
, long usec
)
35 struct sigaction oldact
;
36 struct itimerval timer
;
37 struct itimerval otimer
;
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
45 sigemptyset(&act
.sa_mask
);
46 act
.sa_handler
= ignore_sigalarm
;
48 sigaction(SIGALRM
, &act
, &oldact
);
50 /* Initialize the timer. */
51 timer
.it_value
.tv_sec
= sec
;
52 timer
.it_value
.tv_usec
= usec
;
54 timer
.it_interval
.tv_sec
= 0;
55 timer
.it_interval
.tv_usec
= 0;
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
);
75 /* Try hard to kill the given child process. */
76 void ensure_death(pid_t pid
)
80 switch (waitpid(pid
, &status
, WNOHANG
)) {
85 /* Not dead yet. Continue. */
88 /* Already dead. Nothing to do. */
93 (void) kill(pid
, SIGTERM
);
95 /* SIGTERM handler (if any) has 500ms to finish. */
96 if (wait_for_death(pid
, 0, 500000L))
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
110 static void close_fds(fd_set
*except_fds
)
115 /* Get the maximum number of file descriptors. */
116 if (getrlimit(RLIMIT_NOFILE
, &r
) == 0)
119 /* Hopefully safe default. */
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
))
129 static int open_devnull(void)
131 static int devnull_fd
= -1;
134 devnull_fd
= open("/dev/null", O_RDWR
);
139 bool create_child(struct child_process
*child
)
148 if (pipe(status_pipe
) < 0)
151 (void) fcntl(status_pipe
[1], F_SETFD
, FD_CLOEXEC
);
153 if (child
->stdin_fd
== REDIRECT_PIPE
) {
154 if (pipe(stdin_pipe
) < 0) {
156 goto stdin_pipe_failed
;
160 if (child
->stdout_fd
== REDIRECT_PIPE
) {
161 if (pipe(stdout_pipe
) < 0) {
163 goto stdout_pipe_failed
;
167 if (child
->stderr_fd
== REDIRECT_PIPE
) {
168 if (pipe(stderr_pipe
) < 0) {
170 goto stderr_pipe_failed
;
176 if (child
->pid
== 0) {
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
));
216 execv(child
->path
, (char *const*)child
->argv
);
217 (void) write(status_pipe
[1], &errno
, sizeof errno
);
223 if (child
->pid
< 0) {
228 (void) close(status_pipe
[1]);
230 if (read(status_pipe
[0], &child_errno
, sizeof child_errno
) == sizeof child_errno
) {
235 if (child
->stdin_fd
== REDIRECT_PIPE
) {
237 child
->stdin_fd
= stdin_pipe
[1];
239 (void) close(stdin_pipe
[0]);
242 if (child
->stdout_fd
== REDIRECT_PIPE
) {
244 child
->stdout_fd
= stdout_pipe
[0];
246 (void) close(stdout_pipe
[1]);
249 if (child
->stderr_fd
== REDIRECT_PIPE
) {
251 child
->stderr_fd
= stderr_pipe
[0];
253 (void) close(stderr_pipe
[1]);
260 if (child
->stderr_fd
== REDIRECT_PIPE
) {
261 (void) close(stderr_pipe
[0]);
262 (void) close(stderr_pipe
[1]);
266 if (child
->stdout_fd
== REDIRECT_PIPE
) {
267 (void) close(stdout_pipe
[0]);
268 (void) close(stdout_pipe
[1]);
272 if (child
->stdin_fd
== REDIRECT_PIPE
) {
273 (void) close(stdin_pipe
[0]);
274 (void) close(stdin_pipe
[1]);
278 (void) close(status_pipe
[0]);
279 (void) close(status_pipe
[1]);