| Current Path : /usr/share/exim4/ |
| Current File : //usr/share/exim4/timeout.pl |
#!/usr/bin/perl
## timeout
##
## (c) 2004 Piotr Roszatycki <dexter@debian.org>, GPL
##
## $Id$
=head1 NAME
timeout - Run command with bounded time.
=head1 SYNOPSIS
B<timeout> S<B<-h>>
B<timeout>
S<[-I<signal>]>
I<time>
I<command>
...
=cut
use 5.006;
use strict;
use Config;
BEGIN {
unless( eval "require Pod::Usage" ) {
die "Please install the perl-modules package if you want this script to work\n";
}
}
use POSIX qw(setsid);
##############################################################################
## Default values for constant variables
##
## Program name
my $NAME = "timeout";
## Program version
my $VERSION = "0.1+he1";
##############################################################################
## Signals to handle
##
my @signals = qw( HUP INT QUIT TERM SEGV PIPE XCPU XFSZ ALRM );
##############################################################################
## Signal to send after timeout. Default is KILL.
my $signal = 'KILL';
## Time to wait
my $time = 0;
## Command to execute as array of arguments
my @command = ();
## PID for fork function
my $child_pid;
## PID for wait function
my $pid;
##############################################################################
## usage()
##
## Prints usage message.
##
sub usage() {
pod2usage(2);
}
## help()
##
## Prints help message.
##
sub help() {
pod2usage(-verbose=>1, -message=>"$NAME $VERSION\n");
}
## signal_handler($sig)
##
## Handler for signals to clean up child processes
##
sub signal_handler($) {
my ($sig) = @_;
if ($sig eq 'ALRM') {
printf STDERR "Timeout: aborting command ``%s'' with signal SIG%s\n", join(' ', @command), $signal;
} else {
printf STDERR "Got signal SIG%s: aborting command ``%s'' with signal SIG%s\n", $sig, join(' ', @command), $signal;
}
kill $signal, -$child_pid;
exit -1;
}
##############################################################################
## Main subroutine
##
## Parse command line arguments
my $arg = $ARGV[0];
if ($arg =~ /^-(.*)$/) {
my $opt = $1;
if ($arg eq '-h' || $arg eq '--help') {
help();
} elsif ($opt =~ /^[A-Z0-9]+$/) {
if ($opt =~ /^\d+/) {
#Convert numeric signal to name by using the perl interpreter's
#configuration:
usage() unless defined $Config{sig_name};
$signal = (split(' ', $Config{sig_name}))[$opt];
} else {
$opt =~ s/^SIG//;
$signal = $opt;
}
shift @ARGV;
} else {
usage();
}
}
usage() if @ARGV < 2;
$arg = $ARGV[0];
usage() unless $arg =~ /^\d+$/;
$time = $arg;
shift @ARGV;
@command = @ARGV;
## Fork for exec
if (! defined($child_pid = fork)) {
die "Could not fork: $!\n";
exit 1;
} elsif ($child_pid == 0) {
## child
## Set new process group
setsid;
## Execute command
exec @command or die "Can not run command `" . join(' ', @command) . "': $!\n";
}
## parent
## Set the handle for signals
foreach my $sig (@signals) {
$SIG{$sig} = \&signal_handler;
}
## Set the alarm
alarm $time;
## Wait for child
while (($pid = wait) != -1 && $pid != $child_pid) {}
## Clean exit
exit ($pid == $child_pid ? $? >> 8 : -1);
__END__
=head1 DESCRIPTION
B<timeout> executes a command and imposes an elapsed time limit.
The command is run in a separate POSIX process group so that the
right thing happens with commands that spawn child processes.
=head1 OPTIONS
=over 8
=item -I<signal>
Specify an optional signal name to send to the controlled process. By default,
B<timeout> sends B<KILL>, which cannot be caught or ignored.
=item I<time>
The elapsed time limit after which the command is terminated.
=item I<command>
The command to be executed.
=back
=head1 RETURN CODES
=over 8
=item 0..253
Return code from called command.
=item 254
Internal error. No return code could be fetched.
=item 255
The timeout was occured.
=back
=head1 AUTHOR
(c) 2004 Piotr Roszatycki E<lt>dexter@debian.orgE<gt>
Inspired by timeout.c that is part of The Coroner's Toolkit.
All rights reserved. This program is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License, the
latest version.