Comment terminer à distance appelé "tail -f" lorsque la connexion est fermée?

24

Je viens de remarquer que si j'exécute ssh user@remote_host tail -f /some/file, puis tail -f /some/filecontinue de fonctionner sur le remote_host même si la connexion ssh est fermée!

Ainsi, après plusieurs connexions et déconnexions, le nombre d'exécutions tail -f /some/fileaugmente. Comment se terminer réellement tail -florsque la connexion ssh est fermée?

Dmitry Frank
la source

Réponses:

33

Dans

ssh host tail -f file

Le sshclient se connecte au sshdserveur hostvia une connexion TCP. sshds'exécute tail -favec sa sortie standard redirigée vers un tuyau. sshdlit ce qui vient de l'autre extrémité du tube et l'encapsule dans le protocole sshd pour l'envoyer au sshclient. (avec rshd, tailstdout aurait été le socket directement, mais sshdajoute un chiffrement et est capable de multiplexer plusieurs flux (comme pour la redirection de port / agent / X11 / tunnel, stderr) sur une seule connexion TCP et doit donc recourir à des canaux).

Lorsque vous appuyez sur CTRL-C, un SIGINT est envoyé au sshclient. Cela fait sshmourir. En mourant, la connexion TCP est fermée. Et donc, le host, sshdmeurt aussi. tailn'est pas tué, mais sa sortie standard est maintenant une pipe sans lecteur à l'autre bout. Ainsi, la prochaine fois qu'il écrit quelque chose sur sa sortie standard, il recevra un SIGPIPE et mourra.

Dans:

ssh -t host 'tail -f file'

C'est la même chose sauf qu'au lieu d'être avec une pipe, la communication entre sshdet se tailfait via un pseudo-terminal. tailLa sortie standard est un pseudo-terminal esclave (comme /dev/pts/12) et quelle que soit l' tailécriture readdu côté maître (éventuellement modifiée par la discipline de ligne tty) par sshdet envoyée encapsulée au sshclient.

Côté client, avec -t, sshmet le terminal en rawmode. En particulier, cela désactive le mode canonique du terminal et la gestion du signal du terminal.

Ainsi, lorsque vous appuyez sur Ctrl+C, au lieu que la discipline de ligne de terminal du client envoie un SIGINT au sshtravail, cela envoie simplement le ^Ccaractère sur la connexion sshdet l' sshdécrit ^Csur le côté maître du terminal distant. Et la discipline de ligne du terminal distant envoie un SIGINTà tail. tailpuis meurt, sshdquitte et ferme la connexion et se sshtermine (si elle n'est pas autrement encore occupée avec les transferts de port ou autre).

De plus, avec -t, si le sshclient meurt (par exemple si vous entrez ~.), la connexion est fermée et sshdmeurt. En conséquence, un SIGHUP sera envoyé à tail.

Maintenant, sachez que l'utilisation -ta des effets secondaires. Par exemple, avec les paramètres de terminal par défaut, les \ncaractères sont convertis en \r\net plus de choses peuvent se produire en fonction du système distant, vous pouvez donc vouloir émettre un stty -opost(pour désactiver le post-traitement de sortie) sur l'hôte distant si cette sortie n'est pas destinée à un terminal:

$ ssh  localhost 'echo x' | hd
00000000  78 0a                                             |x.|
00000002
$ ssh -t localhost 'echo x' | hd
00000000  78 0d 0a                                          |x..|
00000003
$ ssh -t localhost 'stty -opost; echo x' | hd
00000000  78 0a                                             |x.|
00000002

Un autre inconvénient de l'utilisation de -t/ -ttest que stdout et stderr ne sont pas différenciés sur le client. Le stdout et le stderr de la commande à distance seront écrits sur le sshstdout du client:

$ ssh localhost ls /x  | wc -l
ls: cannot access /x: No such file or directory
0
$ ssh -t localhost ls /x | wc -l
1
Stéphane Chazelas
la source
Merci beaucoup pour une explication aussi détaillée! Je souhaite pouvoir accepter deux réponses ..
Dmitry Frank
"avec -t, si le sshclient décède (par exemple si vous entrez ~.)" Qu'est-ce que c'est ~.encore?
x-yuri
1
@ x-yuri, ~.est la séquence d'échappement que vous entrez pour déconnecter le client. Voir man sshpour plus de détails.
Stéphane Chazelas
11

Vous avez besoin d'une allocation de terminal du côté distant:

ssh -t user@remote_host tail -f /some/file

ou même

ssh -tt user@remote_host tail -f /some/file
Hauke ​​Laging
la source
1
Merci, les deux -tou -ttça marche. Mais je ne comprends toujours pas la vraie raison de ceci: disons, quand j'appelle le shell à distance et ferme la connexion, le shell est terminé. Mais tail -fnon. Bien sûr, j'ai déjà lu l' -toption man ssh, mais cela n'a pas beaucoup aidé. Il semble que je ne comprenne pas certains génériques, et je serais heureux si vous suggériez des documents à lire à ce sujet, ou probablement l'expliquez vous-même. Merci!
Dmitry Frank
2
@DmitryFrank Ma compréhension est la suivante: si la connexion est rompue, alors sshdenvoie un SIGHUP. Mais là où il n'y a pas de terminal, il ne peut y avoir de blocage de connexion de terminal ...
Hauke ​​Laging
Merci, je vais lire SIGHUPet d'autres signaux, je ne sais toujours presque rien à ce sujet.
Dmitry Frank
fyi: J'ai juste essayé de courir tail -f, puis je l'ai ouvert htopet envoyé SIGHUP(en appuyant sur F9-> 1-> Enter), et tail -fc'est terminé! Donc, la raison devrait être différente ..
Dmitry Frank
3
@DmitryFrank Vous avez mal compris le problème. Le problème n'est pas qu'il tailne réagirait pas SIGHUP. Le problème est que SIGHUP** n'est pas envoyé à tailsans pseudo-terminal. Vous pouvez le voir en l'attachant straceà taildans les deux cas.
Hauke ​​Laging du