Annotation of src/usr.bin/timeout/timeout.c, Revision 1.9
1.1 job 1: /* $NetBSD: timeout.c,v 1.4 2014/08/05 08:20:02 christos Exp $ */
2:
3: /*-
4: * Copyright (c) 2014 Baptiste Daroussin <bapt@FreeBSD.org>
5: * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
6: * All rights reserved.
7: *
8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions
10: * are met:
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer
13: * in this position and unchanged.
14: * 2. Redistributions in binary form must reproduce the above copyright
15: * notice, this list of conditions and the following disclaimer in the
16: * documentation and/or other materials provided with the distribution.
17: *
18: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
19: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21: * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
22: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28: */
29:
1.2 job 30: #include <sys/types.h>
1.1 job 31: #include <sys/time.h>
32: #include <sys/wait.h>
33:
34: #include <err.h>
35: #include <errno.h>
36: #include <getopt.h>
37: #include <limits.h>
38: #include <signal.h>
39: #include <stdbool.h>
40: #include <stdio.h>
41: #include <stdlib.h>
42: #include <string.h>
43: #include <unistd.h>
44:
45: #define EXIT_TIMEOUT 124
46:
47: static sig_atomic_t sig_chld = 0;
48: static sig_atomic_t sig_term = 0;
49: static sig_atomic_t sig_alrm = 0;
50: static sig_atomic_t sig_ign = 0;
51:
52: static void __dead
53: usage(void)
54: {
1.2 job 55: fprintf(stderr, "usage: timeout [-s sig] [-k time] [--preserve-status]"
56: " [--foreground] duration command\n");
1.1 job 57:
1.2 job 58: exit(1);
1.1 job 59: }
60:
61: static double
62: parse_duration(const char *duration)
63: {
64: double ret;
65: char *end;
66:
67: ret = strtod(duration, &end);
68: if (ret == 0 && end == duration)
1.2 job 69: err(1, "invalid duration");
1.1 job 70:
71: if (end == NULL || *end == '\0')
72: return (ret);
73:
74: if (end != NULL && *(end + 1) != '\0')
1.2 job 75: err(1, "invalid duration");
1.1 job 76:
77: switch (*end) {
78: case 's':
79: break;
80: case 'm':
81: ret *= 60;
82: break;
83: case 'h':
84: ret *= 60 * 60;
85: break;
86: case 'd':
87: ret *= 60 * 60 * 24;
88: break;
89: default:
1.2 job 90: err(1, "invalid duration");
1.1 job 91: }
92:
93: if (ret < 0 || ret >= 100000000UL)
1.2 job 94: err(1, "invalid duration");
1.1 job 95:
96: return (ret);
97: }
98:
99: static int
100: parse_signal(const char *str)
101: {
1.9 ! deraadt 102: long long sig;
1.4 job 103: const char *errstr;
1.1 job 104:
105: if (strncasecmp(str, "SIG", 3) == 0) {
1.9 ! deraadt 106: int i;
! 107:
1.1 job 108: str += 3;
109: for (i = 1; i < NSIG; i++) {
110: if (strcasecmp(str, sys_signame[i]) == 0)
111: return (i);
112: }
1.8 deraadt 113: err(1, "invalid signal name");
1.1 job 114: }
115:
1.8 deraadt 116: sig = strtonum(str, 1, NSIG, &errstr);
1.4 job 117: if (errstr != NULL)
1.8 deraadt 118: err(1, "signal %s %s", str, errstr);
1.1 job 119:
120: return (int)sig;
121: }
122:
123: static void
124: sig_handler(int signo)
125: {
126: if (sig_ign != 0 && signo == sig_ign) {
127: sig_ign = 0;
128: return;
129: }
130:
1.7 job 131: switch (signo) {
1.1 job 132: case SIGINT:
133: case SIGHUP:
134: case SIGQUIT:
135: case SIGTERM:
136: sig_term = signo;
137: break;
138: case SIGCHLD:
139: sig_chld = 1;
140: break;
141: case SIGALRM:
142: sig_alrm = 1;
143: break;
144: }
145: }
146:
147: static void
148: set_interval(double iv)
149: {
150: struct itimerval tim;
151:
152: memset(&tim, 0, sizeof(tim));
153: tim.it_value.tv_sec = (time_t)iv;
154: iv -= (double)tim.it_value.tv_sec;
155: tim.it_value.tv_usec = (suseconds_t)(iv * 1000000UL);
156:
157: if (setitimer(ITIMER_REAL, &tim, NULL) == -1)
1.2 job 158: err(1, "setitimer()");
1.1 job 159: }
160:
161: int
162: main(int argc, char **argv)
163: {
164: int ch;
165: unsigned long i;
1.9 ! deraadt 166: int foreground = 0, preserve = 0;
1.1 job 167: int error, pstat, status;
168: int killsig = SIGTERM;
1.9 ! deraadt 169: pid_t pgid = 0, pid, cpid = 0;
1.1 job 170: double first_kill;
1.9 ! deraadt 171: double second_kill = 0;
1.1 job 172: bool timedout = false;
173: bool do_second_kill = false;
174: struct sigaction signals;
1.5 job 175: int signums[] = {-1, SIGTERM, SIGINT, SIGHUP, SIGCHLD,
176: SIGALRM, SIGQUIT};
1.1 job 177:
178: const struct option longopts[] = {
179: { "preserve-status", no_argument, &preserve, 1 },
180: { "foreground", no_argument, &foreground, 1 },
181: { "kill-after", required_argument, NULL, 'k'},
182: { "signal", required_argument, NULL, 's'},
183: { "help", no_argument, NULL, 'h'},
184: { NULL, 0, NULL, 0 }
185: };
186:
1.3 job 187: if (pledge("stdio proc exec", NULL) == -1)
188: err(1, "pledge");
189:
1.1 job 190: while ((ch = getopt_long(argc, argv, "+k:s:h", longopts, NULL)) != -1) {
191: switch (ch) {
192: case 'k':
193: do_second_kill = true;
194: second_kill = parse_duration(optarg);
195: break;
196: case 's':
197: killsig = parse_signal(optarg);
198: break;
199: case 0:
200: break;
201: default:
202: usage();
203: break;
204: }
205: }
206:
207: argc -= optind;
208: argv += optind;
209:
210: if (argc < 2)
211: usage();
212:
213: first_kill = parse_duration(argv[0]);
214: argc--;
215: argv++;
216:
217: if (!foreground) {
1.7 job 218: pgid = setpgid(0, 0);
1.1 job 219:
220: if (pgid == -1)
1.2 job 221: err(1, "setpgid()");
1.1 job 222: }
223:
224: memset(&signals, 0, sizeof(signals));
225: sigemptyset(&signals.sa_mask);
226:
227: if (killsig != SIGKILL && killsig != SIGSTOP)
228: signums[0] = killsig;
229:
1.7 job 230: for (i = 0; i < sizeof(signums) / sizeof(signums[0]); i++)
1.1 job 231: sigaddset(&signals.sa_mask, signums[i]);
232:
233: signals.sa_handler = sig_handler;
234: signals.sa_flags = SA_RESTART;
235:
1.7 job 236: for (i = 0; i < sizeof(signums) / sizeof(signums[0]); i++) {
1.1 job 237: if (signums[i] != -1 && signums[i] != 0 &&
238: sigaction(signums[i], &signals, NULL) == -1)
1.2 job 239: err(1, "sigaction()");
1.1 job 240: }
241:
242: signal(SIGTTIN, SIG_IGN);
243: signal(SIGTTOU, SIG_IGN);
244:
245: pid = fork();
246: if (pid == -1)
1.2 job 247: err(1, "fork()");
1.1 job 248: else if (pid == 0) {
249: /* child process */
250: signal(SIGTTIN, SIG_DFL);
251: signal(SIGTTOU, SIG_DFL);
252:
253: error = execvp(argv[0], argv);
254: if (error == -1)
1.2 job 255: err(1, "exec()");
1.1 job 256: }
1.3 job 257:
258: if (pledge("stdio", NULL) == -1)
259: err(1, "pledge");
1.1 job 260:
261: if (sigprocmask(SIG_BLOCK, &signals.sa_mask, NULL) == -1)
1.2 job 262: err(1, "sigprocmask()");
1.1 job 263:
264: /* parent continues here */
265: set_interval(first_kill);
266:
267: for (;;) {
268: sigemptyset(&signals.sa_mask);
269: sigsuspend(&signals.sa_mask);
270:
271: if (sig_chld) {
272: sig_chld = 0;
273: while (((cpid = wait(&status)) < 0) && errno == EINTR)
274: continue;
275:
276: if (cpid == pid) {
277: pstat = status;
278: break;
279: }
280: } else if (sig_alrm) {
281: sig_alrm = 0;
282:
283: timedout = true;
284: if (!foreground)
285: killpg(pgid, killsig);
286: else
287: kill(pid, killsig);
288:
289: if (do_second_kill) {
290: set_interval(second_kill);
291: second_kill = 0;
292: sig_ign = killsig;
293: killsig = SIGKILL;
294: } else
295: break;
296:
297: } else if (sig_term) {
298: if (!foreground)
299: killpg(pgid, killsig);
300: else
301: kill(pid, (int)sig_term);
302:
303: if (do_second_kill) {
304: set_interval(second_kill);
305: second_kill = 0;
306: sig_ign = killsig;
307: killsig = SIGKILL;
308: } else
309: break;
310: }
311: }
312:
1.7 job 313: while (cpid != pid && wait(&pstat) == -1) {
1.1 job 314: if (errno != EINTR)
1.2 job 315: err(1, "waitpid()");
1.1 job 316: }
317:
318: if (WEXITSTATUS(pstat))
319: pstat = WEXITSTATUS(pstat);
1.7 job 320: else if (WIFSIGNALED(pstat))
1.1 job 321: pstat = 128 + WTERMSIG(pstat);
322:
323: if (timedout && !preserve)
324: pstat = EXIT_TIMEOUT;
325:
326: return (pstat);
327: }