$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 项目