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

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

に問題があるようですopenssh。シェルで にリダイレクトbashすると永久にブロックされるため、 を実行する必要があります。stderrstdoutKeyboardInterrupt

$ 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

回避策はありますか?

catBash では、ブロックしない実行にプロセス置換を使用します。catを省略すると、echo OK本当に問題ない場合 (&&または を使用$?) のみを実行するのが簡単になります。例:

ssh -f … > >(cat -A) 2>&1 && echo OK; echo "The script goes on."

現在はバックグラウンドに移行するcat前と移行した後に動作しますsshが、スクリプトは移行するとすぐにssh(またはバックグラウンドに移行せずに失敗するとすぐにssh)続行されます。

関連情報