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