752 lines
27 KiB
Bash
Executable File
752 lines
27 KiB
Bash
Executable File
#!/bin/bash
|
|
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
|
|
source "$(dirname "$0")/lib/ulib/ulib" || exit 1
|
|
urequire DEFAULTS crontab install
|
|
|
|
function display_help() {
|
|
uecho "$scriptname: lancer une suite de commande en respectant une planification de type cron
|
|
|
|
USAGE
|
|
$scriptname [options] [/path/to/uscrontab] [var=value...]
|
|
$scriptname -e [/path/to/uscrontab]
|
|
$scriptname -l
|
|
|
|
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.
|
|
|
|
Avec la première forme du script, le fichier spécifié est traité. Si aucun
|
|
fichier n'est spécifié, fusionner s'il existe le fichier
|
|
$USCRONTAB_USERFILE
|
|
avec chacun des fichiers du répertoire
|
|
$USCRONTAB_USERDIR
|
|
dans un fichier temporaire, puis exécuter le fichier résultat avec le nom
|
|
virtuel
|
|
$USCRONTAB_USER
|
|
note: le nom virtuel est utilisé pour le verrouillage avec --lock
|
|
|
|
A chaque lancement de ce script, le fichier /path/to/uscrontab spécifié est
|
|
examiné pour déterminer quels commandes doivent être exécutées. Ce fichier est
|
|
composé de lignes dans un format particulier, qui sont analysées et traitées
|
|
dans l'ordre.
|
|
|
|
Quelles que soient les lignes qui sont sélectionnées pour l'exécution, elles
|
|
sont garanties de s'exécuter dans l'ordre du fichier, l'une après l'autre.
|
|
|
|
Les définitions var=value mentionnées sur la ligne de commande sont des
|
|
définitions de variables à effectuer avant de lancer les commandes.
|
|
|
|
Les lignes commençant par # sont des commentaires et sont ignorées
|
|
|
|
== Définitions de variables et exécution de commandes ==
|
|
|
|
Les lignes de la forme suivante sont des définitions de variable:
|
|
|
|
[export] var=\"valeur de la variable\"
|
|
|
|
Ces lignes sont des définitions de variable bash qui sont exécutées telles
|
|
quelles. Il n'est donc pas autorisé de mettre des espaces autour de =. Par
|
|
exemple, les lignes suivantes sont des erreurs de syntaxe:
|
|
|
|
var = bad
|
|
var=pas de quotes autour de la valeur
|
|
|
|
alors que celles-ci sont correctes:
|
|
|
|
var=ok
|
|
var=\"valeur avec des espaces\"
|
|
var='on peut utiliser des quotes aussi'
|
|
|
|
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
|
|
|
|
Bien sûr, il ne faut pas oublier de quoter les espaces:
|
|
|
|
PATH+=\"/path/to/dir with spaces\"
|
|
|
|
La syntaxe ?= permet de définir la valeur d'une variable si elle n'est pas
|
|
déjà définie:
|
|
|
|
var?=default
|
|
|
|
Les lignes de la forme suivante permettent d'exécuter une commande qui est
|
|
exécutée systématiquement et ignore la planification:
|
|
|
|
\$one-line-command
|
|
|
|
Une variante permet de spécifier des commandes sur plusieurs lignes.
|
|
ATTENTION! \${ et $} doivent être tous seuls sur la ligne.
|
|
|
|
\${
|
|
several
|
|
commands
|
|
...
|
|
$}
|
|
|
|
Ces commandes sont exécutées systématiquement et ignorent la planification.
|
|
On peut s'en servir notamment pour lire un fichier de configuration qui
|
|
définit des variables ou des fonctions:
|
|
|
|
\$source path/to/file
|
|
|
|
Le code d'erreur de ces commandes est ignoré, contrairement à ce qui se
|
|
passe pour les commandes qui font l'objet d'une planification.
|
|
|
|
== Planification de commandes ==
|
|
|
|
Les autres lignes doivent être au format d'une ligne de crontab:
|
|
|
|
minutes hours days months dows command-line
|
|
|
|
command-line peut être n'importe quelle ligne de commande bash, pourvu
|
|
qu'elle soit sur une seule ligne.
|
|
|
|
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, les lignes suivantes permettent d'exécuter
|
|
'command' toutes les heures ET à 1h05:
|
|
|
|
0 * * * *
|
|
5 1 * * * command
|
|
|
|
Il est aussi possible d'utiliser la même planification pour plusieurs
|
|
commandes sans devoir répéter la définition de la planification. Les lignes
|
|
suivantes planifient command1 et command2 toutes les heures:
|
|
|
|
0 * * * * command1
|
|
command2
|
|
|
|
Pour être prise en compte, la ligne command2 doit commencer par au moins un
|
|
espace ou une tabulation. Pour la lisibilité, la syntaxe suivante est
|
|
supportée aussi:
|
|
|
|
0 * * * *
|
|
command1
|
|
command2
|
|
|
|
Les deux formats peuvent être utilisés ensemble. Par exemple les lignes
|
|
suivantes exécutent command1 et command2 toutes les heures ET à 1h05:
|
|
|
|
0 * * * *
|
|
5 1 * * * command1
|
|
command2
|
|
|
|
Par défaut, le script s'arrête à la première commande planifiée qui retourne
|
|
avec un code d'erreur. Il est possible d'ignorer le code d'erreur d'une
|
|
commande avec nostop, e.g:
|
|
|
|
0 * * * * nostop command
|
|
|
|
Cf aussi l'option --continuous pour modifier le comportement par défaut
|
|
|
|
== Fonctions disponibles ==
|
|
|
|
La fonction check_pidfile() est disponible, et permet de vérifier qu'une
|
|
opération n'est pas déjà en cours. Si cette fonction est utilisée, il ne
|
|
faut pas modifier la valeur de -k. Par exemple:
|
|
|
|
0 1 * * *
|
|
check_pidfile /path/to/pid [args]
|
|
long-running-script
|
|
|
|
check_pidfile() doit être utilisée toute seule sur la ligne et s'utilise
|
|
avec les argument suivants:
|
|
|
|
check_pidfile PIDFILE [DESC] [BARRIER]
|
|
|
|
- PIDFILE est le fichier de PID qui est vérifié
|
|
- DESC est la description du traitement qui est effectué. La valeur par
|
|
défaut est \"Une synchronisation\". Si le fichier de PID est présent, le
|
|
message suivant est affiché:
|
|
DESC est en cours.
|
|
Si vous pensez que c'est une erreur, veuillez vérifier le process de pid PID
|
|
puis supprimez le cas échéant le fichier PIDFILE
|
|
- BARRIER est un fichier qui est créé avec le contenu 'PID' s'il n'existe
|
|
pas encore, et si la vérification du fichier de PID est faite avec succès.
|
|
La présence de ce fichier peut-être vérifiée par un processus externe pour
|
|
empêcher par exemple de mettre à jour les scripts pendant qu'il sont en
|
|
train de tourner.
|
|
Son contenu peut être examiné pour connaître le PID du processus qui l'a
|
|
créé initialement. Le fichier est automatiquement supprimé à la fin de ce
|
|
script.
|
|
Attention: ce fichier n'est pas un verrou, il peut être supprimé à tout
|
|
moment. Notamment, si deux scripts sont configurés pour créer le même
|
|
fichier barrière, le premier script supprimera le fichier barrière avant
|
|
la fin de l'exécution du second script.
|
|
|
|
La fonction remove_pidfile() permet de supprimer un fichier de pid pour
|
|
spécifier qu'une opération est terminée. Considérons l'exemple suivant:
|
|
|
|
0 1 * * *
|
|
check_pidfile /path/to/pid
|
|
script1
|
|
script2
|
|
remove_pidfile /path/to/pid
|
|
script3
|
|
|
|
Dans cet exemple, il ne faut pas qu'une autre occurence de script1 tourne
|
|
pendant que script2 tourne. Par contre, plusieurs occurences de script3
|
|
peuvent tourner en parallèle.
|
|
|
|
La fonction elogto() permet de spécifier un fichier vers lequel toutes les
|
|
sorties sont redirigées.
|
|
|
|
La fonction osvar() permet d'utiliser une variable de type oneshot définie
|
|
via l'option --osvar. Dans l'exemple suivant:
|
|
|
|
0 1 * * *
|
|
osvar FIRST=default SECOND
|
|
echo \"first is \$FIRST and second is \$SECOND\"
|
|
|
|
En temps normal, FIRST vaut la valeur par défaut 'default' et SECOND vaut la
|
|
chaine vide ''. Si on appelle '$scriptname -v FIRST=1 -v SECOND=2' alors ces
|
|
variables auront la valeur spécifiée *une seule fois* à la prochaine
|
|
invocation avant de retrouver leur valeur par défaut à l'invocation
|
|
suivante.
|
|
|
|
OPTIONS
|
|
-A, --install
|
|
Installer une planification toutes les minutes du script dans la crontab
|
|
de l'utilisateur. Si l'argument /path/to/uscrontab n'est pas spécifié,
|
|
c'est une planification générique qui exécute les fichiers par défaut
|
|
qui est installée.
|
|
-R, --uninstall
|
|
Désinstaller la planification toutes les minutes du script de la crontab
|
|
de l'utilisateur. Si l'argument /path/to/uscrontab est spécifié, cette
|
|
instance est désinstallée. Sinon, ne désinstaller que la planification
|
|
générique.
|
|
-d, --disable-only
|
|
Avec l'option -R, désactiver la planification au lieu de la supprimer.
|
|
-e, --edit
|
|
Lancer un editeur pour modifier l'uscrontab spécifiée. Si aucun fichier
|
|
n'est spécifié, éditer $USCRONTAB_USERFILE
|
|
-a, --add
|
|
Installer un script uscrontab dans le répertoire approprié. L'argument
|
|
doit être de la forme [name:]/path/to/uscrontab
|
|
Si name n'est pas spécifié, le nom de base du fichier spécifié est
|
|
utilisé. Si name est vide ou vaut \$USER (soit $USER en l'occurence),
|
|
copier le fichier spécifié vers le chemin $USCRONTAB_USERFILE
|
|
Sinon, copier le fichier spécifié vers $USCRONTAB_USERDIR/name
|
|
-r, --remove
|
|
Supprimer le script uscrontab spécifié. L'argument doit être le nom du
|
|
script à supprimer. Si l'argument n'est pas spécifié ou vaut \$USER
|
|
(soit $USER en l'occurence), supprimer le fichier $USCRONTAB_USERFILE
|
|
s'il existe
|
|
-l, --list
|
|
Si l'argument /path/to/crontab est spécifié, afficher le contenu de ce
|
|
fichier. Sinon, lister les contenus des fichiers crontab qui sont
|
|
exécutés avec la planification actuelle. Si une planification générique
|
|
est installée, ou si aucune planification n'est en cours, afficher le
|
|
contenu du fichier
|
|
$USCRONTAB_USERFILE
|
|
et chacun des fichiers du répertoire
|
|
$USCRONTAB_USERDIR
|
|
-N, --nop
|
|
Ne pas examiner la planification. Traiter uniquement l'option --osvar le
|
|
cas échéant.
|
|
-n, --fake
|
|
Afficher au lieu de les exécuter les commandes qui doivent être lancées
|
|
-P, --pause-for NBMINS
|
|
Désactiver les planifications pendant NBMINS minutes. Utiliser -1 pour
|
|
désactiver les planifications sans limite de durée. Pendant la période
|
|
de pause, toutes les invocations de uscrontab n'ont aucun effet, sauf si
|
|
on utilise l'option --force
|
|
-Y, --unpause
|
|
Réactiver les planifications après une mise en pause
|
|
-p, --pause
|
|
Désactiver les planifications pendant 1 journée. Equivalent à -P 1440
|
|
-f, --force
|
|
Forcer l'exécution de la planification, même si elle a été mise en pause
|
|
avec l'option --pause
|
|
-v, --osvar NAME[=VALUE]
|
|
Définir une variable de type oneshot. Par défaut, VALUE vaut 1 si elle
|
|
n'est pas spécifiée.
|
|
|
|
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 $USCRONTAB_LOCKDELAY heures, un message d'erreur
|
|
est consigné 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[=$USCRONTAB_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 planifiée qui
|
|
retourne avec un code d'erreur. Notez que les codes d'erreur des
|
|
commandes sans planification sont toujours ignorés. 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. Il est possible d'ignorer le
|
|
code d'erreur pour une commande en particulier avec le préfixe nostop
|
|
-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.
|
|
--show-ctnow
|
|
Afficher l'heure de référence au format crontab 'min hou day mon dow'
|
|
Cette valeur peut être utilisée avec l'option --force-ctnow dans des
|
|
tests pour reproduire une condition spécifique.
|
|
--force-ctnow 'min hou day mon dow'
|
|
Pour le développement ou des tests, forcer la valeur de l'heure de
|
|
référence. Il faut respecter le format, sinon les résultats ne sont pas
|
|
garantis. Le mieux est de reprendre le résultat de l'option --show-ctnow
|
|
en le modifiant un peu si nécessaire.
|
|
-@, --hm H[:M]
|
|
-H, --h00
|
|
-M, --h30
|
|
Options pour simplifier l'utilisation de --force-ctnow. Supposons que
|
|
--show-ctnow affiche 'Y X day mon dow', alors:
|
|
-@ H:M | est équivalent à | --force-ctnow 'M H day mon dow'
|
|
-H | '' | --force-ctnow '0 X day mon dow'
|
|
-M | '' | --force-ctnow '30 X day mon dow'
|
|
-G, --any-ctnow
|
|
Pour le développement ou des tests, lancer toutes les commandes dans
|
|
l'ordre sans tenir compte de l'heure de référence. Cette commande ne
|
|
devrait pas être utilisée en temps normal, mais elle existe pour
|
|
simplifier les tests avec --show-ctnow + --force-ctnow dans les cas
|
|
simples."
|
|
}
|
|
|
|
function set_usercrontabs() {
|
|
# initialiser le tableau $1(=usercrontabs) avec la liste suivante: le
|
|
# fichier $USCRONTAB_USERFILE s'il existe, puis la liste des fichiers dans
|
|
# le répertoire $USCRONTAB_USERDIR
|
|
local -a _userfile _userdir
|
|
[ -f "$USCRONTAB_USERFILE" ] && _userfile=("$USCRONTAB_USERFILE")
|
|
array_lsfiles _userdir "$USCRONTAB_USERDIR"
|
|
eval "${1:-usercrontabs}"'=("${_userfile[@]}" "${_userdir[@]}")'
|
|
}
|
|
|
|
function get_usercrontab() {
|
|
# trouver le chemin correspondant au crontab $1. Si $1 n'est pas spécifié ou
|
|
# vaut $USER, afficher $USCRONTAB_USERFILE, sinon afficher $USCRONTAB_USERDIR/$1
|
|
local name="$1"
|
|
[ "$name" == "$USER" ] && name=
|
|
if [ -n "$name" ]; then
|
|
echo "$USCRONTAB_USERDIR/$name"
|
|
else
|
|
echo "$USCRONTAB_USERFILE"
|
|
fi
|
|
}
|
|
|
|
function check_pause() {
|
|
# retourner 0 si on est en pause
|
|
local pauseuntil pausefile="$pausefile" now="$2"
|
|
if [ -f "$pausefile" ]; then
|
|
pauseuntil="$(<"$pausefile")"
|
|
if [ -z "$pauseuntil" ]; then
|
|
# pause sans limitation de durée
|
|
return 0
|
|
elif [ "$pauseuntil" -gt "$now" ]; then
|
|
return 0
|
|
fi
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
USCRONTAB_CTLINE="* * * * * $script"
|
|
USCRONTAB_LOCKDELAY=8
|
|
USCRONTAB_STOPEC=101
|
|
USCRONTAB_BASEDIR=/var/local/uscrontab
|
|
USCRONTAB_USERFILE="$USCRONTAB_BASEDIR/users/$USER"
|
|
USCRONTAB_USERDIR="$USCRONTAB_BASEDIR/users.d/$USER"
|
|
USCRONTAB_USER="$USCRONTAB_BASEDIR/$USER"
|
|
|
|
action=run
|
|
disable_only=
|
|
lockfile=auto
|
|
lockdelay=
|
|
fake=
|
|
continuous=
|
|
force_ctnow=
|
|
pause=
|
|
force=
|
|
osvars=()
|
|
parse_opts "${PRETTYOPTS[@]}" \
|
|
--help '$exit_with display_help' \
|
|
-A,--add,--install action=install \
|
|
-R,--remove,--uninstall action=uninstall \
|
|
-d,--disable-only '$action=uninstall; disable_only=1' \
|
|
-e,--edit action=edit \
|
|
-a,--add action=add \
|
|
-r,--remove action=remove \
|
|
-l,--list action=list \
|
|
-N,--nop action=nop \
|
|
-n,--fake fake=1 \
|
|
-P:,--pause-for: '$action=pause; set@ pause' \
|
|
-p,--pause action=pause \
|
|
-Y,--unpause action=unpause \
|
|
-f,--force force=1 \
|
|
-v:,--osvar: osvars \
|
|
--lock: lockfile= \
|
|
--lockdelay: lockdelay= \
|
|
-c,--continuous continuous=1 \
|
|
-k:,--stop: USCRONTAB_STOPEC= \
|
|
--show-ctnow action=show-ctnow \
|
|
--force-ctnow: __CTRESOLVE_CTNOW= \
|
|
-@:,--hm: force_ctnow= \
|
|
-H,--h00 force_ctnow=h00 \
|
|
-M,--h30 force_ctnow=h30 \
|
|
-G,--any-ctnow __CTRESOLVE_CTNOW="**ANY**" \
|
|
@ args -- "$@" && set -- "${args[@]}" || die "$args"
|
|
|
|
pausefile="$USCRONTAB_USERFILE.pauseuntil"
|
|
now="$(awk 'BEGIN { print int(systime() / 60) * 60 }')"
|
|
|
|
# ne prendre le premier argument comme un fichier que s'il existe ou s'il n'est
|
|
# pas de la forme *=*
|
|
if [[ "$1" == *=* ]]; then
|
|
if [ -f "$1" ]; then
|
|
uscrontab="$1"; shift
|
|
fi
|
|
else
|
|
uscrontab="$1"; shift
|
|
fi
|
|
|
|
# écrire les variables oneshot
|
|
for osvar in "${osvars[@]}"; do
|
|
if [[ "$osvar" == *=* ]]; then
|
|
splitvar "$osvar" name value
|
|
else
|
|
name="$osvar"
|
|
value=1
|
|
fi
|
|
name="${name##*/}" # au cas où...
|
|
echo -n "$value" >"$USCRONTAB_USERFILE.$name.osvar"
|
|
done
|
|
|
|
if [ "$action" == "pause" ]; then
|
|
[ -n "$pause" ] || pause=1440
|
|
if [ "$pause" -lt 0 ]; then
|
|
pauseuntil=
|
|
else
|
|
pauseuntil=$(($now + $pause * 60))
|
|
fi
|
|
echo "$pauseuntil" >"$pausefile" || die
|
|
enote "Désactivation des planifications pour $pause minute(s)"
|
|
exit 0
|
|
|
|
elif [ "$action" == "unpause" ]; then
|
|
if [ -f "$pausefile" ]; then
|
|
rm "$pausefile" || die
|
|
enote "Réactivation des planifications"
|
|
fi
|
|
exit 0
|
|
|
|
elif [ "$action" == "edit" ]; then
|
|
if [ -z "$uscrontab" ]; then
|
|
basedir="$(dirname "$USCRONTAB_USERFILE")"
|
|
[ -d "$basedir" ] || die "$basedir: ce répertoire n'existe pas. Vérifiez l'installation de nutools"
|
|
uscrontab="$USCRONTAB_USERFILE"
|
|
fi
|
|
enote "Edition de $uscrontab"
|
|
if [ ! -f "$uscrontab" ]; then
|
|
touch "$uscrontab" || die
|
|
chmod 640 "$uscrontab"
|
|
fi
|
|
"${EDITOR:-vi}" "$uscrontab" || die
|
|
exit 0
|
|
|
|
elif [ "$action" == "add" ]; then
|
|
[ -n "$uscrontab" ] || die "Vous devez spécifier le fichier à installer"
|
|
if [[ "$uscrontab" == *:* ]]; then
|
|
splitfsep2 "$uscrontab" : name uscrontab
|
|
[ -f "$uscrontab" ] || die "$uscrontab: fichier introuvable"
|
|
else
|
|
name="$(basename "$uscrontab")"
|
|
fi
|
|
dest="$(get_usercrontab "$name")"
|
|
|
|
destdir="$(dirname -- "$dest")"
|
|
if [ ! -d "$destdir" ]; then
|
|
mkdir -p "$destdir" || die
|
|
chmod 750 "$destdir"
|
|
fi
|
|
|
|
copy_update_ask "$uscrontab" "$dest"
|
|
exit 0
|
|
|
|
elif [ "$action" == "remove" ]; then
|
|
uscrontab="$(get_usercrontab "$uscrontab")"
|
|
if [ -f "$uscrontab" ]; then
|
|
ask_yesno "Voulez-vous supprimer le fichier $uscrontab?" C || die
|
|
enote "Suppression de $uscrontab"
|
|
rm "$uscrontab" || die
|
|
fi
|
|
exit 0
|
|
|
|
elif [ "$action" == "list" ]; then
|
|
if [ -n "$uscrontab" ]; then
|
|
uscrontab="$(abspath "$uscrontab")"
|
|
array_from_lines ctfiles "$(crontab -l 2>/dev/null | awkrun script="$script" uscrontab="$uscrontab" '$6 == script && $7 == uscrontab { print $7 }')"
|
|
if [ ${#ctfiles[*]} -eq 0 ]; then
|
|
ewarn "$(ppath "$uscrontab"): non planifié"
|
|
ctfiles=("$uscrontab")
|
|
fi
|
|
else
|
|
array_from_lines ctfiles "$(crontab -l 2>/dev/null | awkrun script="$script" '$6 == script { if ($7) print $7; else if ($0 ~ /^#/) print "#GENERIC"; else print "GENERIC" }')" #'
|
|
if array_contains ctfiles "GENERIC"; then
|
|
# il y a une planification générique
|
|
array_del ctfiles "GENERIC"
|
|
set_usercrontabs usercrontabs
|
|
array_extend ctfiles usercrontabs
|
|
elif array_contains ctfiles "#GENERIC"; then
|
|
# il y a une planification générique désactivée
|
|
array_del ctfiles "#GENERIC"
|
|
set_usercrontabs usercrontabs
|
|
array_extend ctfiles usercrontabs
|
|
ewarn "Planification générique désactivée. Vous pouvez la réactiver avec $scriptname -A"
|
|
elif [ ${#ctfiles[*]} -eq 0 ]; then
|
|
einfo "Aucune planification en cours"
|
|
set_usercrontabs ctfiles
|
|
fi
|
|
fi
|
|
|
|
r=1
|
|
for ctfile in "${ctfiles[@]}"; do
|
|
r=0 # il y a au moins une planification
|
|
etitle "$(ppath "$ctfile")" \
|
|
cat "$ctfile"
|
|
done
|
|
|
|
if check_pause "$pausefile" "$now"; then
|
|
ewarn "En pause. Réactiver avec $scriptname -Y"
|
|
fi
|
|
exit $r
|
|
|
|
elif [ "$action" == "show-ctnow" ]; then
|
|
ctnow "$now"
|
|
exit 0
|
|
|
|
elif [ "$action" == "nop" ]; then
|
|
exit 0
|
|
fi
|
|
|
|
[ -z "$uscrontab" -o -f "$uscrontab" ] || die "$uscrontab: fichier introuvable"
|
|
[ -n "$uscrontab" ] && uscrontab="$(abspath "$uscrontab")"
|
|
|
|
if [ "$action" == "install" ]; then
|
|
ctline="$USCRONTAB_CTLINE"
|
|
[ -n "$uscrontab" ] && ctline="$ctline $(qvalm "$uscrontab")"
|
|
enable_in_crontab "$ctline" && estep "add_to_crontab $ctline"
|
|
|
|
elif [ "$action" == "uninstall" ]; then
|
|
ctline="$USCRONTAB_CTLINE"
|
|
[ -n "$uscrontab" ] && ctline="$ctline $(qvalm "$uscrontab")"
|
|
if [ -n "$disable_only" ]; then
|
|
disable_in_crontab "$ctline" && estep "disable_in_crontab $ctline"
|
|
else
|
|
remove_from_crontab "$ctline" && estep "remove_from_crontab $ctline"
|
|
fi
|
|
|
|
elif [ "$action" == "run" ]; then
|
|
if [ -n "$force" ]; then
|
|
# ne pas tenir compte de l'état de pause
|
|
:
|
|
elif check_pause "$pausefile" "$now"; then
|
|
isatty && ewarn "En pause. Réactiver avec $scriptname -Y"
|
|
ac_clean "$lockfile"
|
|
exit 0
|
|
elif [ -f "$pausefile" ]; then
|
|
rm "$pausefile"
|
|
fi
|
|
|
|
if [ -n "$uscrontab" ]; then
|
|
default_lockfile="/var/run/$scriptname$uscrontab.lock"
|
|
lockdesc="$scriptname $uscrontab"
|
|
else
|
|
set_usercrontabs usercrontabs
|
|
ac_set_tmpfile uscrontab
|
|
for usercrontab in "${usercrontabs[@]}"; do
|
|
echo "# $usercrontab" >>"$uscrontab"
|
|
cat "$usercrontab" >>"$uscrontab"
|
|
done
|
|
default_lockfile="/var/run/$scriptname$USCRONTAB_USER.lock"
|
|
lockdesc="$scriptname $USCRONTAB_USER"
|
|
fi
|
|
|
|
if [ "$lockfile" == auto ]; then
|
|
if is_root; then
|
|
lockfile="$default_lockfile"
|
|
mkdirof "$lockfile" || die
|
|
else
|
|
lockfile=
|
|
fi
|
|
fi
|
|
[ -n "$lockdelay" ] || lockdelay="$USCRONTAB_LOCKDELAY"
|
|
|
|
if [ -n "$lockfile" ]; then
|
|
lastmsgfile="${lockfile%.lock}.lastmsg"
|
|
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 '$lockdesc' est posé depuis plus de $lockdelay heures. Veuillez faire vos vérification et supprimer le cas échéant le fichier $lockfile"
|
|
lastmsg=
|
|
[ -f "$lastmsgfile" ] && lastmsg="$(<"$lastmsgfile")" || lastmsg=
|
|
|
|
logger -p cron.warn -t "$scriptname" -- "$msg"
|
|
if [ "$msg" != "$lastmsg" ]; then
|
|
ewarn "$msg"
|
|
echo "$msg" >"$lastmsgfile"
|
|
else
|
|
edebug "$msg"
|
|
fi
|
|
exit 1
|
|
;;
|
|
retry) :;;
|
|
*) retry=;;
|
|
esac
|
|
done
|
|
|
|
rm -f "$lastmsgfile" 2>/dev/null
|
|
autoclean "$lockfile"
|
|
fi
|
|
|
|
function __should_fork() {
|
|
# tester si la commande doit être lancée dans un sous-shell
|
|
local cmd
|
|
cmd="${1%% *}"
|
|
case "$cmd" in
|
|
check_pidfile|remove_pidfile|osvar|elogto) return 1;;
|
|
esac
|
|
return 0
|
|
}
|
|
function __ctexec() {
|
|
local ec=0
|
|
|
|
local cmd="${1%% *}"
|
|
if [ "$cmd" == nostop ]; then
|
|
cmd="${1#nostop }"
|
|
shift
|
|
set -- "$cmd" "$@"
|
|
local continuous=1
|
|
fi
|
|
|
|
if [ -n "$fake" ]; then
|
|
echo "$*"
|
|
else
|
|
edebug "$*"
|
|
if __should_fork "$1"; then
|
|
(eval "$*"); ec=$?
|
|
else
|
|
# cas particulier: certaines fonctions doivent être exécutée
|
|
# dans le contexte courant, et non pas dans un sous-shell
|
|
eval "$*"; ec=$?
|
|
fi
|
|
fi
|
|
[ -n "$USCRONTAB_STOPEC" -a "$ec" == "$USCRONTAB_STOPEC" ] && exit 0
|
|
[ -z "$continuous" -a "$ec" != 0 ] && exit "$ec"
|
|
return 0
|
|
}
|
|
function __cterror() {
|
|
die "$*"
|
|
}
|
|
|
|
if [ -n "$force_ctnow" ]; then
|
|
ctnow=($(ctnow "$now"))
|
|
case "$force_ctnow" in
|
|
h00) ctnow[0]=0;;
|
|
h30) ctnow[0]=30;;
|
|
*)
|
|
force_ctnow="${force_ctnow//./:}" # autoriser H.M en plus de H:M
|
|
splitpair "$force_ctnow" h m
|
|
ctnow[0]="${m:-0}"
|
|
ctnow[1]="$h"
|
|
;;
|
|
esac
|
|
__CTRESOLVE_CTNOW="${ctnow[*]}"
|
|
fi
|
|
|
|
__ctscript="$(ctresolve <"$uscrontab")"
|
|
ec=0
|
|
edebug "$__ctscript"
|
|
(
|
|
# tableau des fichiers de pid en cours. la conséquence est que ce n'est
|
|
# pas une erreur d'appeler à plusieurs reprises check_pidfile avec le
|
|
# même fichier
|
|
__USCRONTAB_PIDFILES=()
|
|
function check_pidfile() {
|
|
if [ -n "$1" ]; then
|
|
local pidfile="$(abspath "$1")"
|
|
if ! array_contains __USCRONTAB_PIDFILES "$pidfile"; then
|
|
local status
|
|
pidfile_set -r "$pidfile"; status=$?
|
|
case "$status" in
|
|
1)
|
|
eerror "${2:-Une synchronisation} est en cours.
|
|
Si vous pensez que c'est une erreur, veuillez vérifier le process de pid $(<"$pidfile")
|
|
puis supprimez le cas échéant le fichier $pidfile"
|
|
return "$USCRONTAB_STOPEC"
|
|
;;
|
|
10)
|
|
die "Une erreur s'est produite pendant l'écriture du fichier de pid. Impossible de continuer"
|
|
;;
|
|
esac
|
|
array_add __USCRONTAB_PIDFILES "$pidfile"
|
|
fi
|
|
fi
|
|
if [ -n "$3" -a -w "$(dirname "$3")" ]; then
|
|
(set -o noclobber
|
|
echo_ $$ >"$3" &&
|
|
chmod 644 "$3"
|
|
) 2>/dev/null &&
|
|
autoclean "$3"
|
|
fi
|
|
return 0
|
|
}
|
|
function remove_pidfile() {
|
|
if [ -n "$1" ]; then
|
|
local pidfile="$(abspath "$1")"
|
|
ac_clean "$pidfile"
|
|
array_del __USCRONTAB_PIDFILES "$pidfile"
|
|
fi
|
|
}
|
|
function osvar() {
|
|
local __name __value
|
|
for __name in "$@"; do
|
|
splitvar "$__name" __name __value
|
|
if [ -f "$USCRONTAB_USERFILE.$__name.osvar" ]; then
|
|
__value="$(<"$USCRONTAB_USERFILE.$__name.osvar")"
|
|
rm "$USCRONTAB_USERFILE.$__name.osvar"
|
|
fi
|
|
_setv "$__name" "$__value"
|
|
done
|
|
}
|
|
|
|
__ac_forgetall
|
|
# Si des variables sont spécifiées, les initialiser avant de lancer le
|
|
# script
|
|
for __var in "$@"; do
|
|
splitvar "$__var" __name __value
|
|
edebug "$__name=$__value"
|
|
_setv "$__name" "$__value"
|
|
done
|
|
eval "$__ctscript"
|
|
ac_cleanall
|
|
); ec=$?
|
|
|
|
ac_clean "$uscrontab"
|
|
ac_clean "$lockfile"
|
|
|
|
exit "$ec"
|
|
fi
|