C’est bon, assez d’informatique pour le moment ! Vous avez la possibilité d’imposer des contraintes de temps aux processus en définissant une durée maximale d’exécution via la commande `timeout`. Voici un guide pour apprendre à limiter la durée d’exécution de vos programmes grâce à cette commande.
Quel est le rôle de la commande timeout ?
La commande `timeout` vous permet de déterminer une durée maximale pendant laquelle un programme peut fonctionner. Mais pourquoi vouloir faire cela ?
Une situation typique est lorsque vous connaissez précisément la durée pendant laquelle un processus doit s’exécuter. Par exemple, il peut être utile de contrôler la durée d’un script de journalisation ou de capture de données afin d’éviter que les fichiers de logs ne monopolisent l’espace de votre disque dur de manière excessive.
Dans d’autres cas, vous ne connaissez pas la durée exacte d’exécution nécessaire, mais vous savez que vous ne voulez pas que le processus s’éternise. Il peut arriver de lancer des processus, de minimiser la fenêtre du terminal et de les oublier.
Certains programmes, même de simples utilitaires, peuvent générer un trafic réseau important, ce qui peut impacter les performances de votre réseau. Ils peuvent également immobiliser des ressources sur un appareil, ralentissant son fonctionnement (je pense à `ping` par exemple). Laisser ce type de programmes s’exécuter pendant une longue période lorsque vous n’êtes pas devant votre ordinateur n’est pas une bonne pratique.
La commande `timeout` fait partie des utilitaires GNU Core. De ce fait, les systèmes d’exploitation de type Linux et Unix, comme macOS, intègrent tous `timeout`. Il n’y a rien à installer, vous pouvez l’utiliser immédiatement.
Comment utiliser `timeout` ?
Voici un exemple simple. Par défaut, sans options supplémentaires, la commande `ping` s’exécute jusqu’à ce que vous l’interrompiez en appuyant sur Ctrl+C. Si vous ne l’arrêtez pas, elle continuera indéfiniment.
ping 192.168.4.28
En utilisant `timeout`, nous pouvons nous assurer que la commande `ping` ne s’exécute pas indéfiniment, ce qui pourrait saturer la bande passante du réseau ou perturber le périphérique ciblé.
La commande suivante utilise `timeout` pour limiter la durée d’exécution de `ping`. Nous autorisons 15 secondes d’exécution pour cette commande.
timeout 15 ping 192.168.4.28
Après 15 secondes, `timeout` arrête la session `ping` et nous revenons à l’invite de la ligne de commande.
Utiliser `timeout` avec d’autres unités de temps
Il est à noter que nous n’avons pas eu à ajouter un « s » après le 15. La commande `timeout` considère que la valeur par défaut est en secondes. Vous pouvez ajouter un « s » si vous le souhaitez, cela n’a aucune incidence.
Pour utiliser une valeur de temps exprimée en minutes, heures ou jours, ajoutez respectivement « m », « h » ou « d ».
Pour exécuter la commande `ping` pendant trois minutes, utilisez la commande suivante :
timeout 3m ping 192.168.4.28
La commande `ping` s’exécutera pendant trois minutes avant que `timeout` ne l’arrête.
Limiter la capture de données avec `timeout`
Certains fichiers de capture de données peuvent atteindre une taille importante très rapidement. Pour éviter que ces fichiers ne deviennent trop volumineux ou même problématiques en termes de taille, il est possible de limiter la durée d’exécution du programme de capture.
Dans cet exemple, nous allons utiliser `tcpdump`, un outil de capture de trafic réseau. Sur les machines de test utilisées pour cet article, `tcpdump` était déjà installé sur Ubuntu Linux et Fedora Linux. Il a fallu l’installer sur Manjaro Linux et Arch Linux, en utilisant la commande suivante :
sudo pacman -Syu tcpdump
Nous pouvons exécuter `tcpdump` pendant 10 secondes avec ses options par défaut et rediriger sa sortie vers un fichier nommé `capture.txt` avec la commande suivante :
timeout 10 sudo tcpdump > capture.txt
Que s’est-il passé ? Pourquoi `timeout` n’a-t-il pas arrêté `tcpdump` ?
Tout est lié à la gestion des signaux.
Envoyer le bon signal
Lorsque `timeout` souhaite arrêter un programme, il envoie un signal SIGTERM. Il s’agit d’une requête demandant poliment au programme de se terminer. Certains programmes peuvent choisir d’ignorer le signal SIGTERM. Dans ce cas, il faut dire à `timeout` d’être plus ferme.
Pour cela, nous pouvons demander à `timeout` d’envoyer le signal SIGKILL à la place.
Le signal SIGKILL ne peut pas être « capturé, bloqué ou ignoré », il est toujours pris en compte. SIGKILL ne demande pas poliment au programme de s’arrêter. SIGKILL est plus direct : il se présente avec un chronomètre et une matraque.
Nous pouvons utiliser l’option `-s` (signal) pour indiquer à `timeout` d’envoyer le signal SIGKILL.
timeout -s SIGKILL 10 sudo tcpdump > capture.txt
Demander poliment en premier
Nous pouvons demander à `timeout` d’essayer d’arrêter le programme en utilisant SIGTERM, et de n’envoyer SIGKILL que si SIGTERM n’a pas fonctionné.
Pour cela, nous utilisons l’option `-k` (kill after). L’option `-k` nécessite une valeur de temps comme paramètre.
Dans cette commande, nous demandons à `timeout` de laisser `dmesg` s’exécuter pendant 30 secondes, puis de le terminer avec le signal SIGTERM. Si `dmesg` est toujours en cours d’exécution après 40 secondes, cela signifie que le signal SIGTERM a été ignoré et `timeout` doit envoyer SIGKILL pour arrêter le programme.
`dmesg` est un utilitaire qui permet de surveiller les messages du tampon du noyau et de les afficher dans une fenêtre de terminal.
timeout -k 40 30 dmesg -w
`dmesg` fonctionne pendant 30 secondes et s’arrête lorsqu’il reçoit le signal SIGTERM.
Nous savons que ce n’est pas SIGKILL qui a arrêté `dmesg` car SIGKILL laisse toujours un message d’un seul mot dans la fenêtre du terminal : « Tué ». Ce qui n’est pas le cas ici.
Récupérer le code de sortie du programme
Les programmes bien conçus renvoient une valeur au shell lorsqu’ils se terminent. C’est ce que l’on appelle un code de sortie. En règle générale, il est utilisé pour indiquer au shell (ou à tout autre processus qui a lancé le programme) si des problèmes ont été rencontrés lors de l’exécution du programme.
`timeout` fournit son propre code de sortie, mais cela ne nous intéresse pas forcément. Ce qui nous importe probablement le plus, c’est le code de sortie du processus contrôlé par `timeout`.
Cette commande permet à `ping` de s’exécuter pendant cinq secondes. Elle envoie un ping à un ordinateur nommé Nostromo, qui se trouve sur le réseau de test utilisé pour cet article.
timeout 5 ping Nostromo.local
La commande s’exécute pendant cinq secondes et `timeout` l’arrête. Nous pouvons ensuite vérifier le code de sortie avec la commande suivante :
echo $?
Le code de sortie est 124. Il s’agit de la valeur utilisée par `timeout` pour indiquer que le programme a été arrêté avec SIGTERM. Si SIGKILL met fin au programme, le code de sortie est 137.
Si nous interrompons le programme avec Ctrl+C, le code de sortie de `timeout` est zéro.
timeout 5 ping Nostromo.local
echo $?
Si l’exécution du programme se termine avant que `timeout` ne l’arrête, `timeout` peut renvoyer le code de sortie du programme au shell.
Pour que cela se produise, le programme doit s’arrêter de lui-même (c’est-à-dire qu’il ne doit pas être arrêté par `timeout`) et nous devons utiliser l’option `–preserve-status`.
Si nous utilisons l’option `-c` (count) avec la valeur cinq, la commande `ping` n’effectuera que cinq requêtes. Si nous donnons à `timeout` une durée d’une minute, `ping` se terminera définitivement par lui-même. Nous pouvons ensuite vérifier la valeur de sortie en utilisant `echo`.
timeout --preserve-status 1m ping -c 5 Nostromo.local
echo $?
La commande `ping` termine ses cinq requêtes et s’arrête. Le code de sortie est zéro.
Pour vérifier que le code de sortie provient bien de `ping`, forçons `ping` à générer un code de sortie différent. Si nous essayons d’envoyer des requêtes `ping` à une adresse IP inexistante, la commande échouera avec un code de sortie d’erreur. Nous pouvons ensuite utiliser `echo` pour vérifier que le code de sortie n’est pas nul.
timeout --preserve-status 1m ping -c 5 NotHere.local
echo $?
La commande `ping` ne peut évidemment pas atteindre le périphérique inexistant, elle signale donc l’erreur et se ferme. Le code de sortie est deux. Il s’agit du code de sortie utilisé par `ping` pour les erreurs générales.
Établir des règles de base
`timeout` permet d’imposer des limites à l’exécution des programmes. Si vous craignez que des fichiers de logs ne surchargent votre disque dur ou que vous pourriez oublier que vous avez lancé un outil réseau, utilisez `timeout` pour encadrer ces processus et laissez votre ordinateur s’autoréguler.