support check_pidfile() et remove_pidfile dans uscrontab

support de l'exécution de commandes arbitraires
This commit is contained in:
Jephté Clain 2013-10-02 13:37:10 +04:00
parent 0afd5d3660
commit b0f00b2f34
4 changed files with 173 additions and 41 deletions

View File

@ -1 +1 @@
2
3

View File

@ -3743,13 +3743,14 @@ function simple_menu() {
# autoclean: gérer une liste de fichiers temporaires à supprimer en fin de
# programme
__ac_files=()
function __ac_forgetall() { __ac_files=(); }
__ac_forgetall
function __ac_trap() {
local file
for file in "${__ac_files[@]}"; do
[ -e "$file" ] && rm -rf "$file" 2>/dev/null
done
__ac_files=()
__ac_forgetall
}
trap __ac_trap 1 3 15 EXIT
function autoclean() {

View File

@ -149,12 +149,12 @@ BEGIN {
matches = 0
}
/^[a-zA-Z_][a-zA-Z0-9_]*[-+%#]=/ {
/^(export[ \t]+)?[a-zA-Z_][a-zA-Z0-9_]*[-+%#]=/ {
# manipulation de variables PATH
match($0, /^([a-zA-Z_][a-zA-Z0-9_]*)([-+%#])=(.*)$/, parts)
name = parts[1]
type = parts[2]
value = parts[3]
match($0, /^(export[ \t]+)?([a-zA-Z_][a-zA-Z0-9_]*)([-+%#])=(.*)$/, parts)
name = parts[2]
type = parts[3]
value = parts[4]
if (type == "+" || type == "%") {
print "uaddpath " value " " name
} else if (type == "#") {
@ -162,14 +162,22 @@ BEGIN {
} else if (type == "-") {
print "udelpath " value " " name
}
print "export " name
next
}
/^[a-zA-Z_][a-zA-Z0-9_]*=/ {
/^(export[ \t]+)?[a-zA-Z_][a-zA-Z0-9_]*=/ {
# spécification de variable
sub(/^export[ \t]+/, "", $0)
print "export " $0
next
}
/^[ ]*[-0-9/*,]+[ ]*[-0-9/*,]+[ ]*[-0-9/*,]+[ ]*[-0-9/*,]+[ ]*[-0-9/*,]+/ {
/^\$.+/ {
# exécution de commande arbitraire
sub(/^\$/, "", $0)
print
next
}
/^[ \t]*[-0-9/*,]+[ ]*[-0-9/*,]+[ ]*[-0-9/*,]+[ ]*[-0-9/*,]+[ ]*[-0-9/*,]+/ {
# ligne de planification
M = $1; H = $2; dom = $3; mon = $4; dow = $5
sub(/^[ ]*[^ ]+[ ]+[^ ]+[ ]+[^ ]+[ ]+[^ ]+[ ]+[^ ]+[ ]*/, "", $0)

179
uscrontab
View File

@ -13,33 +13,118 @@ 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]
dans le fichier crontab spécifié. 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 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:
Les lignes commençant par # sont des commentaires et sont ignorées
0 * * * *
5 1 * * * command
== Définitions de variables et exécution de commandes ==
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:
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\"
Les lignes de la forme suivante permettent d'exécuter une commande
quelconque:
$command-line
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
== 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, la ligne suivante permet d'exécuter 'command'
toutes les heures ET à 1h05:
0 * * * *
5 1 * * * command
== 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]
0 1 * * * 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 pour empêcher par exemple la
mise à jour du code de synchronisation. 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
0 1 * * * script1
0 1 * * * script2
0 1 * * * remove_pidfile /path/to/pid
0 1 * * * 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.
OPTIONS
-A, --install
@ -61,14 +146,14 @@ 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é
tourne depuis plus de $USCRONTAB_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]
--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
@ -88,15 +173,15 @@ source "$(dirname "$0")/ulib/ulib" &&
urequire DEFAULTS crontab ||
exit 1
MY_CTLINE="* * * * * $script"
LOCKDELAY=8
USCRONTAB_CTLINE="* * * * * $script"
USCRONTAB_LOCKDELAY=8
USCRONTAB_STOPEC=101
action=run
lockfile=auto
lockdelay=
fake=
continuous=
stopec=101
parse_opts "${PRETTYOPTS[@]}" \
--help '$exit_with display_help' \
-A,--add,--install action=install \
@ -105,7 +190,7 @@ parse_opts "${PRETTYOPTS[@]}" \
--lockdelay: lockdelay= \
-n,--fake fake=1 \
-c,--continuous continuous=1 \
-k:,--stop: stopec=1 \
-k:,--stop: USCRONTAB_STOPEC= \
-l,--list action=list \
@ args -- "$@" && set -- "${args[@]}" || die "$args"
@ -135,10 +220,10 @@ crontab="$1"; shift
crontab="$(abspath "$crontab")"
if [ "$action" == "install" ]; then
enable_in_crontab "$MY_CTLINE $(quoted_arg "$crontab")" && estep "add_to_crontab $MY_CTLINE $(quoted_arg "$crontab")"
enable_in_crontab "$USCRONTAB_CTLINE $(quoted_arg "$crontab")" && estep "add_to_crontab $USCRONTAB_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")"
remove_from_crontab "$USCRONTAB_CTLINE $(quoted_arg "$crontab")" && estep "remove_from_crontab $USCRONTAB_CTLINE $(quoted_arg "$crontab")"
elif [ "$action" == "run" ]; then
if [ "$lockfile" == auto ]; then
@ -149,7 +234,7 @@ elif [ "$action" == "run" ]; then
lockfile=
fi
fi
[ -n "$lockdelay" ] || lockdelay="$LOCKDELAY"
[ -n "$lockdelay" ] || lockdelay="$USCRONTAB_LOCKDELAY"
if [ -n "$lockfile" ]; then
lockwarn="${lockfile%.lock}.lockwarn"
@ -185,9 +270,15 @@ elif [ "$action" == "run" ]; then
echo "$*"
else
edebug "$*"
if [ "${1#check_pidfile }" != "$1" ]; then
# cas particulier, c'est une fonction à exécuter dans le
# contexte courant, et non pas dans un sous-shell
eval "$*"; ec=$?
else
(eval "$*"); ec=$?
fi
[ -n "$stopec" -a "$ec" == "$stopec" ] && exit 0
fi
[ -n "$USCRONTAB_STOPEC" -a "$ec" == "$USCRONTAB_STOPEC" ] && exit 0
[ -z "$continuous" -a "$ec" != 0 ] && exit "$ec"
return 0
}
@ -197,7 +288,39 @@ elif [ "$action" == "run" ]; then
ctscript="$(ctresolve <"$crontab")"
ec=0
edebug "$ctscript"
(eval "$ctscript"); ec=$?
(
function check_pidfile() {
if [ -n "$1" ]; then
local status
pidfile_set -r "$1"; 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 $(<"$1")
puis supprimez le cas échéant le fichier $1"
return "$USCRONTAB_STOPEC"
;;
10)
die "Une erreur s'est produite pendant l'écriture du fichier de pid. Impossible de continuer"
;;
esac
fi
if [ -n "$3" -a -w "$(dirname "$3")" ]; then
(set -o noclobber
echo_ $$ >"$3" &&
chmod 755 "$3"
) 2>/dev/null &&
autoclean "$3"
fi
return 0
}
function remove_pidfile() {
[ -n "$1" ] && ac_clean "$1"
}
__ac_forgetall
eval "$ctscript"
ac_cleanall
); ec=$?
[ -f "$lockfile" ] && rm "$lockfile"
exit "$ec"
fi