#!/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