2014-04-01 20 views
4

我需要通過$ 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; 
$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-> system」調用即刻我終於結束了。

但是,如果您使用下面的本地「系統」語句,SIGINT會被正確捕獲。

在Net :: OpenSSH的源代碼中,我發現$ SIG {INT}在「system」子版中也顯式設置爲「IGNORE」。我不知道爲什麼這不起作用。

我會感謝任何一種可能的解決方案,如果這意味着做不同的事情。最後,我只想遠程執行命令,並保護它們免受CTRL-C的影響。

謝謝

Mazze

UPDATE:

謝謝您的輸入@salva。我進一步剝離下來,最後它似乎是「-S」 SSH的標誌:

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

一旦正在使用的「-S的/ tmp/mysock」變種,SSH似乎是可以中斷,否則。有沒有人可以給出解釋呢? 我應該爲此發佈一個新的獨立問題嗎?再次

感謝,

Mazze

更新2:

我把東西放下就更多了,現在這是完全沒有任何perl的範圍。你可以在你的外殼上做到這一點:

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

現在不可中斷。 在我的情況,但是,下面仍然是可中斷:

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

使用CTRL-C,這個被立即中斷。 root用戶和我的情況是一樣的,但其他同事沒有看到他們的用戶有這種行爲。我們比較了@ENV,但我們沒有找出可能導致不同行爲的原因。

它是如何與你:「-S」版本是否可以在你的外殼「陷阱」「INT」後中斷? (很明顯,你必須在之前爲/ tmp/mysock創建一個主會話)。

此致

Mazze

+0

@Mazze,看到我更新的回覆。 – salva

回答

3

的問題是,當按下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; 

如果你運行它,你會看到,在信號到達並中止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過程。

報告任何問題,你可能會發現,請!

+0

感謝您的Update2。其實,我沒有任何問題與主會議,主會議似乎尊重我的設置$ SIG {INT} ='IGNORE'。因此,SIGINT不會在這裏中斷主會話,只會在客戶端會話中斷。 – Mazze

+0

您不應該依賴某些使用繼承的信號掩碼的進程。當它的工作是偶然的。無論如何看到我的更新3. – salva

+0

更新3和0.61_15使用新的'setpgrp'標誌解決了我的問題。非常感謝您的快速回復。即使在後臺打開大量會話(異步=> 1),也沒有問題:) – Mazze

1

就你而言,通過將PERL_SIGNALS的環境變量設置爲'unsafe',最好是獲得pre-Perl-5.8信號行爲。

因爲Perl 5.8.1信號的正常方法如下所述:perlipc: Deferred Signals (Safe Signals)

但是你也許想要的是即時的行爲,就像這裏:perlrun: PERL_SIGNALS

所以,解決你的問題可能是把這個:您的代碼:)

我想在這裏

$ENV{'PERL_SIGNALS'} = 'unsafe'; 

,工作至今。但是這種方法可能有一個缺點,因爲我不知道你的程序是否會面臨不同的問題。

相關問題