El cliente Openssh se bloquea para siempre si stderr se redirige a stdout

El cliente Openssh se bloquea para siempre si stderr se redirige a stdout

Parece que puede haber un problema en el openssh. En el bashshell, si redirijo stderra stdoutél, se bloqueará para siempre, así que tengo 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

El mismo comando sin la redirección funciona bien:

$ 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 qué está pasando esto? ¿Existe alguna solución?

Respuesta1

Cuando ssh -f"pasa a segundo plano" después de haberse conectado y autenticado en el host, continuará manteniendo abiertos los identificadores de su stdin, stdout y stderr originales, por lo que si esos identificadores estaban conectados a través de tuberías a otros procesos (como su stdout + stderr están en su ejemplo de cat -A), tendrá el efecto de mantener vivos esos procesos, incluso si ya no son necesarios.

sshse demoniza a sí mismo llamando aldaemon(3)función de biblioteca, pero la llama con noclose = 1, evitando que redirija stdin/stderr/stdout desde /dev/null.

Esto se solucionó parcialmente en versiones recientes de openssh(para el proceso de control maestro: stdin y stdout en2010, el estándar en2016; para el proceso de sesión: la salida estándar en2017), pero si tiene que ejecutar un ssh anterior, o necesita que deje de aferrarse a stderr también, la única "solución" puede ser usar un LD_PRELOADtruco que anule la daemon(3)función con un contenedor que llame al original con 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
...

Respuesta2

¿Por qué está pasando esto?

Es catalgo que debe terminarse antes de que el shell llegue a echo. Está "bloqueando para siempre" porque las catvidas.

¿Existe alguna solución?

En Bash, usaría la sustitución de procesos para ejecutar catque no se bloquee. Con catfuera del camino, entonces es fácil echo OKsolo si está realmente bien (con &&o $?). Ejemplo:

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

Ahora catfunciona antes y después sshde pasar a segundo plano, pero el script continúa tan pronto como sshlo hace (o tan pronto como sshfalla sin pasar a segundo plano).

información relacionada