#!/bin/bash # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 function display_help() { uecho "$scriptname: lancer une suite de commande en respectant une planification de type cron USAGE $scriptname [options] /path/to/crontab $scriptname -l [/path/to/crontab] La première forme du script doit normalement être lancé toutes les minutes par une tâche cron. Utiliser l'option --install pour ajouter automatique la ligne dans la crontab de l'utilisateur. A chaque lancement du script, il examine quels scripts doivent être exécutés dans le fichier crontab spécifié. Ce fichier est composé de lignes d'une des formes suivantes, qui sont analysées et traitées dans l'ordre: # commentaire ignoré var=\"initialiser une variable\" minutes hours days months dows command [args] Quelles que soient les lignes qui sont sélectionnées pour le lancement, elles sont garanties de s'exécuter dans l'ordre du fichier, l'une après l'autre. Certaines extensions par rapport à la syntaxe de crontab sont autorisées. Il est en particulier possible de spécifier plusieurs planifications pour une seule commande. Par exemple, la ligne suivante permet d'exécuter 'command' toutes les heures ET à 1h05: 0 * * * * 5 1 * * * command De plus, il est possible de manipuler les variables de type PATH avec une syntaxe particulière de l'opérateur d'assignation. Les opérateurs += et %= utilisent uaddpath(), #= utilise uinspath() et -= utilise udelpath(). Par exemple, les lignes suivantes ajoutent respectivement /usr/local/nutools puis enlèvent /opt/rogue au PATH: PATH+=/usr/local/nutools PATH-=/opt/rogue OPTIONS -A, --install Installer une planification toutes les minutes de ce script dans la crontab de l'utilisateur. L'argument /path/to/crontab est requis. -R, --uninstall Désinstaller la planification toutes les minutes de ce script du crontab de l'utilisateur. L'argument /path/to/crontab est requis, et seule cette instance est désinstallée le cas échéant. -l, --list Lister les contenus des fichiers crontab dont l'exécution a été planifiée avec --install Si /path/to/crontab est spécifié, ne lister le contenu de ce fichier que si et seulement si son exécution a été planifiée. -n, --fake Afficher au lieu de les exécuter les commandes qui doivent être lancées OPTIONS AVANCEES --lock LOCKFILE Inscrire dans le fichier spécifié des informations permettant d'éviter les invocations simultanées de ce script. Si selon ce fichier, le script tourne depuis plus de $LOCKDELAY heures, un message d'erreur est loggé et un message d'avertissement est affiché au plus une fois. Utiliser --lock '' pour désactiver cette fonctionnalité Par défaut, si ce script est lancé en root, le fichier utilisé pour le verrouillage est de la forme /var/run/$scriptname/abspath/to/crontab Si le script est lancé avec un compte utilisateur, aucun verrouillage n'est effectué. --lockdelay LOCKDELAY[=$LOCKDELAY] Changer le nombre d'heures pendant lesquelles on autorise le script a verrouiller l'exécution avant d'afficher un avertissement. -c, --continuous Par défaut, ce script s'arrête à la première commande qui retourne avec une code d'erreur. Avec cette option, ce script ne s'arrête jamais, bien qu'il retourne toujours un code d'erreur si une erreur s'est produite. -k, --stopec EXITCODE[=101] Spécifier un code d'erreur spécial qui arrête ce script sans erreur, ou '' pour désactiver cette fonctionnalité. Ceci permet en début de script de faire des tests par exemple sur l'environnement avant de lancer les scripts planifiés. Si l'environnement ne convient pas, il suffit au script de contrôle de retourner le code d'erreur spécifique pour arrêter le traitement." } source "$(dirname "$0")/ulib/ulib" && urequire DEFAULTS crontab || exit 1 MY_CTLINE="* * * * * $script" LOCKDELAY=8 action=run lockfile=auto lockdelay= fake= continuous= stopec=101 parse_opts "${PRETTYOPTS[@]}" \ --help '$exit_with display_help' \ -A,--add,--install action=install \ -R,--remove,--uninstall action=uninstall \ --lock: lockfile= \ --lockdelay: lockdelay= \ -n,--fake fake=1 \ -c,--continuous continuous=1 \ -k:,--stop: stopec=1 \ -l,--list action=list \ @ args -- "$@" && set -- "${args[@]}" || die "$args" if [ "$action" == "list" ]; then crontab="$1"; shift [ -n "$crontab" ] && crontab="$(abspath "$crontab")" array_from_lines ctfiles "$(crontab -l 2>/dev/null | awkrun script="$script" '$6 == script { print $7 }')" found= for ctfile in "${ctfiles[@]}"; do if [ -z "$crontab" -o "$ctfile" == "$crontab" ]; then found=1 etitle "$(ppath "$ctfile")" cat "$ctfile" eend fi done if [ -n "$crontab" -a -z "$found" ]; then ewarn "$(ppath "$crontab"): non planifié" fi exit 0 fi crontab="$1"; shift [ -n "$crontab" ] || die_with "Vous devez spécifier le fichier crontab" display_help [ -f "$crontab" ] || die "$crontab: fichier introuvable" crontab="$(abspath "$crontab")" if [ "$action" == "install" ]; then enable_in_crontab "$MY_CTLINE $(quoted_arg "$crontab")" && estep "add_to_crontab $MY_CTLINE $(quoted_arg "$crontab")" elif [ "$action" == "uninstall" ]; then remove_from_crontab "$MY_CTLINE $(quoted_arg "$crontab")" && estep "remove_from_crontab $MY_CTLINE $(quoted_arg "$crontab")" elif [ "$action" == "run" ]; then if [ "$lockfile" == auto ]; then if is_root; then lockfile="/var/run/$scriptname$crontab.lock" mkdirof "$lockfile" || die else lockfile= fi fi [ -n "$lockdelay" ] || lockdelay="$LOCKDELAY" if [ -n "$lockfile" ]; then lockwarn="${lockfile%.lock}.lockwarn" retry=1 while [ -n "$retry" ]; do case "$(lf_trylock -h "$lockdelay" "$lockfile")" in locked) edebug "Arrêt du script parce que le verrou $lockfile est posé" exit 0 ;; stale) msg="Un verrou sur '$scriptname $crontab' est posé depuis plus de $lockdelay heures. Veuillez faire vos vérification et supprimer le cas échéant le fichier $lockfile" logger -p cron.warn -t "$scriptname" -- "$msg" if [ -f "$lockwarn" ]; then edebug "$msg" else touch "$lockwarn" ewarn "$msg" fi exit 1 ;; retry) :;; *) retry=;; esac done [ -f "$lockwarn" ] && rm "$lockwarn" autoclean "$lockfile" fi function __ctexec() { local ec=0 if [ -n "$fake" ]; then echo "$*" else edebug "$*" (eval "$*"); ec=$? fi [ -n "$stopec" -a "$ec" == "$stopec" ] && exit 0 [ -z "$continuous" -a "$ec" != 0 ] && exit "$ec" return 0 } function __cterror() { die "$*" } ctscript="$(ctresolve <"$crontab")" ec=0 edebug "$ctscript" (eval "$ctscript"); ec=$? [ -f "$lockfile" ] && rm "$lockfile" exit "$ec" fi