Perl $SIG{INT}=&x27;忽略';不使用Net::OpenSSH

Perl $SIG{INT}=&x27;忽略';不使用Net::OpenSSH,perl,perl-module,openssh,Perl,Perl Module,Openssh,我需要在后台通过$ssh->system执行命令,并且有文档记录,这应该像本地“system”命令一样: 实际上,这似乎不是真的,因为当收到SIGINT时,子系统中的$ssh->system会立即终止,即使我明确希望先“忽略”它: use warnings; use strict; use POSIX ":sys_wait_h"; use Net::OpenSSH; my $CTRLC=0; $SIG{CHLD}='IGNORE'; sub _int_handler { $CTRLC++

我需要在后台通过$ssh->system执行命令,并且有文档记录,这应该像本地“system”命令一样:

实际上,这似乎不是真的,因为当收到SIGINT时,子系统中的$ssh->system会立即终止,即使我明确希望先“忽略”它:

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);
当运行此代码并试图在第一个“sleep3”启动后中断时,“$SSH->system”调用立即结束

但是,如果您使用下面的本地“system”语句,则可以正确捕获SIGINT

在Net::OpenSSH的源代码中,我发现$SIG{INT}在“system”子目录中也显式地设置为“IGNORE”。我不知道这为什么不起作用

我会感谢任何可能的解决方案,如果它意味着以不同的方式做事的话。最后,我只想远程执行命令,并且仍然保护它们不受CTRL-C的影响

谢谢,

玛兹

更新:

感谢您在salva上的投入。我进一步简化了内容,最后它似乎是ssh的“-S”标志:

$SIG{INT}='IGNORE';
# system('ssh -S /tmp/mysock lnx0001a -- sleep 3');
  system('ssh                lnx0001a -- sleep 3');
只要使用“-S/tmp/mysock”变体,ssh就似乎是可中断的,而不是其他方式。有人能解释一下吗? 我应该为此发布一个新的、独立的问题吗

再次感谢

玛兹

更新2:

我把事情弄得更糟了,现在这完全没有任何perl作用域。您可以在shell中执行此操作:

$ trap '' INT
$ ssh                lnx0001a -- sleep 3
现在不可中断。 然而,在我的情况下,以下仍然是可中断的:

$ ssh -S /tmp/mysock lnx0001a -- sleep 3
使用CTRL-C,这会立即中断。root用户的情况与我的相同,但其他同事看不到他们的用户有这种行为。我们比较了@ENV,但没有发现可能导致不同行为的原因

您的情况如何?“-S”版本在shell中的“trap”“INT”之后是否可以中断? (显然,您之前必须为/tmp/mysock创建主会话)

真诚地


Mazze

问题在于,当您在控制台上按CTRL-C时,内核会向进程组上的所有进程发送一个信号(请参阅)

我认为您在行为上看到的差异实际上是由子进程上的差异造成的,一些重置了信号标志,而另一些没有

无论如何,我将添加对在不同进程组中运行SSH进程的支持。请在模块上添加错误报告,这样我就不会忘记它

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

my @cmd = $SSH->make_remote_command("sleep 3 && echo hello");
warn "running command @cmd\n";
local $SIG{INT} = 'IGNORE';
system @cmd;
如果运行它,您将看到信号到达并中止
ssh
进程,即使INT信号处理程序设置为
IGNORE

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

我添加了一个新选项,明确要求将主进程作为一个新的进程组运行,但由于选项太多,这部分代码非常复杂,因此这不是一项容易的任务

更新3:我发布了一个新的模块

现在,你可以做

my $ssh = Net::OpenSSH->new($host, master_setpgrp => 1, ...);
$ssh->system({setpgrp => 1}, "sleep 10; echo uninterruptible");
。。。希望主SSH进程或从SSH进程中不会出现SIGINT


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

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

由于Perl 5.8.1,这里描述了信号的正常方法:

但你可能想要的是即时行为,比如:

因此,您的问题的解决方案可能是:

 $ENV{'PERL_SIGNALS'} = 'unsafe';
在您的代码中:)


我在这里试过了,到目前为止还有效。但这种方法可能有缺点,因为我不知道您的程序是否会因此面临不同的问题。

@Mazze,请参阅我的最新回复。谢谢您的更新2。实际上,我对主会话没有任何问题,主会话似乎尊重我的$SIG{INT}='IGNORE'设置。因此,SIGINT在这里不中断主会话,只中断客户端会话。您不应该依赖于使用继承信号掩码的某个进程。它起作用只是偶然的。无论如何,请参阅我的更新3。更新3和0.61_15已使用新的“setpgrp”标志解决了我的问题。非常感谢您的快速回复。即使在后台打开大量会话(async=>1),也没有问题:)
 $ENV{'PERL_SIGNALS'} = 'unsafe';