web-dev-qa-db-ja.com

stderrがstdoutにリダイレクトされた場合、OpenSSHクライアントは永久にブロックされます

opensshに問題があるようです。 bashシェルでstderrstdoutにリダイレクトすると、永久にブロックされるため、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

なんでこんなことが起こっているの?回避策はありますか?

2
NarūnasK

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
...
3
mosvy

なんでこんなことが起こっているの?

シェルが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が失敗するとすぐに)続行されます。背景に)。

1