tchrist@convex.com (Tom Christiansen) (10/02/89)
It is occasionally useful to set a timeout on a command that would otherwise hang or run forever. Instead of building a timeout into each command, the UNIX philosophy would be to build a tool to do the timeout for you, so here it is. It's pretty easy to write, and has surely been done many times before, but I didn't have one handy when I needed it, so here it is. #include <stdio.h> #include <signal.h> #include <sysexits.h> #include <sys/wait.h> int pid,count; union wait status; int bang(); char **commands; main(ac,av) char **av; { if (ac < 3) { usage: fprintf (stderr, "usage: %s seconds command\n",*av); exit (EX_USAGE); } if ((count=atoi(av[1])) < 1) { fprintf (stderr, "seconds (%s) malformed or nonpositive\n",av[1]); goto usage; } commands = &av[2]; switch (pid=fork()) { default: parent(); break; case 0: child(); /* NOTREACHED */ case -1: perror("fork"); exit(EX_OSERR); /* NOTREACHED */ } printf("exit status was %d\n",status.w_retcode); printf("termsig was %d\n",status.w_termsig); } parent() { (void) signal(SIGALRM,bang); alarm(count); while(wait(&status) != pid) /* VOID */; if (WIFSIGNALED(status)) exit(-status.w_termsig); exit(status.w_retcode); } bang() { fprintf(stderr,"Timeout!\n"); (void) signal(SIGALRM,SIG_DFL); (void) kill(pid,SIGTERM); if (!kill(pid,0)) { sleep(3); (void) kill(pid,SIGKILL); } exit(EX_TEMPFAIL); } child() { execvp(*commands,commands); perror(*commands); _exit(EX_DATAERR); /* NOTREACHED */ } /* lint output: * timeout.c: */ Tom Christiansen {uunet,uiucdcs,sun}!convex!tchrist Convex Computer Corporation tchrist@convex.COM "EMACS belongs in <sys/errno.h>: Editor too big!"
mfuller@prandtl.nas.nasa.gov (Mike J. Fuller) (10/03/89)
I had to write such a program not too long ago, so here is my two cents worth. I'm not posting this because I think that it's any better, I just figure that if we see it done a bunch of different ways maybe we can arrive at a much better final product. Also, after I went to all the trouble of writing my own version I found a version someone had written out at Ames that was also different from mine. If the author reads this news group, maybe he'd like to post it. I'd post it myself, but I'm not willing to take responsibility for my own code, let alone someone else's :-) #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # Makefile # timeout.c # timeout.man # This archive created: Mon Oct 2 16:30:54 1989 export PATH; PATH=/bin:/usr/bin:$PATH if test -f 'Makefile' then echo shar: "will not over-write existing file 'Makefile'" else cat << \SHAR_EOF > 'Makefile' # Makefile for timeout. BINDIR = /usr/local/bin MANEXT = l MANDIR = /usr/local/man/man$(MANEXT) CFLAGS = -O LDFLAGS = LINTFLAGS = -abch timeout: timeout.c cc $(CFLAGS) -o timeout timeout.c $(LDFLAGS) lint: timeout.c lint $(LINTFLAGS) timeout.c install: timeout timeout.man install -cs timeout $(BINDIR) install -c timeout.man $(MANDIR)/timeout.$(MANEXT) clean: rm -f core timeout *~ \#* SHAR_EOF fi if test -f 'timeout.c' then echo shar: "will not over-write existing file 'timeout.c'" else cat << \SHAR_EOF > 'timeout.c' /* timeout.c -- execute a program with specified timeout period. */ #ifndef lint static char sccsid[] = "@(#)timeout.c 1.0"; #endif #include <ctype.h> #include <signal.h> #include <stdio.h> /* SysV with no BSD extensions at all? */ #ifndef SIGCHLD #define SIGCHLD SIGCLD #endif /* Function to call when SIGCHLD is received. */ void normal_exit() { exit(0); } /* Main program. */ main(argc,argv) int argc; char *argv[]; { int child; char *c; /* Check arg count. */ if(argc < 3) { fprintf(stderr,"Usage: %s time program [program args]...\n",argv[0]); exit(2); } /* Make sure time is a number. */ for(c = argv[1]; *c; ++c) if(! isdigit(*c)) { fprintf(stderr,"Usage: %s time program [program args]...\n",argv[0]); exit(2); } /* Fork in the road. */ if((child = fork()) < 0) { perror("fork"); exit(2); } /* Child -- run program. */ if(child == 0) { execvp(argv[2],argv+2); /* We will only be here if the exec fails. */ fprintf(stderr,"%s: fatal error trying to exec %s\n",argv[0],argv[2]); } /* Parent -- wait for child to finish, kill child if it takes too long. */ else { /* Exit normally if child finishes in time. */ signal(SIGCHLD,normal_exit); /* Take a nap. */ sleep(atoi(argv[1])); /* Child didn't finish it time. Ignore SIGCHLD now, else we'll exit with 0 immediately upon killing the child. */ signal(SIGCHLD,SIG_IGN); /* Pedocide. */ if(kill(child,SIGKILL)) { perror("kill"); exit(2); } /* Exit with error code since child didn't finish in time. */ exit(1); } } SHAR_EOF fi if test -f 'timeout.man' then echo shar: "will not over-write existing file 'timeout.man'" else cat << \SHAR_EOF > 'timeout.man' .TH TIMEOUT L "7 August 1989" .SH NAME .B timeout \- execute a program with specified timeout period .SH SYNOPSIS .B timeout .I time program [ .B program args... ] .SH DESCRIPTION .LP .B timeout executes a program with a specified timeout period of .I time seconds. If the program finishes before the specified timeout period, .B timeout exits normally. If the program doesn't finish in the specified amount of time, it is killed and .B timeout exits with an exit status of 1. .SH AUTHOR Mike Fuller .br NASA Lewis Research Center .br mikef@sarah.lerc.nasa.gov .SH BUGS .B timeout exits with an exit status of 0 if the program isn't found. SHAR_EOF fi exit 0 # End of shell archive /-----------------------------------------------------------------------------\ | Mike J. Fuller |Internet: mikef@sarah.lerc.nasa.gov |You'd be paranoid, | |----------------| mikef@zippysun.math.uakron.edu|too, if everyone | |/\/\/\/\/\/\/\/\|Bitnet: r3mjf1@akronvm |was out to get you!| \-----------------------------------------------------------------------------/
stuart@amc-gw.UUCP (Stuart Poulin) (10/05/89)
How about an option (-r) that would kill any children of the process that timeout kills?
tchrist@convex.COM (Tom Christiansen) (10/05/89)
In article <942@amc-gw.UUCP> stuart@amc-gw.UUCP (Stuart Poulin) writes: >How about an option (-r) that would kill any children of the process >that timeout kills? Use killpg() instead of kill(). --tom Tom Christiansen {uunet,uiucdcs,sun}!convex!tchrist Convex Computer Corporation tchrist@convex.COM "EMACS belongs in <sys/errno.h>: Editor too big!"