
Es scheint, dass da ein Problem vorliegt openssh
. Wenn ich in der bash
Shell zu umleite, stderr
wird stdout
es dauerhaft blockiert, also muss ich 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
Derselbe Befehl ohne Umleitung funktioniert einwandfrei:
$ 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
Warum passiert das? Gibt es eine Problemumgehung?
Antwort1
Wenn ssh -f
es „in den Hintergrund geht“, nachdem eine Verbindung zum Host hergestellt und eine Authentifizierung durchgeführt wurde, hält es weiterhin Handles zu seinem ursprünglichen stdin, stdout und stderr offen. Wenn diese Handles also über Pipes mit anderen Prozessen verbunden waren (wie stdout + stderr in Ihrem Beispiel mit cat -A
), hat dies den Effekt, dass diese Prozesse am Leben bleiben, auch wenn sie nicht mehr benötigt werden.
ssh
dämonisiert sich selbst durch den Aufruf desdaemon(3)
Bibliotheksfunktion, ruft diese jedoch mit auf noclose = 1
und verhindert so die Umleitung von stdin/stderr/stdout von /dev/null
.
Dies wurde in neueren Versionen teilweise behoben openssh
(für den Master-Steuerungsprozess -- die Standardeingabe- und Standardausgabedateien in2010, der Standardfehler in2016; für den Sitzungsprozess -- die Standardausgabe in2017), aber wenn Sie ein älteres SSH ausführen müssen oder es auch nicht mehr an stderr hängen bleiben soll, besteht die einzige „Lösung“ möglicherweise darin, einen LD_PRELOAD
Hack zu verwenden, der die Funktion mit einem Wrapper überschreibt daemon(3)
, der das Original mit aufruft 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
...
Antwort2
Warum passiert das?
Es cat
muss beendet werden, bevor die Shell dorthin gelangt echo
. Es „blockiert für immer“, weil das cat
Leben.
Gibt es eine Problemumgehung?
In Bash würde ich Prozesssubstitution verwenden, um einen Prozess auszuführen cat
, der nicht blockiert. Mit cat
out of the way ist es dann einfach, echo OK
nur zu verwenden, wenn es wirklich OK ist (mit &&
oder $?
). Beispiel:
ssh -f … > >(cat -A) 2>&1 && echo OK; echo "The script goes on."
Funktioniert jetzt cat
vor und nach ssh
dem Wechsel in den Hintergrund, aber das Skript wird fortgesetzt, sobald ssh
dies geschieht (oder sobald ssh
es fehlschlägt, ohne in den Hintergrund zu wechseln).