
Кажется, проблема может быть в 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
(для главного процесса управления — stdin и stdout в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
жизни.
Есть ли обходной путь?
В Bash я бы использовал подстановку процесса для запуска cat
, который не блокируется. С cat
убранным с пути это тогда легко сделать, echo OK
только если это действительно нормально (с &&
или $?
). Пример:
ssh -f … > >(cat -A) 2>&1 && echo OK; echo "The script goes on."
Теперь cat
работает до и после ssh
перехода в фоновый режим, но скрипт продолжает работу сразу после ssh
этого (или сразу после ssh
сбоя без перехода в фоновый режим).