El comando ssh continúa inesperadamente en otro sistema después de que ssh termina

11

Estoy ejecutando el siguiente comando y monitoreando el archivo de salida en el otro sistema:

ssh $ip_address 'for n in 1 2 3 4 5; do sleep 10; echo $n >>/tmp/count; done'

Si elimino el comando ssh usando ^Co simplemente eliminando el terminal en el que he iniciado sesión, esperaría que el comando remoto también finalice. Sin embargo, esto no sucede: /tmp/countobtiene todos los números del 1 al 5 independientemente y ps -ejHmuestra que el shell y su sleephijo continúan ejecutándose.

¿Es este comportamiento esperado y está documentado en alguna parte? ¿Puedo desactivarlo? Después de leer, esperaba tener que habilitar explícitamente este tipo de comportamiento con nohup, no para que sea el predeterminado.

He echado un vistazo a las páginas de manual de ssh y sshd, pero no vi nada obvio, y Google me señala las instrucciones para activar este comportamiento, no para desactivarlo.

Estoy ejecutando Red Hat Enterprise Linux 6.2, con un inicio de sesión raíz y bash shell en ambos sistemas.

yo y
fuente

Respuestas:

11

la respuesta de uther le dice que asigne un terminal pero no explica por qué. La razón no es específica de ssh, es una cuestión de generación y propagación de señales. Los invito a leer ¿Qué causa que se envíen varias señales? para más antecedentes

En el host remoto, hay dos procesos relevantes:

  • una instancia del ssh daemon ( sshd), que está transmitiendo la entrada y salida del programa remoto al terminal local;
  • un shell, que ejecuta ese forciclo.

El caparazón puede morir de forma natural cuando llega al final del bucle o experimenta un error fatal o una señal. La pregunta es, ¿por qué el shell recibiría una señal?

Si el shell remoto está conectado a sshdtravés de tuberías, que es lo que sucede cuando especifica un comando en la sshlínea de comando, entonces morirá de un SIGPIPE si sshdsale y el shell intenta escribir en la tubería. Mientras el shell no escriba en la tubería, no recibirá un SIGPIPE. Aquí, el shell nunca escribe nada en su salida estándar, por lo que puede vivir para siempre.

Puede pasar la -topción a ssh, para decirle que emule un terminal en el lado remoto y ejecute el comando especificado en este terminal. Luego, si el cliente SSH desaparece, sshdcierra la conexión y sale, destruyendo el terminal. Cuando el dispositivo terminal desaparece, cualquier proceso que se ejecute en él recibe un SIGHUP . Entonces, si mata al cliente SSH (con kill, o al cerrar el terminal en el que se está ejecutando el cliente), el shell remoto se SIGHUPped.

Si pasa la -topción, SSH también retransmite SIGINT . Si presiona Ctrl+ C, el shell remoto recibe un SIGINT.

Gilles 'SO- deja de ser malvado'
fuente
Use en -ttlugar de -tsi ssh no tiene un tty asignado. SSH no obtendrá un tty asignado si ssh se pone en segundo plano inmediatamente después de la invocación mediante opciones como -fo -n.
Miron V
3

Intente asignar un psuedo-tty con su sshcomando.

ssh -t $ip_address 'for n in 1 2 3 4 5; do sleep 10; echo $n >>/tmp/count; done'

Cuando desconecta la sesión ssh, el proceso debería finalizar.

Como se trata de una pseudo-tty, su inicialización de shell probablemente no vaya a los archivos de configuración de origen, dejando su comando con un entorno muy simple. Es posible que deba configurar un .ssh/environmentarchivo que defina variables de entorno como PATH. Desdeman 1 ssh

Additionally, ssh reads ~/.ssh/environment, and adds lines of the format 
“VARNAME=value” to the environment if the file exists and users are allowed
to change their environment.  For more information, see the 
PermitUserEnvironment option in sshd_config(5).
George M
fuente
2

Como alternativa al uso de la -topción para sshhacer que el comando remoto termine cuando el sshcliente sale o (se mata), se puede usar una tubería con nombre para convertir "EOF a SIGHUP" cuando sshdse cierra el stdin de (ver Bug 396 - sshd los huérfanos procesan cuando no se asigna pty ).

# sample code in Bash
# press ctrl-d for EOF
ssh localhost '
TUBE=/tmp/myfifo.fifo
rm -f "$TUBE"
mkfifo "$TUBE"
#exec 3<>"$TUBE"

<"$TUBE" sleep 100 &  appPID=$!

# cf. "OpenSSH and non-blocking mode", 
# http://lists.mindrot.org/pipermail/openssh-unix-dev/2005-July/023090.html
#cat >"$TUBE"
#socat -u STDIN "PIPE:$TUBE"
dd of="$TUBE" bs=1 2>/dev/null
#while IFS="" read -r -n 1 char; do printf '%s' "$char"; done > "$TUBE"

#kill -HUP -$appPID 
kill $appPID

rm -f "$TUBE"
'
teru
fuente
1

Esta es la mejor manera que he encontrado para hacer esto. Desea algo en el lado del servidor que intente leer stdin y luego mata al grupo de procesos cuando eso falla, pero también desea un stdin en el lado del cliente que se bloquee hasta que se complete el proceso del lado del servidor y no deje procesos persistentes como <( dormir infinito) poder.

ssh localhost "sleep 99 < <(cat; kill -INT 0)" <&1

En realidad, no parece redirigir stdout a ninguna parte, pero funciona como una entrada de bloqueo y evita capturar pulsaciones de teclas.

Error relevante de openssh: https://bugzilla.mindrot.org/show_bug.cgi?id=396#c14

Eric Woodruff
fuente
0

Si desea deshabilitar eso (aparentemente comportamiento predeterminado), debe habilitar ssh-keep-alive en el lado del cliente o del servidor.

Si observa las opciones ssh-keep-alive-en las páginas de manual, puede ver que están deshabilitadas de forma predeterminada.

Nils
fuente
0

Mi respuesta se basa en teru. Necesito un script de ayuda ~/helperen el servidor server.ip:

#!/bin/bash
TUBE=/tmp/sshCoLab_myfifo.$$;
mkfifo "$TUBE"
( <"$TUBE" "$@" ; rm "$TUBE" ; kill -TERM 0 ) &  
cat >"$TUBE" ;
rm "$TUBE" ;
kill -TERM 0 ;

Si se llama, por ejemplo, con

ssh server.ip ~/helper echo hello from server

y se ejecuta echo hello from serveren server.ip, y luego el cliente ssh terminará.

Si se llama, por ejemplo, con

ssh server.ip ~/helper sleep 1000 &
CHID=$!

kill -9 $CHIDtambién detendrá el script en el servidor. Para mí, kill -INT $CHIDno funciona, pero no sé por qué.

En la respuesta de teru, el comando ssh esperará para siempre cuando termine el comando remoto, porque catnunca termina.

Todas las respuestas con ssh -t no funcionaron para mí kill, sino solo con Ctrl-C.

Editar: descubrí que esto funcionó desde un nuevo Ubuntu 14.01 a una caja de linux científica más antigua, pero no al revés. Por lo tanto, creo que no hay una solución general. Extraño.

Peter Steier
fuente