Inoltra SIGTERM a figlio in Bash


86

Ho uno script Bash, che assomiglia a questo:

#!/bin/bash
echo "Doing some initial work....";
/bin/start/main/server --nodaemon

Ora se la shell bash che esegue lo script riceve un segnale SIGTERM, dovrebbe anche inviare un SIGTERM al server in esecuzione (che blocca, quindi nessuna trap possibile). È possibile?

Risposte:


91

Provare:

#!/bin/bash 

_term() { 
  echo "Caught SIGTERM signal!" 
  kill -TERM "$child" 2>/dev/null
}

trap _term SIGTERM

echo "Doing some initial work...";
/bin/start/main/server --nodaemon &

child=$! 
wait "$child"

Normalmente, bashignorerà qualsiasi segnale durante l'esecuzione di un processo figlio. L'avvio del server con &lo sfondo nel sistema di controllo dei processi della shell, con $!il PID del server (da utilizzare con waite kill). La chiamata waitattenderà quindi il completamento del lavoro con il PID specificato (il server) o l'eventuale attivazione di qualsiasi segnale .

Quando la shell riceve SIGTERM(o il server esce in modo indipendente), la waitchiamata ritornerà (uscendo con il codice di uscita del server o con il numero di segnale + 128 nel caso in cui fosse ricevuto un segnale). Successivamente, se la shell ha ricevuto SIGTERM, chiamerà la _termfunzione specificata come gestore trap SIGTERM prima di uscire (in cui eseguiamo qualsiasi cleanup e propagiamo manualmente il segnale al processo del server utilizzando kill).


Sembra buono! Lo proverò e risponderò quando l'ho provato.
Lorenz,

7
Ma exec sostituisce la shell con il programma dato , non sono chiaro il motivo per cui waitè quindi necessaria la chiamata successiva?
Iruvar,

5
Penso che il punto di 1_CR sia valido. O semplicemente usi exec /bin/start/main/server --nodaemon(nel qual caso il processo di shell viene sostituito con il processo del server e non hai bisogno di propagare alcun segnale) o usi /bin/start/main/server --nodaemon &, ma execnon è davvero significativo.
Andreas Veithen,

2
Se vuoi che lo script della shell termini solo dopo che il figlio è stato terminato, nella _term()funzione dovresti di wait "$child"nuovo. Questo potrebbe essere necessario se hai qualche altro processo di supervisione in attesa che lo script della shell muoia prima di riavviarlo di nuovo, o se sei anche intrappolato EXITper fare un po 'di pulizia e devi eseguirlo solo dopo che il processo figlio è terminato.
LeoRochael

1
@AlexanderMills Leggi le altre risposte. O stai cercando execo vuoi creare trappole .
Stuart P. Bentley,

78

Bash non inoltra segnali come SIGTERM ai processi su cui è attualmente in attesa. Se vuoi terminare lo script seguendo il tuo server (permettendogli di gestire i segnali e qualsiasi altra cosa, come se avessi avviato direttamente il server), dovresti usare exec, che sostituirà la shell con il processo aperto :

#!/bin/bash
echo "Doing some initial work....";
exec /bin/start/main/server --nodaemon

Se è necessario mantenere il guscio intorno per qualche motivo (ad es. Avete bisogno di fare un po 'di pulizia dopo che il server termina), è necessario utilizzare una combinazione di trap, waite kill. Vedi la risposta di SensorSmith .


Questa è la risposta corretta! Molto più conciso e indirizza esattamente l'originale di OP
BrDaHa,

20

Andreas Veithen sottolinea che se non è necessario tornare dalla chiamata (come nell'esempio del PO) execè sufficiente semplicemente chiamare tramite il comando ( la risposta di @Stuart P. Bentley ). Altrimenti il ​​"tradizionale" trap 'kill $CHILDPID' TERM(la risposta di @ cuonglm) è un inizio, ma la waitchiamata in realtà ritorna dopo l'esecuzione del gestore trap che può essere ancora prima che il processo figlio sia effettivamente terminato. Pertanto waitè consigliabile una chiamata "extra" a ( la risposta di @ user1463361 ).

Sebbene questo sia un miglioramento, ha ancora una condizione di competizione, il che significa che il processo potrebbe non uscire mai (a meno che il segnalatore non tenti di inviare il segnale TERM). La finestra della vulnerabilità è tra la registrazione del gestore trap e la registrazione del PID del bambino.

Quanto segue elimina quella vulnerabilità (impacchettata in funzioni per il riutilizzo).

prep_term()
{
    unset term_child_pid
    unset term_kill_needed
    trap 'handle_term' TERM INT
}

handle_term()
{
    if [ "${term_child_pid}" ]; then
        kill -TERM "${term_child_pid}" 2>/dev/null
    else
        term_kill_needed="yes"
    fi
}

wait_term()
{
    term_child_pid=$!
    if [ "${term_kill_needed}" ]; then
        kill -TERM "${term_child_pid}" 2>/dev/null 
    fi
    wait ${term_child_pid}
    trap - TERM INT
    wait ${term_child_pid}
}

# EXAMPLE USAGE
prep_term
/bin/something &
wait_term

2
Ottimo lavoro - Ho aggiornato il link nella mia risposta per indicare qui (oltre a essere una soluzione più completa, sono ancora un po 'infastidito dal fatto che l'interfaccia utente StackExchange non mi accrediti nella risposta di cuonglm per aver corretto lo script su effettivamente fare ciò che dovrebbe e scrivere praticamente tutto il testo esplicativo dopo il PO che non ha nemmeno capito apportato alcune piccole modifiche).
Stuart P. Bentley,

2
@ StuartP.Bentley, grazie. Sono stato sorpreso di assemblare questo richiesto due risposte (non accettate) e un riferimento esterno, e quindi ho dovuto scendere le condizioni di gara. Aggiornerò i miei riferimenti ai collegamenti come piccoli complimenti che posso dare.
SensorSmith,

3

La soluzione fornita non funziona per me perché il processo è stato interrotto prima che il comando wait fosse effettivamente terminato. Ho scoperto che l'articolo http://veithen.github.io/2014/11/16/sigterm-propagation.html , l'ultimo frammento funziona correttamente nel mio caso di applicazione avviata in OpenShift con sh runner personalizzato. Lo script sh è necessario perché devo avere la capacità di ottenere i dump del thread, il che è impossibile nel caso in cui il PID del processo Java sia 1.

trap 'kill -TERM $PID' TERM INT
$JAVA_EXECUTABLE $JAVA_ARGS &
PID=$!
wait $PID
trap - TERM INT
wait $PID
EXIT_STATUS=$?
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.