[vox-tech] signal in perl not being caught

vox-tech@lists.lugod.org vox-tech@lists.lugod.org
Tue, 14 May 2002 17:14:40 -0400


On Tue, May 14, 2002 at 10:02:39AM -0700, Peter Jay Salzman wrote:
> what's wrong with this code snippet?

  You are using the perl system function and expecting to be able to 
catch SIGINT.


On Tue, May 14, 2002 at 11:20:40AM -0700, Jeff Newmiller wrote:
> > $SIG{INT} = sub { die("caught sigint.  aborting."); };
> 
> detail: should have newline in die string to avoid cancelling the process
> before the message gets flushed out to the terminal.

  Great idea for printf to stdout, but actually die sends output to 
stderr (which is unbuffered) and as a bonus supplies it's own newline 
character.  If you specify a newline character in the die output you
actually lose the line number information it would normally provide:

die "signal failed\n";
========
Uncaught exception from user code:
        signal failed
========

die "signal failed";
========
Uncaught exception from user code:
        signal failed at ./signal.pl line 17.
========


> >    system("$program $T > $result_file");

  The reason you don't get the signal is perl blocks it.

from man perlfunc:
#               Because "system" and backticks block "SIGINT" and
#               "SIGQUIT", killing the program they're running
#               doesn't actually interrupt your program.

  According to the documentation then `` should not work either.

  I highly recommend that you check out the perlfunc man page because
it goes on to show how you can use the return code from system to 
detect if the child was killed, dumped core, or exited abnormally.

  Also keep in mind if you have any shell magic characters in the 
string passed to system there may be a copy of bash spawned to 
interpret the characters in addition to the command you wanted,
so unless you do something like:
  system("exec my_command >> result.$foo");
then the return code information will be mangled by bash.


> Perhaps you want to use backticks so you can keep control of the
> terminal?

  Jeff's suggestion works... but only because `` doesn't block SIGINT
correctly, like the man page says it does.  I can see that `` _does_
block SIGINT but only _after_ reading end of file from it's child,
if the child process closes both stdout and stderr then `` behaves
the same as system, in that SIGINT is not catchable in the parent
perl script.

  Without a doubt there is a bug here, either in documentation or
more likely implementation.


So onto an un-asked question:
- How do I abort running some shell command from perl with SIGINT?

something _like_ this... note that run_me appends to an output file
you may or may not want that... handling output from the commands
run can be done a couple of dozen ways.
========
#! /usr/bin/perl -w

my $loop = 5;

sub run_me($)
{
  if (!defined($child_id = fork())) {
    die "fork failed\n";
  } elsif ($child_id) {
    waitpid $child_id, 0;
  } else {
    open STDOUT, ">> output";
    close STDIN;
    close STDERR;

    exec $_[0];
  }
}

$SIG{"INT"} = sub { die "foo bar" };

while ($loop--)
{
  print STDERR ".";
  run_me("date; sleep 5");
}
========

    Cheers,
      Mike

ps:
  There is another bug in strace if you try to trace the above perl
script with '-f' it appears that strace gets confused and stops tracing
the living perl script when the bash script child kills itself.  I'm
going to pretend I didn't see this and go back to what I was working on.

pps:
  I also have an unfounded aversion to the perl mailing lists
so if anyone would like to be barbecued they can ask if the perl 
bug above is documentation or implementation...