[alt.sources] Timeout on shell command.

tchrist@convex.COM (Tom Christiansen) (08/12/90)

In article <BRISTER.90Aug10222433@westworld.decwrl.dec.com> brister@decwrl.dec.com (James Brister) writes:
>I'd like to have a shell script run a command, but if that command doesn't
>finish in X seconds, then the script should kill it, if the command
>finishes sooner then the script should immediately continue. Any ideas on
>how one could achieve this?

Here's timeout.c; syntax is 'timeout seconds command'.  

--tom

#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(); 
		 /* NOTREACHED */
		 break;
	case 0: child();  
		 /* NOTREACHED */
	case -1: perror("fork"); 
		 exit(EX_OSERR); 
		 /* NOTREACHED */
    } 
} 

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(1);
	(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!"

maart@cs.vu.nl (Maarten Litmaath) (08/16/90)

In article <3716@sactoh0.UUCP>,
	jak@sactoh0.UUCP (Jay A. Konigsberg) writes:
)...
)command &                # execute in background

What if the command is supposed to run in the _foreground_?
The following timeout shell script can be easily converted to a C program
if desired.
--------------------cut here--------------------
#!/bin/sh
# @(#)timeout 6.2 90/03/01 Maarten Litmaath

prog=`basename $0`
usage="Usage: $prog [-signal] [timeout] [:interval] [+delay] [--] <command>"

SIG=-KILL	# default signal sent to the process when the timer expires,
		# unless a delay option has been given: then it is -TERM
sigopt=0
timeout=60	# default timeout
interval=15	# default interval between checks if the process is still alive
delay=		# (if specified) the delay between posting the given signal and
		# destroying the process (kill -KILL)

while :
do
	case $1 in
	--)
		shift
		break
		;;
	-*)
		SIG=$1
		sigopt=1
		;;
	[0-9]*)
		timeout=$1
		;;
	:*)
		EXPR='..\(.*\)'
		interval=`expr x"$1" : "$EXPR"`
		;;
	+*)
		EXPR='..\(.*\)'
		delay=`expr x"$1" : "$EXPR"`
		case $sigopt in
		0)
			SIG=-TERM
		esac
		;;
	*)
		break
	esac
	shift
done

case $# in
0)
	echo "$usage" >&2
	exit 2
esac

(
	for t in $timeout $delay
	do
		while test $t -gt $interval
		do
			sleep $interval
			kill -0 $$ || exit
			t=`expr $t - $interval`
		done
		sleep $t
		kill $SIG $$ && kill -0 $$ || exit
		SIG=-KILL
	done
) 2> /dev/null &

exec "$@"
--
   "UNIX was never designed to keep people from doing stupid things, because
    that policy would also keep them from doing clever things."  (Doug Gwyn)