]> CyberLeo.Net >> Repos - FreeBSD/releng/10.3.git/blob - usr.bin/timeout/timeout.c
- Copy stable/10@296371 to releng/10.3 in preparation for 10.3-RC1
[FreeBSD/releng/10.3.git] / usr.bin / timeout / timeout.c
1 /*-
2  * Copyright (c) 2014 Baptiste Daroussin <bapt@FreeBSD.org>
3  * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer
11  *    in this position and unchanged.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/procctl.h>
32 #include <sys/time.h>
33 #include <sys/wait.h>
34
35 #include <err.h>
36 #include <errno.h>
37 #include <getopt.h>
38 #include <signal.h>
39 #include <stdbool.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <sysexits.h>
44 #include <unistd.h>
45
46 #define EXIT_TIMEOUT 124
47
48 static sig_atomic_t sig_chld = 0;
49 static sig_atomic_t sig_term = 0;
50 static sig_atomic_t sig_alrm = 0;
51 static sig_atomic_t sig_ign = 0;
52
53 static void
54 usage(void)
55 {
56
57         fprintf(stderr, "Usage: %s [--signal sig | -s sig] [--preserve-status]"
58             " [--kill-after time | -k time] [--foreground] <duration> <command>"
59             " <arg ...>\n", getprogname());
60
61         exit(EX_USAGE);
62 }
63
64 static double
65 parse_duration(const char *duration)
66 {
67         double ret;
68         char *end;
69
70         ret = strtod(duration, &end);
71         if (ret == 0 && end == duration)
72                 errx(125, "invalid duration");
73
74         if (end == NULL || *end == '\0')
75                 return (ret);
76
77         if (end != NULL && *(end + 1) != '\0')
78                 errx(EX_USAGE, "invalid duration");
79
80         switch (*end) {
81         case 's':
82                 break;
83         case 'm':
84                 ret *= 60;
85                 break;
86         case 'h':
87                 ret *= 60 * 60;
88                 break;
89         case 'd':
90                 ret *= 60 * 60 * 24;
91                 break;
92         default:
93                 errx(125, "invalid duration");
94         }
95
96         if (ret < 0 || ret >= 100000000UL)
97                 errx(125, "invalid duration");
98
99         return (ret);
100 }
101
102 static int
103 parse_signal(const char *str)
104 {
105         int sig, i;
106         const char *errstr;
107
108         sig = strtonum(str, 1, sys_nsig - 1, &errstr);
109
110         if (errstr == NULL)
111                 return (sig);
112         if (strncasecmp(str, "SIG", 3) == 0)
113                 str += 3;
114
115         for (i = 1; i < sys_nsig; i++) {
116                 if (strcasecmp(str, sys_signame[i]) == 0)
117                         return (i);
118         }
119
120         errx(125, "invalid signal");
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
131         switch(signo) {
132         case 0:
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 -= (time_t)iv;
156         tim.it_value.tv_usec = (suseconds_t)(iv * 1000000UL);
157
158         if (setitimer(ITIMER_REAL, &tim, NULL) == -1)
159                 err(EX_OSERR, "setitimer()");
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 pid, cpid;
171         double first_kill;
172         double second_kill;
173         bool timedout = false;
174         bool do_second_kill = false;
175         bool child_done = false;
176         struct sigaction signals;
177         struct procctl_reaper_status info;
178         struct procctl_reaper_kill killemall;
179         int signums[] = {
180                 -1,
181                 SIGTERM,
182                 SIGINT,
183                 SIGHUP,
184                 SIGCHLD,
185                 SIGALRM,
186                 SIGQUIT,
187         };
188
189         foreground = preserve = 0;
190         second_kill = 0;
191
192         const struct option longopts[] = {
193                 { "preserve-status", no_argument,       &preserve,    1 },
194                 { "foreground",      no_argument,       &foreground,  1 },
195                 { "kill-after",      required_argument, NULL,        'k'},
196                 { "signal",          required_argument, NULL,        's'},
197                 { "help",            no_argument,       NULL,        'h'},
198                 { NULL,              0,                 NULL,         0 }
199         };
200
201         while ((ch = getopt_long(argc, argv, "+k:s:h", longopts, NULL)) != -1) {
202                 switch (ch) {
203                         case 'k':
204                                 do_second_kill = true;
205                                 second_kill = parse_duration(optarg);
206                                 break;
207                         case 's':
208                                 killsig = parse_signal(optarg);
209                                 break;
210                         case 0:
211                                 break;
212                         case 'h':
213                         default:
214                                 usage();
215                                 break;
216                 }
217         }
218
219         argc -= optind;
220         argv += optind;
221
222         if (argc < 2)
223                 usage();
224
225         first_kill = parse_duration(argv[0]);
226         argc--;
227         argv++;
228
229         if (!foreground) {
230                 /* Aquire a reaper */
231                 if (procctl(P_PID, getpid(), PROC_REAP_ACQUIRE, NULL) == -1)
232                         err(EX_OSERR, "Fail to acquire the reaper");
233         }
234
235         memset(&signals, 0, sizeof(signals));
236         sigemptyset(&signals.sa_mask);
237
238         if (killsig != SIGKILL && killsig != SIGSTOP)
239                 signums[0] = killsig;
240
241         for (i = 0; i < sizeof(signums) / sizeof(signums[0]); i ++)
242                 sigaddset(&signals.sa_mask, signums[i]);
243
244         signals.sa_handler = sig_handler;
245         signals.sa_flags = SA_RESTART;
246
247         for (i = 0; i < sizeof(signums) / sizeof(signums[0]); i ++)
248                 if (signums[i] != -1 && signums[i] != 0 &&
249                     sigaction(signums[i], &signals, NULL) == -1)
250                         err(EX_OSERR, "sigaction()");
251
252         signal(SIGTTIN, SIG_IGN);
253         signal(SIGTTOU, SIG_IGN);
254
255         pid = fork();
256         if (pid == -1)
257                 err(EX_OSERR, "fork()");
258         else if (pid == 0) {
259                 /* child process */
260                 signal(SIGTTIN, SIG_DFL);
261                 signal(SIGTTOU, SIG_DFL);
262
263                 error = execvp(argv[0], argv);
264                 if (error == -1) {
265                         if (errno == ENOENT)
266                                 err(127, "exec(%s)", argv[0]);
267                         else
268                                 err(126, "exec(%s)", argv[0]);
269                 }
270         }
271
272         if (sigprocmask(SIG_BLOCK, &signals.sa_mask, NULL) == -1)
273                 err(EX_OSERR, "sigprocmask()");
274
275         /* parent continues here */
276         set_interval(first_kill);
277
278         for (;;) {
279                 sigemptyset(&signals.sa_mask);
280                 sigsuspend(&signals.sa_mask);
281
282                 if (sig_chld) {
283                         sig_chld = 0;
284
285                         while ((cpid = waitpid(-1, &status, WNOHANG)) != 0) {
286                                 if (cpid < 0) {
287                                         if (errno == EINTR)
288                                                 continue;
289                                         else
290                                                 break;
291                                 } else if (cpid == pid) {
292                                         pstat = status;
293                                         child_done = true;
294                                 }
295                         }
296                         if (child_done) {
297                                 if (foreground) {
298                                         break;
299                                 } else {
300                                         procctl(P_PID, getpid(),
301                                             PROC_REAP_STATUS, &info);
302                                         if (info.rs_children == 0)
303                                                 break;
304                                 }
305                         }
306                 } else if (sig_alrm) {
307                         sig_alrm = 0;
308
309                         timedout = true;
310                         if (!foreground) {
311                                 killemall.rk_sig = killsig;
312                                 killemall.rk_flags = 0;
313                                 procctl(P_PID, getpid(), PROC_REAP_KILL,
314                                     &killemall);
315                         } else
316                                 kill(pid, killsig);
317
318                         if (do_second_kill) {
319                                 set_interval(second_kill);
320                                 second_kill = 0;
321                                 sig_ign = killsig;
322                                 killsig = SIGKILL;
323                         } else
324                                 break;
325
326                 } else if (sig_term) {
327                         if (!foreground) {
328                                 killemall.rk_sig = sig_term;
329                                 killemall.rk_flags = 0;
330                                 procctl(P_PID, getpid(), PROC_REAP_KILL,
331                                     &killemall);
332                         } else
333                                 kill(pid, sig_term);
334
335                         if (do_second_kill) {
336                                 set_interval(second_kill);
337                                 second_kill = 0;
338                                 sig_ign = killsig;
339                                 killsig = SIGKILL;
340                         } else
341                                 break;
342                 }
343         }
344
345         while (!child_done && wait(&pstat) == -1) {
346                 if (errno != EINTR)
347                         err(EX_OSERR, "waitpid()");
348         }
349
350         if (!foreground)
351                 procctl(P_PID, getpid(), PROC_REAP_RELEASE, NULL);
352
353         if (WEXITSTATUS(pstat))
354                 pstat = WEXITSTATUS(pstat);
355         else if(WIFSIGNALED(pstat))
356                 pstat = 128 + WTERMSIG(pstat);
357
358         if (timedout && !preserve)
359                 pstat = EXIT_TIMEOUT;
360
361         return (pstat);
362 }