201 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			201 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
 | 
						|
 | 
						|
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."
 | 
						|
}
 | 
						|
 | 
						|
source "$(dirname "$0")/ulib/ulib" &&
 | 
						|
urequire base pretty ||
 | 
						|
exit 1
 | 
						|
 | 
						|
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
 | 
						|
    if [ -n "$datafile" ]; then
 | 
						|
        cat "$datafile" >>"$podata"
 | 
						|
    fi
 | 
						|
    if [ -n "$data" ]; then
 | 
						|
        echo "$data" >>"$podata"
 | 
						|
    fi
 | 
						|
}
 | 
						|
 | 
						|
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
 |