nutools/utrigger

195 lines
5.5 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 base pretty
function display_help() {
uecho "$scriptname: lancer une commande en différé
USAGE
$scriptname [options] -- command [args]
La commande est lancée après un certain temps, sauf si ce script est rappelé
(auquel cas le compte est réinitialisé), ou si l'opération est annulée.
Attention! La commande est lancée en tâche de fond, et son entrée standard est
connectée à un fichier qui peut être provisionné avec l'option -a
note: ce script ne fonctionne que sous Linux puisqu'il utilise la commande flock
OPTIONS
-n, --name NAME
Spécifier un nom identifiant la tâche. Par défaut, le nom est généré à
partir des détails de la tâche à lancer. Ce nom est utilisé pour
identifier les invocations successives.
-f, --cmdfile CMDFILE
Spécifier un fichier contenant les commandes à lancer. Le fichier est
sourcé dans un sous-shell. Utiliser - pour lire les commandes depuis
l'entrée standard.
--rundelay RUNDELAY[=$RUNDELAY]
Nombre de secondes au bout desquelles la commande est lancée. Si ce
script est relancé avant la fin de ce décompte, le compte est remis à
zéro.
Utiliser --rundelay '' pour désactiver cette fonctionnalité, auquel cas
la commande est lancée immédiatement.
-s, --sudo
Forcer l'exécution de la commande avec l'utilisateur root si ce n'est
pas déjà le cas
-a, --datafile DATAFILE
Accumuler des données à fournir à la commande. Les informations du
fichier DATAFILE (utiliser - pour l'entrée standard) sont ajoutées à un
fichier temporaires, et sont fournies en une seule fois à la commande
sur son entrée standard.
-A, --data DATA
Variante de --datafile où les données sont fournies sur la ligne de
commande au lieu d'un fichier externe. Si les deux options -a et -A sont
spécifiées, les données sont accumulées dans l'ordre --datafile puis
--data
-k, --cancel
Annuler le lancement planifié d'une commande. Si la commande est déjà en
train de tourner, cette option n'a aucun effet."
}
RUNDELAY=5
function acquire_lock() {
eval "exec $1>'$2'"
flock $3 "$1"
}
function release_lock() {
rm "$2"
eval "exec $1>&-"
}
name=
cmdfile=
rundelay="$RUNDELAY"
run_as_root=
datafile=
data=
cancel=
parse_opts "${PRETTYOPTS[@]}" \
--help '$exit_with display_help' \
-n:,--name: name= \
-f:,--cmdfile: cmdfile= \
--rundelay: rundelay= \
-s,--sudo run_as_root=1 \
-a:,--datafile: datafile= \
-A:,--data: data= \
-k,--cancel cancel=1 \
@ args -- "$@" && set -- "${args[@]}" || die "$args"
if [ -n "$run_as_root" ]; then
args=(
-n "$name"
-f "$cmdfile"
--rundelay "$rundelay"
-a "$datafile"
-A "$data"
${cancel:+-k}
-- "$@"
)
run_as_root "${args[@]}"
fi
[ -n "$cmdfile" -o $# -gt 0 ] || die "Vous devez spécifier la commande à lancer"
[ -n "$cmdfile" -a $# -gt 0 ] && ewarn "Vous avez spécifié une commande et un fichier de commandes. La commande '$*' sera ignorée"
if [ -n "$name" ]; then
if is_root; then
base="/var/run/$scriptname/name/$name"
else
base="/tmp/$scriptname/name/$name"
fi
else
if is_root; then
base="/var/run/$scriptname/cmd"
else
base="/tmp/$scriptname/cmd"
fi
[ "$cmdfile" == "-" ] && cmdfile=/dev/stdin
if [ -n "$cmdfile" ]; then
# fichier de commande
cmdfile="$(abspath "$cmdfile")"
base="$base$cmdfile"
elif [ "${1#/}" != "$1" ]; then
# commande avec un chemin absolu
base="$base$1"
else
# commande avec un chemin relatif: il faut calculer le chemin absolu
if progexists "$1"; then
base="$base$(which "$1")"
else
base="$base/$1"
fi
fi
fi
mkdirof "$base"
if [ "$cmdfile" == "/dev/stdin" ]; then
# commandes lues depuis stdin
cmdfile="$base"
cat >"$cmdfile"
fi
# données en cours de collecte
podata="$base.podata"
polock="$base.polock"
function acquire_po() { acquire_lock 7 "$polock" "$*"; }
function release_po() { release_lock 7 "$polock" "$*"; }
# données à traiter
codata="$base.codata"
# commande à lancer
uslock="$base.uslock"
function acquire_us() { acquire_lock 8 "$uslock" "$*"; }
function release_us() { release_lock 8 "$uslock" "$*"; }
uspid="$base.uspid"
function __update_pending() {
touch "$podata"
[ "$datafile" == "-" ] && datafile=/dev/stdin
[ -n "$datafile" ] && stdredir "$datafile" ">>$podata" "" cat
[ -n "$data" ] && echo "$data" >>"$podata"
}
function __run_command() { (
exec <"$codata"
if [ -n "$cmdfile" ]; then
source "$cmdfile"
else
"$@"
fi
); }
acquire_po
__update_pending
release_po
if acquire_us -n; then
(
[ -f "$uspid" ] && {
pid=$(<"$uspid")
kill -0 "$pid" >&/dev/null && kill "$pid"
rm "$uspid"
}
if [ -n "$cancel" ]; then
release_us
exit 0
fi
echo $BASHPID >"$uspid"
release_us
[ -n "$rundelay" ] && sleep "$rundelay"
acquire_us
while [ -f "$podata" ]; do
acquire_po
mv "$podata" "$codata"
release_po
__run_command "$@"
rm "$codata"
done
rm "$uspid"
release_us
) &
fi