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
なんでこんなことが起こっているの?回避策はありますか?
ssh -f
がホストに接続して認証された後、「バックグラウンドに移行」している場合、ホストは元のstdin、stdout、およびstderrへの開いたハンドルを保持し続けるため、これらのハンドルがパイプを介して他のプロセスに接続されている場合(stdout + stderrはcat -A
の例です)、不要になった場合でも、これらのプロセスを存続させる効果があります。
ssh
は、 daemon(3)
ライブラリ関数を呼び出すことによって自身をデーモン化しますが、noclose = 1
で呼び出し、/dev/null
からstdin/stderr/stdoutをリダイレクトしないようにします。
これは、最近のバージョンのopenssh
で部分的に修正されました(マスター制御プロセスの場合 201 のstdinとstdout、 2016 のstderr;セッションプロセス 2017 )のstdoutですが、古い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
...
なんでこんなことが起こっているの?
シェルがcat
に到達する前に終了する必要があるのは、echo
です。 cat
が生きているので、それは「永遠にブロック」しています。
回避策はありますか?
Bashでは、プロセス置換を使用して、ブロックしないcat
を実行します。 cat
が邪魔にならないので、echo OK
それが本当にOKである場合のみ(&&
または$?
)。例:
ssh -f … > >(cat -A) 2>&1 && echo OK; echo "The script goes on."
これで、cat
がバックグラウンドに移行する前後にssh
が機能しますが、スクリプトはssh
が実行するとすぐに(またはssh
が失敗するとすぐに)続行されます。背景に)。