views:

85

answers:

2

Whats going on here? I thought SIGINT would be sent to the foreground process group.

(I think, maybe, that system() is running a shell which is creating a new process group for the child process? Can anyone confirm this?)

% perl
local $SIG{INT} = sub { print "caught signal\n"; };
system('sleep', '10');

Then hit ctrl+d then ctrl+c immediately and notice that "caught signal" is never printed.

I feel like this is a simple thing... anyway to work around this? The problem is that when running a bunch of commands via system results in holding ctrl+c until all iterations are completed (because perl never gets the SIGINT) and is rather annoying...

How can this be worked around? (I already tested using fork() directly and understand that this works... this is not an acceptable solution at this time)

UPDATE: please note, this has nothing to do with "sleeping", only the fact that the command takes some arbitrary long amount of time to run which is considerably more than that of the perl around it. So much so that pressing ctrl+c gets sent to the command (as its in the foreground process group?) and somehow manages to never be sent to perl.

A: 

I don't quite get what you're trying to achieve here... but have you tried simply comparing to:

perl -wle'local $SIG{INT} = sub { print "caught signal"; }; sleep 10;'

Can you explain what effect you're trying to go for, and why you are invoking the shell? Can you simply call into the external program directly without involving the shell?

Ether
please see my UPDATE section, "sleeping" has nothing to do with it... its simply a placeholder for some arbitrary process. It could easily be switched with "grep something" which will hang forever since it's expecting to read data from stdin
xyld
@xyld: I realize the sleep is a placeholder. I was asking why you needed to invoke the shell, rather than just calling your process directly.
Ether
@Ether I'm not explicitly, but the the perl interpretor may be doing it as a part of "system()".
xyld
@xyld: you can bypass the shell by passing more than one argument to `system`. It is only invoked to parse the command if you pass the command as a single string.
Ether
+3  A: 

from perldoc system:

Since SIGINT and SIGQUIT are ignored during the execution of system, if you expect your program to terminate on receipt of these signals you will need to arrange to do so yourself based on the return value.

@args = ("command", "arg1", "arg2");
system(@args) == 0
   or die "system @args failed: $?"

If you'd like to manually inspect system's failure, you can check all possible failure modes by inspecting $? like this:

if ($? == -1) {
    print "failed to execute: $!\n";
}
elsif ($? & 127) {
    printf "child died with signal %d, %s coredump\n",
       ($? & 127),  ($? & 128) ? 'with' : 'without';
}
else {
    printf "child exited with value %d\n", $? >> 8;
}

Alternatively, you may inspect the value of ${^CHILD_ERROR_NATIVE} with the W*() calls from the POSIX module

Hasturkun
methinks I need to read the documentation all over again :-/ Thanks.
xyld
I'd add that the reason you need to inspect the return value of the child is that your shell delivers the ^c/INT to the whole process group, to perl and children, in this case terminating your child `sleep 10`. If you simply `kill -INT $perl_pid` from another shell, perl will happily SIG_IGNore you.
pilcrow