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>
26 GQuark
vlock_process_error_quark(void)
28 return g_quark_from_static_string("vlock-process-error-quark");
32 static void ignore_sigalarm(int __attribute__((unused
)) signum
)
36 bool wait_for_death(pid_t pid
, long sec
, long usec
)
40 struct sigaction oldact
;
41 struct itimerval timer
;
42 struct itimerval otimer
;
45 /* Ignore SIGALRM. The handler must be a real function instead of SIG_IGN
46 * otherwise waitpid() would not get interrupted.
48 * There is a small window here where a previously set alarm might be
50 sigemptyset(&act
.sa_mask
);
51 act
.sa_handler
= ignore_sigalarm
;
53 sigaction(SIGALRM
, &act
, &oldact
);
55 /* Initialize the timer. */
56 timer
.it_value
.tv_sec
= sec
;
57 timer
.it_value
.tv_usec
= usec
;
59 timer
.it_interval
.tv_sec
= 0;
60 timer
.it_interval
.tv_usec
= 0;
63 setitimer(ITIMER_REAL
, &timer
, &otimer
);
65 /* Wait until the child exits or the timer fires. */
66 result
= (waitpid(pid
, &status
, 0) == pid
);
68 /* Possible race condition. If an alarm was set before it may get ignored.
69 * This is probably better than getting killed by our own alarm. */
71 /* Restore the timer. */
72 setitimer(ITIMER_REAL
, &otimer
, NULL
);
74 /* Restore signal handler for SIGALRM. */
75 sigaction(SIGALRM
, &oldact
, NULL
);
80 /* Try hard to kill the given child process. */
81 void ensure_death(pid_t pid
)
85 switch (waitpid(pid
, &status
, WNOHANG
)) {
90 /* Not dead yet. Continue. */
93 /* Already dead. Nothing to do. */
98 (void) kill(pid
, SIGTERM
);
100 /* SIGTERM handler (if any) has 500ms to finish. */
101 if (wait_for_death(pid
, 0, 500000L))
105 (void) kill(pid
, SIGKILL
);
106 /* Child may be stopped. Send SIGCONT just to be sure. */
107 (void) kill(pid
, SIGCONT
);
109 /* Wait until dead. Shouldn't take long. */
110 (void) waitpid(pid
, &status
, 0);
113 /* Close all possibly open file descriptors except the ones specified in the
115 static void close_fds(fd_set
*except_fds
)
120 /* Get the maximum number of file descriptors. */
121 if (getrlimit(RLIMIT_NOFILE
, &r
) == 0)
124 /* Hopefully safe default. */
127 /* Close all possibly open file descriptors except STDIN_FILENO,
128 * STDOUT_FILENO and STDERR_FILENO. */
129 for (int fd
= 0; fd
< maxfd
; fd
++)
130 if (!FD_ISSET(fd
, except_fds
))
134 static int open_devnull(void)
136 static int devnull_fd
= -1;
139 devnull_fd
= open("/dev/null", O_RDWR
);
144 bool create_child(struct child_process
*child
, GError
**error
)
152 if (pipe(status_pipe
) < 0)
155 (void) fcntl(status_pipe
[1], F_SETFD
, FD_CLOEXEC
);
157 if (child
->stdin_fd
== REDIRECT_PIPE
)
158 if (pipe(stdin_pipe
) < 0) {
161 VLOCK_PROCESS_ERROR_FAILED
,
162 "could not open stdin pipe: %s",
164 goto stdin_pipe_failed
;
167 if (child
->stdout_fd
== REDIRECT_PIPE
)
168 if (pipe(stdout_pipe
) < 0) {
171 VLOCK_PROCESS_ERROR_FAILED
,
172 "could not open stdout pipe: %s",
174 goto stdout_pipe_failed
;
177 if (child
->stderr_fd
== REDIRECT_PIPE
)
178 if (pipe(stderr_pipe
) < 0) {
181 VLOCK_PROCESS_ERROR_FAILED
,
182 "could not open stderr pipe: %s",
184 goto stderr_pipe_failed
;
189 if (child
->pid
== 0) {
193 if (child
->stdin_fd
== REDIRECT_PIPE
)
194 (void) dup2(stdin_pipe
[0], STDIN_FILENO
);
195 else if (child
->stdin_fd
== REDIRECT_DEV_NULL
)
196 (void) dup2(open_devnull(), STDIN_FILENO
);
197 else if (child
->stdin_fd
!= NO_REDIRECT
)
198 (void) dup2(child
->stdin_fd
, STDIN_FILENO
);
200 if (child
->stdout_fd
== REDIRECT_PIPE
)
201 (void) dup2(stdout_pipe
[1], STDOUT_FILENO
);
202 else if (child
->stdout_fd
== REDIRECT_DEV_NULL
)
203 (void) dup2(open_devnull(), STDOUT_FILENO
);
204 else if (child
->stdout_fd
!= NO_REDIRECT
)
205 (void) dup2(child
->stdout_fd
, STDOUT_FILENO
);
207 if (child
->stderr_fd
== REDIRECT_PIPE
)
208 (void) dup2(stderr_pipe
[1], STDERR_FILENO
);
209 else if (child
->stderr_fd
== REDIRECT_DEV_NULL
)
210 (void) dup2(open_devnull(), STDERR_FILENO
);
211 else if (child
->stderr_fd
!= NO_REDIRECT
)
212 (void) dup2(child
->stderr_fd
, STDERR_FILENO
);
214 FD_ZERO(&except_fds
);
215 FD_SET(STDIN_FILENO
, &except_fds
);
216 FD_SET(STDOUT_FILENO
, &except_fds
);
217 FD_SET(STDERR_FILENO
, &except_fds
);
218 FD_SET(status_pipe
[1], &except_fds
);
220 (void) close_fds(&except_fds
);
222 (void) setgid(getgid());
223 (void) setuid(getuid());
225 if (child
->function
!= NULL
) {
226 (void) close(status_pipe
[1]);
227 _exit(child
->function(child
->argument
));
229 execv(child
->path
, (char *const*) child
->argv
);
230 (void) write(status_pipe
[1], &errno
, sizeof errno
);
236 if (child
->pid
< 0) {
239 VLOCK_PROCESS_ERROR_FAILED
,
240 "could not fork: %s",
245 (void) close(status_pipe
[1]);
247 /* Get the error status from the child, if any. */
248 if (read(status_pipe
[0], &child_errno
,
249 sizeof child_errno
) == sizeof child_errno
) {
252 child_errno
== ENOENT
?
253 VLOCK_PROCESS_ERROR_NOT_FOUND
:
254 VLOCK_PROCESS_ERROR_FAILED
,
255 "child process could not exec: %s",
256 g_strerror(child_errno
));
260 (void) close(status_pipe
[0]);
262 if (child
->stdin_fd
== REDIRECT_PIPE
) {
264 child
->stdin_fd
= stdin_pipe
[1];
266 (void) close(stdin_pipe
[0]);
269 if (child
->stdout_fd
== REDIRECT_PIPE
) {
271 child
->stdout_fd
= stdout_pipe
[0];
273 (void) close(stdout_pipe
[1]);
276 if (child
->stderr_fd
== REDIRECT_PIPE
) {
278 child
->stderr_fd
= stderr_pipe
[0];
280 (void) close(stderr_pipe
[1]);
287 if (child
->stderr_fd
== REDIRECT_PIPE
) {
288 (void) close(stderr_pipe
[0]);
289 (void) close(stderr_pipe
[1]);
293 if (child
->stdout_fd
== REDIRECT_PIPE
) {
294 (void) close(stdout_pipe
[0]);
295 (void) close(stdout_pipe
[1]);
299 if (child
->stdin_fd
== REDIRECT_PIPE
) {
300 (void) close(stdin_pipe
[0]);
301 (void) close(stdin_pipe
[1]);
305 (void) close(status_pipe
[0]);
306 (void) close(status_pipe
[1]);