195 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			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
 |