web-dev-qa-db-fra.com

Transférer SIGTERM à l'enfant dans Bash

J'ai un script Bash, qui ressemble à ceci:

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

Maintenant, si le shell bash exécutant le script reçoit un signal SIGTERM, il doit également envoyer un SIGTERM au serveur en cours d'exécution (qui bloque, donc aucun piège possible). Est-ce possible?

98
Lorenz

Essayer:

#!/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"

Normalement, bash ignorera tous les signaux pendant l'exécution d'un processus enfant. Démarrage du serveur avec & le fondera dans le système de contrôle des tâches de Shell, avec $! contenant le PID du serveur (à utiliser avec wait et kill). L'appel de wait attendra alors que le travail avec le PID spécifié (le serveur) se termine, ou que tout signal soit déclenché.

Lorsque le shell reçoit SIGTERM (ou que le serveur se ferme indépendamment), l'appel wait revient (en quittant avec le code de sortie du serveur ou avec le numéro de signal + 128 en cas de réception d'un signal) . Par la suite, si le Shell a reçu SIGTERM, il appellera le _term fonction spécifiée comme gestionnaire d'interruptions SIGTERM avant de quitter (dans laquelle nous effectuons tout nettoyage et propagons manuellement le signal au processus serveur à l'aide de kill).

103
cuonglm

Bash ne transmet pas de signaux comme SIGTERM aux processus qu'il attend actuellement. Si vous souhaitez mettre fin à votre script en enchaînant votre serveur (lui permettant de gérer les signaux et tout le reste, comme si vous aviez démarré le serveur directement), vous devez utiliser exec, qui remplacera le shell par le processus en cours d'ouverture :

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

Si vous devez conserver le shell pour une raison quelconque (c'est-à-dire que vous devez effectuer un nettoyage après la fin du serveur), vous devez utiliser une combinaison de trap, wait et kill. Voir réponse de SensorSmith .

83
Stuart P. Bentley

Andreas Veithen souligne que si vous n'avez pas besoin de revenir de l'appel (comme dans l'exemple de l'OP), un simple appel via la commande exec est suffisant ( @ Stuart P Réponse de Bentley ). Sinon, le "traditionnel" trap 'kill $CHILDPID' TERM (réponse de @ cuonglm) est un début, mais l'appel wait revient en fait après l'exécution du gestionnaire d'interruptions, ce qui peut être encore avant la fin du processus enfant. Un appel "supplémentaire" à wait est donc conseillé ( réponse de @ user1463361 ).

Bien qu'il s'agisse d'une amélioration, il a toujours une condition de concurrence critique qui signifie que le processus peut ne jamais se terminer (à moins que le signaleur ne réessaye d'envoyer le signal TERM). La fenêtre de vulnérabilité se situe entre l'enregistrement du gestionnaire d'interruptions et l'enregistrement du PID de l'enfant.

Ce qui suit élimine cette vulnérabilité (emballé dans des fonctions à réutiliser).

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
26
SensorSmith

La solution fournie ne fonctionne pas pour moi car le processus a été tué avant la fin de la commande d'attente. J'ai trouvé cet article http://veithen.github.io/2014/11/16/sigterm-propagation.html , le dernier extrait de code fonctionne bien dans mon cas d'application démarrée dans OpenShift avec sh personnalisé coureur. Le script sh est requis car j'ai besoin d'avoir la possibilité d'obtenir des vidages de threads, ce qui est impossible dans le cas où le PID de Java process is 1.

trap 'kill -TERM $PID' TERM INT
$Java_EXECUTABLE $Java_ARGS &
PID=$!
wait $PID
trap - TERM INT
wait $PID
EXIT_STATUS=$?
4
user1463361