2010-07-13 12 views
11

Voglio avviare un paio di lavori su macchine diverse usando ssh. Se l'utente interrompe lo script principale, voglio chiudere tutti i lavori con garbo.Avviare un processo su ssh usando bash e quindi ucciderlo su sigint

Ecco un breve esempio di quello che sto cercando di fare:

#!/bin/bash 
trap "aborted" SIGINT SIGTERM 
aborted() { 
    kill -SIGTERM $bash2_pid 
    exit 
} 

ssh -t remote_machine /foo/bar.sh & 
bash2_pid=$! 
wait 

Tuttavia il processo è ancora in esecuzione bar.sh macchina remota. Se eseguo gli stessi comandi in una finestra di terminale, arresta il processo sull'host remoto.

C'è un modo semplice per farlo accadere quando eseguo lo script di bash? O devo fare in modo che acceda al computer remoto, trovi il processo giusto e lo uccida in questo modo?

edit: Sembra che devo andare con l'opzione B, uccidendo il RemoteScript tramite un'altra connessione ssh

Quindi non voglio sapere come ottengo il remotepid? Ho provato un qualcosa sulla falsariga di:

remote_pid=$(ssh remote_machine '{ /foo/bar.sh & } ; echo $!') 

Questo non funziona in quanto blocca.

Come attendere la stampa di una variabile e quindi "rilasciare" un sottoprocesso?

+1

Hai provato una trappola nel tuo script remoto? Forse SIGHUP? –

+0

Ho provato a intrappolare tutti i segnali a cui riesco a pensare senza alcun successo ... Immagino che dovrò solo ssh alla macchina remota di nuovo ed eseguire kill sulla sceneggiatura, ma poi ho bisogno del pid di il processo remoto ... – getekha

+0

Provate 'pkill' se è così che andrete. Grezzo ma solitamente efficace – bstpierre

risposta

16

Sarebbe sicuramente preferibile mantenere la pulizia gestita da ssh che avvia il processo anziché spostarsi per l'eliminazione con una seconda sessione SSH in seguito.

Quando ssh è collegato al terminale; si comporta abbastanza bene. Tuttavia, staccalo dal tuo terminale e diventa (come hai notato) un segnale per segnalare o gestire i processi remoti. È possibile arrestare il collegamento, ma non i processi remoti.

Ciò lascia una sola opzione: utilizzare il collegamento per consentire al processo remoto di essere avvisato della necessità di spegnersi. Il modo più semplice per farlo è utilizzare l'I/O di blocco. Rende l'input di lettura remoto da ssh e quando vuoi che il processo si spenga; inviare alcuni dati in modo che il funzionamento di lettura del telecomando sblocca e può procedere con la pulizia:

command & read; kill $! 

Questo è ciò che vorremmo eseguire sul telecomando. Invochiamo il nostro comando che vogliamo eseguire in remoto; leggiamo una riga di testo (blocchi finché non ne riceviamo uno) e quando abbiamo finito, segnaliamo il comando di terminare.

Per inviare il segnale dal nostro script locale al telecomando, tutto ciò che dobbiamo fare ora è inviare una riga di testo. Sfortunatamente, Bash non ti offre molte buone opzioni, qui. Almeno, non se vuoi essere compatibile con bash < 4.0.

Con bash 4 possiamo usare co-processi:

coproc ssh [email protected] 'command & read; kill $!' 
trap 'echo >&"${COPROC[1]}"' EXIT 
... 

Ora, quando lo script locale esce (non trappola INT, TERM, ecc Basta EXIT) invia una nuova linea per la file nel secondo elemento dell'array COPROC. Quel file è una pipe che è connessa allo stdinssh, instradando efficacemente la nostra linea a ssh. Il comando remoto legge la linea, termina il comando read e kill s.

Prima di bash 4 le cose diventano un po 'più difficili dal momento che non abbiamo co-processi. In questo caso, abbiamo bisogno di fare le tubazioni noi stessi:

mkfifo /tmp/mysshcommand 
ssh [email protected] 'command & read; kill $!' < /tmp/mysshcommand & 
trap 'echo > /tmp/mysshcommand; rm /tmp/mysshcommand' EXIT 

Questo dovrebbe funzionare in praticamente qualsiasi versione bash.

+0

Questo dovrebbe funzionare. Lo script deve essere eseguito su bash 3.2 quindi dovrò fare il mio piping personale. Il caso normale è che il processo remoto termina normalmente, quindi non posso fare affidamento sulla trappola per eseguire la pulizia del fifo. (potrebbe se li avessi tutti in una lista e fatto la pulizia alla fine del programma, ma preferirei rimuoverli non appena avrò finito con loro). – getekha

+0

Questo tipo funziona ma il problema si presenta ora se il programma termina normalmente: l'invocazione di lettura ha ancora bisogno di un input per terminare. Qualche idea su come coprire entrambi i casi? – jkp

+0

@jkp: trigger EXIT quando il programma termina anche normalmente. Dovrebbe coprire entrambi i casi. – lhunath

-1

È possibile considerare di montare il file system remoto ed eseguire lo script dalla casella master. Ad esempio, se il kernel è stato compilato con il fusibile (può verificare con il seguente):

/sbin/lsmod | grep -i fuse 

È quindi possibile montare il file system remoto con il seguente comando:

sshfs [email protected]_system: mount_point 

Ora basta eseguire lo script sul file che si trova in mount_point.

+0

lo scopo del vero script è distribuire il lavoro su una manciata di macchine slave, quindi questo non avrebbe funzionato me .. – getekha

6

Prova questo:

ssh -tt host command </dev/null & 

Quando si uccide il processo ssh locale, il pty a distanza si chiude e SIGHUP verrà inviato al processo remoto.

+0

Questo non è l'ideale per lo scripting, specialmente quando si esegue il piping dello script su ssh, lo farà stampare l'output riga per riga come se fosse un terminale interattivo. –

0

La soluzione per bash 3.2:

mkfifo /tmp/mysshcommand 
ssh [email protected] 'command & read; kill $!' < /tmp/mysshcommand & 
trap 'echo > /tmp/mysshcommand; rm /tmp/mysshcommand' EXIT 

non funziona. Il comando ssh non si trova nell'elenco ps sulla macchina "client". Solo dopo aver fatto eco a qualcosa nella pipe apparirà nell'elenco dei processi del computer client. Il processo che appare sulla macchina "server" sarebbe solo il comando stesso, non la parte di lettura/uccisione.

La scrittura di nuovo nel tubo non termina il processo.

Riassumendo, ho bisogno di scrivere nella pipe per il comando da avviare, e se scrivo di nuovo, non uccide il comando remoto, come previsto.

1

Riferimento la risposta lhunath e https://unix.stackexchange.com/questions/71205/background-process-pipe-input sono arrivato fino a questo script

run.sh:

#/bin/bash 
log="log"                     
eval "[email protected]" \&                    
PID=$!                      
echo "running" "[email protected]" "in PID $PID"> $log             
{ (cat <&3 3<&- >/dev/null; kill $PID; echo "killed" >> $log) & } 3<&0        
trap "echo EXIT >> $log" EXIT                
wait $PID 

con la differenza che questa versione uccide il processo quando la connessione è chiusa, ma anche i rendimenti il codice di uscita del comando quando viene eseguito fino al completamento.

$ ssh localhost ./run.sh true; echo $?; cat log 
0 
running true in PID 19247 
EXIT 

$ ssh localhost ./run.sh false; echo $?; cat log 
1 
running false in PID 19298 
EXIT 

$ ssh localhost ./run.sh sleep 99; echo $?; cat log 
^C130 
running sleep 99 in PID 20499 
killed 
EXIT 

$ ssh localhost ./run.sh sleep 2; echo $?; cat log 
0 
running sleep 2 in PID 20556 
EXIT 

Per una battuta:

ssh localhost "sleep 99 & PID=\$!; { (cat <&3 3<&- >/dev/null; kill \$PID) & } 3<&0; wait \$PID" 

Per comodità:

HUP_KILL="& PID=\$!; { (cat <&3 3<&- >/dev/null; kill \$PID) & } 3<&0; wait \$PID" 
ssh localhost "sleep 99 $HUP_KILL" 

Nota: uccidere 0 può essere preferito per uccidere $ PID a seconda del comportamento necessario per quanto riguarda ha generato processi figli. Puoi anche uccidere -HUP o kill -INT se lo desideri.

Aggiornamento: Un canale di controllo lavoro secondario è migliore della lettura da stdin.

ssh -n -R9002:localhost:8001 -L8001:localhost:9001 localhost ./test.sh sleep 2 

Impostare la modalità di controllo di posti di lavoro e monitorare il canale di controllo di posti di lavoro:

set -m 
trap "kill %1 %2 %3" EXIT 
(sleep infinity | netcat -l 127.0.0.1 9001) & 
(netcat -d 127.0.0.1 9002; kill -INT $$) & 
"[email protected]" & 
wait %3 

Infine, ecco un altro approccio e un riferimento a un bug depositata in OpenSSH: https://bugzilla.mindrot.org/show_bug.cgi?id=396#c14

questo è il migliore modo che ho trovato per fare questo. Si desidera qualcosa sul lato server che tenti di leggere stdin e poi uccida il gruppo di processi quando questo non funziona, ma si desidera anche uno stdin sul lato client che blocchi fino a quando il processo lato server non viene terminato e non lascerà processi persistenti come < (sonno infinito) potrebbe.

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

In realtà non sembra reindirizzare stdout ovunque, ma lo fa funzionare come un ingresso di blocco ed evita le battiture cattura.

Problemi correlati