
に問題があるようですopenssh
。シェルで にリダイレクトbash
すると永久にブロックされるため、 を実行する必要があります。stderr
stdout
KeyboardInterrupt
$ ssh -fTNF './config' -MS './sockd5/ctrl_socket' -i './keys/id_rsa' -l 'root' -p '22' 'example.com' 2>&1 | cat -A; echo OK
ControlSocket ./sockd5/ctrl_socket already exists, disabling multiplexing^M$
^C
リダイレクトなしの同じコマンドは正常に動作します:
$ ssh -fTNF './config' -MS './sockd/ctrl_socket' -i './keys/id_rsa' -l 'root' -p '22' 'example.com' | cat -A; echo OK
ControlSocket ./sockd/ctrl_socket already exists, disabling multiplexing
OK
なぜこのようなことが起こるのでしょうか? 回避策はありますか?
答え1
ssh -f
がホストに接続して認証された後に「バックグラウンドに入る」場合、元の stdin、stdout、および stderr へのオープン ハンドルを保持し続けます。そのため、それらのハンドルがパイプ経由で他のプロセスに接続されている場合 (例では stdout + stderr が に接続されている) cat -A
、それらのプロセスが不要になった場合でも、それらのプロセスを存続させる効果があります。
ssh
呼び出して自分自身をデーモン化するdaemon(3)
ライブラリ関数ですが、 を使用して呼び出すためnoclose = 1
、 からの stdin/stderr/stdout のリダイレクトが防止されます/dev/null
。
これは、最近のバージョンで部分的に修正されましたopenssh
(マスター制御プロセスの場合 -2010、stderrの2016; セッションプロセスの場合 -- stdoutは2017) ですが、古い ssh を実行する必要がある場合、または stderr への固執も停止する必要がある場合、唯一の「解決策」は、で元の関数を呼び出すラッパーで関数をLD_PRELOAD
オーバーライドするハックを使用することです。daemon(3)
noclose = 0
$ cat daemon-force-close.c
#define _GNU_SOURCE
#include <unistd.h>
#include <dlfcn.h>
#include <err.h>
int daemon(int nochdir, int noclose){
static int (*orig)(int, int);
if(!orig && !(*(void**)&orig = dlsym(RTLD_NEXT, "daemon")))
errx(1, "%s", dlerror());
return orig(nochdir, 0);
}
$ cc -shared -Wall -O2 daemon-force-close.c -ldl -o daemon-force-close.so
$ LD_PRELOAD=./daemon-force-close.so \
ssh -Nf dummy@localhost -MS ./ctlsock 2>&1 | cat -A
dummy@localhost's password:
$
[no Ctrl-C needed]
$ ssh -S ~/w/c/ctlsock dummy@localhost
Last login: Tue May 28 21:04:26 2019 from ::1
...
答え2
なぜこうなった?
cat
シェルが に到達する前に終了する必要があるのはですecho
。 が存続するため、「永久にブロック」されますcat
。
回避策はありますか?
cat
Bash では、ブロックしない実行にプロセス置換を使用します。cat
を省略すると、echo OK
本当に問題ない場合 (&&
または を使用$?
) のみを実行するのが簡単になります。例:
ssh -f … > >(cat -A) 2>&1 && echo OK; echo "The script goes on."
現在はバックグラウンドに移行するcat
前と移行した後に動作しますssh
が、スクリプトは移行するとすぐにssh
(またはバックグラウンドに移行せずに失敗するとすぐにssh
)続行されます。