nutools/uscrontab

756 lines
28 KiB
Plaintext
Raw Normal View History

2013-08-27 15:14:44 +04:00
#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
source "$(dirname -- "$0")/lib/ulib/auto" || exit 1
urequire crontab install
2013-08-27 15:14:44 +04:00
function display_help() {
uecho "$scriptname: lancer une suite de commande en respectant une planification de type cron
USAGE
2014-08-20 00:13:36 +04:00
$scriptname [options] [/path/to/uscrontab] [var=value...]
$scriptname -e [/path/to/uscrontab]
$scriptname -l
2013-08-27 15:14:44 +04:00
2013-09-29 16:58:08 +04:00
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.
2014-08-19 11:20:02 +04:00
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
2014-04-23 12:26:18 +04:00
$USCRONTAB_USERFILE
avec chacun des fichiers du répertoire
2014-04-23 12:26:18 +04:00
$USCRONTAB_USERDIR
2014-08-19 11:20:02 +04:00
dans un fichier temporaire, puis exécuter le fichier résultat avec le nom
virtuel
2014-04-23 12:26:18 +04:00
$USCRONTAB_USER
note: le nom virtuel est utilisé pour le verrouillage avec --lock
2014-08-20 00:13:36 +04:00
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.
2013-08-27 15:14:44 +04:00
2014-08-20 00:13:36 +04:00
Quelles que soient les lignes qui sont sélectionnées pour l'exécution, elles
2013-09-29 16:58:08 +04:00
sont garanties de s'exécuter dans l'ordre du fichier, l'une après l'autre.
2013-08-27 15:14:44 +04:00
2014-08-19 11:20:02 +04:00
Les définitions var=value mentionnées sur la ligne de commande sont des
2014-08-20 00:13:36 +04:00
définitions de variables à effectuer avant de lancer les commandes.
2014-08-19 11:20:02 +04:00
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\"
2014-04-23 19:30:36 +04:00
La syntaxe ?= permet de définir la valeur d'une variable si elle n'est pas
déjà définie:
var?=default
2014-08-19 11:20:02 +04:00
Les lignes de la forme suivante permettent d'exécuter une commande qui est
exécutée systématiquement et ignore la planification:
2013-11-27 17:36:17 +04:00
\$one-line-command
2013-11-15 13:27:18 +04:00
Une variante permet de spécifier des commandes sur plusieurs lignes.
2013-11-27 17:36:17 +04:00
ATTENTION! \${ et $} doivent être tous seuls sur la ligne.
2013-11-15 13:27:18 +04:00
2013-11-27 17:36:17 +04:00
\${
2013-11-15 13:27:18 +04:00
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:
2013-11-27 17:36:17 +04:00
\$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
2014-08-20 00:13:36 +04:00
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:
2013-10-02 14:14:01 +04:00
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]
2013-08-27 15:14:44 +04:00
- 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.
2013-11-14 09:16:49 +04:00
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.
2013-08-27 15:14:44 +04:00
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:
2013-08-27 15:14:44 +04:00
2013-10-02 14:14:01 +04:00
0 1 * * *
check_pidfile /path/to/pid
script1
script2
remove_pidfile /path/to/pid
script3
2013-08-27 15:14:44 +04:00
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.
2013-08-27 15:14:44 +04:00
2014-08-20 00:13:36 +04:00
La fonction elogto() permet de spécifier un fichier vers lequel toutes les
sorties sont redirigées.
2017-09-27 21:58:29 +04:00
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
2019-11-06 15:19:46 +04:00
echo \"first is \$FIRST and second is \$SECOND\"
2017-09-27 21:58:29 +04:00
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.
2013-08-27 15:14:44 +04:00
OPTIONS
2013-09-29 16:58:08 +04:00
-A, --install
Installer une planification toutes les minutes du script dans la crontab
2014-08-20 00:13:36 +04:00
de l'utilisateur. Si l'argument /path/to/uscrontab n'est pas spécifié,
2014-08-19 11:20:02 +04:00
c'est une planification générique qui exécute les fichiers par défaut
qui est installée.
2013-09-29 16:58:08 +04:00
-R, --uninstall
2014-08-20 00:13:36 +04:00
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.
2014-09-05 13:43:30 +04:00
-d, --disable-only
Avec l'option -R, désactiver la planification au lieu de la supprimer.
-e, --edit
2014-08-20 00:13:36 +04:00
Lancer un editeur pour modifier l'uscrontab spécifiée. Si aucun fichier
2014-04-23 12:26:18 +04:00
n'est spécifié, éditer $USCRONTAB_USERFILE
2014-09-05 13:55:15 +04:00
-a, --add
2014-08-20 00:13:36 +04:00
Installer un script uscrontab dans le répertoire approprié. L'argument
doit être de la forme [name:]/path/to/uscrontab
2014-08-20 00:13:36 +04:00
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
2014-08-20 00:13:36 +04:00
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
2013-09-29 16:58:08 +04:00
-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
2014-04-23 12:26:18 +04:00
$USCRONTAB_USERFILE
et chacun des fichiers du répertoire
2014-04-23 12:26:18 +04:00
$USCRONTAB_USERDIR
2017-09-21 02:09:05 +04:00
-N, --nop
Ne pas examiner la planification. Traiter uniquement l'option --osvar le
cas échéant.
2013-09-29 16:58:08 +04:00
-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
2017-09-21 02:09:05 +04:00
-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.
2013-09-29 16:58:08 +04:00
OPTIONS AVANCEES
2013-08-27 15:14:44 +04:00
--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
2014-04-23 11:36:26 +04:00
tourne depuis plus de $USCRONTAB_LOCKDELAY heures, un message d'erreur
est consigné et un message d'avertissement est affiché au plus une fois.
2013-08-27 15:14:44 +04:00
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]
2013-08-27 15:14:44 +04:00
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
2013-08-27 15:14:44 +04:00
-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."
2013-08-27 15:14:44 +04:00
}
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
}
# $USER est requis
[ -n "$LOGNAME" ] || export LOGNAME="$(id -un)"
[ -n "$USER" ] || export USER="$LOGNAME"
USCRONTAB_CTLINE="* * * * * $script"
USCRONTAB_LOCKDELAY=8
USCRONTAB_STOPEC=101
2014-09-11 19:41:57 +04:00
USCRONTAB_BASEDIR=/var/local/uscrontab
USCRONTAB_USERFILE="$USCRONTAB_BASEDIR/users/$USER"
USCRONTAB_USERDIR="$USCRONTAB_BASEDIR/users.d/$USER"
2014-04-23 12:26:18 +04:00
USCRONTAB_USER="$USCRONTAB_BASEDIR/$USER"
2013-09-12 14:06:14 +04:00
2013-08-27 15:14:44 +04:00
action=run
2014-09-05 13:43:30 +04:00
disable_only=
2013-08-27 15:14:44 +04:00
lockfile=auto
lockdelay=
fake=
continuous=
force_ctnow=
pause=
force=
2017-09-21 02:09:05 +04:00
osvars=()
2013-08-27 15:14:44 +04:00
parse_opts "${PRETTYOPTS[@]}" \
--help '$exit_with display_help' \
2013-09-29 16:58:08 +04:00
-A,--add,--install action=install \
-R,--remove,--uninstall action=uninstall \
2014-09-05 13:43:30 +04:00
-d,--disable-only '$action=uninstall; disable_only=1' \
-e,--edit action=edit \
-a,--add action=add \
-r,--remove action=remove \
-l,--list action=list \
2017-09-21 02:09:05 +04:00
-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 \
2017-09-21 02:09:05 +04:00
-v:,--osvar: osvars \
2013-08-27 15:14:44 +04:00
--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**" \
2013-08-27 15:14:44 +04:00
@ args -- "$@" && set -- "${args[@]}" || die "$args"
pausefile="$USCRONTAB_USERFILE.pauseuntil"
now="$(awk 'BEGIN { print int(systime() / 60) * 60 }')"
2017-09-21 02:09:05 +04:00
# 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
2013-09-29 16:58:08 +04:00
r=1
2013-09-29 16:58:08 +04:00
for ctfile in "${ctfiles[@]}"; do
r=0 # il y a au moins une planification
etitle "$(ppath "$ctfile")" \
2013-09-29 16:58:08 +04:00
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
2017-09-21 02:09:05 +04:00
elif [ "$action" == "nop" ]; then
exit 0
2013-09-29 16:58:08 +04:00
fi
[ -z "$uscrontab" -o -f "$uscrontab" ] || die "$uscrontab: fichier introuvable"
[ -n "$uscrontab" ] && uscrontab="$(abspath "$uscrontab")"
2013-08-27 15:14:44 +04:00
if [ "$action" == "install" ]; then
ctline="$USCRONTAB_CTLINE"
[ -n "$uscrontab" ] && ctline="$ctline $(qvalm "$uscrontab")"
enable_in_crontab "$ctline" && estep "add_to_crontab $ctline"
2013-08-27 15:14:44 +04:00
elif [ "$action" == "uninstall" ]; then
ctline="$USCRONTAB_CTLINE"
[ -n "$uscrontab" ] && ctline="$ctline $(qvalm "$uscrontab")"
2014-09-05 13:43:30 +04:00
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
2013-08-27 15:14:44 +04:00
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
2013-08-27 15:14:44 +04:00
if [ "$lockfile" == auto ]; then
if is_root; then
lockfile="$default_lockfile"
2013-08-27 15:14:44 +04:00
mkdirof "$lockfile" || die
else
lockfile=
fi
fi
[ -n "$lockdelay" ] || lockdelay="$USCRONTAB_LOCKDELAY"
2013-08-27 15:14:44 +04:00
if [ -n "$lockfile" ]; then
lastmsgfile="${lockfile%.lock}.lastmsg"
2013-08-27 15:14:44 +04:00
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=
2013-08-27 15:14:44 +04:00
logger -p cron.warn -t "$scriptname" -- "$msg"
if [ "$msg" != "$lastmsg" ]; then
2013-08-27 15:14:44 +04:00
ewarn "$msg"
echo "$msg" >"$lastmsgfile"
else
edebug "$msg"
2013-08-27 15:14:44 +04:00
fi
exit 1
;;
retry) :;;
*) retry=;;
esac
done
rm -f "$lastmsgfile" 2>/dev/null
2013-08-27 15:14:44 +04:00
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
2017-09-21 02:09:05 +04:00
check_pidfile|remove_pidfile|osvar|elogto) return 1;;
esac
return 0
}
2013-08-27 15:14:44 +04:00
function __ctexec() {
local ec=0
local cmd="${1%% *}"
if [ "$cmd" == nostop ]; then
cmd="${1#nostop }"
shift
set -- "$cmd" "$@"
local continuous=1
fi
2013-08-27 15:14:44 +04:00
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
2013-08-27 15:14:44 +04:00
fi
[ -n "$USCRONTAB_STOPEC" -a "$ec" == "$USCRONTAB_STOPEC" ] && exit 0
2013-08-27 15:14:44 +04:00
[ -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")"
2013-08-27 15:14:44 +04:00
ec=0
edebug "$__ctscript"
(
2014-04-23 18:51:10 +04:00
# 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
2014-04-23 18:51:10 +04:00
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() {
2014-04-23 18:51:10 +04:00
if [ -n "$1" ]; then
local pidfile="$(abspath "$1")"
ac_clean "$pidfile"
array_del __USCRONTAB_PIDFILES "$pidfile"
fi
}
2017-09-21 02:09:05 +04:00
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"
2013-08-27 15:14:44 +04:00
exit "$ec"
fi