$ SIG {INT} ='IGNORE'不适用于Net :: OpenSSH [英] $SIG{INT}='IGNORE' not working with Net::OpenSSH

查看:94
本文介绍了$ SIG {INT} ='IGNORE'不适用于Net :: OpenSSH的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要在后台通过$ ssh-> system执行命令,据记录,该命令的行为应与本地"system"命令类似:

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

实际上,这似乎并不正确,因为接收到SIGINT时,子级中的$ ssh-> system会立即终止,即使我明确想先"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);

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

但是,如果您改用下面的本地"system"语句,则会正确捕获SIGINT.

在Net :: OpenSSH的源代码中,我发现$ SIG {INT}也已在系统"子目录中显式设置为"IGNORE".我不知道为什么这不起作用.

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

谢谢

马兹

更新:

感谢您的输入@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范围.您可以在外壳中执行此操作:

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

现在不可中断. 但是,就我而言,以下内容仍然可以中断:

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

使用CTRL-C,将立即中断此操作.根用户和我的用户都是这种情况,但是其他同事看不到其用户的这种行为.我们比较了@ENV,但没有发现可能导致不同行为的原因.

您的情况如何:在外壳程序中的"trap INT"之后,-S"版本是否可中断? (显然,之前您必须为/tmp/mysock创建一个主会话).

此致

马兹

解决方案

问题是,当您在控制台上按CTRL-C时,内核会向进程组中的所有进程发送信号(请参见防止control-c将SIGINT发送给所有进程组子进程).

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

无论如何,我将添加对在另一个进程组中运行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信号处理程序设置为IGNORE,也会看到信号到达并中止ssh进程.

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

我正在添加一个新选项,以明确要求将主服务器作为新的进程组运行,但是由于有大量的选项,这部分代码相当复杂,所以这不是一件容易的事.

更新3 :我发布了一个新的开发版本(0.61_15).

现在,您可以...

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

...并且希望没有SIGINT会进入主SSH或从SSH进程.

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

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

解决方案

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!

这篇关于$ SIG {INT} ='IGNORE'不适用于Net :: OpenSSH的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆