ssh経由でリモートホストで実行してるプロセスにSIGINT送りたい時
perl で色々管理スクリプト書いてるんですが、そのなかでこんなコードを書きました。
system("ssh remote 'rsync ...'");
で、rsync
が走ってる途中でやっぱやめたと思ってCtrl+C=SIGINT
を送ったんですが、もちろん perl のプロセスは死ぬんですけど、remote
で動いてるrsync
はそのままゾンビになって残ってしまいました。
はて、いろんなところに原因が考えられるなぁということで調べてみました。
host1> ssh host2 'some-command'
host2> strace -p 20279 # some-command's pid
Process 20279 attached - interrupt to quit
read(0,
# then "Ctrl+C" on host1
(host2)
"", 4096) = 0
write(2, "Use of uninitialized value in co"..., 85) = -1 EPIPE (Broken pipe)
--- SIGPIPE (Broken pipe) @ 0 (0) ---
rt_sigreturn(0) = -1 EPIPE (Broken pipe)
rt_sigprocmask(SIG_BLOCK, [PIPE], NULL, 8) = 0
rt_sigprocmask(SIG_UNBLOCK, [PIPE], NULL, 8) = 0
write(2, "sigpipe at hogehoge line "..., 35) = -1 EPIPE (Broken pipe)
--- SIGPIPE (Broken pipe) @ 0 (0) ---
rt_sigreturn(0) = -1 EPIPE (Broken pipe)
exit_group(32) = ?
Process 20279 detached
なるほど、ssh に SIGINT を送っても、リモートホストには SIGINT が飛んでないんですね。でも、ssh 自体は死んでしまうのでリモート側のsome-command
は SIGPIPE が送られることになり、今回は結果としてそのシグナルで死んでる様です。未確認ですが、おそらく rsync は SIGPIPE では死んでくれなかったから、ゾンビで動きつづけたんでしょうね。
とすると、ssh でのシグナルのハンドリングを調査しないといけないなーと思ってたんですが、普通にググってたらぴったりの質問が stackoverflow にありました。
-
signals – How to send SIGINT to a remote process over SSH? – Stack Overflow
host1> ssh -t host2 ‘some-command’
host2> strace -p 4480 … read(0, 0x2b73920, 4096) = ? ERESTARTSYS (To be restarted) --- SIGINT (Interrupt) @ 0 (0) --- rt_sigreturn(0) = -1 EINTR (Interrupted system call) rt_sigprocmask(SIG_BLOCK, [INT], NULL, 8) = 0 rt_sigprocmask(SIG_UNBLOCK, [INT], NULL, 8) = 0 write(2, “sigint at hogehoge line 4”…, 34) = 34 exit_group(4) = ? Process 4480 detached
というわけで無事 SIGINT がやってきました。
ssh -t
というオプションはpseudo-tty
を強制すると書いてあるのですが正直pseudo-tty
がなんなのかよくわからない。。。これもググッてみると分かりやすい例がありました。
なるほどなるほど。-t
しない場合、こういうことが起こっちゃうんですね。pseudo-tty
を割り当てることでいい感じに動いてくれます。
というわけで、冒頭のスクリプトも以下の様にsystem
で呼ぶコマンドに-t
を与えてしまえば無事 SIGINT が送られましたとさ!
system("ssh -t remote 'rsync ...'");