LPM: Neat alarm trick
Mik Firestone
fireston at lexmark.com
Wed Oct 4 13:32:49 CDT 2000
To the best of my ( incredibly limitted ) knowledge, not the way you are
trying. And I have been looking for a few weeks myself.
I have a few suggestions. The system call is a convenience - it is really
just forks a child and spins the parent into a wait loop while the child
exec's the shell call. What if you were to do it yourself?
Something like this ( completely untested ):
$timeout = 0;
LOOP:
while ( 1 ) {
if ( $spid = fork() ) {
#---
# I am the parent, do a few things and waitpid
#---
select undef,undef,undef,0.5; # Keep my loop loose
$result = waitpid( $spid, 1 ); # non-blocking
last LOOP if ( $result == $spid ); # Child process exited
if ( ++$counter > 20 ) { # 10 seconds
$timeout = 1;
kill HUP, $spid;
last LOOP;
}
}
else {
#---
# This is important - you *must* use the LIST format here -
# strings and shell characters are bad. Exec never returns, so we
# needn't worry about an exit.
#---
exec "/fully/qualified/path/to/rcp", "option", "option",...;
}
}
if ( $timeout ) {
print "The horror, the horror\n";
}
This WILL NOT work if the exec causes a shell to spawn - you will then be at
least a few PIDs removed from the original child process. See perlfunc:exec
for a complete explanation of when exec spawns shells and when it won't. This
is also going to be somewhat dependant on the quirks of your execvp(3) call.
HTH ( but no promises :)
Mik
On Wed, 4 Oct 2000, Frank Price is rumored to have said:
>Hi Lexpm:
>
>I found a neat trick and a related question. First the trick: I have
>a script which does rcp system calls. Sometimes the other server is
>screwed up and the system call just hangs for a long time. I wanted a
>away to time it out after a certain amount of time.
>
>The solution is to use alarm(). Alarm sends an ALRM signal to any
>children, which works since system() forks a child. You wrap this in
>an eval block, and set a ALRM signal handler. Outside the eval, you
>can test for $@ and take action.
>
>Example:
>
>eval {
> local $SIG{'ALRM'} = sub {
> local $SIG{'TERM'} = 'IGNORE';
> kill TERM => -$$;
> die 'Caught alarm signal';
> };
> alarm(10);
> print "starting system call ...\n";
> system("cp /dev/zero /dev/null");
> alarm(0);
>};
>if ($@) {
> if ($@ =~ /Caught alarm signal/) {
> die ("system call blocked!!!");
> } else { die }
>}
>
>Here's the question: since I want to kill the system call if the
>alarm goes off, looks like I have to explicitly send it a TERM signal.
>That's why the $SIG{'ALRM'} does 'kill TERM => -$$' (this sends a TERM
>to everything in the process group). First I ignore TERM for this
>process so the script doesn't get killed off. But I really just want
>to kil the system() child; however I couldn't find a good way to find
>it's PID. Is there a better way?
>
>Thanks,
>
>-Frank.
>
--
Mik Firestone fireston at lexmark.com
When I become an Evil Overlord:
If I am engaged in a duel to the death with the hero and I am fortunate enough
to knock the weapon out of his hand, I will graciously allow him to retrieve
it. This is not from a sense of fair play; rather, he will be so startled and
confused that I will easily be able to dispatch him.
--
Mik Firestone fireston at lexmark.com
When I become an Evil Overlord:
My force-field generators will be located inside the shield they generate.
--
Mik Firestone fireston at lexmark.com
When I become an Evil Overlord:
I will design all doomsday machines myself. If I must hire a mad scientist to
assist me, I will make sure that he is sufficiently twisted to never regret
his evil ways and seek to undo the damage he's caused.
More information about the Lexington-pm
mailing list