Команда ssh неожиданно продолжается в другой системе после завершения работы ssh

11

Я запускаю команду ниже и отслеживаю выходной файл в другой системе:

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

Если я убью команду ssh, используя ^Cили просто убив терминал, на котором я вошел, я бы также ожидал, что удаленная команда завершится. Однако этого не происходит: /tmp/countполучает все числа 1–5 независимо и ps -ejHпоказывает, что оболочка и ее sleepдочерний элемент продолжают работать.

Это ожидаемое поведение и задокументировано ли оно где-нибудь? Могу ли я отключить это? После прочтения я ожидал, что мне придется явно включить такое поведение с помощью nohup, а не по умолчанию.

Я просмотрел справочные страницы по ssh и sshd, но не заметил ничего очевидного, и Google указывает мне на инструкции по включению этого поведения, а не по его выключению.

Я использую Red Hat Enterprise Linux 6.2 с обоими учетными записями root и bash.

я и
источник

Ответы:

11

Ответ Утера говорит вам выделить терминал, но не объясняет почему. Причина не специфична для ssh, это вопрос генерации и распространения сигнала. Я приглашаю вас прочитать Что вызывает различные сигналы для отправки? для получения дополнительной информации.

На удаленном хосте есть два соответствующих процесса:

  • экземпляр ssh daemon ( sshd), который ретранслирует ввод и вывод удаленной программы на локальный терминал;
  • оболочка, которая выполняет этот forцикл.

Оболочка может умереть либо естественным образом, когда достигнет конца цикла, либо столкнется с фатальной ошибкой, либо с сигналом. Вопрос в том, почему оболочка получает сигнал?

Если удаленная оболочка подключена к sshdканалам через каналы, что происходит, когда вы указываете команду в sshкомандной строке, то она умрет от SIGPIPE, если завершится, sshdи оболочка попытается выполнить запись в канал. Пока оболочка не пишет в канал, она не получит SIGPIPE. Здесь оболочка никогда ничего не записывает в свой стандартный вывод, поэтому она может жить вечно.

Вы можете передать -tопцию в ssh, чтобы сказать ей эмулировать терминал на удаленной стороне и выполнить указанную команду в этом терминале. Затем, если SSH-клиент исчезает, sshdзакрывает соединение и выходит, уничтожая терминал. Когда оконечное устройство отключается, любой запущенный в нем процесс получает сигнал SIGHUP . Поэтому, если вы убьете клиент SSH (с помощью killили закрыв терминал, на котором работает клиент), удаленная оболочка будет SIGHUPped.

Если вы пропустите -tопцию, SSH также передает SIGINT . Если вы нажмете Ctrl+ C, то удаленная оболочка получит SIGINT.

Жиль "ТАК - прекрати быть злым"
источник
Используйте -ttвместо, -tесли у самого ssh нет выделенного tty. SSH не получит выделенный tty, если ssh сразу после вызова вызывается с помощью опций, таких как -fили -n.
Мирон V
3

Попробуйте выделить psuedo-tty своей sshкомандой.

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

Когда вы отключаете сессию ssh, процесс должен завершиться.

Так как это псевдотермическая информация, ваша инициализация оболочки, вероятно, не собирается исходные файлы конфигурации, оставляя вашу команду в очень чистой среде. Вам может потребоваться настроить .ssh/environmentфайл, который определяет переменные среды, такие как PATH. Отman 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).
Джордж М
источник
2

В качестве альтернативы использованию -tопции для sshпринудительного завершения удаленной команды при sshвыходе из клиента или (его уничтожении) можно использовать именованный канал для преобразования «EOF в SIGHUP» при sshdзакрытии stdin of (см. Ошибку 396 - sshd процессы сирот, когда 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"
'
Теру
источник
1

Это лучший способ, который я нашел для этого. Вы хотите что-то на стороне сервера, которое пытается прочитать стандартный ввод, а затем убивает группу процессов при сбое, но вы также хотите, чтобы ввод на стороне клиента блокировал до тех пор, пока процесс на стороне сервера не будет завершен, и не оставлял длительные процессы, такие как <( сон бесконечность) может.

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

На самом деле он, кажется, нигде не перенаправляет стандартный вывод, но он функционирует как блокирующий ввод и избегает ввода нажатий клавиш.

Соответствующая ошибка openssh: https://bugzilla.mindrot.org/show_bug.cgi?id=396#c14

Эрик Вудрафф
источник
0

Если вы хотите отключить это (по-видимому, поведение по умолчанию), вам нужно включить ssh-keep-alive на стороне клиента или сервера.

Если вы посмотрите на ssh-keep-alive-options в man-страницах, то увидите, что по умолчанию они отключены.

Nils
источник
0

Мой ответ основан на Теру. Мне нужен вспомогательный скрипт ~/helperна сервере server.ip:

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

Если это называется, например, с

ssh server.ip ~/helper echo hello from server

и выполняется echo hello from serverв server.ip, а затем ssh клиент будет прерван.

Если это называется, например, с

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

kill -9 $CHIDостановит скрипт на сервере тоже. Для меня kill -INT $CHIDне работает, но я не знаю почему.

В ответе teru, команда ssh будет ждать вечно, когда удаленная команда завершится, потому что catникогда не завершится .

Все ответы с ssh -t у меня не работали kill, а только с Ctrl-C.

Редактировать: я узнал, что это работает от новой Ubuntu 14.01 до старшей научной коробки Linux - но не наоборот. Таким образом, я думаю, что нет общего решения. Weird.

Питер Стайер
источник