Как завершить удаленно называемый «tail -f», когда соединение закрыто?

24

Я только что заметил, что если я выполняю ssh user@remote_host tail -f /some/file, то tail -f /some/fileпродолжает работать на remote_host, даже если соединение ssh закрыто!

Итак, после нескольких подключений и отключений количество работающих tail -f /some/fileрастет. Как на самом деле завершить, tail -fкогда SSH-соединение закрыто?

Дмитрий Франк
источник

Ответы:

33

В

ssh host tail -f file

sshКлиент подключается к sshdсерверу на hostчерез соединение TCP. sshdработает tail -fсо своим stdout, перенаправленным на канал. sshdчитает то, что приходит с другого конца канала, и инкапсулирует его в протокол sshd для отправки sshклиенту. (с помощью rshd, tailstdout был бы сокетом напрямую, но sshdдобавляет шифрование и может мультиплексировать несколько потоков (например, для перенаправления порта / агента / X11 / tunnel, stderr) в одном соединении TCP, поэтому приходится прибегать к каналам).

Когда вы нажимаете CTRL-C, SIGINT отправляется sshклиенту. Это вызывает sshсмерть. После смерти TCP-соединение закрывается. И поэтому, на host, sshdплашки , а также. tailне убит, но его стандартный вывод теперь труба без читателя на другом конце. Таким образом, в следующий раз, когда он что-то пишет в свой стандартный вывод, он получит SIGPIPE и умрет.

В:

ssh -t host 'tail -f file'

Это то же самое, за исключением того, что вместо канала используется связь между псевдотерминалом sshdи tailчерез него. tailStdout - это подчиненный псевдотерминал (например /dev/pts/12), и все tailзаписи, которые есть readна главной стороне (возможно, модифицированные дисциплиной линии tty), sshdотправляются инкапсулированным sshклиенту.

На стороне клиента, с -t, sshпереводит терминал в rawрежим. В частности, это отключает канонический режим терминала и обработку сигналов терминала.

Таким образом, когда вы нажимаете Ctrl+Cвместо дисциплины линии терминала клиента отправку SIGINT на sshзадание, он просто отправляет ^Cсимвол по соединению sshdи sshdзаписывает ^Cего на ведущую сторону удаленного терминала. И дисциплина линии удаленного терминала отправляет SIGINTв tail. tailзатем умирает, sshdвыходит из системы, закрывает соединение и sshзавершает его (если он по-прежнему не занят переадресацией портов или другим способом).

Кроме того, с помощью -t, если sshклиент умирает (например, если вы введете ~.), соединение закрывается и sshdумирает. В результате SIGHUP будет отправлен на tail.

Теперь знайте, что использование -tимеет побочные эффекты. Например, при настройках терминала по умолчанию \nсимволы преобразуются в, \r\nи в зависимости от удаленной системы может происходить больше вещей, поэтому вы можете захотеть выполнить stty -opost(чтобы отключить постобработку вывода) на удаленном хосте, если этот вывод не предназначен для терминал:

$ 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

Другим недостатком использования -t/ -ttявляется то, что stdout и stderr не различаются на клиенте. И стандартный вывод, и стандартный вывод удаленной команды будут записаны в стандартный sshвывод клиента:

$ ssh localhost ls /x  | wc -l
ls: cannot access /x: No such file or directory
0
$ ssh -t localhost ls /x | wc -l
1
Стефан Шазелас
источник
Большое спасибо за такое подробное объяснение! Я хотел бы принять два ответа ..
Дмитрий Франк
-t, если sshклиент умрет (например, если вы введете ~.)" Что ~.снова?
x-yuri
1
@ x-yuri, ~.это escape-последовательность, которую вы вводите для отключения клиента. Смотрите man sshподробности.
Стефан Шазелас
11

Вам необходимо выделить терминал на удаленной стороне:

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

или даже

ssh -tt user@remote_host tail -f /some/file
Хауке Лагинг
источник
1
Спасибо, оба -tили -ttработает. Но я до сих пор не могу понять истинную причину этого: скажем, когда я вызываю shell удаленно и закрываю соединение, shell завершается. Но tail -fэто не так. Конечно, я уже читал об -tопциях man ssh, но это не сильно помогло. Кажется, я не понимаю некоторые дженерики, и я был бы рад, если бы вы предложили несколько документов, чтобы прочитать об этом, или, возможно, объясните это сами. Благодарность!
Дмитрий Франк
2
@DmitryFrank Мое понимание таково: если разрывается соединение, то sshdотправляет SIGHUP. Но там, где нет терминала, не может быть никакого зависания терминального соединения ...
Hauke ​​Laging
Спасибо, буду читать про SIGHUPи другие сигналы, до сих пор почти ничего не знаю об этом.
Дмитрий Франк
К вашему сведению: я просто попытался запустить tail -f, затем я открыл htopи отправил SIGHUPна него (нажав F9-> 1-> Enter), и tail -fзавершается! Итак, причина должна быть несколько иной ..
Дмитрий Франк
3
@DmitryFrank Вы неправильно поняли проблему. Проблема не в том, что tailбы на это не реагировали SIGHUP. Проблема в том, что SIGHUP** не отправляется tailбез псевдотерминала. Вы можете видеть , что путем присоединения straceк tailв обоих случаях.
Hauke ​​Laging