#include #include #include #include #include #include struct slist { char *name; int num; } sigs[] = { #ifdef SIGHUP { "HUP", SIGHUP}, #endif #ifdef SIGINT { "INT", SIGINT}, #endif #ifdef SIGQUIT { "QUIT", SIGQUIT}, #endif #ifdef SIGILL { "ILL", SIGILL}, #endif #ifdef SIGTRAP { "TRAP", SIGTRAP}, #endif #ifdef SIGABRT { "ABRT", SIGABRT}, #endif #ifdef SIGIOT { "IOT", SIGIOT}, #endif #ifdef SIGEMT { "EMT", SIGEMT}, #endif #ifdef SIGFPE { "FPE", SIGFPE}, #endif #ifdef SIGKILL { "KILL", SIGKILL}, #endif #ifdef SIGBUS { "BUS", SIGBUS}, #endif #ifdef SIGSEGV { "SEGV", SIGSEGV}, #endif #ifdef SIGSYS { "SYS", SIGSYS}, #endif #ifdef SIGPIPE { "PIPE", SIGPIPE}, #endif #ifdef SIGALRM { "ALRM", SIGALRM}, #endif #ifdef SIGTERM { "TERM", SIGTERM}, #endif #ifdef SIGURG { "URG", SIGURG}, #endif #ifdef SIGSTOP { "STOP", SIGSTOP}, #endif #ifdef SIGTSTP { "TSTP", SIGTSTP}, #endif #ifdef SIGCONT { "CONT", SIGCONT}, #endif #ifdef SIGCHLD { "CHLD", SIGCHLD}, #endif #ifdef SIGCLD { "CLD", SIGCLD}, #endif #ifdef SIGTTIN { "TTIN", SIGTTIN}, #endif #ifdef SIGTTOU { "TTOU", SIGTTOU}, #endif #ifdef SIGIO { "IO", SIGIO}, #endif #ifdef SIGXCPU { "XCPU", SIGXCPU}, #endif #ifdef SIGXFSZ { "XFSZ", SIGXFSZ}, #endif #ifdef SIGVTALRM { "VTALRM", SIGVTALRM}, #endif #ifdef SIGPROF { "PROF", SIGPROF}, #endif #ifdef SIGWINCH { "WINCH", SIGWINCH}, #endif #ifdef SIGINFO { "INFO", SIGINFO}, #endif #ifdef SIGUSR1 { "USR1", SIGUSR1}, #endif #ifdef SIGUSR2 { "USR2", SIGUSR2}, #endif #ifdef SIGPWR { "PWR", SIGPWR}, #endif { NULL, 0} }; void to_kill (); void usage (); int verbose = 0; int proc_group = 0; int kill_signal = SIGTERM; pid_t pid; int i; int main (int argc, char **argv) { int c; extern char *optarg; extern int optind; char *sflag = NULL; char *tflag = NULL; struct sigaction sact; struct sigaction *osact = NULL; int status; while ((c = getopt (argc, argv, "s:t:vgk")) != EOF) { switch (c) { case 'v': verbose = 1; break; case 'g': proc_group = 1; break; case 's': sflag = optarg; break; case 't': tflag = optarg; break; case '?': usage (*argv); exit (1); } } if (tflag == NULL) { tflag = *(argv + optind); optind++; } if (tflag == NULL || !isdigit (*tflag)) { usage (*argv); exit (1); } if (sflag) { if (isdigit (*sflag)) { kill_signal = atoi (sflag); } else { for (c = 0; sigs[c].name; c++) { if (!strcmp (sigs[c].name, sflag)) { kill_signal = sigs[c].num; break; } } if (sigs[c].name == NULL) { fprintf (stderr, "\n%s: Unknown signal %s\n", *argv, sflag); usage (*argv); exit (1); } } } if (optind > argc - 1) { usage (*argv); exit (1); } sact.sa_handler = to_kill; sact.sa_flags = SA_RESTART; sigemptyset (&sact.sa_mask); pid = fork (); if (pid < 0) { fprintf (stderr, "%s: fork failed: ", *(argv + optind)); perror (""); exit (1); } if (pid == (pid_t) 0) { if (proc_group) { setpgrp (); } execvp (argv[optind], argv + optind); fprintf (stderr, "%s: can't execute: ", *(argv + optind)); perror (""); exit (1); } if (sigaction (SIGALRM, &sact, osact) < 0) { fprintf (stderr, "can not set signal handler for SIGTALRM.\n"); exit (1); } alarm (atoi (tflag)); waitpid (pid, &status, 0); alarm (0); /* disable alarm */ if (WIFEXITED (status)) { if (verbose) { fprintf (stderr, "normal termination, exit status = %d.\n", WEXITSTATUS (status)); } exit (WEXITSTATUS (status)); } else if (WIFSIGNALED (status)) { if (verbose) { fprintf (stderr, "abnormal termination, signal number = %d%s.\n", WTERMSIG (status), WCOREDUMP (status) ? " (core file generated)" : ""); /* #ifdef HAVE_STRSIGNAL * fprintf ( stderr, " (%s)", strsignal(signal_status) ); * #endif */ } else if (WIFSTOPPED (status)) { fprintf (stderr, "child stopped, signal number = %d.\n", WSTOPSIG (status)); exit (0); } exit (128 + WTERMSIG (status)); } } void to_kill () { if (verbose) { fprintf (stderr, "kill with signal %d.\n", kill_signal); } if (proc_group) { kill (-pid, kill_signal); } else { kill (pid, kill_signal); } } void usage (char *av) { fprintf (stderr, "\n"); fprintf (stderr, "Usage: %s [-s signal] [-v] [-g] -t program [args] - OR -\n", av); fprintf (stderr, " %s [-s signal] [-v] [-g] program [args]\n\n", av); fprintf (stderr, "You can use a numerical signal, or one of these (signal 0 has no effect):\n"); for (i = 0; sigs[i].name; i++) { if (i % 8 == 0) { fprintf (stderr, "\n"); } fprintf (stderr, "\t%s", sigs[i].name); } fprintf (stderr, "\n"); fprintf (stderr, "\n-v: print verbose message.\n"); fprintf (stderr, "-g: kill process group.\n"); fprintf (stderr, "\n"); } /* ## POD_START ## =head1 NAME ## ## B - Send a signal to a program after a certain time. ## ## =head1 SYNOPSIS ## ## B ## [-s I] ## [-v] ## [-g] ## -t I | I ## program [args] ## ## =head1 DESCRIPTION ## ## B executes a program (with arguments args) and sends a signal to ## it after a certain amount of I. ## ## =head1 OPTIONS ## ## =head2 -s signal ## ## Signal to send to the spawned process. This can be a numerical ## or symbolic ID. This defaults to TERM. ## ## The default is TERM followed by KILL. ## ## =head2 -t I | I ## ## Number of seconds timeout waits for the program before sending a signal. ## You can use either -t I or just I without -t. The latter ## is for compatibility with Linux timeout program in I package. ## ## =head2 -g ## ## Send signal to all processes in process group. If the program spawns ## new processes, all the processes in the group will be killed. Please ## see the example in EXAMPLES section below to decide when to use and not ## to use this option. This option is not used by default. ## ## =head2 -v ## ## Verbose mode. ## ## =head1 EXAMPLES ## ## =head2 timeout 10 pap foo.ps ## ## Execute "pap foo.ps" and send a SIGTERM if pap doesn't return ## after 10 seconds. ## ## =head2 timeout -s HUP 60 sh ## ## Spawn a shell and send a hangup signal after one minute. ## ## =head2 timeout -s 9 10 evilprog ## ## Execute a program and KILL it if it doesn't quit after 10 seconds. ## ## =head2 timeout -gv -s 15 -t 3 trap.ksh ## ## Send a signal to a program which traps the signal: ## ## trap 'echo got signal 15' 15 ## /bin/sleep 7 ## ## Because the program traps the signal, it is normal termination (not killed). ## Because I<-g> is used, "/bin/sleep" child process is killed ## with return code 143 (128+15). Because "/bin/sleep" is the last statement in ## the trap.ksh script, its return code will be the return code of trap.ksh script. ## Therefore, you get: ## ## "normal termination, exit status = 143." ## ## (If you have "exit N" after /bin/sleep, then exit status of trap.ksh will be N.) ## ## If you do not use I<-g> option, "/bin/sleep" will NOT be killed, ## the trap.ksh will sleep for 7 seconds, and terminate normally with exit status 0. ## ## =head1 REVISION HOSTORY ## ## =head2 version 2.0, 2008-07-24, Michael Wang . ## ## - Added compatibilities with Linux timeout program in I ## package. Copied part of Linux source code and man page. ## ## - Compatible exit code for killed program with shell. ## ## - I<-g> option. ## ## =head2 version 1.1, 1998-04, Michael Wang . ## ## - Incorporated code from Stefan `Sec` Zehl : ## "It prints to stderr now and has a switch to remain ## completely silent. It compiles on BSD the way it is now, and ## a small buglet (when omitting -t it cored here) was fixed." ## ## - setpgrp was added. kill(-pid, signal) was used to kill the ps group. ## ## =head2 version 1.0, 1998-02, Michael Wang . ## ## IPO. ## POD_STOP */