Il comando ssh continua inaspettatamente su un altro sistema al termine di ssh


11

Sto eseguendo il comando seguente e monitorando il file di output sull'altro sistema:

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

Se uccido il comando ssh usando ^Co semplicemente uccidendo il terminale a cui ho effettuato l'accesso, mi aspetto che anche il comando remoto venga interrotto. Questo non accade, però: /tmp/countottiene tutti i numeri 1–5 a prescindere e ps -ejHmostra la shell e il suo sleepbambino continua a correre.

Questo comportamento è previsto ed è documentato ovunque? Posso disabilitarlo? Da quando ho letto, mi sarei aspettato di dover esplicitamente abilitare questo tipo di comportamento senza nohup, non perché fosse quello predefinito.

Ho dato un'occhiata alle pagine man per ssh e sshd, ma non ho notato nulla di ovvio, e Google mi indica le istruzioni per attivare questo comportamento, non per disattivarlo.

Sto eseguendo Red Hat Enterprise Linux 6.2, con un login di root e una shell bash su entrambi i sistemi.

Risposte:


11

La risposta di uther ti dice di allocare un terminale ma non spiega perché. Il motivo non è specifico di ssh, è una questione di generazione e propagazione del segnale. Vi invito a leggere Cosa causa l'invio di vari segnali? per più sfondo.

Sull'host remoto, ci sono due processi rilevanti:

  • un'istanza del demone ssh ( sshd), che sta inoltrando l'input e l'output del programma remoto al terminale locale;
  • una shell, che esegue quel forciclo.

La shell può morire naturalmente quando raggiunge la fine del loop o si verifica un errore fatale, oppure a un segnale. La domanda è: perché la shell dovrebbe ricevere un segnale?

Se la shell remota è collegata a sshdover pipe, che è ciò che accade quando si specifica un comando sulla sshriga di comando, morirà di un SIGPIPE se sshdesce e la shell tenta di scrivere sulla pipe. Finché la shell non sta scrivendo sulla pipe, non riceverà un SIGPIPE. Qui, la shell non scrive mai nulla nel suo output standard, quindi può vivere per sempre.

È possibile passare l' -topzione a ssh, per dirgli di emulare un terminale sul lato remoto ed eseguire il comando specificato in questo terminale. Quindi, se il client SSH scompare, sshdchiude la connessione ed esce, distruggendo il terminale. Quando il dispositivo terminale scompare, qualsiasi processo in esecuzione in esso riceve un SIGHUP . Quindi, se si uccide il client SSH (con killo chiudendo il terminale in cui è in esecuzione il client), la shell remota è SIGHUPped.

Se si passa l' -topzione, SSH inoltra anche SIGINT . Se si preme Ctrl+ C, la shell remota riceve un SIGINT.


Usare -ttinvece di -tse ssh stesso non ha un tty allocato. SSH non riceverà un tty allocato se ssh viene immediatamente messo in background al momento dell'invocazione tramite opzioni come -fo -n.
Miron V,

3

Prova ad allocare un psuedo-tty con il tuo sshcomando.

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

Quando si disconnette la sessione ssh, il processo dovrebbe terminare.

Dato che si tratta di una pseudo-tty, l'inizializzazione della shell probabilmente non porterà ai file di configurazione di origine, lasciando il comando in un ambiente molto spoglio. Potrebbe essere necessario impostare un .ssh/environmentfile che definisce le variabili di ambiente come PATH. A partire dalman 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

In alternativa all'utilizzo -tdell'opzione per sshfar terminare il comando remoto quando il sshclient esce o (viene ucciso), una pipe denominata può essere usata per convertire "EOF in SIGHUP" quando lo stdin di sshdviene chiuso (vedi Bug 396 - sshd processi orfani quando non è stato assegnato alcun 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

Questo è il modo migliore che ho trovato per farlo. Vuoi qualcosa sul lato server che tenti di leggere stdin e poi uccida il gruppo di processi quando fallisce, ma vuoi anche uno stdin sul lato client che si blocchi fino a quando il processo sul lato server non viene completato e non lascerà processi persistenti come <( dormire all'infinito) potrebbe.

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

In realtà non sembra reindirizzare stdout da nessuna parte ma funziona come input di blocco ed evita di catturare i tasti.

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


0

Se vuoi disabilitare questo (apparentemente comportamento predefinito) devi abilitare ssh-keep-alive sul client o sul lato server.

Se guardi le opzioni ssh-keep-alive-nelle pagine man, puoi vedere che sono disabilitate di default.


0

La mia risposta si basa su Teru's. Ho bisogno di uno script di supporto ~/helpersul server server.ip:

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

Se si chiama ad es. Con

ssh server.ip ~/helper echo hello from server

e viene eseguito echo hello from serversu server.ip, quindi termina il client ssh.

Se si chiama ad es. Con

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

kill -9 $CHIDfermerà anche lo script sul server. Per me kill -INT $CHIDnon funziona, ma non so perché.

Nella risposta di Teru, il comando ssh attenderà per sempre quando il comando remoto è finito, perché catnon finisce mai.

Tutte le risposte con ssh -t non hanno funzionato con me kill, ma solo con Ctrl-C.

Modifica: ho scoperto che ha funzionato da un nuovo Ubuntu 14.01 a un vecchio box Linux scientifico - ma non viceversa. Quindi, penso che non ci sia una soluzione generale. Strano.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.