add binary timeout itself
[grml-terminalserver.git] / timeout.c
1 /*++
2 /* NAME
3 /*      timeout 1
4 /* SUMMARY
5 /*      run command with bounded time
6 /* SYNOPSIS
7 /*      \fBtimeout\fR [-\fIsignal\fR] \fItime\fR \fIcommand\fR ...
8 /* DESCRIPTION
9 /*      \fBtimeout\fR executes a command and imposes an elapsed time limit.
10 /*      The command is run in a separate POSIX process group so that the
11 /*      right thing happens with commands that spawn child processes.
12 /*
13 /*      Arguments:
14 /* .IP \fI-signal\fR
15 /*      Specify an optional signal to send to the controlled process.
16 /*      By default, \fBtimeout\fR sends SIGKILL, which cannot be caught
17 /*      or ignored.
18 /* .IP \fItime\fR
19 /*      The elapsed time limit after which the command is terminated.
20 /* .IP \fIcommand\fR
21 /*      The command to be executed.
22 /* DIAGNOSTICS
23 /*      The command exit status is the exit status of the command
24 /*      (status 1 in case of a usage error).
25 /* AUTHOR(S)
26 /*      Wietse Venema
27 /*      This program is part of SATAN.
28 /*--*/
29
30 /* System libraries. */
31
32 #include <sys/types.h>
33 #include <signal.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <stdio.h>
37
38 extern int optind;
39
40 /* Application-specific. */
41
42 #define perrorexit(s) { perror(s); exit(1); }
43
44 static int kill_signal = SIGKILL;
45 static char *progname;
46 static char *commandname;
47
48 static void usage()
49 {
50     fprintf(stderr, "usage: %s [-signal] time command...\n", progname);
51     exit(1);
52 }
53
54 static void terminate(sig)
55 int     sig;
56 {
57     signal(kill_signal, SIG_DFL);
58     fprintf(stderr, "Timeout: aborting command ``%s'' with signal %d\n",
59             commandname, kill_signal);
60     kill(0, kill_signal);
61 }
62
63 int     main(argc, argv)
64 int     argc;
65 char  **argv;
66 {
67     int     time_to_run;
68     pid_t   pid;
69     pid_t   child_pid;
70     int     status;
71
72     progname = argv[0];
73
74     /*
75      * Parse JCL.
76      */
77     while (--argc && *++argv && **argv == '-')
78         if ((kill_signal = atoi(*argv + 1)) <= 0)
79             usage();
80
81     if (argc < 2 || (time_to_run = atoi(argv[0])) <= 0)
82         usage();
83
84     commandname = argv[1];
85
86     /*
87      * Run the command and its watchdog in a separate process group so that
88      * both can be killed off with one signal.
89      */
90     setsid();
91     switch (child_pid = fork()) {
92     case -1:                                    /* error */
93         perrorexit("timeout: fork");
94     case 00:                                    /* run controlled command */
95         execvp(argv[1], argv + 1);
96         perrorexit(argv[1]);
97     default:                                    /* become watchdog */
98         (void) signal(SIGHUP, terminate);
99         (void) signal(SIGINT, terminate);
100         (void) signal(SIGQUIT, terminate);
101         (void) signal(SIGTERM, terminate);
102         (void) signal(SIGALRM, terminate);
103         alarm(time_to_run);
104         while ((pid = wait(&status)) != -1 && pid != child_pid)
105              /* void */ ;
106         return (pid == child_pid ? status : -1);
107     }
108 }