HowToKillAZombie-248x300Il peut arriver, que ce soit sur des machines en production ou de test, que certains services deviennent subitement hors de contrôle. Cela se traduit souvent par une utilisation anormale du temps CPU et la prolongation exceptionnelle de la durée de vie d’un processus qui aurait du se terminer rapidement.
C’est rare mais cela arrive. Les programmes ne sont évidemment pas exempts de bugs. A défaut de pouvoir tracer et corriger un problème exceptionnel immédiatement, ou d’attendre une mise à jour de l’éditeur, il ne reste très souvent qu’à redémarrer le service.

Pourquoi alors ne pas automatiser cette tâche? Cela ne corrige pas le problème en soi, mais cela évite de perturber les autres services sur une période longue si l’on ne surveille pas continuellement l’apparition de ce type de problèmes, qui reste exceptionnel au demeurant.

Le script Shell ci-dessous doit être lancé automatiquement à intervalle régulier via une tâche cron ou un script de maintenance périodique spécifique à NetBSD pour être utile.
Le script se charge alors de chercher tous les programmes qui sont toujours actifs 200 heures après leur lancement et qui n’appartiennent pas à une liste blanche. Ces programmes sont alors automatiquement relancés via leur propre script rc.d d’initialisation s’il existe, dans le cas contraire, il leur sera gentillement et poliment  demandé de s’arrêter. Si jamais ils resistent et ne répondent plus, alors il n’y aura plus d’autres solutions que de les tuer sur le champs, sans sommation.
L’utilisation d’une liste blanche est obligatoire car il est normal pour certains daemons de rester actifs non-stop. C’est notamment le cas de certains processus système tels que system et ioflush sous NetBSD. C’est aussi le cas de certains services qui ne se relancent jamais tout seuls tels que Squid ou Mysql.
Pour que ce script soit pleinement utilisable, il est nécessaire de renommer les scripts rc.d du nom du processus principal dont ils ont la charge. A titre d’exemple, le script « apache » doit être renommé en « httpd ».

Comme d’habitude, toute action est notifiée à l’administrateur système via email, et les noms des fichiers et des chemins doivent être adaptés à la discrétion du lecteur.



#! /bin/sh
#
# Sanity script to prevent uncontrolled processes from eating resources
# May help killing zombies too
#
# Place somewhere and run via cron jobs like this:
# 5 */96 * * * nice /bin/sh /usr/pkg/sbin/kill_cancerous_programs.sh
#

ps -a -x -c -o pid= -o time= -o command= | egrep -v "(system)|(ioflush)" | while read line ; do #rajouter grep -v nom_de_la_commande_a_ne_jamais_tuer au besoin
echo $line | while IFS=" " read DEAD_PID DEAD_TIME DEAD_COMMAND
do
if test "`echo $DEAD_TIME | cut -f1 -d:`" -gt "200" #soit plus de 200 heures que le programme tourne, il serait peut etre temps de le tuer non?
then
LOGME="$DEAD_COMMAND (PID $DEAD_PID) no action taken; please verify what is appropriate yourself"
# On essaye d'abord de voir si un script d'initialisation lui est associe et dans ce cas l'utiliser; il est sage de renommer les scripts d'initialisation par les daemons appelles
if [ -f /etc/rc.d/${DEAD_COMMAND} ]; then
/etc/rc.d/${DEAD_COMMAND} restart
LOGME="$DEAD_COMMAND (PID $DEAD_PID) seemed out of control: restarting it using its own rc.d script"
logger -t dead_process -p user.err "$LOGME"
else
kill -s TERM $DEAD_PID
LOGME="$DEAD_COMMAND (PID $DEAD_PID) seemed out of control: SIG_TERM sent"
logger -t dead_process -p user.err "$LOGME"
fi
sleep 60
if [ -n "`ps -a -x -c -o pid= -p $DEAD_PID`" ]; then
kill -s KILL $DEAD_PID
LOGME="$DEAD_COMMAND (PID $DEAD_PID) didn't want to die!!!: zombie dismembered by SIG_KILL blast"
logger -t dead_process -p user.err "$LOGME"
fi
(echo "To: ${MAILTO:-root}";
echo "Subject: `hostname` $0 output for `date`";
echo "";
echo "$LOGME";) | sendmail -t
fi
done
done