Comment utiliser les signaux Linux dans les scripts Bash

0
247
fatmawati achmad zaenuri/Shutterstock.com

Le noyau Linux envoie des signaux aux processus sur les événements auxquels ils doivent réagir. Les scripts bien comportés gèrent les signaux avec élégance et robustesse et peuvent nettoyer derrière eux même si vous appuyez sur Ctrl+C. Voici comment procéder.

Signaux et processus

Les signaux sont des messages courts, rapides et unidirectionnels envoyés à des processus tels que des scripts, programmes et démons. Ils informent le processus de quelque chose qui s'est passé. L'utilisateur a peut-être appuyé sur Ctrl+C, ou l'application a peut-être essayé d'écrire dans la mémoire à laquelle elle n'a pas accès.

Si l'auteur du processus a prévu qu'un certain signal pourrait lui être envoyé, il peut écrire une routine dans le programme ou le script pour gérer ce signal. Une telle routine est appelée un gestionnaire de signal. Il capte ou piège le signal et exécute une action en réponse.

RELATEDComment gérer les processus depuis le terminal Linux : 10 commandes que vous devez connaître

Linux utilise beaucoup de signaux, comme nous le verrons, mais du point de vue des scripts, il n'y a qu'un petit sous-ensemble de signaux qui vous intéresseront probablement. scripts, les signaux qui indiquent au script de s'arrêter doivent être interceptés (si possible) et un arrêt progressif doit être effectué.

Par exemple, les scripts qui créent des fichiers temporaires ou ouvrent des ports de pare-feu peuvent avoir la possibilité de supprimer les fichiers temporaires ou de fermer les ports avant qu'ils ne s'arrêtent. Si le script meurt juste à l'instant où il reçoit le signal, votre ordinateur peut se retrouver dans un état imprévisible.

Voici comment vous pouvez gérer les signaux dans vos propres scripts.

Rencontrez les signaux

Certaines commandes Linux ont des noms cryptiques. Ce n'est pas le cas de la commande qui piège les signaux. C'est ce qu'on appelle un piège. Nous pouvons également utiliser trap avec l'option -l (liste) pour afficher la liste complète des signaux utilisés par Linux.

trap -l

Bien que notre liste numérotée se termine à 64, il y a en fait 62 signaux. Les signaux 32 et 33 sont manquants. Ils ne sont pas implémentés sous Linux. Ils ont été remplacés par des fonctionnalités du compilateur gcc pour gérer les threads en temps réel. Tout, du signal 34, SIGRTMIN, au signal 64, SIGRTMAX, sont des signaux en temps réel.

Vous verrez différentes listes sur différents systèmes d'exploitation de type Unix. Sur OpenIndiana par exemple, les signaux 32 et 33 sont présents, ainsi qu'un tas de signaux supplémentaires portant le nombre total à 73.

Les signaux peuvent être référencés par nom, numéro ou par leur nom abrégé. Leur nom abrégé est simplement leur nom avec le premier “SIG” supprimé.

Les signaux sont émis pour de nombreuses raisons différentes. Si vous pouvez les déchiffrer, leur but est contenu dans leur nom. L'impact d'un signal appartient à l'une des catégories suivantes :

  • Terminer : le processus est terminé.
  • Ignorer : le signal n'affecte pas le processus. Il s'agit d'un signal informatif uniquement.
  • Core : un fichier dump-core est créé. Cela se produit généralement parce que le processus a transgressé d'une manière ou d'une autre, comme une violation de la mémoire.
  • Arrêter : le processus est arrêté. C'est-à-dire qu'il est suspendu et non terminé.
  • Continuer : indique à un processus arrêté de poursuivre son exécution.

Ce sont les signaux que vous rencontrerez le plus fréquemment.

  • SIGHUP : Signal 1. La connexion à un hôte distant, tel qu'un serveur SSH, 8212;a chuté de manière inattendue ou l'utilisateur s'est déconnecté. Un script recevant ce signal peut se terminer normalement ou peut choisir de tenter de se reconnecter à l'hôte distant.
  • SIGINT: Signal 2. L'utilisateur a appuyé sur la combinaison Ctrl+C pour forcer la fermeture d'un processus, ou la commande kill a été utilisée avec le signal 2. Techniquement, il s'agit d'un signal d'interruption, pas d'un signal de fin, mais d'un script interrompu sans le gestionnaire de signal se terminera généralement.
  • SIGQUIT : Signal 3. L'utilisateur a appuyé sur la combinaison Ctrl+D pour forcer un processus à quitter, ou la commande kill a été utilisée avec le signal 3.
  • SIGFPE : Signal 8. Le processus a tenté d'effectuer une opération mathématique illégale (impossible), telle qu'une division par zéro.
  • SIGKILL: Signal 9. C'est le signal équivalent d'une guillotine. Vous ne pouvez pas l'attraper ou l'ignorer, et cela se produit instantanément. Le processus se termine immédiatement.
  • SIGTERM : Signal 15. Il s'agit de la version la plus prévenante de SIGKILL. SIGTERM indique également à un processus de se terminer, mais il peut être piégé et le processus peut exécuter ses processus de nettoyage avant de se fermer. Cela permet un arrêt en douceur. C'est le signal par défaut déclenché par la commande kill.

Signaux sur la ligne de commande

Une façon de piéger un signal est d'utiliser trap avec le numéro ou le nom du signal, et une réponse que vous voulez qu'il se produise si le signal est reçu. Nous pouvons le démontrer dans une fenêtre de terminal.

Cette commande intercepte le signal SIGINT. La réponse est d'imprimer une ligne de texte dans la fenêtre du terminal. Nous utilisons l'option -e (activer les échappements) avec echo afin de pouvoir utiliser l'option “n” spécificateur de format.

trap 'echo -e “+c détecté.”' SIGINT

Notre ligne de texte est imprimée chaque fois que nous appuyons sur la combinaison Ctrl+C.

Pour voir si un trap est défini sur un signal, utilisez l'option -p (print trap).

trap -p SIGINT

L'utilisation de trap sans options a le même effet.

Pour réinitialiser le signal à son état normal non piégé, utilisez un trait d'union “-” et le nom du signal piégé.

trap – SIGINT trap -p SIGINT

Aucune sortie de la commande trap -p n'indique qu'aucun piège n'est défini sur ce signal.

Trapping Signals in Scripts

Nous pouvons utiliser la même commande trapping de format général dans un script. Ce script intercepte trois signaux différents, SIGINT, SIGQUIT et SIGTERM.

#!/bin/bash trap “echo J'étais SIGINT terminé ; exit” SIGINT trap “echo J'étais SIGQUIT terminé ; exit” SIGQUIT trap “echo J'étais SIGTERM terminé ; exit” SIGTERM echo $$ counter=0 while true do   echo ” Numéro de boucle : ” $((++counter))   sleep 1 done

Les trois instructions d'interruption se trouvent en haut du script. Notez que nous avons inclus la commande exit dans la réponse à chacun des signaux. Cela signifie que le script réagit au signal puis se termine.

Copiez le texte dans votre éditeur et enregistrez-le dans un fichier appelé “simple-loop.sh”, et rendez-le exécutable à l'aide de la commande chmod. Vous devrez le faire pour tous les scripts de cet article si vous souhaitez suivre sur votre propre ordinateur. Utilisez simplement le nom du script approprié dans chaque cas.

chmod +x simple-loop.sh

< p>Le reste du script est très simple. Nous avons besoin de connaître l'ID de processus du script, nous avons donc le script qui nous l'écho. La variable $$ contient l'ID de processus du script.

Nous créons une variable appelée compteur et la mettons à zéro.

La boucle while s'exécutera indéfiniment à moins qu'elle ne soit arrêtée de force. Il incrémente la variable compteur, la renvoie à l'écran et s'endort une seconde.

Exécutons le script et envoyons-lui différents signaux.

./simple-loop. sh

Lorsque nous appuyons sur “Ctrl+C” notre message est imprimé dans la fenêtre du terminal et le script est terminé.

Exécutons-le à nouveau et envoyons le signal SIGQUIT à l'aide de la commande kill. Nous devrons le faire à partir d'une autre fenêtre de terminal. Vous devrez utiliser l'ID de processus signalé par votre propre script.

./simple-loop.sh kill -SIGQUIT 4575

Comme prévu, le script signale l'arrivée du signal puis se termine. Et enfin, pour prouver le point, nous allons le refaire avec le signal SIGTERM.

./simple-loop.sh kill -SIGTERM 4584

Nous avons vérifié que nous pouvons piéger plusieurs signaux dans un script et réagir à chacun indépendamment. L'étape qui fait passer tout cela d'intéressant à utile consiste à ajouter des gestionnaires de signaux.

Gestion des signaux dans les scripts

Nous pouvons remplacer la réponse chaîne avec le nom d'une fonction dans votre script. La commande trap appelle ensuite cette fonction lorsque le signal est détecté.

Copiez ce texte dans un éditeur et enregistrez-le dans un fichier appelé “grace.sh”, et rendez-le exécutable avec chmod.

#!/bin/bash trap graceful_shutdown SIGINT SIGQUIT SIGTERM graceful_shutdown() { echo -e “nSuppression du fichier temporaire :” $temp_file   rm -rf “$temp_file”   exit } temp_file=$(mktemp -p /tmp tmp.XXXXXXXXXX) echo “Fichier temporaire créé :” $temp_file counter=0 while true do   echo “Numéro de boucle :” $((++counter))   sleep 1 done

Le script place un piège pour trois signaux différents— SIGHUP, SIGINT et SIGTERM à l'aide d'une seule instruction d'interruption. La réponse est le nom de la fonction graceful_shutdown(). La fonction est appelée chaque fois que l'un des trois signaux piégés est reçu.

Le script crée un fichier temporaire dans le dossier “/tmp” répertoire, en utilisant mktemp. Le modèle de nom de fichier est “tmp.XXXXXXXXXX”, donc le nom du fichier sera “tmp.” suivi de dix caractères alphanumériques aléatoires. Le nom du fichier s'affiche en écho à l'écran.

Le reste du script est le même que le précédent, avec une variable compteur et une boucle while infinie.

./grace.sh

Lorsque le fichier reçoit un signal qui provoque sa fermeture, la fonction graceful_shutdown() est appelée. Cela supprime notre unique fichier temporaire. Dans une situation réelle, il pourrait effectuer tout le nettoyage requis par votre script.

De plus, nous avons regroupé tous nos signaux piégés et les avons traités avec une seule fonction. Vous pouvez piéger les signaux individuellement et les envoyer à leurs propres fonctions de gestion dédiées.

Copiez ce texte et enregistrez-le dans un fichier appelé “triple.sh”, et rendez-le exécutable à l'aide du chmod commande.

#!/bin/bash trap sigint_handler SIGINT trap sigusr1_handler SIGUSR1 trap exit_handler EXIT function sigint_handler() {   ((++sigint_count))   echo -e “nSIGINT a reçu $sigint_count fois.” if [[ “$sigint_count” -eq 3 ]] ; puis     echo “Démarrage de la fermeture”. loop_flag=1   fi } function sigusr1_handler() {   echo “SIGUSR1 a envoyé et reçu $((++sigusr1_count)) fois.” } function exit_handler() {   echo “Gestionnaire de sortie : le script se ferme…” } echo $$ sigusr1_count=0 sigint_count=0 loop_flag=0 while [[ $loop_flag -eq 0 ]] ; do   kill -SIGUSR1 $$   sleep 1 done

Nous définissons trois pièges en haut du script.

  • L'un piège SIGINT et possède un gestionnaire appelé sigint_handler().
  • < li>Le second piège un signal appelé SIGUSR1 et utilise un gestionnaire appelé sigusr1_handler() .

  • Le piège numéro trois piège le signal EXIT. Ce signal est déclenché par le script lui-même lorsqu'il se ferme. Définir un gestionnaire de signal pour EXIT signifie que vous pouvez définir une fonction qui sera toujours appelée lorsque le script se termine (sauf s'il est tué avec le signal SIGKILL). Notre gestionnaire s'appelle exit_handler() .

SIGUSR1 et SIGUSR2 sont des signaux fournis pour que vous puissiez envoyer des signaux personnalisés à vos scripts. La façon dont vous les interprétez et y réagissez dépend entièrement de vous.

Laissant de côté les gestionnaires de signaux pour l'instant, le corps du script devrait vous être familier. Il renvoie l'ID de processus à la fenêtre du terminal et crée des variables. La variable sigusr1_count enregistre le nombre de fois que SIGUSR1 a été traité et sigint_count enregistre le nombre de fois que SIGINT a été traité. La variable loop_flag est définie sur zéro.

La boucle while n'est pas une boucle infinie. Il arrêtera de boucler si la variable loop_flag est définie sur une valeur non nulle. Chaque rotation de la boucle while utilise kill pour envoyer le signal SIGUSR1 à ce script, en l'envoyant à l'ID de processus du script. Les scripts peuvent s'envoyer des signaux !

La fonction sigusr1_handler() incrémente la variable sigusr1_count et envoie un message à la fenêtre du terminal.

Chaque fois que le signal SIGINT est reçu, le siguint_handler( ) la fonction incrémente la variable sigint_count et renvoie sa valeur à la fenêtre du terminal.

Si la variable sigint_count est égale à trois, la variable loop_flag est définie sur un et un message est envoyé à la fenêtre du terminal informant l'utilisateur que le processus d'arrêt a commencé.

Parce que loop_flag n'est plus égal à zéro, le while boucle se termine et le script est terminé. Mais cette action déclenche automatiquement le signal EXIT et la fonction exit_handler() est appelée.

./triple.sh

Après trois appuis sur Ctrl+C, le script se termine et invoque automatiquement la fonction exit_handler().

Lire les signaux

En piégeant les signaux et en les traitant dans les fonctions de gestionnaire simples, vous pouvez faire en sorte que vos scripts Bash se rangent derrière eux même s'ils se terminent de manière inattendue. Cela vous donne un système de fichiers plus propre. Cela empêche également l'instabilité la prochaine fois que vous exécutez le script et, selon l'objectif de votre script, cela peut même empêcher les failles de sécurité.

RELATED : Comment auditer la sécurité de votre système Linux avec Lynis

LIRE LA SUITE

  • › Courte critique de l'ordinateur portable Lenovo Yoga 7i 14 pouces : un appareil polyvalent et attrayant
  • › Quels accessoires pour smartphone valent la peine d'être achetés ?
  • › Test de l'Edifier Neobuds S : le bon, le mauvais et le buggy
  • › N'achetez pas de répéteur Wi-Fi : achetez plutôt celui-ci
  • › Premier PC de Radio Shack : 45 ans de TRS-80
  • &rsaquo ; Nouveautés de Chrome 104, disponible dès maintenant