sshの終了後、sshコマンドが予期せず他のシステムで続行される


11

以下のコマンドを実行して、他のシステムの出力ファイルを監視しています。

ssh $ip_address 'for n in 1 2 3 4 5; do sleep 10; echo $n >>/tmp/count; done'

^Cログインしている端末を使用して、または単に端末を強制終了することによってsshコマンドを強制終了した場合、リモートコマンドも終了することを期待しています。ただし、これは発生しません。1から/tmp/count5までのすべての数値を取得しps -ejH、シェルとそのsleep子が実行を続けていることを示します。

これは予想される動作であり、どこかに文書化されていますか?無効にできますか?周りから読むと、私はこの種の動作をデフォルトではなく、nohupで明示的に有効にする必要があると予想していました。

私はsshとsshdのmanページを調べましたが、明らかなものは何も見つかりませんでした。Googleは、この動作をオフにするためではなく、オンにするための手順を示しています。

Red Hat Enterprise Linux 6.2を実行していて、両方のシステムでrootログインとbashシェルを使用しています。

回答:


11

utherの回答では、端末を割り当てるように指示されていますが、理由は説明されていません。その理由はsshに固有のものではなく、信号の生成と伝播の問題です。さまざまな信号が送信される原因を教えてくださいより多くの背景のために。

リモートホストには、2つの関連プロセスがあります。

  • sshdリモートプログラムの入力と出力をローカル端末に中継しているsshデーモン()のインスタンス。
  • そのforループを実行しているシェル。

シェルは、ループの終わりに達したとき、または致命的なエラーが発生したときに自然に死ぬか、シグナルによって死ぬ可能性があります。問題は、なぜシェルがシグナルを受け取るのかということです。

リモートシェルがsshdパイプを介して接続されている場合(sshコマンドラインでコマンドを指定した場合)、終了してシェルがパイプに書き込もうとすると、SIGPIPEでsshd終了します。シェルがパイプに書き込んでいない限り、シェルはSIGPIPEを受け取りません。ここでは、シェルは標準出力に何も書き込まないため、永久に存続できます。

-tオプションをsshに渡して、リモート側の端末をエミュレートし、この端末で指定されたコマンドを実行するように指示できます。次に、SSHクライアントが消えた場合、sshd接続を閉じて終了し、端末を破棄します。端末デバイスがなくなると、その中で実行されているプロセスはすべてSIGHUPを受け取ります。そのため、SSHクライアントをkill強制終了した場合(または、クライアントが実行されている端末を閉じることにより)、リモートシェルはSIGHUPされます。

-tオプションを渡すと、SSHはSIGINTもリレーします。Ctrl+ を押すとC、リモートシェルはSIGINTを受信します。


ssh自体にttyが割り当てられていない場合-tt-t、代わりに使用してください。-fまたはのようなオプションを介して呼び出した直後にsshがバックグラウンド化された場合、SSHは割り当てられたttyを取得しません-n
Miron V

3

sshコマンドで疑似ttyを割り当ててみてください。

ssh -t $ip_address 'for n in 1 2 3 4 5; do sleep 10; echo $n >>/tmp/count; done'

sshセッションを切断すると、プロセスが終了します。

これは疑似ttyであるため、シェルの初期化はおそらく構成ファイルをソースにしないため、コマンドは非常に最小限の環境のままになります。.ssh/environmentPATHなどの環境変数を定義するファイルを設定する必要がある場合があります。からman 1 ssh

Additionally, ssh reads ~/.ssh/environment, and adds lines of the format 
“VARNAME=value” to the environment if the file exists and users are allowed
to change their environment.  For more information, see the 
PermitUserEnvironment option in sshd_config(5).

2

-tオプションを使用sshして、リモートコマンドをsshクライアントの終了時または終了時に終了させるオプションの代わりに、名前付きパイプを使用して、stdin sshdが閉じているときに「EOFをSIGHUPに変換」できます(バグ396-sshdを参照)。ptyが割り当てられていない場合、プロセスを孤立させます)。

# sample code in Bash
# press ctrl-d for EOF
ssh localhost '
TUBE=/tmp/myfifo.fifo
rm -f "$TUBE"
mkfifo "$TUBE"
#exec 3<>"$TUBE"

<"$TUBE" sleep 100 &  appPID=$!

# cf. "OpenSSH and non-blocking mode", 
# http://lists.mindrot.org/pipermail/openssh-unix-dev/2005-July/023090.html
#cat >"$TUBE"
#socat -u STDIN "PIPE:$TUBE"
dd of="$TUBE" bs=1 2>/dev/null
#while IFS="" read -r -n 1 char; do printf '%s' "$char"; done > "$TUBE"

#kill -HUP -$appPID 
kill $appPID

rm -f "$TUBE"
'

1

これは私がこれを行うために見つけた最良の方法です。stdinを読み込もうとしてサーバーグループでプロセスグループを強制終了するサーバーサイドの何かが必要ですが、サーバーサイドプロセスが完了するまでブロックし、<(睡眠無限大)かもしれません。

ssh localhost "sleep 99 < <(cat; kill -INT 0)" <&1

実際にはstdoutをどこにもリダイレクトしないようですが、ブロッキング入力として機能し、キーストロークのキャプチャを回避します。

関連するopensshバグ:https ://bugzilla.mindrot.org/show_bug.cgi ?id= 396#c14


0

これを無効にしたい場合(デフォルトの動作と思われる)、クライアント側またはサーバー側でssh-keep-aliveを有効にする必要があります。

マンページのssh-keep-alive-optionsを見ると、デフォルトで無効になっていることがわかります。


0

私の答えはテルのに基づいています。~/helperサーバーserver.ipにヘルパースクリプトが必要です。

#!/bin/bash
TUBE=/tmp/sshCoLab_myfifo.$$;
mkfifo "$TUBE"
( <"$TUBE" "$@" ; rm "$TUBE" ; kill -TERM 0 ) &  
cat >"$TUBE" ;
rm "$TUBE" ;
kill -TERM 0 ;

それが例えばと呼ばれる場合

ssh server.ip ~/helper echo hello from server

そしてecho hello from serverserver.ipで実行すると、sshクライアントが終了します。

それが例えばと呼ばれる場合

ssh server.ip ~/helper sleep 1000 &
CHID=$!

kill -9 $CHIDサーバーでもスクリプトを停止します。私にとって、kill -INT $CHID動作しませんが、理由はわかりません。

teruの答えでは、sshコマンドはリモートコマンドが終了すると、cat決して終了しないため、永久に待機します。

ssh -tを使用したすべての回答がで機能せずkill、Ctrl-Cでのみ機能しました。

編集:私は、これが新しいUbuntu 14.01から古い科学的なLinuxボックスまで機能することを発見しましたが、その逆ではありませんでした。したがって、一般的な解決策はないと思います。変だ。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.