
Parece que pode haver um problema no openssh
. No bash
shell, se eu redirecionar stderr
para stdout
ele, ele bloqueará para sempre, então tenho que 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
O mesmo comando sem o redirecionamento funciona perfeitamente:
$ 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
Por que isso está acontecendo? Existe uma solução alternativa?
Responder1
Quando ssh -f
estiver "indo para segundo plano" após ter se conectado e autenticado no host, ele continuará mantendo identificadores abertos em seu stdin, stdout e stderr originais, portanto, se esses identificadores foram conectados via pipes a outros processos (como seu stdout + stderr estão no seu exemplo para cat -A
), terá o efeito de manter esses processos ativos, mesmo que eles não sejam mais necessários.
ssh
daemoniza-se chamando odaemon(3)
função de biblioteca, mas ela a chama com noclose = 1
, impedindo-a de redirecionar o stdin/stderr/stdout de /dev/null
.
Isso foi parcialmente corrigido em versões recentes openssh
(para o processo de controle mestre - o stdin e o stdout em2010, o stderr em2016; para o processo de sessão - o stdout em2017), mas se você precisar executar um ssh mais antigo ou precisar que ele pare de se apegar ao stderr também, a única "solução" pode ser usar um LD_PRELOAD
hack que substitua a daemon(3)
função por um wrapper que chame o original com 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
...
Responder2
Por que isso está acontecendo?
É cat
que precisa ser finalizado antes que o shell chegue ao echo
. Está “bloqueando para sempre” porque as cat
vidas.
Existe uma solução alternativa?
No Bash eu usaria a substituição de processos para executar cat
que não bloqueia. Com cat
fora do caminho, é fácil apenas echo OK
se estiver realmente OK (com &&
ou $?
). Exemplo:
ssh -f … > >(cat -A) 2>&1 && echo OK; echo "The script goes on."
Agora cat
funciona antes e depois de ssh
ir para segundo plano, mas o script continua assim que ssh
o faz (ou assim que ssh
falha sem ir para segundo plano).