src/util.c: shut up gcc warnings
[vlock.git] / src / process.c
blob775f9791db0ae7d41f498e19393ba4555660120f
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 GQuark vlock_process_error_quark(void)
28 return g_quark_from_static_string("vlock-process-error-quark");
31 /* Do nothing. */
32 static void ignore_sigalarm(int __attribute__((unused)) signum)
36 bool wait_for_death(pid_t pid, long sec, long usec)
38 int status;
39 struct sigaction act;
40 struct sigaction oldact;
41 struct itimerval timer;
42 struct itimerval otimer;
43 bool result;
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
49 * ignored. */
50 sigemptyset(&act.sa_mask);
51 act.sa_handler = ignore_sigalarm;
52 act.sa_flags = 0;
53 sigaction(SIGALRM, &act, &oldact);
55 /* Initialize the timer. */
56 timer.it_value.tv_sec = sec;
57 timer.it_value.tv_usec = usec;
58 /* No repetition. */
59 timer.it_interval.tv_sec = 0;
60 timer.it_interval.tv_usec = 0;
62 /* Set the timer. */
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);
77 return result;
80 /* Try hard to kill the given child process. */
81 void ensure_death(pid_t pid)
83 int status;
85 switch (waitpid(pid, &status, WNOHANG)) {
86 case -1:
87 /* Not your child? */
88 return;
89 case 0:
90 /* Not dead yet. Continue. */
91 break;
92 default:
93 /* Already dead. Nothing to do. */
94 return;
97 /* Send SIGTERM. */
98 (void) kill(pid, SIGTERM);
100 /* SIGTERM handler (if any) has 500ms to finish. */
101 if (wait_for_death(pid, 0, 500000L))
102 return;
104 // Send SIGKILL. */
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
114 * given set. */
115 static void close_fds(fd_set *except_fds)
117 struct rlimit r;
118 int maxfd;
120 /* Get the maximum number of file descriptors. */
121 if (getrlimit(RLIMIT_NOFILE, &r) == 0)
122 maxfd = r.rlim_cur;
123 else
124 /* Hopefully safe default. */
125 maxfd = 1024;
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))
131 (void) close(fd);
134 static int open_devnull(void)
136 static int devnull_fd = -1;
138 if (devnull_fd < 0)
139 devnull_fd = open("/dev/null", O_RDWR);
141 return devnull_fd;
144 bool create_child(struct child_process *child, GError **error)
146 int child_errno = 0;
147 int status_pipe[2];
148 int stdin_pipe[2];
149 int stdout_pipe[2];
150 int stderr_pipe[2];
152 if (pipe(status_pipe) < 0)
153 return false;
155 (void) fcntl(status_pipe[1], F_SETFD, FD_CLOEXEC);
157 if (child->stdin_fd == REDIRECT_PIPE)
158 if (pipe(stdin_pipe) < 0) {
159 g_set_error(error,
160 VLOCK_PROCESS_ERROR,
161 VLOCK_PROCESS_ERROR_FAILED,
162 "could not open stdin pipe: %s",
163 g_strerror(errno));
164 goto stdin_pipe_failed;
167 if (child->stdout_fd == REDIRECT_PIPE)
168 if (pipe(stdout_pipe) < 0) {
169 g_set_error(error,
170 VLOCK_PROCESS_ERROR,
171 VLOCK_PROCESS_ERROR_FAILED,
172 "could not open stdout pipe: %s",
173 g_strerror(errno));
174 goto stdout_pipe_failed;
177 if (child->stderr_fd == REDIRECT_PIPE)
178 if (pipe(stderr_pipe) < 0) {
179 g_set_error(error,
180 VLOCK_PROCESS_ERROR,
181 VLOCK_PROCESS_ERROR_FAILED,
182 "could not open stderr pipe: %s",
183 g_strerror(errno));
184 goto stderr_pipe_failed;
187 child->pid = fork();
189 if (child->pid == 0) {
190 /* Child. */
191 fd_set except_fds;
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));
228 } else {
229 execv(child->path, (char *const*) child->argv);
230 (void) write(status_pipe[1], &errno, sizeof errno);
233 _exit(1);
236 if (child->pid < 0) {
237 g_set_error(error,
238 VLOCK_PROCESS_ERROR,
239 VLOCK_PROCESS_ERROR_FAILED,
240 "could not fork: %s",
241 g_strerror(errno));
242 goto fork_failed;
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) {
250 g_set_error(error,
251 VLOCK_PROCESS_ERROR,
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));
257 goto child_failed;
260 (void) close(status_pipe[0]);
262 if (child->stdin_fd == REDIRECT_PIPE) {
263 /* Write end. */
264 child->stdin_fd = stdin_pipe[1];
265 /* Read end. */
266 (void) close(stdin_pipe[0]);
269 if (child->stdout_fd == REDIRECT_PIPE) {
270 /* Read end. */
271 child->stdout_fd = stdout_pipe[0];
272 /* Write end. */
273 (void) close(stdout_pipe[1]);
276 if (child->stderr_fd == REDIRECT_PIPE) {
277 /* Read end. */
278 child->stderr_fd = stderr_pipe[0];
279 /* Write end. */
280 (void) close(stderr_pipe[1]);
283 return true;
285 child_failed:
286 fork_failed:
287 if (child->stderr_fd == REDIRECT_PIPE) {
288 (void) close(stderr_pipe[0]);
289 (void) close(stderr_pipe[1]);
292 stderr_pipe_failed:
293 if (child->stdout_fd == REDIRECT_PIPE) {
294 (void) close(stdout_pipe[0]);
295 (void) close(stdout_pipe[1]);
298 stdout_pipe_failed:
299 if (child->stdin_fd == REDIRECT_PIPE) {
300 (void) close(stdin_pipe[0]);
301 (void) close(stdin_pipe[1]);
304 stdin_pipe_failed:
305 (void) close(status_pipe[0]);
306 (void) close(status_pipe[1]);
308 return false;