1 /*-
2 * Copyright (c) 2014 Baptiste Daroussin <[email protected]>
3 * Copyright (c) 2014 Vsevolod Stakhov <[email protected]>
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 #include <sys/procctl.h>
30 #include <sys/time.h>
31 #include <sys/wait.h>
32
33 #include <err.h>
34 #include <errno.h>
35 #include <getopt.h>
36 #include <signal.h>
37 #include <stdbool.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42
43 #define EXIT_TIMEOUT 124
44
45 static sig_atomic_t sig_chld = 0;
46 static sig_atomic_t sig_term = 0;
47 static sig_atomic_t sig_alrm = 0;
48 static sig_atomic_t sig_ign = 0;
49
50 static void
usage(void)51 usage(void)
52 {
53
54 fprintf(stderr, "Usage: %s [--signal sig | -s sig] [--preserve-status]"
55 " [--kill-after time | -k time] [--foreground] <duration> <command>"
56 " <arg ...>\n", getprogname());
57
58 exit(EXIT_FAILURE);
59 }
60
61 static double
parse_duration(const char * duration)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)
69 errx(125, "invalid duration");
70
71 if (end == NULL || *end == '\0')
72 return (ret);
73
74 if (end != NULL && *(end + 1) != '\0')
75 errx(125, "invalid duration");
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:
90 errx(125, "invalid duration");
91 }
92
93 if (ret < 0 || ret >= 100000000UL)
94 errx(125, "invalid duration");
95
96 return (ret);
97 }
98
99 static int
parse_signal(const char * str)100 parse_signal(const char *str)
101 {
102 int sig, i;
103 const char *errstr;
104
105 sig = strtonum(str, 1, sys_nsig - 1, &errstr);
106
107 if (errstr == NULL)
108 return (sig);
109 if (strncasecmp(str, "SIG", 3) == 0)
110 str += 3;
111
112 for (i = 1; i < sys_nsig; i++) {
113 if (strcasecmp(str, sys_signame[i]) == 0)
114 return (i);
115 }
116
117 errx(125, "invalid signal");
118 }
119
120 static void
sig_handler(int signo)121 sig_handler(int signo)
122 {
123 if (sig_ign != 0 && signo == sig_ign) {
124 sig_ign = 0;
125 return;
126 }
127
128 switch (signo) {
129 case 0:
130 case SIGINT:
131 case SIGHUP:
132 case SIGQUIT:
133 case SIGTERM:
134 sig_term = signo;
135 break;
136 case SIGCHLD:
137 sig_chld = 1;
138 break;
139 case SIGALRM:
140 sig_alrm = 1;
141 break;
142 }
143 }
144
145 static void
set_interval(double iv)146 set_interval(double iv)
147 {
148 struct itimerval tim;
149
150 memset(&tim, 0, sizeof(tim));
151 tim.it_value.tv_sec = (time_t)iv;
152 iv -= (time_t)iv;
153 tim.it_value.tv_usec = (suseconds_t)(iv * 1000000UL);
154
155 if (setitimer(ITIMER_REAL, &tim, NULL) == -1)
156 err(EXIT_FAILURE, "setitimer()");
157 }
158
159 int
main(int argc,char ** argv)160 main(int argc, char **argv)
161 {
162 int ch;
163 unsigned long i;
164 int foreground, preserve;
165 int error, pstat, status;
166 int killsig = SIGTERM;
167 pid_t pid, cpid;
168 double first_kill;
169 double second_kill;
170 bool timedout = false;
171 bool do_second_kill = false;
172 bool child_done = false;
173 struct sigaction signals;
174 struct procctl_reaper_status info;
175 struct procctl_reaper_kill killemall;
176 int signums[] = {
177 -1,
178 SIGTERM,
179 SIGINT,
180 SIGHUP,
181 SIGCHLD,
182 SIGALRM,
183 SIGQUIT,
184 };
185
186 foreground = preserve = 0;
187 second_kill = 0;
188
189 const struct option longopts[] = {
190 { "preserve-status", no_argument, &preserve, 1 },
191 { "foreground", no_argument, &foreground, 1 },
192 { "kill-after", required_argument, NULL, 'k'},
193 { "signal", required_argument, NULL, 's'},
194 { "help", no_argument, NULL, 'h'},
195 { NULL, 0, NULL, 0 }
196 };
197
198 while ((ch = getopt_long(argc, argv, "+k:s:h", longopts, NULL)) != -1) {
199 switch (ch) {
200 case 'k':
201 do_second_kill = true;
202 second_kill = parse_duration(optarg);
203 break;
204 case 's':
205 killsig = parse_signal(optarg);
206 break;
207 case 0:
208 break;
209 case 'h':
210 default:
211 usage();
212 break;
213 }
214 }
215
216 argc -= optind;
217 argv += optind;
218
219 if (argc < 2)
220 usage();
221
222 first_kill = parse_duration(argv[0]);
223 argc--;
224 argv++;
225
226 if (!foreground) {
227 /* Acquire a reaper */
228 if (procctl(P_PID, getpid(), PROC_REAP_ACQUIRE, NULL) == -1)
229 err(EXIT_FAILURE, "Fail to acquire the reaper");
230 }
231
232 memset(&signals, 0, sizeof(signals));
233 sigemptyset(&signals.sa_mask);
234
235 if (killsig != SIGKILL && killsig != SIGSTOP)
236 signums[0] = killsig;
237
238 for (i = 0; i < sizeof(signums) / sizeof(signums[0]); i++)
239 sigaddset(&signals.sa_mask, signums[i]);
240
241 signals.sa_handler = sig_handler;
242 signals.sa_flags = SA_RESTART;
243
244 for (i = 0; i < sizeof(signums) / sizeof(signums[0]); i++)
245 if (signums[i] != -1 && signums[i] != 0 &&
246 sigaction(signums[i], &signals, NULL) == -1)
247 err(EXIT_FAILURE, "sigaction()");
248
249 signal(SIGTTIN, SIG_IGN);
250 signal(SIGTTOU, SIG_IGN);
251
252 pid = fork();
253 if (pid == -1)
254 err(EXIT_FAILURE, "fork()");
255 else if (pid == 0) {
256 /* child process */
257 signal(SIGTTIN, SIG_DFL);
258 signal(SIGTTOU, SIG_DFL);
259
260 error = execvp(argv[0], argv);
261 if (error == -1) {
262 if (errno == ENOENT)
263 err(127, "exec(%s)", argv[0]);
264 else
265 err(126, "exec(%s)", argv[0]);
266 }
267 }
268
269 if (sigprocmask(SIG_BLOCK, &signals.sa_mask, NULL) == -1)
270 err(EXIT_FAILURE, "sigprocmask()");
271
272 /* parent continues here */
273 set_interval(first_kill);
274
275 for (;;) {
276 sigemptyset(&signals.sa_mask);
277 sigsuspend(&signals.sa_mask);
278
279 if (sig_chld) {
280 sig_chld = 0;
281
282 while ((cpid = waitpid(-1, &status, WNOHANG)) != 0) {
283 if (cpid < 0) {
284 if (errno == EINTR)
285 continue;
286 else
287 break;
288 } else if (cpid == pid) {
289 pstat = status;
290 child_done = true;
291 }
292 }
293 if (child_done) {
294 if (foreground) {
295 break;
296 } else {
297 procctl(P_PID, getpid(),
298 PROC_REAP_STATUS, &info);
299 if (info.rs_children == 0)
300 break;
301 }
302 }
303 } else if (sig_alrm) {
304 sig_alrm = 0;
305
306 timedout = true;
307 if (!foreground) {
308 killemall.rk_sig = killsig;
309 killemall.rk_flags = 0;
310 procctl(P_PID, getpid(), PROC_REAP_KILL,
311 &killemall);
312 } else
313 kill(pid, killsig);
314
315 if (do_second_kill) {
316 set_interval(second_kill);
317 second_kill = 0;
318 sig_ign = killsig;
319 killsig = SIGKILL;
320 } else
321 break;
322
323 } else if (sig_term) {
324 if (!foreground) {
325 killemall.rk_sig = sig_term;
326 killemall.rk_flags = 0;
327 procctl(P_PID, getpid(), PROC_REAP_KILL,
328 &killemall);
329 } else
330 kill(pid, sig_term);
331
332 if (do_second_kill) {
333 set_interval(second_kill);
334 second_kill = 0;
335 sig_ign = killsig;
336 killsig = SIGKILL;
337 } else
338 break;
339 }
340 }
341
342 while (!child_done && wait(&pstat) == -1) {
343 if (errno != EINTR)
344 err(EXIT_FAILURE, "waitpid()");
345 }
346
347 if (!foreground)
348 procctl(P_PID, getpid(), PROC_REAP_RELEASE, NULL);
349
350 if (WEXITSTATUS(pstat))
351 pstat = WEXITSTATUS(pstat);
352 else if (WIFSIGNALED(pstat))
353 pstat = 128 + WTERMSIG(pstat);
354
355 if (timedout && !preserve)
356 pstat = EXIT_TIMEOUT;
357
358 return (pstat);
359 }
360