Question

I need to execute commands via $ssh->system in background, and it is documented that this should behave just like the local "system" command:

'As for "system" builtin, "SIGINT" and "SIGQUIT" signals are blocked.  (see "system" in perlfunc).'

Actually, this does not seem to be true, as $ssh->system in a child is immediately terminated when SIGINT is received, even when I explicitly want to "IGNORE" it first:

use warnings;
use strict;
use POSIX ":sys_wait_h";
use Net::OpenSSH;

my $CTRLC=0;
$SIG{CHLD}='IGNORE';

sub _int_handler {
  $CTRLC++;
  print "CTRL-C was pressed $CTRLC times!!!\n";
}

$SIG{INT}='IGNORE';
my $SSH=Net::OpenSSH->new('testhost',
                               forward_agent => 1,
                       master_stderr_discard => 1,
                                 master_opts => [ -o =>"PasswordAuthentication=no"]);
$SIG{INT}=\&_int_handler;
sub _ssh {
  $SIG{INT}='IGNORE';
  $SSH->system('sleep 3; sleep 3');
#       system('sleep 3; sleep 3');
  print "Exiting Child!\n";
  exit 0;
}

print "Starting shell ...\n";
_ssh if not (my $PID=fork);
print $PID, "\n";
waitpid ($PID,0);

When running this code and trying to interrupt just after the first "sleep 3" is starting, the "$SSH->system" call is immediately ended.

If you, however, use the local "system" statement just below instead, SIGINT is caught correctly.

In the source code of Net::OpenSSH I found out, that $SIG{INT} is also explicitly set to "IGNORE" in the "system" sub. I have no idea why this is not working.

I would we thankful for any kind of possible solution, also if it means doing things differently. In the end, I just want to remotely execute commands and still protect them against CTRL-C.

Thank you,

Mazze

UPDATE:

Thank you for your input @salva. I further stripped things down and in the end it seems to be the "-S " flag of ssh:

$SIG{INT}='IGNORE';
# system('ssh -S /tmp/mysock lnx0001a -- sleep 3');
  system('ssh                lnx0001a -- sleep 3');

As soon as the "-S /tmp/mysock" variant is being used, ssh seems to be interruptable, not otherwise. Is there anybody who could give an explanation for that? Should I post a new, independent question for that?

Thanks again,

Mazze

UPDATE 2:

I brought things down even more, and now this is completely without any perl scope. You can do that in your shell:

$ trap '' INT
$ ssh                lnx0001a -- sleep 3

is not interruptable now. In my case, however, the following still is interruptable:

$ ssh -S /tmp/mysock lnx0001a -- sleep 3

With CTRL-C, this gets immediately interrupted. It is the same case with the root user as with mine, but other colleagues don't see this behaviour with their users. We compared @ENV, but we did not find out what might cause the different behaviour.

How is it with you: Is the "-S" version interruptable after "trap '' INT" in your shell? (Obviously, you have to have created a master session for /tmp/mysock before).

Sincerely,

Mazze

Was it helpful?

Solution

The problem is that when you press CTRL-C on the console, the kernel sends a signal to all the processes on the process group (see Prevent control-c from sending SIGINT to all process group children).

I think the difference you see in behavior is actually caused by differences on the child processes, some reset the signal flags while others don't.

Anyway, I will add support for running the SSH process in a different process group. Add a bug report on the module RT so I don't forget about it, please.

Update: The following experiment shows that OpenSSH system method and the builtin behave in the same way:

my @cmd = $SSH->make_remote_command("sleep 3 && echo hello");
warn "running command @cmd\n";
local $SIG{INT} = 'IGNORE';
system @cmd;

If you run it you will see that the signal reaches and aborts the ssh process even if the INT signal handler is set to IGNORE.

Update 2: After some experimentation I have found that the problem actually lays on the master SSH process running on the background. Unless you use password authentication, it also hangs in the process group and so gets the INT signal.

I am adding a new option to explicitly ask for running the master as a new process group but that part of the code is quite convoluted due to the large number of options, so it is not an easy task.

Update 3: I have released a new development version (0.61_15) of the module.

Now, you can do...

my $ssh = Net::OpenSSH->new($host, master_setpgrp => 1, ...);
$ssh->system({setpgrp => 1}, "sleep 10; echo uninterruptible");

... and hopefully, no SIGINT would arrive into the master or slave SSH processes.

Report any issue you may find, please!

OTHER TIPS

In your case, it would be better to get pre-Perl-5.8 signal behaviour, by setting the environment var of PERL_SIGNALS to 'unsafe'.

Since Perl 5.8.1 the normal approach of signals is described here: perlipc: Deferred Signals (Safe Signals)

But what you maybe want is immediate behaviour, like here: perlrun: PERL_SIGNALS

so, the solution to your problem might be to put this:

 $ENV{'PERL_SIGNALS'} = 'unsafe';

in your code :)

I tried it here, worked so far. But there may be a downside of this approach since I don't know if your program will face different problems due to that.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top