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