$ SIG {INT} =' IGNORE'不使用Net :: OpenSSH

时间:2014-04-01 15:34:19

标签: perl perl-module openssh

我需要在后台通过$ ssh->系统执行命令,并且记录下这应该像本地"系统"命令:

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

实际上,这似乎并不正确,因为当接收到SIGINT时,子节点中的$ ssh->系统会立即终止,即使我明确地想要" IGNORE"它首先:

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);

运行此代码并尝试在第一次"睡眠3"之后中断。正在启动," $ SSH->系统"电话会立即结束。

但是,如果您使用本地"系统"而在下面的语句中,SIGINT被正确捕获。

在我发现的Net :: OpenSSH的源代码中,$ SIG {INT}也明确地设置为" IGNORE"在"系统"子。我不知道为什么这不起作用。

我会感谢任何可能的解决方案,如果它意味着以不同的方式做事。最后,我只想远程执行命令并仍然保护它们免受CTRL-C的攻击。<​​/ p>

谢谢,

Mazze

更新

感谢您输入@salva。我进一步剥离了东西,最后它似乎是&#34; -S&#34; ssh的旗帜:

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

&#34; -S / tmp / mysock&#34;正在使用变体,ssh似乎是可中断的,而不是其他。有没有人可以解释一下? 我应该为此发布一个新的独立问题吗?

再次感谢,

Mazze

更新2:

我把事情搞得更多,现在这完全没有任何perl范围。你可以在你的shell中做到这一点:

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

现在不能打断。 但是,在我的情况下,以下内容仍然是可以中断的:

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

使用CTRL-C,会立即中断。与root用户的情况与我的情况相同,但其他同事不会在其用户身上看到此行为。我们比较了@ ENV,但我们没有发现可能导致不同行为的原因。

你怎么样:&#34; -S&#34;版本可以在&#34;陷阱&#39;&#39; INT&#34;在你的壳? (显然,您必须先为/ tmp / mysock创建一个主会话。)

此致

Mazze

2 个答案:

答案 0 :(得分:4)

问题是当你在控制台上按CTRL-C时,内核会向进程组的所有进程发送一个信号(参见Prevent control-c from sending SIGINT to all process group children)。

我认为你在行为上看到的差异实际上是由子进程的差异引起的,有些是重置信号标志,而有些则没有。

无论如何,我将添加对在不同进程组中运行SSH进程的支持。在模块RT上添加错误报告,所以我不要忘记它。

更新:以下实验显示OpenSSH system方法和内置版的行为方式相同:

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

如果运行它,即使INT信号处理程序设置为ssh,您也会看到信号到达并中止IGNORE进程。

更新2 :经过一些实验,我发现问题实际上存在于后台运行的主SSH进程上。除非您使用密码验证,否则它也会在进程组中挂起,因此会获得INT信号。

我正在添加一个新选项,明确要求将主服务器作为一个新的流程组运行,但由于选项数量很多,这部分代码非常复杂,所以这不是一件容易的事。

更新3 :我发布了一个新的development version (0.61_15)模块。

现在,你可以......

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

...希望没有SIGINT进入主进程或从进程SSH进程。

请报告您可能发现的任何问题!

答案 1 :(得分:1)

在您的情况下,通过将PERL_SIGNALS的环境变量设置为“不安全”,最好获得pre-Perl-5.8信号行为。

从Perl 5.8.1开始,这里描述了信号的正常方法:perlipc: Deferred Signals (Safe Signals)

但您可能想要的是直接行为,例如:perlrun: PERL_SIGNALS

所以,问题的解决方案可能是这个:

 $ENV{'PERL_SIGNALS'} = 'unsafe';
你的代码中的

:)

我在这里尝试过,到目前为止工作。但是这种方法可能存在缺点,因为我不知道你的程序是否会因此而面临不同的问题。