in reply to SIGINT and system calls

You have a zombie problem. Call wait before exit in the signal handler.

You don't say what version of perl you use, but "safe" signals might be involved. That could prevent Ctrl-C from interrupting a blocking read in <CMD>. Your code looks fine.

You can break up the print <CMD>; by calling sysread, perhaps inside select.

# replaces print <CMD>; vec( my $rin, fileno(*CMD), 1) = 1; while (select my $rd = $rin, undef, undef, undef;) { my $bytes = sysread *CMD, my $buf, 4096; print $buf if 0 < $bytes; last if 1 > $bytes; } # wait added to lay the zombies sub myhand() { print "Parent $$ caught SIGINT.\n"; print "Parent $$ will KILL $pid.\n"; kill INT, $pid; waitpid $pid; exit 0; }
It's a potential problem that the signal handler is set before the child is launched. That makes the child inherit a handler that is clearly meant for the parent. If you changed the kill signal to the more proper SIGINT, the child would try to signal a nonexistent child.

After Compline,
Zaxo

Replies are listed 'Best First'.
Re^2: SIGINT and system calls
by Anonymous Monk on Jan 20, 2005 at 05:26 UTC
    You monks are great! I am running ActiveState 5.8.6 on Windows. So here's what it translates into:
    $SIG{INT}=\&myhand; print "Parent pid = $$ \n"; $pid = open(CMD, "ping google.com -n 15 |"); print "Ping running... pid = $pid \n\n"; # replaces print <CMD>; vec( my $rin, fileno(*CMD), 1) = 1; while (select (my $rd = $rin, undef, undef, undef)) { my $bytes = sysread *CMD, my $buf, 4096; print $buf if 0 < $bytes; } print "I am finished with my task. My results are: \n"; # where are my results stored if I want them in a variable? # print $results; # wait added to lay the zombies sub myhand() { print "Parent $$ caught SIGINT.\n"; print "Parent $$ will KILL $pid.\n"; kill 9, $pid; waitpid $pid, 0; exit 0; }
    I am not sure if I completely understand what is happening with the output that you have shown -- it appears to me that it stays in the while loop and I can't get it to "I am finished with my task."

    Also, do one of those variables currently have the complete results (of the ping output in this case) or will I need to build a new variable if I want it STDOUT as well as in a variable to pass to another routine later?

    Thanks!!

      I'm surprised that that works on windows. Both select and signals are pretty unixy for the windows ports to emulate. I didn't know they could do that at all.

      You're right that I omitted a way out of the while select loop. Probably last if 1 > $bytes; at the end of the loop would do it. I'll edit that into the original.

      Update: Yes, concatenation in the loop would work fine, or if you wanted to get fancy I think you could say,

      my $result; while (select my $rd = $rin, undef, undef, 60) { my $bytes = sysread *CMD, substr($result, -1), 4096; last if 1 > $bytes; }
      Or else you could use the offset argument of sysread to position at the end of $result.

      The 60 second timeout is new, too. Adjust to taste.

      After Compline,
      Zaxo

        That works well to break the loop.

        What about putting the results back into a variable (such as $results)? Do I need to do a concatenation in the while( ... $results .= $buf; ... ) or is there a better way to store the output in a variable?
        print "I am finished with my task. My results are: \n"; # where are my results stored if I want them in a variable? # print $results;

        Thanks again!
        I like the opportunity to get fancy, but I noticed an odd reaction of the sysread(). Using:
        my $result; while (select my $rd = $rin, undef, undef, 60) { my $bytes = sysread *CMD, substr($result, -1), 4096; last if 1 > $bytes; } ... print "I am finished with my task. My results are: \n"; print $result."\n";
        My output is:
        I am finished with my task. My result are: Pinging google.com [216.239.37.99] with 32 bytes of data: Reply from 216.239.37.99:bytes=32 time=36ms TTL=244 Ping statistics for 216.239.37.99: Packets: Sent = 15, Received = 15, Lost = 0 (0% loss), Approximate round trip times in milli-seconds: Minimum = 36ms, Maximum = 37ms, Average = 36ms
        What happened to all of the other lines from the ping (should be 15 result lines)? I would expect it to be:
        I am finished with my task. My result are: Pinging google.com [216.239.37.99] with 32 bytes of data: Reply from 216.239.37.99: bytes=32 time=37ms TTL=244 Reply from 216.239.37.99: bytes=32 time=36ms TTL=244 Reply from 216.239.37.99: bytes=32 time=36ms TTL=244 Reply from 216.239.37.99: bytes=32 time=36ms TTL=244 Reply from 216.239.37.99: bytes=32 time=36ms TTL=244 Reply from 216.239.37.99: bytes=32 time=36ms TTL=244 Reply from 216.239.37.99: bytes=32 time=36ms TTL=244 Reply from 216.239.37.99: bytes=32 time=36ms TTL=244 Reply from 216.239.37.99: bytes=32 time=36ms TTL=244 Reply from 216.239.37.99: bytes=32 time=36ms TTL=244 Reply from 216.239.37.99: bytes=32 time=36ms TTL=244 Reply from 216.239.37.99: bytes=32 time=36ms TTL=244 Reply from 216.239.37.99: bytes=32 time=37ms TTL=244 Reply from 216.239.37.99: bytes=32 time=37ms TTL=244 Reply from 216.239.37.99: bytes=32 time=37ms TTL=244 Ping statistics for 216.239.37.99: Packets: Sent = 15, Received = 15, Lost = 0 (0% loss), Approximate round trip times in milli-seconds: Minimum = 36ms, Maximum = 37ms, Average = 36ms
        What happens that makes the $result apparently overwrite the output of the *CMD? I would still want to output the live results and set the variable, so would it be best to use the concatenation? I can't print $result in the loop, since that would contain all of the output, not just that loop's progress.

        Also, why is the sysread() length set to 4096. Does that have a significant value?

        Thanks!