Annotation of src/usr.bin/timeout/timeout.c, Revision 1.7
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.4 job 102: char *ep;
103: int i;
104: long sig;
105: const char *errstr;
1.1 job 106:
107: if (strncasecmp(str, "SIG", 3) == 0) {
108: str += 3;
109:
110: for (i = 1; i < NSIG; i++) {
111: if (strcasecmp(str, sys_signame[i]) == 0)
112: return (i);
113: }
114:
115: goto err;
116: }
117:
118: errno = 0;
1.4 job 119: sig = strtonum(str, LONG_MIN, LONG_MAX, &errstr);
120: if (errstr != NULL)
1.1 job 121: goto err;
122: if (sig >= NSIG || sig < 0)
123: goto err;
124:
125: return (int)sig;
126:
127: err:
1.2 job 128: err(1, "invalid signal");
1.1 job 129: }
130:
131: static void
132: sig_handler(int signo)
133: {
134: if (sig_ign != 0 && signo == sig_ign) {
135: sig_ign = 0;
136: return;
137: }
138:
1.7 ! job 139: switch (signo) {
1.1 job 140: case SIGINT:
141: case SIGHUP:
142: case SIGQUIT:
143: case SIGTERM:
144: sig_term = signo;
145: break;
146: case SIGCHLD:
147: sig_chld = 1;
148: break;
149: case SIGALRM:
150: sig_alrm = 1;
151: break;
152: }
153: }
154:
155: static void
156: set_interval(double iv)
157: {
158: struct itimerval tim;
159:
160: memset(&tim, 0, sizeof(tim));
161: tim.it_value.tv_sec = (time_t)iv;
162: iv -= (double)tim.it_value.tv_sec;
163: tim.it_value.tv_usec = (suseconds_t)(iv * 1000000UL);
164:
165: if (setitimer(ITIMER_REAL, &tim, NULL) == -1)
1.2 job 166: err(1, "setitimer()");
1.1 job 167: }
168:
169: int
170: main(int argc, char **argv)
171: {
172: int ch;
173: unsigned long i;
174: int foreground, preserve;
175: int error, pstat, status;
176: int killsig = SIGTERM;
177: pid_t pgid, pid, cpid;
178: double first_kill;
179: double second_kill;
180: bool timedout = false;
181: bool do_second_kill = false;
182: struct sigaction signals;
1.5 job 183: int signums[] = {-1, SIGTERM, SIGINT, SIGHUP, SIGCHLD,
184: SIGALRM, SIGQUIT};
1.1 job 185:
186: const struct option longopts[] = {
187: { "preserve-status", no_argument, &preserve, 1 },
188: { "foreground", no_argument, &foreground, 1 },
189: { "kill-after", required_argument, NULL, 'k'},
190: { "signal", required_argument, NULL, 's'},
191: { "help", no_argument, NULL, 'h'},
192: { NULL, 0, NULL, 0 }
193: };
194:
1.3 job 195: if (pledge("stdio proc exec", NULL) == -1)
196: err(1, "pledge");
197:
198: foreground = preserve = 0;
199: second_kill = 0;
200: cpid = -1;
201: pgid = -1;
202:
1.1 job 203: while ((ch = getopt_long(argc, argv, "+k:s:h", longopts, NULL)) != -1) {
204: switch (ch) {
205: case 'k':
206: do_second_kill = true;
207: second_kill = parse_duration(optarg);
208: break;
209: case 's':
210: killsig = parse_signal(optarg);
211: break;
212: case 0:
213: break;
214: default:
215: usage();
216: break;
217: }
218: }
219:
220: argc -= optind;
221: argv += optind;
222:
223: if (argc < 2)
224: usage();
225:
226: first_kill = parse_duration(argv[0]);
227: argc--;
228: argv++;
229:
230: if (!foreground) {
1.7 ! job 231: pgid = setpgid(0, 0);
1.1 job 232:
233: if (pgid == -1)
1.2 job 234: err(1, "setpgid()");
1.1 job 235: }
236:
237: memset(&signals, 0, sizeof(signals));
238: sigemptyset(&signals.sa_mask);
239:
240: if (killsig != SIGKILL && killsig != SIGSTOP)
241: signums[0] = killsig;
242:
1.7 ! job 243: for (i = 0; i < sizeof(signums) / sizeof(signums[0]); i++)
1.1 job 244: sigaddset(&signals.sa_mask, signums[i]);
245:
246: signals.sa_handler = sig_handler;
247: signals.sa_flags = SA_RESTART;
248:
1.7 ! job 249: for (i = 0; i < sizeof(signums) / sizeof(signums[0]); i++) {
1.1 job 250: if (signums[i] != -1 && signums[i] != 0 &&
251: sigaction(signums[i], &signals, NULL) == -1)
1.2 job 252: err(1, "sigaction()");
1.1 job 253: }
254:
255: signal(SIGTTIN, SIG_IGN);
256: signal(SIGTTOU, SIG_IGN);
257:
258: pid = fork();
259: if (pid == -1)
1.2 job 260: err(1, "fork()");
1.1 job 261: else if (pid == 0) {
262: /* child process */
263: signal(SIGTTIN, SIG_DFL);
264: signal(SIGTTOU, SIG_DFL);
265:
266: error = execvp(argv[0], argv);
267: if (error == -1)
1.2 job 268: err(1, "exec()");
1.1 job 269: }
1.3 job 270:
271: if (pledge("stdio", NULL) == -1)
272: err(1, "pledge");
1.1 job 273:
274: if (sigprocmask(SIG_BLOCK, &signals.sa_mask, NULL) == -1)
1.2 job 275: err(1, "sigprocmask()");
1.1 job 276:
277: /* parent continues here */
278: set_interval(first_kill);
279:
280: for (;;) {
281: sigemptyset(&signals.sa_mask);
282: sigsuspend(&signals.sa_mask);
283:
284: if (sig_chld) {
285: sig_chld = 0;
286: while (((cpid = wait(&status)) < 0) && errno == EINTR)
287: continue;
288:
289: if (cpid == pid) {
290: pstat = status;
291: break;
292: }
293: } else if (sig_alrm) {
294: sig_alrm = 0;
295:
296: timedout = true;
297: if (!foreground)
298: killpg(pgid, killsig);
299: else
300: kill(pid, killsig);
301:
302: if (do_second_kill) {
303: set_interval(second_kill);
304: second_kill = 0;
305: sig_ign = killsig;
306: killsig = SIGKILL;
307: } else
308: break;
309:
310: } else if (sig_term) {
311: if (!foreground)
312: killpg(pgid, killsig);
313: else
314: kill(pid, (int)sig_term);
315:
316: if (do_second_kill) {
317: set_interval(second_kill);
318: second_kill = 0;
319: sig_ign = killsig;
320: killsig = SIGKILL;
321: } else
322: break;
323: }
324: }
325:
1.7 ! job 326: while (cpid != pid && wait(&pstat) == -1) {
1.1 job 327: if (errno != EINTR)
1.2 job 328: err(1, "waitpid()");
1.1 job 329: }
330:
331: if (WEXITSTATUS(pstat))
332: pstat = WEXITSTATUS(pstat);
1.7 ! job 333: else if (WIFSIGNALED(pstat))
1.1 job 334: pstat = 128 + WTERMSIG(pstat);
335:
336: if (timedout && !preserve)
337: pstat = EXIT_TIMEOUT;
338:
339: return (pstat);
340: }