/* run a command with a limited timeout
tridge@samba.org, June 2005
+ metze@samba.org, March 2006
attempt to be as portable as possible (fighting posix all the way)
*/
#include <sys/types.h>
#include <sys/wait.h>
+static pid_t child_pid;
+
static void usage(void)
{
printf("usage: timelimit <time> <command>\n");
+ printf(" SIGUSR1 - passes SIGTERM to command's process group\n");
+ printf(" SIGALRM - passes SIGTERM to command's process group\n");
+ printf(" after 5s SIGKILL will be passed and exit(1)\n");
+ printf(" SIGTERM - passes SIGTERM to command's process group\n");
+ printf(" after 1s SIGKILL will be passed and exit(1)\n");
+ printf(" SIGINT - handled like SIGTERM\n");
+ printf(" SIGQUIT - handled like SIGTERM\n");
}
-static void sig_alrm(int sig)
+static void sig_alrm_kill(int sig)
{
fprintf(stderr, "\nMaximum time expired in timelimit - killing\n");
- kill(0, SIGKILL);
+ kill(-child_pid, SIGKILL);
exit(1);
}
+static void sig_alrm_term(int sig)
+{
+ fprintf(stderr, "\nMaximum time expired in timelimit - sending TERM To child\n");
+ kill(-child_pid, SIGTERM);
+ signal(SIGALRM, sig_alrm_kill);
+ alarm(5);
+}
+
+static void sig_term(int sig)
+{
+ fprintf(stderr, "\nReceived TERM/INT/QUIT signal\n");
+ kill(-child_pid, SIGTERM);
+ signal(SIGALRM, sig_alrm_kill);
+ alarm(1);
+}
+
+static void sig_usr1(int sig)
+{
+ kill(-child_pid, SIGTERM);
+}
+
+static void new_process_group(void)
+{
+ if (setpgid(0,0) == -1) {
+ perror("setpgid");
+ exit(1);
+ }
+}
+
+
int main(int argc, char *argv[])
{
int maxtime, ret=1;
exit(1);
}
- if (setpgrp() == -1) {
- perror("setpgrp");
- exit(1);
- }
-
maxtime = atoi(argv[1]);
- signal(SIGALRM, sig_alrm);
- alarm(maxtime);
- if (fork() == 0) {
+ child_pid = fork();
+ if (child_pid == 0) {
+ new_process_group();
execvp(argv[2], argv+2);
perror(argv[2]);
exit(1);
}
+ signal(SIGTERM, sig_term);
+ signal(SIGINT, sig_term);
+ signal(SIGQUIT, sig_term);
+ signal(SIGUSR1, sig_usr1);
+ signal(SIGALRM, sig_alrm_term);
+ alarm(maxtime);
+
do {
int status;
pid_t pid = wait(&status);
if (pid != -1) {
- ret = WEXITSTATUS(status);
+ if (WIFEXITED(status)) {
+ ret = WEXITSTATUS(status);
+ }
} else if (errno == ECHILD) {
break;
}
} while (1);
+ kill(-child_pid, SIGKILL);
+
exit(ret);
}