signal handler is not called while piped open is active

Discussion in 'Perl Misc' started by Glenn, Dec 29, 2010.

  1. Glenn

    Glenn Guest

    Given the following simple script:

    #!/usr/bin/perl -w --

    my $terminate = 0;
    my $failed = 0;

    sub print_signal {
    my $signame = shift;
    $terminate = 1;
    die "got a SIG$signame signal\n";
    }

    $SIG{INT} = \&print_signal;
    $SIG{QUIT} = \&print_signal;
    $SIG{TERM} = \&print_signal;

    open PIPE, '|-', "sleep 30";
    print PIPE "nighty night\n";
    $failed |= ! close PIPE;
    print "OS error: $!\n";
    print "Child error: $?\n";
    if (!$terminate) {
    print "sleeping after PIPE handling ...\n";
    sleep 30;
    }
    print "terminated by signal\n" if $terminate;
    print "failed: $failed\n";

    Why is the signal hander not called if I interrupt the script with
    SIGINT or SIGQUIT while the PIPE is open? I don't see this behavior
    documented anywhere. I expect the %SIG settings to be obeyed as
    documented, both during and after the PIPE processing.

    Here's the complete behavior, in detail, when the signal is sent while
    the PIPE i/o is underway. In each case, I run the script in a shell,
    in the foreground.

    SIGINT from keyboard => kills PIPE i/o but does not call signal
    handler
    SIGQUIT from keyboard => kills PIPE i/o but does not call signal
    handler
    SIGINT from another terminal => has no effect whatsoever
    SIGQUIT from another terminal => has no effect whatsoever
    SIGTERM from another terminal => signal handler is called

    Why is there any difference at all depending on where the signal comes
    from, and on what signal is sent?

    How do we get these blatant failures documented in the Perl manual?

    Note that the signal handlers are clearly still in play after the PIPE
    i/o is killed, because a signal sent from any source during the
    script's own later sleep() call (either SIGINT, SIGQUIT, or SIGTERM)
    does kill the script.

    Under this circumstance, how do I get a reliable indication that a
    signal was sent (and not just that the PIPE handling failed for quite
    possibly some other reason)?

    I'm using Perl 5.8.8 for my testing.
    Glenn, Dec 29, 2010
    #1
    1. Advertising

  2. Glenn

    C.DeRykus Guest

    On Dec 28, 5:48 pm, Glenn <> wrote:
    > Given the following simple script:
    >
    > #!/usr/bin/perl -w --
    >
    > my $terminate = 0;
    > my $failed    = 0;
    >
    > sub print_signal {
    >     my $signame = shift;
    >     $terminate = 1;
    >     die "got a SIG$signame signal\n";
    >
    > }
    >
    > $SIG{INT}  = \&print_signal;
    > $SIG{QUIT} = \&print_signal;
    > $SIG{TERM} = \&print_signal;
    >
    > open PIPE, '|-', "sleep 30";
    > print PIPE "nighty night\n";
    > $failed |= ! close PIPE;
    > print "OS error:  $!\n";
    > print "Child error:  $?\n";
    > if (!$terminate) {
    >     print "sleeping after PIPE handling ...\n";
    >     sleep 30;}
    >
    > print "terminated by signal\n" if $terminate;
    > print "failed:  $failed\n";
    >
    > Why is the signal hander not called if I interrupt the script with
    > SIGINT or SIGQUIT while the PIPE is open?  I don't see this behavior
    > documented anywhere.  I expect the %SIG settings to be obeyed as
    > documented, both during and after the PIPE processing.
    >
    > Here's the complete behavior, in detail, when the signal is sent while
    > the PIPE i/o is underway.  In each case, I run the script in a shell,
    > in the foreground.
    >
    > SIGINT from keyboard => kills PIPE i/o but does not call signal
    > handler
    > SIGQUIT from keyboard => kills PIPE i/o but does not call signal
    > handler
    > SIGINT from another terminal => has no effect whatsoever
    > SIGQUIT from another terminal => has no effect whatsoever
    > SIGTERM from another terminal => signal handler is called
    >
    > Why is there any difference at all depending on where the signal comes
    > from, and on what signal is sent?
    >
    > How do we get these blatant failures documented in the Perl manual?
    >
    > Note that the signal handlers are clearly still in play after the PIPE
    > i/o is killed, because a signal sent from any source during the
    > script's own later sleep() call (either SIGINT, SIGQUIT, or SIGTERM)
    > does kill the script.


    Yes, I see evidence of the INT handler if,
    for instance, I add:

    END { sleep 5; } # --> got a SIGINT signal

    I'm not sure why only the signal handler output is
    seen only in the parent process however.

    >
    > Under this circumstance, how do I get a reliable indication that a
    > signal was sent (and not just that the PIPE handling failed for quite
    > possibly some other reason)?
    >
    > I'm using Perl 5.8.8 for my testing.


    That should reliably be returned by your
    check of $! and $? after close().

    On 5.10.1 and FreeBSD, your child $? check was:

    Child error: 2

    which is waitpid output indicating that child
    termination was due to a SIGINT.
    (${^CHILD_ERROR_NATIVE} is also 2.)
    See perldoc -f waitpid and perldoc -f wait.

    You can inspect the child status to see if there
    was signal termination:

    $signal_termination = $? & 127
    See perldoc -f system

    --
    Charles DeRykus
    C.DeRykus, Dec 29, 2010
    #2
    1. Advertising

  3. Glenn

    Glenn Guest

    > Yes, I see evidence of the INT handler if,
    > for instance, I add:
    >
    >     END { sleep 5; }  # --> got a SIGINT signal
    >
    > I'm not sure why only the signal handler output is
    > seen only in the parent process however.


    The child process is running /bin/sleep, not Perl.

    > > Under this circumstance, how do I get a reliable indication that a
    > > signal was sent (and not just that the PIPE handling failed for quite
    > > possibly some other reason)?

    >
    > > I'm using Perl 5.8.8 for my testing.

    >
    > That should reliably be returned by your
    > check of $! and $? after close().
    >
    > On 5.10.1 and FreeBSD, your child $? check was:
    >
    >    Child error: 2
    >
    > which is waitpid output indicating that child
    > termination  was due to a SIGINT.
    > (${^CHILD_ERROR_NATIVE} is also 2.)
    > See perldoc -f waitpid and perldoc -f wait.
    >
    > You can inspect the child status to see if there
    > was signal termination:
    >
    >    $signal_termination = $? & 127
    > See perldoc -f system


    I was afraid the test code would elicit that kind of response. The
    point is, I'm not really interested in the child status -- that's just
    a red herring, something I'm reporting in this test program only
    because it's something I can probe. What I care about is whether or
    not the parent process received a signal. In this testing, the child
    happens to receive the same signal if I generate it from the keyboard,
    simply because (I'm guessing) it's sent to the entire process tree (by
    the shell). But if I send the signal from a different terminal,
    targeted specifically at the parent process, then the child process
    won't be receiving it, and thus its exit status is irrelevant.
    Glenn, Dec 29, 2010
    #3
  4. Glenn

    C.DeRykus Guest

    On Dec 29, 9:37 am, Glenn <> wrote:
    > > Yes, I see evidence of the INT handler if,
    > > for instance, I add:

    >
    > >     END { sleep 5; }  # --> got a SIGINT signal

    >
    > > I'm not sure why only the signal handler output is
    > > seen only in the parent process however.

    >
    > The child process is running /bin/sleep, not Perl.
    >


    Oh d'oh... exec happens immediately.

    >
    > > > Under this circumstance, how do I get a reliable indication that a
    > > > signal was sent (and not just that the PIPE handling failed for quite
    > > > possibly some other reason)?

    >
    > > > I'm using Perl 5.8.8 for my testing.

    >
    > > That should reliably be returned by your
    > > check of $! and $? after close().

    >
    > > On 5.10.1 and FreeBSD, your child $? check was:

    >
    > >    Child error: 2

    >
    > > which is waitpid output indicating that child
    > > termination  was due to a SIGINT.
    > > (${^CHILD_ERROR_NATIVE} is also 2.)
    > > See perldoc -f waitpid and perldoc -f wait.

    >
    > > You can inspect the child status to see if there
    > > was signal termination:

    >
    > >    $signal_termination = $? & 127
    > > See perldoc -f system

    >
    > I was afraid the test code would elicit that kind of response.  The
    > point is, I'm not really interested in the child status -- that's just
    > a red herring, something I'm reporting in this test program only
    > because it's something I can probe.  What I care about is whether or
    > not the parent process received a signal.  In this testing, the child
    > happens to receive the same signal if I generate it from the keyboard,
    > simply because (I'm guessing) it's sent to the entire process tree (by
    > the shell).  But if I send the signal from a different terminal,
    > targeted specifically at the parent process, then the child process
    > won't be receiving it, and thus its exit status is irrelevant.


    Hm, the program does ignore signals
    (except KILL and STOP of course) sent to
    just the parent from another terminal.

    However, and I'm not sure why, if I signal
    the process group - not just the parent,
    it works:

    kill -2 -11730 # from another terminal

    The result in the running program:

    parent=11730; # added debug print

    got a SIGINT signal

    --
    Charles DeRykus
    C.DeRykus, Dec 29, 2010
    #4
  5. On Dec 28 2010, 5:48 pm, Glenn <> wrote:
    >
    > <snip code>
    >
    > $SIG{INT}  = \&print_signal;
    > $SIG{QUIT} = \&print_signal;
    > $SIG{TERM} = \&print_signal;
    >
    > open PIPE, '|-', "sleep 30";
    > print PIPE "nighty night\n";
    > $failed |= ! close PIPE;
    >
    > <snip code>



    > Why is the signal hander not called if I interrupt the script with
    > SIGINT or SIGQUIT while the PIPE is open?  I don't see this behavior
    > documented anywhere.  
    > I expect the %SIG settings to be obeyed as
    > documented


    What docs are you referring to?

    Modern perls can defer the delivery of signals to the %SIG handlers.
    Here it appears that when close() is blocking on a handle attached to
    a child process perl never delivers the INT and QUIT signals.

    In similar cases these signals are delivered: a fork()/wait()
    combination, blocking for IO, etc...

    Maybe this behavior underscores the nice convenience Perl provides via
    a piped open()? Not only do you not have to pipe(), dup(), fork(),
    exec() and wait(), but you can almost always reap your child without
    having to get caught up in the signal world.

    > Here's the complete behavior, in detail, when the signal is sent while
    > the PIPE i/o is underway.  In each case, I run the script in a shell,
    > in the foreground.
    >
    > SIGINT from keyboard => kills PIPE i/o but does not call signal
    > handler
    > SIGQUIT from keyboard => kills PIPE i/o but does not call signal
    > handler
    > SIGINT from another terminal => has no effect whatsoever
    > SIGQUIT from another terminal => has no effect whatsoever
    > SIGTERM from another terminal => signal handler is called


    Given the close() issue above this sounds "right".

    The parent ignores all but SIGTERM and, in the first two cases, the
    signal was sent to the process group so the child receives it and the
    parent's close() returns.

    > Why is there any difference at all depending on where the signal comes
    > from, and on what signal is sent?


    Signals are odd beasts.
    Skye Shaw!@#$, Jan 3, 2011
    #5
  6. Glenn

    C.DeRykus Guest

    On Jan 2, 11:08 pm, "Skye Shaw!@#$" <> wrote:
    > On Dec 28 2010, 5:48 pm, Glenn <> wrote:
    >
    >
    >
    > > <snip code>

    >
    > > $SIG{INT}  = \&print_signal;
    > > $SIG{QUIT} = \&print_signal;
    > > $SIG{TERM} = \&print_signal;

    >
    > > open PIPE, '|-', "sleep 30";
    > > print PIPE "nighty night\n";
    > > $failed |= ! close PIPE;

    >
    > > <snip code>
    > > Why is the signal hander not called if I interrupt the script with
    > > SIGINT or SIGQUIT while the PIPE is open?  I don't see this behavior
    > > documented anywhere.  
    > > I expect the %SIG settings to be obeyed as
    > > documented

    >
    > What docs are you referring to?
    >
    > Modern perls can defer the delivery of signals to the %SIG handlers.



    > Here it appears that when close() is blocking on a handle attached to
    > a child process perl never delivers the INT and QUIT signals.
    >


    Yes, perl ignores INT and QUIT in the parent so they'll
    kill only the child and not the parent. As I think about
    my earlier response, that's why an INT to the process
    group works and you'll see the close() complete with a
    child status set to 2, ie., INT on most Unix systems.

    Perl does the same thing with system() calls by design
    so the child will terminate and the parent can then
    check its status. perldoc -f system.


    > In similar cases these signals are delivered: a fork()/wait()
    > combination, blocking for IO, etc...


    Yes, and, in the case of I/O, the signal delivery
    may be deferred though. From perlipc:

    With the "deferred" scheme the handler is not
    called immediately, and if Perl is using system's
    "stdio" library that library may re-start the "read"
    without returning to Perl and giving it a chance to
    call the %SIG handler.

    I also notice that perl now may fail and set $! to
    EINTR in the case of restartable system calls like
    read according to perlipc. In the OP's case for
    instance, sending a SIGTERM to the parent with
    a slight tweak to his handler will demo this:

    die "got a SIG$signame signal \$!=$!\n";

    --> got a SIGTERM signal $!=Interrupted signal call


    >
    > Maybe this behavior underscores the nice convenience Perl provides via
    > a piped open()? Not only do you not have to pipe(), dup(), fork(),
    > exec() and wait(), but you can almost always reap your child without
    > having to get caught up in the signal world.



    downside is that
    > > Here's the complete behavior, in detail, when the signal is sent while
    > > the PIPE i/o is underway.  In each case, I run the script in a shell,
    > > in the foreground.

    >
    > > SIGINT from keyboard => kills PIPE i/o but does not call signal
    > > handler
    > > SIGQUIT from keyboard => kills PIPE i/o but does not call signal
    > > handler
    > > SIGINT from another terminal => has no effect whatsoever
    > > SIGQUIT from another terminal => has no effect whatsoever
    > > SIGTERM from another terminal => signal handler is called

    >
    > Given the close() issue above this sounds "right".
    >
    > The parent ignores all but SIGTERM and, in the first two cases, the
    > signal was sent to the process group so the child receives it and the
    > parent's close() returns.
    >
    > > Why is there any difference at all depending on where the signal comes
    > > from, and on what signal is sent?

    >
    > Signals are odd beasts.


    Really odd and needs lot of careful reading
    of perlipc, etc.

    --
    Charles DeRykus
    C.DeRykus, Jan 3, 2011
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    7
    Views:
    319
  2. Grzegorz Kokosiñski
    Replies:
    1
    Views:
    313
    Chris
    May 26, 2008
  3. fishfry

    Inconsistent returns from piped open

    fishfry, Feb 17, 2006, in forum: Perl Misc
    Replies:
    4
    Views:
    92
    Csaba
    Feb 18, 2006
  4. Rohit

    Taint mode piped open problem

    Rohit, Jan 26, 2008, in forum: Perl Misc
    Replies:
    4
    Views:
    134
    Rohit
    Jan 27, 2008
  5. John Kelly

    piped open and shell metacharacters

    John Kelly, Jul 30, 2010, in forum: Perl Misc
    Replies:
    22
    Views:
    371
    Ilya Zakharevich
    Aug 3, 2010
Loading...

Share This Page