$SIGINT='IGNORE' 不适用于 Net::OpenSSH

Posted

技术标签:

【中文标题】$SIGINT=\'IGNORE\' 不适用于 Net::OpenSSH【英文标题】:$SIGINT='IGNORE' not working with Net::OpenSSH$SIGINT='IGNORE' 不适用于 Net::OpenSSH 【发布时间】:2014-05-12 11:53:26 【问题描述】:

我需要在后台通过 $ssh->system 执行命令,据记录,这应该就像本地“系统”命令一样:

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

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

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

my $CTRLC=0;
$SIGCHLD='IGNORE';

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


$SIGINT='IGNORE';
my $SSH=Net::OpenSSH->new('testhost',
                               forward_agent => 1,
                       master_stderr_discard => 1,
                                 master_opts => [ -o =>"PasswordAuthentication=no"]);
$SIGINT=\&_int_handler;
sub _ssh 
  $SIGINT='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);

当运行此代码并在第一个“sleep 3”开始后尝试中断时,“$SSH->system”调用立即结束。

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

在 Net::OpenSSH 的源代码中,我发现 $SIGINT 在“system”子项中也显式设置为“IGNORE”。我不知道为什么这不起作用。

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

谢谢,

迷宫

更新:

感谢您的意见@salva。我进一步剥离了一些东西,最后它似乎是 ssh 的“-S”标志:

$SIGINT='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,但没有找出可能导致不同行为的原因。

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

此致,

迷宫

【问题讨论】:

@Mazze,查看我的更新回复。 【参考方案1】:

问题在于,当您在控制台上按 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 $SIGINT = 'IGNORE';
system @cmd;

如果您运行它,您将看到信号到达并中止 ssh 进程,即使 INT 信号处理程序设置为 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 进程或从属 SSH 进程。

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

【讨论】:

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

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

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

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

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

 $ENV'PERL_SIGNALS' = 'unsafe';

在你的代码中:)

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

【讨论】:

以上是关于$SIGINT='IGNORE' 不适用于 Net::OpenSSH的主要内容,如果未能解决你的问题,请参考以下文章

.Net Framework dll 不适用于 .Net Standard 项目

MSBUILDEMITSOLUTION 不适用于 .NET 4?

UNC 路径不适用于 .NET?

注销功能不适用于 ASP.NET 标识

注销功能不适用于 ASP.NET 标识

excel聚光灯-GDI