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