La commande ssh continue de façon inattendue sur un autre système après la fin de ssh

11

J'exécute la commande ci-dessous et surveille le fichier de sortie sur l'autre système:

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

Si je tue la commande ssh en utilisant ^Cou en tuant simplement le terminal auquel je suis connecté, je m'attends à ce que la commande à distance se termine également. Cependant, cela ne se produit pas: /tmp/countobtient tous les nombres de 1 à 5 et ps -ejHmontre que le shell et son sleepenfant continuent de fonctionner.

Ce comportement est-il attendu et est-il documenté quelque part? Puis-je le désactiver? En lisant, je m'attendais à devoir explicitement activer ce type de comportement avec nohup, pas pour que ce soit la valeur par défaut.

J'ai jeté un coup d'œil dans les pages de manuel pour ssh et sshd, mais je n'ai rien repéré d'évident, et Google me montre les instructions pour activer ce comportement, pas pour le désactiver.

J'utilise Red Hat Enterprise Linux 6.2, avec un login root et un shell bash sur les deux systèmes.

moi et
la source

Réponses:

11

La réponse de uther vous indique d'allouer un terminal mais n'explique pas pourquoi. La raison n'est pas spécifique à ssh, c'est une question de génération et de propagation de signal. Je vous invite à lire Qu'est-ce qui provoque l'envoi de divers signaux? pour plus de fond.

Sur l'hôte distant, il existe deux processus pertinents:

  • une instance du démon ssh ( sshd), qui relaie l'entrée et la sortie du programme distant au terminal local;
  • un shell, qui exécute cette forboucle.

La coquille peut mourir soit naturellement lorsqu'elle atteint la fin de la boucle ou subit une erreur fatale, soit à un signal. La question est, pourquoi le shell recevrait-il un signal?

Si le shell distant est connecté à des sshdcanaux, ce qui se produit lorsque vous spécifiez une commande sur la sshligne de commande, il mourra d'un SIGPIPE s'il se sshdtermine et le shell essaie d'écrire dans le tube. Tant que le shell n'écrit pas dans le canal, il ne recevra pas de SIGPIPE. Ici, le shell n'écrit jamais rien sur sa sortie standard, il peut donc vivre éternellement.

Vous pouvez passer l' -toption à ssh, pour lui dire d'émuler un terminal du côté distant et d'exécuter la commande spécifiée dans ce terminal. Ensuite, si le client SSH disparaît, sshdferme la connexion et se ferme, détruisant le terminal. Lorsque le terminal disparaît, tout processus en cours d'exécution reçoit un SIGHUP . Donc, si vous tuez le client SSH (avec killou en fermant le terminal dans lequel le client s'exécute), le shell distant est SIGHUPped.

Si vous passez cette -toption, SSH relaie également SIGINT . Si vous appuyez sur Ctrl+ C, le shell distant reçoit un SIGINT.

Gilles 'SO- arrête d'être méchant'
la source
Utilisez -ttau lieu de -tsi ssh lui-même n'a pas de tty alloué. SSH n'obtiendra pas de tty alloué si ssh est mis en arrière-plan immédiatement lors de l'appel via des options comme -fou -n.
Miron V
3

Essayez d'allouer un pseudo-tty avec votre sshcommande.

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

Lorsque vous déconnectez la session ssh, le processus doit se terminer.

Comme il s'agit d'un pseudo-tty, l'initialisation de votre shell ne va probablement pas aux fichiers de configuration source, laissant votre commande dans un environnement très nu. Vous devrez peut-être configurer un .ssh/environmentfichier qui définit les variables d'environnement telles que PATH. Deman 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
la source
2

Au lieu d'utiliser l' -toption permettant sshde terminer la commande à distance lorsque le sshclient se ferme ou (est tué), un canal nommé peut être utilisé pour convertir "EOF en SIGHUP" lorsque le stdin de sshdest fermé (voir bogue 396 - sshd processus orphelins quand aucun pty n'est alloué ).

# 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
la source
1

C'est le meilleur moyen que j'ai trouvé pour le faire. Vous voulez quelque chose du côté serveur qui tente de lire stdin puis tue le groupe de processus en cas d'échec, mais vous voulez également un stdin côté client qui bloque jusqu'à ce que le processus côté serveur soit terminé et ne laisse pas de processus persistants comme <( dormir à l'infini) pourrait.

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

Il ne semble pas réellement rediriger stdout n'importe où, mais il fonctionne comme une entrée de blocage et évite de capturer les frappes.

Bogue openssh pertinent: https://bugzilla.mindrot.org/show_bug.cgi?id=396#c14

Eric Woodruff
la source
0

Si vous souhaitez désactiver cela (apparemment comportement par défaut), vous devez activer ssh-keep-alive côté client ou serveur.

Si vous regardez les options ssh-keep-alive-dans les pages de manuel, vous pouvez voir qu'elles sont désactivées par défaut.

Nils
la source
0

Ma réponse est basée sur celle de teru. J'ai besoin d'un script d'aide ~/helpersur le serveur server.ip:

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

Si elle est appelée par exemple avec

ssh server.ip ~/helper echo hello from server

et s'exécute echo hello from serversur server.ip, puis le client ssh se terminera.

Si elle est appelée par exemple avec

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

kill -9 $CHIDarrêtera également le script sur le serveur. Pour moi, kill -INT $CHIDça ne marche pas, mais je ne sais pas pourquoi.

Dans la réponse de teru, la commande ssh attendra indéfiniment lorsque la commande à distance est terminée, car elle catne se termine jamais.

Toutes les réponses avec ssh -t ne fonctionnaient pas pour moi avec kill, mais seulement avec Ctrl-C.

Edit: J'ai découvert que cela fonctionnait à partir d'un nouvel Ubuntu 14.01 vers une ancienne boîte Linux scientifique - mais pas l'inverse. Ainsi, je pense qu'il n'y a pas de solution générale. Bizarre.

Peter Steier
la source