Cron

Exécuter cron un jour spécifique dans le mois (e.g. deuxième lundi)

Comment exécuter un cron sur un jour spécifique de la semaine une fois par mois ?
Ceci pourrait sembler simple au premier abord, puisque cette ligne pourrait semblait faire l’affaire :

# Run on every second Tuesday of the month
15 3 8-14 * 2  /usr/bin/bash /opt/myscriptfortuesday.sh

Mais ceci ne marcherait pas car le ‘2’ pour vérifier que nous sommes bien un mardi vient comme une condition OR, et donc la commande pourrait s’exécuter du jour 8 au jour 14 et tous les mardis du mois.

Pour contourner cela, vous pouvez utiliser cette commande :

# Run on every second Tuesday of the month
15 3 8-14 * * test $(date +\%u) -eq 2 && /usr/bin/bash /opt/myscriptfortuesday.sh

Voici l’explication de cette ligne de cron :

15   = 15th minute
3    = 3am
8-14 = between day 8 and day 14 (second week)
*    = every month
*    = every day of the week
test $(date +\%u) -eq 2 && /usr/bin/bash /opt/myscriptfortuesday.sh = the command to execute with a check on the date

En effectuant cette vérification, nous vérifions alors d’abord que nous sommes bien un mardi avant d’exécuter la commande. N’oubliez pas d’ajouter un antislash avant le caractère ‘%’ pour l’échapper.

Systemd Timer

Le fonctionnement de systemd impose cependant d’avoir deux fichiers :
service, qui contient la définition du programme
timer, qui dit “quand” le lancer.

Ils doivent porter le même nom et se situer dans /etc/systemd/system/.

Création service et timer

Si vous gérez déjà vos services via systemd, vous avez déjà utilisé des “unit” systemd de type “service”.
Ces “unit” permettent de définir un process et son mode d’éxécution.
Pour implémenter un “timer” sous systemd, il va nous falloir un fichier “service”.

Pour notre tâche à planifier, nous allons avoir au final 3 fichiers :

Le script à exécuter
Le fichier “service” qui va dire quel script exécuter
Le fichier “timer” qui va indiquer quand il doit être exécuté.

A noter que par convention, les fichiers service et timer doivent avoir le même nom

Nous devons exécuter ,une fois par jour , un script de sauvegarde /home/yannick/scripts/savarch sur un ordinateur qui n’est pas sous tension 24/24h.

Pour le fichier service /etc/systemd/system/savarch.service, une base simple

[Unit]
Description=Sauvegarde jour

[Service]
Type=simple
ExecStart=/bin/bash /home/yannick/scripts/savarch
StandardError=journal
Restart=on-abort


[Install]
WantedBy=multi-user.target

Je fournis une description à mon service, indique que c’est un process de type simple, le chemin vers mon script et je rajoute que le flux d’erreur est envoyé dans le journal.Il ne faut pas de section [Install] car le script va être piloté par le fichier timer.
La ligne Type=oneshot est importante, c’est elle qui dit à systemd de ne pas relancer le service en boucle.

Le fichier “timer” /etc/systemd/system/savarch.timer

[Unit]
Description=Sauvegarde jour

[Timer]
# lisez le man systemd.timer(5) pour tout ce qui est disponible
OnCalendar=daily
# Autoriser la persistence entre les reboot
Persistent=true
Unit=savarch.service

[Install]
WantedBy=timers.target
  • OnCalendar permet d’indiquer l’occurrence et la fréquence d’exécution du script. Il y a les abréviations classiques (“minutely”, “hourly”, “daily”, “monthly”, “weekly”, “yearly”, “quarterly”, “semiannually”, etc) mais vous pouvez avoir des choses plus complexes comme “Mon,Tue --01..04 12:00:00” - voir systemd.time
  • Persistent va forcer l’exécution du script si la dernière exécution a été manquée suite à un reboot de serveur ou autre événement.
  • Install va créer la dépendance pour que votre “timer” soit bien exécuté et pris en compte par systemd.

Syntaxe date “OnCalendar”

Minimal form Normalized form
Sat,Thu,Mon-Wed,Sat-Sun Mon-Thu,Sat,Sun *-*-* 00:00:00
Mon,Sun 12-*-* 2,1:23 Mon,Sun 2012-*-* 01,02:23:00
Wed *-1 Wed *-*-01 00:00:00
Wed-Wed,Wed *-1 Wed *-*-01 00:00:00
Wed, 17:48 Wed *-*-* 17:48:00
Wed-Sat,Tue 12-10-15 1:2:3 Tue-Sat 2012-10-15 01:02:03
*-*-7 0:0:0 *-*-07 00:00:00
10-15 *-10-15 00:00:00
monday *-12-* 17:00 Mon *-12-* 17:00:00
Mon,Fri *-*-3,1,2 *:30:45 Mon,Fri *-*-01,02,03 *:30:45
12,14,13,12:20,10,30 *-*-* 12,13,14:10,20,30:00
mon,fri *-1/2-1,3 *:30:45 Mon,Fri *-01/2-01,03 *:30:45
03-05 08:05:40 *-03-05 08:05:40
08:05:40 *-*-* 08:05:40
05:40 *-*-* 05:40:00
Sat,Sun 12-05 08:05:40 Sat,Sun *-12-05 08:05:40
Sat,Sun 08:05:40 Sat,Sun *-*-* 08:05:40
2003-03-05 05:40 2003-03-05 05:40:00
2003-03-05 2003-03-05 00:00:00
03-05 *-03-05 00:00:00
hourly *-*-* *:00:00
daily *-*-* 00:00:00
monthly *-*-01 00:00:00
weekly Mon *-*-* 00:00:00
*:20/15 *-*-* *:20/15:00

Note: *:20/15 means *:20:00, *:35:00, *:50:00, restarting the next hour at *:20:00.

Activation et démarrage du timer

Il est possible de tester le service avec un simple systemctl start savarch.service, de regarder les logs avec journalctl -u savarch.service.

Ensuite, pour qu’il soit actif, il faut prévenir systemd

sudo systemctl enable savarch.timer
sudo systemctl start savarch.timer

Gestion et suivi d’un timer

Pour voir la liste des “timers” actifs et la date de leur dernière et prochaine exécution

systemctl list-timers
NEXT                         LEFT     LAST                         PASSED    UNIT                         ACTIVATES
Fri 2018-03-02 00:00:00 CET  15h left Thu 2018-03-01 07:49:43 CET  56min ago logrotate.timer              logrotate.service
Fri 2018-03-02 00:00:00 CET  15h left Thu 2018-03-01 07:49:43 CET  56min ago man-db.timer                 man-db.service
Fri 2018-03-02 00:00:00 CET  15h left Thu 2018-03-01 07:49:43 CET  56min ago savarch.timer                savarch.service
Fri 2018-03-02 00:00:00 CET  15h left Thu 2018-03-01 07:49:43 CET  56min ago shadow.timer                 shadow.service
Fri 2018-03-02 00:00:00 CET  15h left Thu 2018-03-01 07:49:43 CET  56min ago updatedb.timer               updatedb.service
Fri 2018-03-02 08:04:45 CET  23h left Thu 2018-03-01 08:04:45 CET  41min ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service

et accéder aux logs de vos “timers” :

journalctl -u savarch.service
...
mars 01 08:43:47 yannick-pc bash[1932]: 2018-03-01 08:43:15 Départ sauvegarde
mars 01 08:43:47 yannick-pc bash[1932]: 2018-03-01 08:43:15 synchronisation source partagée /home/yannick/media/Musique cible locale /home/yannick/media/savlocal/Musique
mars 01 08:43:47 yannick-pc bash[1932]: 2018-03-01 08:43:21 synchronisation partagée /home/yannick/media/devel cible locale /home/yannick/media/savlocal/devel
mars 01 08:43:47 yannick-pc bash[1932]: 2018-03-01 08:43:33 synchronisation partagée /home/yannick/media/BiblioCalibre cible locale /home/yannick/media/savlocal/BiblioCalibre
mars 01 08:43:47 yannick-pc bash[1932]: 2018-03-01 08:43:41 SAUVEGARDE /home/yannick -> /home/yannick/media/savlocal/yannick-pc/yannick
mars 01 08:43:47 yannick-pc bash[1932]: 2018-03-01 08:43:42 SAUVEGARDE /home/yannick/media/dplus -> /home/yannick/media/savlocal/dplus
mars 01 08:43:47 yannick-pc bash[1932]: 2018-03-01 08:43:47 SAUVEGARDE /home/yannick/media/yanspm-md -> /home/yannick/media/savlocal/yanspm-md
mars 01 08:43:47 yannick-pc bash[1932]: 2018-03-01 08:43:47 FIN sauvegarde
mars 01 08:43:47 yannick-pc systemd[1]: Started Sauvegarde jour.

En cas de modification du .timer ou du .service, ne pas oublier de faire un systemctl daemon-reload pour que la version actualisée de vos fichiers soit prise en compte par systemd.

Archlinux Nettoyage du cache (systemd-timer)

pkgcacheclean est un petit programme qui nettoie le cache pacman mais conserve n versions du paquet dans le cache.

yaourt -S pkgcacheclean

Manuellement

pkgcacheclean -nv 3

Cette commande va afficher (et seulement afficher, aucun fichier n’est modifié) ce qu’il ferait si vous vouliez conserver 3 versions pour chaque paquet installé.
Pour effectuer les changements sur le disque :

pkgcacheclean -v 3

Automatiquement
Nettoyage du cache pacman une fois par mois avec conservation de 3 versions (2 précédentes + 1 en cours)

Fichier /etc/systemd/system/pkgcacheclean.timer

[Unit]
Description=Minuterie mensuelle pour pkgcacheclean (nettoyage cache pacman)

[Timer]
OnCalendar=monthly
AccuracySec=5d
Persistent=true
Unit=pkgcacheclean.service

[Install]
WantedBy=multi-user.target

Fichier /etc/systemd/system/pkgcacheclean.service

[Unit]
Description=Nettoyage cache pacman avec conservation de 3 versions

[Service]
Nice=19
IOSchedulingClass=2
IOSchedulingPriority=7
Type=oneshot
ExecStart=/usr/bin/pkgcacheclean -v 3

Ensuite, pour qu’il soit actif, il faut prévenir systemd

sudo systemctl enable pkgcacheclean.timer
sudo systemctl start pkgcacheclean.timer

Ne pas oublier un sudo systemctl daemon-reload après avoir modifier un fichier, sinon ce n’est pas pris en compte.

Tutoriel Systemd Timer avec un exemple d’envoi automatique d’e-mails

Unité de service (Service Unit)

Comme mentionné précédemment, l’unité de service est la tâche à effectuer, comme l’envoi d’un e-mail.
La création d’un nouveau Service est très simple, qui consiste à créer un nouveau fichier dans le répertoire /usr/lib/systemd/system, tel que le fichier mytimer.service. Par exemple, vous pouvez taper le code suivant.

[Unit]
Description=MyTimer

[Service]
ExecStart=/bin/bash /path/to/mail.sh

Comme vous pouvez le voir, le fichier de l’unité de service est divisé en deux parties.

La partie[Unit] décrit les informations de base de l’unité (c’est-à-dire les métadonnées) et le champ Description donne une brève introduction de l’unité (c’est-à-dire MyTimer).

La partie[Service] sert à personnaliser les actions et Systemd fournit de nombreux champs.

  • ExecStart : commandes à exécuter par systemctl start
  • ExecStop : commandes à exécuter par systemctl stop
  • ExecReload : commandes à exécuter par rechargement systemctl
  • ExecStartPre : commandes à exécuter automatiquement avant ExecStartPre
  • ExecStartPost : commandes à exécuter automatiquement après ExecStartPost
  • ExecStopPost : commandes à exécuter après ExecStop automatiquement

Notez que lors de la définition, tous les chemins doivent être écrits en tant que chemins absolus. Par exemple, bash doit être écrit comme /bin/bash, sinon Systemd ne le trouvera pas.

Démarrez maintenant le Service.

sudo systemctl démarrer mytimer.service

Si tout va bien, vous recevrez un email.

Unité de temporisarion (Timer Unit)

L’unité de service (Service Unit) définit simplement comment exécuter la tâche.
Vous devez définir l’unité Minuterie (Timer Unit) afin d’exécuter le Service périodiquement.
Créez un nouveau fichier mytimer.timer dans le répertoire /usr/lib/systemd/system, et tapez le code suivant.

[Unit]
Description=Runs mytimer every hour

[Timer]
OnUnitActiveSec=1h
Unit=mytimer.service

[Install]
WantedBy=multi-user.target

Le fichier de l’unité de temporisation (Timer) est divisé en plusieurs parties.

La partie[Unit] définit les métadonnées.

La partie[Timer] personnalise la minuterie.
Et Systemd fournit les champs suivants.

  • OnActiveSec : Combien de temps faut-il pour démarrer la tâche après que la minuterie ait pris effet ?
  • OnBootSec : Combien de temps faut-il pour lancer la tâche après le démarrage du système ?
  • OnStartupSec : Combien de temps faut-il pour démarrer la tâche après le démarrage du processus Systemd ?
  • OnUnitActiveSec : Combien de temps faut-il pour que l’unité exécute à nouveau après la dernière exécution de l’unité ?
  • OnUnitInactiveSec : Combien de temps faut-il pour exécuter une nouvelle fois depuis que la minuterie a été désactivée la dernière fois ?
  • OnCalendar : Exécuter en fonction du temps absolu et non du temps relatif.
  • AccuracySec : Si la tâche doit être reportée pour une raison quelconque, le nombre maximum de secondes à reporter est de 60 secondes par défaut.
  • Unit : L’intervention à exécuter. L’unité par défaut est l’unité avec le suffixe .service du même nom.
  • Persistent : Si le champ est défini, l’unité correspondante sera automatiquement exécutée même si la minuterie ne démarre pas.
  • WakeSystem : S’il faut réveiller le système automatiquement si le système se met en veille.

Dans le script ci-dessus, OnUnitActiveSec=1h indique que la tâche sera effectuée par heure.
D’autres façons d’écrire sont : OnUnitActiveSec=--- 02:00:00:00 signifie être exécuté à deux heures du matin, et OnUnitActiveSec=Mon --* 02:00:00:00 signifie être exécuté à deux heures du matin chaque lundi. Pour plus de détails, veuillez vous référer à la documentation officielle.

Installer et cible ([Install] and target)

Dans le fichier mytimer.timer, il y a une partie [Install] qui définit les commandes à exécuter lors de l’activation ou de la désactivation de systemctl de l’appareil.
Dans le script ci-dessus, la partie [Install] ne définit qu’un seul champ, qui est WantedBy=multi-user.target.
Cela signifie que si systemctl enable mytimer.timer est exécuté (tant que Systemd est démarré, le timer prend automatiquement effet), alors le timer appartient à multi-user.target.

La cible se réfère à un groupe de processus apparentés, un peu comme le niveau de démarrage sous le mode de processus init. Lorsqu’une Cible est démarrée, tous les processus appartenant à cette Cible seront également démarrés.
multi-user.target est la cible la plus couramment utilisée. Lorsqu’un système est démarré en mode multi-utilisateur, mytimer.timer est démarré avec. La façon de travailler sous le capot est assez simple. Lors de l’exécution de la commande systemctl enable mytimer.timer, un lien symbolique sera créé dans le répertoire multi-user.target.wants, pointant vers mytimer.timer.

Commandes relatives à la minuterie

démarrez la minuterie nouvellement créée.

sudo systemctl start mytimer.timer

Vous recevrez le courriel immédiatement et recevrez le même courriel toutes les heures.

Vérifiez l’état de la minuterie.

systemctl status mytimer.timer

Voir tous les chronomètres (rimers) en cours d’exécution.

systemctl list-timers

Éteignez la minuterie.

sudo systemctl stop myscript.timer

Systemctl active automatiquement la minuterie.

sudo systemctl enable myscript.timer

Systemctl désactive la minuterie.

sudo systemctl disable myscript.timer

Commandes relatives au journal

Si une exception survient, vous devez vérifier le protocole. Voici les commandes que Systemd fournit.

# view the entire log
$ sudo journalctl

# view the log for mytimer.timer
$ sudo journalctl -u mytimer.timer

# view the logs for mytimer.timer and mytimer.service
$ sudo journalctl -u mytimer

# view the latest log from the end
$ sudo journalctl -f

# view the log for mytimer.timer from the end
$ journalctl -f -u timer.timer

Liens