##@cooked comments # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 ## Fonctions pour la commande runs ##@cooked nocomments ##@require base uprovide runs urequire base ################################################################################ # Configuration function __runs_initt() { if is_yes "$verbose"; then rscriptt="# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 desc \"CHANGEME: Description du script $rscriptname\" #var name=value arr+=value arr-=value #var arr value0 value1... script: # pour un script d'installation qui ne doit tourner qu'une seule fois: #shouldrun || exit #... #setdone" runsconft="# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 # Ce fichier contient les paramètres qui sont partagés par tous les scripts de $host #var name=value arr+=value arr-=value #var arr value0 value1..." else rscriptt="# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 desc \"\" script:" runsconft="# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 # Ce fichier contient les paramètres qui sont partagés par tous les scripts de $host" fi sysinfost="# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 # Ce fichier contient les informations sur le type de système installé sur $host # Il est possible de recopier la valeur affichée par la commande usysinfos" if [ -n "$sysinfos_data" ]; then sysinfost="$sysinfost $sysinfos_data" else sysinfost="$sysinfost #sysname=() #sysdist=() #sysver=() #bits=" fi defaultt="# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 # Ce fichier contient la liste des script à lancer avec le compte root pour # configurer $host # Chaque ligne contient le nom du script suivi des arguments éventuels #initenv #dump-users #base #@services #@config" configt="# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 # Ce fichier contient la recette pour configurer le serveur: configuration des # mappings, configuration du serveur de courrier, de la sauvegarde, etc... Il # faut relancer cette recette à chaque fois que la configuration change. #mailrelay rootmail= #hostmappings mappings #backupclient bckhost=" servicest="# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 # Ce fichier contient la recette pour installer les services du serveur. Cette # installation ne se fait en principe qu'une seule fois." userdefaultt="# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 # Ce fichier contient la liste des script à lancer avec le compte utilisateur # pour configurer $host # Chaque ligne contient le nom du script suivi des arguments éventuels" } function runs_initdir() { # Initialiser le répertoire d'hôte. $1 est un nom d'hôte pleinement # qualifié, et les fichiers sont créés dans le premier répertoire de # RUNSHOSTSDIRS qui convient: si un fichier .udir existe avec un tableau # runs_domains qui contient le domaine de l'hôte spécifié, alors c'est ce # répertoire qui est sélectionné. Sinon, on prend le premier répertoire de # RUNSHOSTSDIRS. # $2 spécifie si le fichier doit être créé avec de l'aide (yes) ou avec le # script minimum (no) # $3 est le contenu à placer dans le fichier sysinfos.conf, s'il n'a pas # déjà été provisionné. # Il faut lancer __runs_setpath avant d'utiliser cette fonction et # RUNSHOSTDIRS ne doit pas être vide [ -n "${RUNSHOSTSDIRS[*]}" ] || return 1 local runshostdir local edit_default local host hostname domain splithost "$1" hostname domain local verbose="${2:-yes}" local sysinfos_data="$3" if [ -n "$domain" ]; then host="$hostname.$domain" local dir found # d'abord chercher si le répertoire existe déjà for runshostdir in "${RUNSHOSTSDIRS[@]}"; do found= for dir in "$runshostdir/$domain/$hostname" "$runshostdir/$host"; do if [ -d "$dir" ]; then found=1 break fi done done # sinon chercher un répertoire qui convient if [ -z "$found" ]; then for runshostdir in "${RUNSHOSTSDIRS[@]}"; do if [ -f "$runshostdir/.udir" ]; then (source "$runshostdir/.udir"; array_contains runs_domains "$domain") && { found=1 break } fi done fi if [ -n "$found" ]; then found= for dir in "$runshostdir/$host" "$runshostdir/$domain/$hostname"; do if [ -d "$dir" ]; then runshostdir="$dir" found=1 break fi done [ -n "$found" ] || runshostdir="$runshostdir/$host" else runshostdir="${RUNSHOSTSDIRS[0]}/$host" fi else host="$hostname" runshostdir="${RUNSHOSTSDIRS[0]}/$host" fi local rscriptt runsconft sysinfost defaultt configt servicest __runs_initt mkdir -p "$runshostdir" if [ ! -f "$runshostdir/runs.conf" ]; then estep "Création de $(ppath "$runshostdir/runs.conf")" echo "$runsconft" >"$runshostdir/runs.conf" fi local create_sysinfos if [ -f "$runshostdir/sysinfos.conf" ]; then # si le fichier sysinfos.conf ne contient que des commentaires, # considérer qu'il n'existe pas. local -a sysinfoslines array_from_lines sysinfoslines "$(<"$runshostdir/sysinfos.conf" filter_conf)" [ "${#sysinfoslines[*]}" -eq 0 ] && create_sysinfos=1 else create_sysinfos=1 fi if [ -n "$create_sysinfos" ]; then estep "Création de $(ppath "$runshostdir/sysinfos.conf")" echo "$sysinfost" >"$runshostdir/sysinfos.conf" fi if [ ! -f "$runshostdir/default" ]; then estep "Création de $(ppath "$runshostdir/default")" echo "$defaultt" >"$runshostdir/default" edit_default=1 fi if [ ! -f "$runshostdir/config" ]; then estep "Création de $(ppath "$runshostdir/config")" echo "$configt" >"$runshostdir/config" fi if [ ! -f "$runshostdir/services" ]; then estep "Création de $(ppath "$runshostdir/services")" echo "$servicest" >"$runshostdir/services" fi if [ ! -f "$runshostdir/userdefault" ]; then estep "Création de $(ppath "$runshostdir/userdefault")" echo "$userdefaultt" >"$runshostdir/userdefault" fi [ -n "$edit_default" ] && "${EDITOR:-vi}" "$runshostdir/default" return 0 } function __runs_create_rscript() { local rscript="$1" local rscriptname="$(basename "$rscript")" local overwrite="$2" local template if [ "$rscriptname" == "runs.conf" ]; then template="$runsconft" elif [ "$rscriptname" == "sysinfos.conf" ]; then template="$sysinfost" elif [ "$rscriptname" == "default" ]; then template="$defaultt" elif [ "$rscriptname" == "userdefault" ]; then template="$userdefaultt" else withext "$rscript" || rscript="$rscript.rs" template="$rscriptt" fi if [ -f "$rscript" ] && is_no "$overwrite"; then eerror "$(ppath "$rscript"): fichier déjà existant" return 1 fi mkdirof "$rscript" estep "Création de $(ppath "$rscript")" echo "$template" >"$rscript" array_add new_rscripts "$(abspath "$rscript")" return 0 } function runs_create_rscript() { # Créer un modèle de script. Si $2 est spécifié, c'est un nom d'hôte # pleinement qualifié. Le répertoire d'hôte correspondant *doit* exister. # $3 spécifie si le fichier doit être créé avec de l'aide (yes) ou avec le # script minimum (no) # Si $2!="", il faut lancer __runs_setpath avant d'utiliser cette fonction # et RUNSHOSTDIRS ne doit pas être vide # Le chemin du nouveau script est ajouté au tableau new_rscripts local rscript="$1" local host="$2" local verbose="${3:-yes}" local overwrite="${4:-no}" local rscriptt runsconft sysinfost defaultt userdefaultt __runs_initt if [ -n "$host" ]; then if withpath "$rscript"; then # si c'est un chemin relatif à '.', '..', ou un chemin absolu, le # prendre tel quel __runs_create_rscript "$rscript" "$overwrite" || return 1 else # trouver le répertoire d'hôte [ -n "${RUNSHOSTSDIRS[*]}" ] || return 1 local found runsdir hostname domain splithost "$host" hostname domain for runsdir in "${RUNSHOSTSDIRS[@]}"; do found= for dir in "$runsdir/$host" "$runsdir/$domain/$hostname"; do if [ -d "$dir" ]; then runsdir="$dir" found=1 break fi done [ -n "$found" ] && break done [ -n "$found" ] || return 1 # puis exprimer le chemin spécifié par rapport à runsdir __runs_create_rscript "$runsdir/$rscript" "$overwrite" || return 1 fi else __runs_create_rscript "$rscript" "$overwrite" || return 1 fi } function runs_unsupported_system() { # Afficher un message d'erreur indiquant que le système actuel n'est pas # supporté, et quitter le script local msg="Ce script n'est pas supporté sur $MYSYSNAME${MYSYSDIST:+/$MYSYSDIST}${MYSYSVER:+/$MYSYSVER}" [ -n "$*" ] && msg="$msg Il faut au moins l'un des systèmes suivants: $*" die "$msg" } function runs_require_sysinfos() { # Vérifier le système actuel avec check_sysinfos(), et afficher un message # d'erreur avec runs_unsupported_system() s'il ne correspond pas à la # requête check_sysinfos "$@" && return 0 local infos info # Construire une description du type de système attendu en fonction des # arguments. Actuellement, on se contente de copier la ligne de commande, en # sautant les options. # XXX améliorer l'algorithme pour décrire précisément le système attendu for info in "$@"; do if ! [[ "$info" == -* ]]; then infos=("${infos[@]}" "$info") fi done runs_unsupported_system "${infos[@]}" } ################################################################################ # Lancement de scripts nommés function __runs_setpath() { # Initialiser les tableaux $1(=RUNSSCRIPTSDIRS), $2(=RUNSMODULESDIRS), # $3(=RUNSHOSTSDIRS) avec les chemins de RUNSSCRIPTSPATH, RUNSMODULESPATH, # RUNSHOSTSPATH function __runs_addsuffix() { echo "$1${2:+/$2}"; } local -a __runsdirs __tmpa local __runsscriptsdirs="${1:-RUNSSCRIPTSDIRS}" local __runsmodulesdirs="${2:-RUNSMODULESDIRS}" local __runshostsdirs="${3:-RUNSHOSTSDIRS}" array_from_path "$__runsscriptsdirs" "$RUNSSCRIPTSPATH" array_map "$__runsscriptsdirs" abspath array_from_path "$__runsmodulesdirs" "$RUNSMODULESPATH" array_map "$__runsmodulesdirs" abspath array_from_path "$__runshostsdirs" "$RUNSHOSTSPATH" array_map "$__runshostsdirs" abspath } function __runs_find_host() { # Si $1 est un nom d'hôte pleinement qualifié, retourner cette valeur # Sinon, chercher en utilisant RUNSDOMAINS un nom d'hôte qui aurait déjà été # configuré local host="$1" hostname domain splithost "$host" hostname domain if [ -n "$domain" ]; then echo "$host" return 0 fi local runsdomain runsdir for runsdomain in "${RUNSDOMAINS[@]}"; do for runsdir in "${RUNSHOSTSDIRS[@]}"; do if [ -d "$runsdir/$hostname.$runsdomain" ]; then echo "$hostname.$runsdomain" return 0 elif [ -d "$runsdir/$runsdomain/$hostname" ]; then echo "$hostname.$runsdomain" return 0 fi done done echo "$host" return 1 } function runs_find_host() { local RUNSSCRIPTSDIRS RUNSMODULESDIRS RUNSHOSTSDIRS; __runs_setpath __runs_find_host "$@" } function runs_add_domain() { # Si $1 est nom d'hôte pleinement qualifié, retourner cette valeur # Sinon, lui rajouter le domaine RUNSDOMAIN local host="$1" hostname domain splithost "$host" hostname domain if [ -n "$domain" -o -z "$RUNSDOMAIN" ]; then echo "$host" return 0 fi echo "$host.$RUNSDOMAIN" return 1 } function runs_find_hostfile() { # Trouver et afficher le fichier d'hôte $1 dans les répertoires du tableau # $3(=RUNSHOSTSDIRS), pour l'hôte $2(=$RUNSHOST). Retourner 0 en cas de # succès, 1 en cas d'échec. # Si host=$2 est une valeur non vide, la recherche est effectuée dans # {$RUNSHOSTSDIRS}/$host et {$RUNSHOSTSDIRS}/$domain/$hostname. Sinon, # retourner 1, car il faut spécifier un nom d'hôte. local __hostfile="$1" __runshost="${2:-$RUNSHOST}" __runshostsdirs="${3:-RUNSHOSTSDIRS}[@]" local __hostname __domain local __runsdir __runsfile [ -n "$__runshost" ] || return 1 splithost "$__runshost" __hostname __domain for __runsdir in "${!__runshostsdirs}"; do for __runsfile in \ "$__runsdir/$__runshost/$__hostfile" \ "$__runsdir/$__domain/$__hostname/$__hostfile"; do if [ -e "$__runsfile" ]; then echo "$__runsfile" return 0 fi done done return 1 } function runs_find_datafile() { # Trouver et afficher le fichier de données $1 dans le répertoire $3 s'il # est non vide puis dans les répertoires des tableaux $4(=RUNSSCRIPTSDIRS), # $5(=RUNSMODULESDIRS) et $6(=RUNSHOSTSDIRS), pour l'hôte # $2(=$RUNSHOST). Retourner 0 en cas de succès, 1 en cas d'échec. # - D'abord, si $1 *n'est pas* de la forme "./path" ou "../path", chercher # dans $3. # - Puis si l'hôte est spécifié, chercher dans {$RUNSHOSTSDIRS}/$host et # {$RUNSHOSTSDIRS}/$domain/$hostname. # - Puis chercher dans {$RUNSSCRIPTSDIRS} puis {$RUNSMODULESDIRS}. # - Puis, si $1 est de la forme "./path" ou "../path", chercher dans $3. # - Sinon, retourner 1 local __datafile="$1" __runshost="${2:-$RUNSHOST}" local __runsscriptdir="$3" local __runsscriptsdirs="${4:-RUNSSCRIPTSDIRS}[@]" local __runsmodulesdirs="${5:-RUNSMODULESDIRS}[@]" local __runshostsdirs="${6:-RUNSHOSTSDIRS}[@]" local __domain __hostname local __runsdir __runsfile local __withpath withpath "$__datafile" && __withpath=1 # D'abord chercher dans le répertoire du script if [ -n "$__withpath" -a -n "$__runsscriptdir" ]; then __runsfile="$__runsscriptdir/$__datafile" if [ -e "$__runsfile" ]; then echo "$__runsfile" return 0 fi fi # puis chercher dans les répertoire d'hôtes if [ -n "$__runshost" ]; then splithost "$__runshost" __hostname __domain for __runsdir in "${!__runshostsdirs}"; do for __runsfile in \ "$__runsdir/$__runshost/$__datafile" \ "$__runsdir/$__domain/$__hostname/$__datafile"; do if [ -e "$__runsfile" ]; then echo "$__runsfile" return 0 fi done done fi # puis chercher dans les répertoires des scripts et des modules for __runsdir in "${!__runsscriptsdirs}" "${!__runsmodulesdirs}"; do __runsfile="$__runsdir/$__datafile" if [ -e "$__runsfile" ]; then echo "$__runsfile" return 0 fi done # puis chercher dans le répertoire du script if [ -z "$__withpath" -a -n "$__runsscriptdir" ]; then __runsfile="$__runsscriptdir/$__datafile" if [ -e "$__runsfile" ]; then echo "$__runsfile" return 0 fi fi # fichier non trouvé return 1 } function runs_initvars() { # Initialiser les variables RUNSDIR, RUNSSCRIPT, RUNSDIRPATH, # RUNSSCRIPTPATH, RUNSSCRIPTDIR et RUNSSCRIPTNAME pour le script $1. # Les valeurs sont initialisées comme suit: # RUNSSCRIPT="$(abspath "$1")" # RUNSDIR="$2" (le répertoire de $RUNS*PATH dans lequel a été trouvé le # script) # Si $3!="", RUNSDIRPATH="$3" et RUNSSCRIPTPATH="$4" # Sinon, RUNSDIRPATH="$RUNSSCRIPTDIR" et RUNSSCRIPTPATH="$RUNSSCRIPTNAME" RUNSSCRIPT="$(abspath "$1")" RUNSSCRIPTDIR="$(dirname "$RUNSSCRIPT")" RUNSSCRIPTNAME="$(basename "$RUNSSCRIPT")" RUNSDIR="$2" if [ -n "$3" ]; then RUNSDIRPATH="$3" RUNSSCRIPTPATH="$4" else RUNSDIRPATH="$RUNSSCRIPTDIR" RUNSSCRIPTPATH="$RUNSSCRIPTNAME" fi } function runs_find_scriptfile() { # Trouver sans l'afficher le script $1 dans les répertoires des tableaux # $3(=RUNSSCRIPTSDIRS), $4(=RUNSMODULESDIRS) et $5(=RUNSHOSTSDIRS), en # considérant que le script sera lancé sur l'hôte $2(=$RUNSHOST), et # initialiser les variables RUNSDIR, RUNSSCRIPT, RUNSSCRIPTDIR, # RUNSSCRIPTNAME, RUNSDIRPATH et RUNSSCRIPTPATH. Retourner 0 en cas de # succès, 1 en cas d'échec. # RUNSDIR est le répertoire dans lequel a été trouvé le script (parmi les # valeurs fournies dans les tableaux RUNSSCRIPTSDIRS, RUNSMODULESDIRS, # RUNSHOSTSDIRS), RUNSDIRPATH est le répertoire à partir duquel est exprimé # le chemin du script (i.e RUNSDIRPATH + RUNSSCRIPTPATH == RUNSSCRIPT), # RUNSSCRIPT contient le chemin absolu vers le script, RUNSSCRIPTPATH # contient le chemin du script dans RUNSDIRPATH, RUNSSCRIPTDIR le répertoire # du script, et RUNSSCRIPTNAME le nom du script. # D'abord, si l'hôte est spécifié, chercher dans {$RUNSHOSTSDIRS}/$host et # {$RUNSHOSTSDIRS}/$domain/$hostname. Puis chercher dans {$RUNSSCRIPTSDIRS} # puis {$RUNSMODULESDIRS}. Sinon, retourner 1 local __scriptfile="$1" __runshost="${2:-$RUNSHOST}" local __runsscriptsdirs="${3:-RUNSSCRIPTSDIRS}[@]" local __runsmodulesdirs="${4:-RUNSMODULESDIRS}[@]" local __runshostsdirs="${5:-RUNSHOSTSDIRS}[@]" local __domain __hostname local __runsdir __runsfile # chercher le cas échéant dans les répertoire d'hôtes if [ -n "$__runshost" ]; then splithost "$__runshost" __hostname __domain for __runsdir in "${!__runshostsdirs}"; do for __runsfile in \ "$__runsdir/$__runshost/$__scriptfile" \ "$__runsdir/$__domain/$__hostname/$__scriptfile"; do if [ ! -f "$__runsfile" -a -f "$__runsfile.rs" ]; then __runsfile="$__runsfile.rs" fi if [ -e "$__runsfile" ]; then runs_initvars "$__runsfile" "$__runsdir" "$__runsdirpath" "$__scriptfile" return 0 fi done done fi # puis chercher dans les répertoires des scripts et des modules for __runsdir in "${!__runsscriptsdirs}" "${!__runsmodulesdirs}"; do __runsfile="$__runsdir/$__scriptfile" if [ ! -f "$__runsfile" -a -f "$__runsfile.rs" ]; then __runsfile="$__runsfile.rs" fi if [ -e "$__runsfile" ]; then runs_initvars "$__runsfile" "$__runsdir" "$__runsdir" "$__scriptfile" return 0 fi done return 1 } function runs_find_scriptfile_reverse() { # Soit le fichier de script $1, exprimée de façon absolue, trouver le # fichier parmi les tableaux $3(=RUNSSCRIPTSDIRS), $4(=RUNSMODULESDIRS) # et $5(=RUNSHOSTSDIRS), en considérant que le script sera lancé sur l'hôte # $2(=$RUNSHOST), puis initialiser les variables RUNSDIR, RUNSSCRIPT, # RUNSSCRIPTDIR, RUNSSCRIPTNAME, RUNSDIRPATH et RUNSSCRIPTPATH. Retourner 0 # en cas de succès, 1 en cas d'échec. local __runsscript="$1" __runshost="${2:-$RUNSHOST}" local __runsscriptsdirs="${3:-RUNSSCRIPTSDIRS}[@]" local __runsmodulesdirs="${4:-RUNSMODULESDIRS}[@]" local __runshostsdirs="${5:-RUNSHOSTSDIRS}[@]" local __domain __hostname local __runsdir __runsfile __prefix # chercher le cas échéant dans les répertoire d'hôtes if [ -n "$__runshost" ]; then splithost "$__runshost" __hostname __domain for __runsdir in "${!__runshostsdirs}"; do __prefix="$__runsdir/$__runshost/" if [ "${__runsscript#$__prefix}" != "$__runsscript" ]; then runs_initvars "$__runsscript" "$__runsdir" "$__prefix" "${__runsscript#$__prefix/}" return 0 fi __prefix="$__runsdir/$__domain/$__hostname/" if [ "${__runsscript#$__prefix}" != "$__runsscript" ]; then runs_initvars "$__runsscript" "$__runsdir" "$__prefix" "${__runsscript#$__prefix/}" return 0 fi done fi # puis chercher dans les répertoires des scripts et des modules for __runsdir in "${!__runsscriptsdirs}"; do __prefix="$__runsdir/" if [ "${__runsscript#$__prefix}" != "$__runsscript" ]; then runs_initvars "$__runsscript" "$__runsdir" "$__prefix" "${__runsscript#$__prefix/}" return 0 fi done for __runsdir in "${!__runsmodulesdirs}"; do __prefix="$__runsdir/" if [ "${__runsscript#$__prefix}" != "$__runsscript" ]; then runs_initvars "$__runsscript" "$__runsdir" "$__prefix" "${__runsscript#$__prefix/}" return 0 fi done return 1 } function runs_rscript() { # Lancer le fichier $1 comme un script avec les arguments $2..$*. Retourner # la valeur de retour du script. local state local RUNSDIR RUNSSCRIPT RUNSSCRIPTDIR RUNSSCRIPTNAME RUNSDIRPATH RUNSSCRIPTPATH local RUNSSCRIPTSDIRS RUNSMODULESDIRS RUNSHOSTSDIRS; __runs_setpath local rscript="$1"; shift if [ ! -f "$rscript" ]; then eerror "$rscript: fichier introuvable." return 123 fi # trouver runs*dir correspondant à $rscript rscript="$(abspath "$rscript")" runs_find_scriptfile_reverse "$rscript" "$RUNSHOST" || runs_initvars "$rscript" # puis lancer le script RUNSSTORY=("${RUNSSTORY[@]}" "$RUNSSCRIPTPATH") state=0 "$RUNSACTION" "$@" || { state=$? eerror "$RUNSSCRIPTPATH: Une erreur s'est produite." } return $state } function __runs_iscont() { # tester si $1 est une ligne de continuation dans une recette [ "${1# }" != "$1" ] && return 0 [ "${1#$TAB}" != "$1" ] && return 0 return 1 } function runs_recipe() { # Lancer les scripts de la recette contenue dans le fichier $1. Arrêter au # premier script qui est en erreur local recipe rcmds rcmd i count rscripts rscript recipe="$(abspath "$1")" if [ -d "$recipe" ]; then if is_root; then recipe="$recipe/default" else recipe="$recipe/userdefault" fi fi if [ ! -f "$recipe" ]; then eerror "$(ppath "$recipe"): fichier introuvable" return 1 fi if array_contains RUNSRECIPES "$recipe"; then eerror "$(ppath "$recipe"): inclusion récursive" return 1 fi RUNSRECIPES=("${RUNSRECIPES[@]}" "$recipe") # lire les ingredients de la recette recipe="$(<"$recipe" filter_comment -m)" array_from_lines rcmds "$recipe" # fusionner les lignes d'ingrédients let i=0 let count=${#rcmds[@]} rscript= rscripts=() while [ $i -lt $count ]; do rcmd="${rcmds[$i]}" if ! __runs_iscont "$rcmd"; then # il faut recommencer une nouvelle commande # mais d'abord, traiter l'ancienne commande si nécessaire [ -n "$rscript" ] && rscripts=("${rscripts[@]}" "$rscript") rscript="$rcmd" elif [ -z "$rscript" ]; then # nous avons une ligne de continuation mal placée ewarn "$rcmd: Ligne de continuation mal placée, elle sera ignorée" else # nous avons une ligne de continuation rscript="$rscript \; $rcmd" fi let i=$i+1 [ $i -eq $count -a -n "$rscript" ] && rscripts=("${rscripts[@]}" "$rscript") done # lancer les ingrédients et s'arrêter à la premier erreur for rscript in "${rscripts[@]}"; do eval "set -- $rscript" if [ "${1#@}" != "$1" ]; then # Le caractère @ identifie une recette au lieu d'un script individuel local RUNSDIR RUNSSCRIPT RUNSSCRIPTDIR RUNSSCRIPTNAME RUNSDIRPATH RUNSSCRIPTPATH local RUNSSCRIPTSDIRS RUNSMODULESDIRS RUNSHOSTSDIRS; __runs_setpath recipe="${1#@}" if ! runs_find_scriptfile "$recipe"; then eerror "$recipe: fichier introuvable. Vérifiez les valeurs suivantes: RUNSSCRIPTSPATH=$RUNSSCRIPTSPATH RUNSMODULESPATH=$RUNSMODULESPATH RUNSHOSTSPATH=$RUNSHOSTSPATH RUNSHOST=$RUNSHOST" return 123 fi etitle "$1" runs_recipe "$RUNSSCRIPT" || return else etitle "$1" runs_rscriptpath "$@" || return fi done return 0 } function runs_rscriptpath() { # Lancer le script $1 avec les arguments $2..$*. Le script est cherché dans # les répertoires de RUNSSCRIPTSPATH. Retourner 123 si le script n'est pas # trouvé, sinon retourner la valeur de retour du script. local state local RUNSDIR RUNSSCRIPT RUNSSCRIPTDIR RUNSSCRIPTNAME RUNSDIRPATH RUNSSCRIPTPATH local RUNSSCRIPTSDIRS RUNSMODULESDIRS RUNSHOSTSDIRS; __runs_setpath if ! runs_find_scriptfile "$1"; then eerror "$1: fichier introuvable. Vérifiez les valeurs suivantes: RUNSSCRIPTSPATH=$RUNSSCRIPTSPATH RUNSMODULESPATH=$RUNSMODULESPATH RUNSHOSTSPATH=$RUNSHOSTSPATH RUNSHOST=$RUNSHOST" return 123 fi shift RUNSSTORY=("${RUNSSTORY[@]}" "$RUNSSCRIPTPATH") state=0 "$RUNSACTION" "$@" || { state=$? eerror "$RUNSSCRIPTPATH: Une erreur s'est produite." } return $state } function runs_recipepath() { # Lancer la recette $1. Le fichier de recette est cherché dans les # répertoires de RUNSSCRIPTSPATH. Retourner 123 si le fichier de recette n'a # pas été trouvé, sinon retourner la valeur de retour de runs_recipe() local RUNSDIR RUNSSCRIPT RUNSSCRIPTDIR RUNSSCRIPTNAME RUNSDIRPATH RUNSSCRIPTPATH local RUNSSCRIPTSDIRS RUNSMODULESDIRS RUNSHOSTSDIRS; __runs_setpath if ! runs_find_scriptfile "$1"; then eerror "$1: fichier introuvable. Vérifiez les valeurs suivantes: RUNSSCRIPTSPATH=$RUNSSCRIPTSPATH RUNSMODULESPATH=$RUNSMODULESPATH RUNSHOSTSPATH=$RUNSHOSTSPATH RUNSHOST=$RUNSHOST" return 123 fi runs_recipe "$RUNSSCRIPT" } ################################################################################ # Moteur de script function runs_init() { RUNSSRCDIR="${1:-$scriptdir}" # répertoire d'où sont copiés les scripts RUNSACTION=runs_action_run # informations sur le système RUNSHOST="$MYHOST" RUNSACTUALSYSNAME=("${MYSYSNAME[@]}") RUNSACTUALSYSDIST=("${MYSYSDIST[@]}") RUNSACTUALSYSVER=("${MYSYSVER[@]}") RUNSACTUALBITS="$MYBITS" RUNSEXPORTDIR= # avec --export, répertoire de base de l'export RUNSROOTDIR= # avec --export, répertoire correspondant à la racine RUNSWORKDIR= RUNSUPDATERECIPE=1 # faut-il rajouter la ligne de recette lors d'un export RUNSCONFFILES= # fichier de configuration qui ont été chargés # liste des varibles définies RUNSVARS=() # parmis les variables de RUNSVARS, liste de celles qui sont des tableaux RUNSARRAYS=() # parmis les variables de RUNSVARS, liste de celles qui sont des indirections RUNSVARSINDIRECT=() # liste des références définies RUNSREFS=() # recettes qui ont déjà été lancées, pour éviter les inclusions récursives RUNSRECIPES=() # scripts qui ont déjà été lancés, pour la commande after RUNSSTORY=() # flags valides et ceux qui ont été activés RUNSVALIDCONFS=(root local proxy nolang) RUNSCONFS=() # Faut-il réinitialiser les scripts qui se basent sur shouldrun/setdone RUNSRESET= } function runs_initdomains() { # Si ce n'est pas déjà le cas, initialiser RUNSDOMAINS en fonction de # /etc/resolv.conf [ -n "${RUNSDOMAINS[*]}" ] && return local domains="$(awk '$1 == "search" { $1 = ""; print }' /etc/resolv.conf)" eval "RUNSDOMAINS=($domains)" } function runs_inithost() { [ -n "$1" ] && RUNSHOST="$1" } function runs_initsysinfos() { local RUNSSCRIPTSDIRS RUNSMODULESDIRS RUNSHOSTSDIRS; __runs_setpath local sysname="$1" sysdist="$2" sysver="$3" bits="$4" # on a spécifié le nombre de bits [ -n "$bits" ] && MYBITS="$bits" if [ -n "$sysname" -o -n "$sysdist" -o -n "$sysver" ]; then # on a spécifié un système en ligne de commande ensure_sysinfos sysname sysdist sysver else # Si le fichier sysinfos.conf existe dans le répertoire d'hôte, y # prendre les valeurs local sysinfos if sysinfos="$(runs_find_hostfile sysinfos.conf)"; then sysname=() sysdist=() sysver=() bits= source "$sysinfos" [ -n "$bits" ] && MYBITS="$bits" if [ -n "$sysname" -o -n "$sysdist" -o -n "$sysver" ]; then ensure_sysinfos sysname sysdist sysver else # aucune modification n'a été faite par le fichier de # configuration. ne pas modifier les valeurs courantes return fi else # aucun fichier de configuration. ne pas modifier les valeurs # courantes return fi fi MYSYSNAME=("${sysname[@]}") MYSYSDIST=("${sysdist[@]}") MYSYSVER=("${sysver[@]}") MYBITS="$bits" } function runs_initworkdir() { local runsexportdir="$1" runsworkdir="$2" scriptdir="${3:-$scriptdir}" if [ -z "$runsexportdir" -a "$RUNSACTION" == runs_action_export ]; then # en mode export, il faut définir runsexportdir ac_set_tmpdir runsexportdir fi if [ -n "$runsexportdir" ]; then if [ -n "$runsworkdir" ]; then ewarn "Avec --export, la valeur de --runsworkdir est ignorée" fi RUNSEXPORTDIR="$(abspath "$runsexportdir")" RUNSROOTDIR="$RUNSEXPORTDIR/root" RUNSWORKDIR="$RUNSEXPORTDIR/work" mkdir -p "$RUNSEXPORTDIR" mkdir -p "$RUNSROOTDIR" mkdir -p "$RUNSWORKDIR" if [ "$RUNSACTION" == "runs_action_export" ]; then # préparer les fichiers pour l'export >"$RUNSEXPORTDIR/varsfile" >"$RUNSEXPORTDIR/localuser" >"$RUNSEXPORTDIR/localroot" >"$RUNSEXPORTDIR/remoteuser" >"$RUNSEXPORTDIR/remoteroot" # synchronisation ulib urequire ulib nutools/pyulib ulib_sync "$RUNSEXPORTDIR" pyulib_sync "$RUNSEXPORTDIR" # copie runs, uinst cp "$RUNSSRCDIR/runs" "$RUNSEXPORTDIR" cp "$RUNSSRCDIR/uinst" "$RUNSEXPORTDIR" # faire les scripts args_def="args=($(quoted_args ./runs --runsscriptspath "$RUNSSCRIPTSPATH" --runsmodulespath "$RUNSMODULESPATH" --runshostspath "$RUNSHOSTSPATH" --runsexportdir . --runsvarsfile varsfile -h "$RUNSHOST" ${RUNSRESET:+ -z}))" echo '#!/bin/bash # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 cd "$(dirname "$0")" '"$args_def"' [ -s localuser ] && { "${args[@]}" -r localuser || exit 1; } [ -s localroot ] && { "${args[@]}" -r localroot -s || exit 1; } exit 0 ' >"$RUNSEXPORTDIR/runs-local" echo '#!/bin/bash # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 cd "$(dirname "$0")" '"$args_def"' if [ -s remote ]; then [ -f remote-needs-root ] && args=("${args[@]}" -s) "${args[@]}" -r remote || exit 1 fi exit 0 ' >"$RUNSEXPORTDIR/runs-remote" chmod +x "$RUNSEXPORTDIR/runs-local" "$RUNSEXPORTDIR/runs-remote" else # corriger RUNS*PATH à partir de RUNSROOTDIR function __runs_add_runsroot() { echo "$RUNSROOTDIR$1"; } local runspath runsdirs for runspath in RUNSSCRIPTSPATH RUNSMODULESPATH RUNSHOSTSPATH; do array_from_path runsdirs "${!runspath}" array_map runsdirs abspath array_map runsdirs __runs_add_runsroot set_var "$runspath" "$(array_join runsdirs :)" done # XXX quelles autres variables doivent être corrigées? fi elif [ -n "$runsworkdir" ]; then RUNSWORKDIR="$runsworkdir" else ac_set_tmpdir RUNSWORKDIR fi } function runs_after_export() { # après l'export, initialiser varsfile avec les valeurs qu'il faut garder # entre le déploiement local et le déploiement distant. if [ "$RUNSACTION" == runs_action_export ]; then set_array_cmd RUNSSTORY >>"$RUNSEXPORTDIR/varsfile" fi } function __runs_is_runsscript() { if [ -f "$1" ]; then quietgrep "^script:" "$1" elif [ -d "$1" ]; then [ -f "$1/.udir" ] else return 1 fi } function runs_check_runsscript() { if [ -f "$1" ] && ! __runs_is_runsscript "$1"; then eerror "$1: ce n'est pas un fichier de script valide: il manque la ligne 'script:'" return 1 elif [ -d "$1" ]; then return 0 fi return 0 } function __runs_check_varname() { if [ "${1#RUNS}" != "$1" -o "${1#_}" != "$1" ]; then ewarn "Variable invalide: $1. Cette valeur a été ignorée" return 1 fi return 0 } function __runs_check_notref() { if array_contains RUNSREFS "$1"; then ewarn "Vous ne pouvez pas modifier la référence $1. La valeur sera ignorée" return 1 fi return 0 } function __runs_splitref() { if [[ "$1" == *=* ]]; then set_var "${2:-__name}" "${1%%=*}" set_var "${3:-__value}" "${1#*=}" else set_var "${2:-__name}" "$1" set_var "${3:-__value}" "$1" fi } function runs_var() { # Initialiser les variables selon les directives données en ligne de # commande. # Les arguments peuvent être une suite de définitions de la forme # 'scalar=value', 'scalar!=name', 'array+=value', 'array-=value' ou # 'array@=name'. # Sinon, le *dernier* argument peut-être de l'une des formes suivantes: # 'array value0 [value1...]' pour initialiser un tableau, # 'array+ value0 [value1...]' pour ajouter des valeurs à un tableau, # 'array- value0 [value1...]' pour enlever des valeurs à un tableau. # Les formes 'scalar!=value' et 'array@=value' sont des indirections et # permettent d'initialiser la variable avec la valeur d'une autre # variable. L'avantage est que la résolution de la valeur est faite # uniquement lors de l'appel de cette fonction, ce qui est utile avec des # fonction comme 'after -r' local __name __value # variables scalaires ou manipulation de tableaux while [ -n "$1" ]; do if [[ "$1" == *+=* ]]; then __name="${1%%+=*}" __value="${1#*+=}" if __runs_check_varname "$__name" && __runs_check_notref "$__name"; then array_set RUNSVARS "$__name" array_set RUNSARRAYS "$__name" array_del RUNSVARSINDIRECT "$__name" array_add "$__name" "$__value" fi elif [[ "$1" == *-=* ]]; then __name="${1%%-=*}" __value="${1#*-=}" if __runs_check_varname "$__name" && __runs_check_notref "$__name"; then array_set RUNSVARS "$__name" array_set RUNSARRAYS "$__name" array_del RUNSVARSINDIRECT "$__name" array_del "$__name" "$__value" fi elif [[ "$1" == *!=* ]]; then __name="${1%%!=*}" __value="${1#*!=}" if __runs_check_varname "$__name" && __runs_check_notref "$__name"; then array_set RUNSVARS "$__name" array_del RUNSARRAYS "$__name" array_set RUNSVARSINDIRECT "$__name" set_var "$__name" "$__value" fi elif [[ "$1" == *@=* ]]; then __name="${1%%@=*}" __value="${1#*@=}" if __runs_check_varname "$__name" && __runs_check_notref "$__name"; then array_set RUNSVARS "$__name" array_set RUNSARRAYS "$__name" array_set RUNSVARSINDIRECT "$__name" set_var "$__name" "$__value" fi elif [[ "$1" == *=* ]]; then __name="${1%%=*}" __value="${1#*=}" if __runs_check_varname "$__name" && __runs_check_notref "$__name"; then array_set RUNSVARS "$__name" array_del RUNSARRAYS "$__name" array_del RUNSVARSINDIRECT "$__name" set_var "$__name" "$__value" fi else break fi [ -n "$RUNSVARDESC" ] && set_var "RUNSVARDESC_$__name" "$RUNSVARDESC" shift done # variables tableau if [ -n "$1" ]; then __name="$1"; shift if __runs_check_varname "$__name" && __runs_check_notref "$__name"; then if [ "${__name%+}" != "$__name" ]; then __name="${__name%+}" for __value in "$@"; do array_add "$__name" "$__value" done elif [ "${__name%-}" != "$__name" ]; then __name="${__name%-}" for __value in "$@"; do array_del "$__name" "$__value" done else set_array "$__name" @ "$@" fi # Placer $__name dans les tableau après qu'il aie été corrigé # si par exemple il se terminer par '+' ou '-' array_set RUNSVARS "$__name" array_set RUNSARRAYS "$__name" array_del RUNSVARSINDIRECT "$__name" fi fi } function runs_conf() { # Activer les flags $* for __conf in "$@"; do if array_contains RUNSVALIDCONFS "$__conf"; then array_set RUNSCONFS "$__conf" else ewarn "Flag inconnu: $__conf. Il sera ignoré" fi done } function runs_indref() { # fonction de convenance pour créer des références $3..* avec le fichier de # configuration $1 et les variables $2 local __sconf="$1" __vars="$2"; shift; shift ref -s "$__sconf" -v "$__vars" "$@" } function runs_refcerts() { # fonction de convenance pour créer une référence à un répertoire contenant # des certificats mentionnés dans le fichier de configuration $1. Si les # références $2..* ne sont pas mentionnées, la variable certsdir dans le # fichier de configuration est utilisée. local __sconf="$1"; shift runs_indref "$__sconf" ca,cert,key -V certsdir "$@" } function runs_refapacheconfig() { # fonction de convenance pour créer les références à un répertoire de # configuration pour apache. # USAGE: refapacheconfig autoconfdir=path/to/autoconfdir [certsdir=[path/to/certsdir]] # - autoconfdir= est requis et permet de définir à la fois la variable qui # contiendra la référence ainsi que le répertoire à référencer. # - certsdir= est optionel. Si cet argument est spécifié sous la forme # certsdir=path/to/certsdir, il permet de définir à la fois la variable qui # contiendra la référence ainsi que le répertoire à référencer. Si # l'argument est spécifié sous la forme certsdir=, il permet de définir la # variable qui contiendra la référence. C'est cette variable qui sera lue # dans les fichiers de configuration. Si l'argument n'est pas spécifié, on # considère que l'argument 'certsdir=' a été utilisé. : #local -a __args #parse_opts + \ # @ __args -- "$@" && set -- "${__args[@]}" || die "$__args" } function runs_set_lang() { # Charger la valeur de LANG depuis l'environnement. La variable LANG est # initialisée if ! array_contains RUNSCONFS nolang; then if check_sysinfos -d debianlike; then eval "$(LANG= source_ifexists /etc/default/locale set_var_cmd LANG "$LANG" )" fi export LANG fi } function runs_set_proxy() { # Charger la valeur du proxy depuis l'environnement. Les variables # http_proxy, ftp_proxy et no_proxy sont initialisées if array_contains RUNSCONFS proxy; then if check_sysinfos -d debianlike; then eval "$(http_proxy=; ftp_proxy=; no_proxy= source_ifexists /etc/environment set_var_cmd http_proxy "$http_proxy" set_var_cmd ftp_proxy "$ftp_proxy" set_var_cmd no_proxy "$no_proxy" )" fi export http_proxy ftp_proxy no_proxy fi } function runs_check_confs() { # Vérifier l'état courant par rapport aux flags if array_contains RUNSCONFS root; then is_root || die "Ce script requière d'être lancé en root (essayer avec l'option -s)" fi } function runs_after() { # Vérifier que ce script est lancé après le scriptpath $1, par rapport à # RUNSSTORY local -a __args local runslevel=0 parse_opts + \ -r,--run runslevel \ @ __args -- "$@" && set -- "${__args[@]}" || die "$__args" # Le traitement est fait en fonction de runslevel if [ $runslevel -eq 0 ]; then # sans l'option -r, on ne fait que vérifier que les scriptpaths # correspondant aux arguments ont déjà été lancés. On peut spécifier # autant de scriptpaths que nécessaire, puisque que des arguments ne # sont pas nécessaires local scriptpath for scriptpath in "$@"; do if ! array_contains RUNSSTORY "$scriptpath"; then eerror "Ce script requière d'être lancé après $scriptpath" return 1 fi done return 0 fi # Protéger la liste actuelle des variables en en faisant une copie # locale. En effet, cette liste sera supprimée à la fin de l'invocation # récursive avec -r. Protéger également les valeurs de variables en en # faisant une copie locale, puis fusionner avec les valeurs saisies sur la # ligne de commande. # Attention! si on veut utiliser dans les paramètre du script dépendant une # valeur d'une variable déjà définie, il faut utiliser une indirection # var!=othervar, parce que la valeur définitive est calculée dans cette # fonction, et non avant son appel! local __var __tmp for __var in "${RUNSVARS[@]}"; do if array_contains RUNSARRAYS "$__var"; then eval "array_copy __tmp $__var; local -a $__var; array_copy $__var __tmp" else eval "set_var __tmp \"\${!__var}\"; local $__var; $__var=\"\$__tmp\"" fi done runs_clvars "${RUNSCLVARS[@]}" runs_indvars # Etendre tout de suite les indirections var!=othervar et array@=otherarray, # parce que la valeur qui doit être prise en compte est la valeur actuelle. local -a __args local __arg __name __value __args=("$1"); shift for __arg in "$@"; do if [[ "$__arg" == *!=* ]]; then __name="${__arg%%!=*}" __value="${__arg#*!=}" __args=("${__args[@]}" "$__name=$(quoted_arg "${!__value}")") elif [[ "$__arg" == *@=* ]]; then __name="${__arg%%@=*}" __value="${__arg#*@=}[@]" __args=("${__args[@]}" \; "$__name") for __var in "${!__value}"; do __args=("${__args[@]}" "$__var") done __args=("${__args[@]}" \;) else __args=("${__args[@]}" "$__arg") fi done set -- "${__args[@]}" if [ $runslevel -eq 1 ]; then # Avec une seule option -r, si le scriptpath a déjà été lancé, on ne # fait rien. Sinon, le lancer maintenant avec les arguments $2..$* # Ne pas mettre à jour la ligne de recette lors d'un export pour éviter # de lancer le script deux fois local RUNSUPDATERECIPE= if ! array_contains RUNSSTORY "$1"; then etitle "Lancement du script dépendant $1" \ runs_rscriptpath "$@" fi return 0 else # Avec deux options -r et plus, forcer le lancer de scriptpath avec les # arguments $2..$*, même s'il a déjà été lancé auparavant. # Ne pas mettre à jour la ligne de recette lors d'un export pour éviter # de lancer le script deux fois local RUNSUPDATERECIPE= etitle "Lancement du script dépendant $1" \ runs_rscriptpath "$@" fi } function runs_clvars() { # Traiter les spécifications de variables données en ligne de commande ou # dans un fichier de recettes local __vars __vars=() while [ -n "$1" ]; do if [ "$1" == ";" ]; then runs_var "${__vars[@]}" __vars=() else __vars=("${__vars[@]}" "$1") fi shift done [ -n "${__vars[*]}" ] && runs_var "${__vars[@]}" } function runs_indvars() { # Résoudre les valeurs effectives des variables qui sont des indirections local __var __ind for __var in "${RUNSVARSINDIRECT[@]}"; do if array_contains RUNSARRAYS "$__var"; then # tableau array_copy "$__var" "${!__var}" else # variable scalaire __ind="${!__var}" set_var "$__var" "${!__ind}" fi done RUNSVARSINDIRECT=() } function runs_clvars_cmd() { # écrire la ligne de recette correspondant au script $1 et aux variables # $2..$* local __vars __prefix __vars=() while [ -n "$1" ]; do if [ "$1" == ";" ]; then echo "$__prefix$(quoted_args "${__vars[@]}")" __vars=() __prefix=" " else __vars=("${__vars[@]}" "$1") fi shift done [ -n "${__vars[*]}" ] && echo "$__prefix$(quoted_args "${__vars[@]}")" } function runs_loadconfs() { local __conffile __path __done RUNSCONFFILES=() if [ -n "$RUNSDIR" ]; then __path="$RUNSSCRIPT" while [ -n "$__path" ]; do __path="$(dirname "$__path")" __conffile="$__path/runs.conf" if [ -f "$__conffile" ]; then RUNSCONFFILES=("$__conffile" "${RUNSCONFFILES[@]}") fi [ "$__path" == "$RUNSDIR" ] && __path= done fi for __conffile in "${RUNSCONFFILES[@]}"; do source "$__conffile" done } function runs_clearvars() { local __var for __var in "${RUNSVARS[@]}"; do eval "unset $__var" done } function runs_action_desc() { local -a RUNSVARS RUNSARRAYS RUNSVARSINDIRECT local -a RUNSCLVARS RUNSCONFS RUNSCONFFILES local RUNSVARDESC __state function desc() { eecho "$(ppath "$RUNSSCRIPT"): $*"; exit 0; } function unsupported_system() { runs_unsupported_system "$@"; } function require_sysinfos() { runs_require_sysinfos "$@"; } function vardesc() { RUNSVARDESC="$*"; } function var() { runs_var "$@"; RUNSVARDESC=; } function conf() { runs_conf "$@"; } function after() { local scriptpath for scriptpath in "$@"; do einfo "Ce script doit être lancé après $scriptpath" done } function ref() { local -a __args parse_opts + \ -s:,--sconf: '$:' \ -V:,--refvar: '$:' \ -v:,--vars: '$:' \ -l:,--lconf: '$:' \ @ __args -- "$@" && set -- "${__args[@]}" || die "$__args" runs_var "$@" RUNSVARDESC= local __name __value; __runs_splitref "$1" __name __value array_add RUNSREFS "$__name" } function indref() { runs_indref "$@"; } function refcerts() { runs_refcerts "$@"; } function refapacheconfig() { runs_refapacheconfig "$@"; } function out() { runs_var "$@"; } function script:() { eecho "(pas de description)" exit 0 } runs_check_runsscript "$RUNSSCRIPT" || die runs_loadconfs (source "$RUNSSCRIPT"); __state=$? runs_clearvars return $__state } function runs_action_dump() { local -a RUNSVARS RUNSARRAYS RUNSVARSINDIRECT local -a RUNSCLVARS RUNSCONFS RUNSCONFFILES local RUNSVARDESC __state RUNSCLVARS=("$@") function desc() { eecho "$(ppath "$RUNSSCRIPT"): $*"; } function unsupported_system() { runs_unsupported_system "$@"; } function require_sysinfos() { runs_require_sysinfos "$@"; } function vardesc() { RUNSVARDESC="$*"; } function var() { runs_var "$@"; RUNSVARDESC=; } function conf() { runs_conf "$@"; } function after() { :; } function ref() { local -a __args parse_opts + \ -s:,--sconf: '$:' \ -V:,--refvar: '$:' \ -v:,--vars: '$:' \ -l:,--lconf: '$:' \ @ __args -- "$@" && set -- "${__args[@]}" || die "$__args" runs_var "$@" RUNSVARDESC= local __name __value; __runs_splitref "$1" __name __value array_add RUNSREFS "$__name" } function indref() { runs_indref "$@"; } function refcerts() { runs_refcerts "$@"; } function refapacheconfig() { runs_refapacheconfig "$@"; } function out() { runs_var "$@"; } function script:() { runs_set_lang runs_set_proxy runs_clvars "${RUNSCLVARS[@]}" runs_indvars local __var __desc __values __count for __var in "${RUNSVARS[@]}"; do __desc="RUNSVARDESC_$__var" [ -n "${!__desc}" ] && eecho "${COULEUR_BLEUE}$__var${COULEUR_NORMALE}: ${!__desc}" __values="$__var[@]" eval "__count=\"\${#$__var[@]}\"" if [ "$__count" -eq 1 ]; then # variable scalaire eecho "$__var=$(quoted_args "${!__values}")" else # variable tableau eecho "$__var=($(quoted_args "${!__values}"))" fi done exit 0 } runs_check_runsscript "$RUNSSCRIPT" || die runs_loadconfs (source "$RUNSSCRIPT"); __state=$? runs_clearvars return $__state } function runs_action_run() { local -a RUNSVARS RUNSARRAYS RUNSVARSINDIRECT local -a RUNSCLVARS RUNSCONFS RUNSCONFFILES local RUNSVARDESC __state RUNSCLVARS=("$@") function desc() { :; } function unsupported_system() { runs_unsupported_system "$@"; } function require_sysinfos() { runs_require_sysinfos "$@"; } function vardesc() { RUNSVARDESC="$*"; } function var() { runs_var "$@"; RUNSVARDESC=; } function conf() { runs_conf "$@"; } function after() { runs_after "$@" || exit 1; } function ref() { local -a __args local __ref __name __value __found __required __rdir __rfile __rsconf local __shellconfs __shellconf __refvarname=refdir __refvar local __tmpconf __wildconf # ignorer les options -v, -l et leur argument parse_opts + \ -r,--required,-m,--mandatory __required=1 \ -f,--required-file '$__required=1; __rfile=1' \ -d,--required-dir '$__required=1; __rdir=1' \ -s:,--sconf: __shellconf= \ --rs,--rsconf,--required-sconf __rsconf=1 \ -V:,--refvar: __refvarname= \ -v:,--vars: '$:' \ -l:,--lconf: '$:' \ @ __args -- "$@" && set -- "${__args[@]}" || die "$__args" if [ -n "$__shellconf" ]; then splitwcs "$__shellconf" __tmpconf __wildconf [ -n "$__wildconf" ] && __shellconf="$__tmpconf" if [ -e "$__shellconf" -a "${__shellconf#/}" != "$__shellconf" ]; then # un chemin absolu _existant_, le garder tel quel : elif withpath "$__shellconf" && [ -e "$RUNSSCRIPTDIR/$__shellconf" ]; then # d'abord dans le répertoire du script __shellconf="$RUNSSCRIPTDIR/$__shellconf" elif __tmpconf="$(runs_find_datafile "$__shellconf" "" "$RUNSSCRIPTDIR")"; then # puis chercher dans RUNS*DIRS __shellconf="$__tmpconf" fi if [ ! -e "$__shellconf" ]; then if [ -n "$__rsconf" ]; then die "$__shellconf: fichier introuvable" else ewarn "$__shellconf: fichier introuvable" fi fi [ -n "$__wildconf" ] && __shellconf="$__shellconf/$__wildconf" if [ -z "$*" ]; then # Si aucun argument n'est spécifié, essayer de prendre la # configuration dans le fichier $__shellconf __runs_check_varname "$__refvarname" || die set_var "$__refvarname" "" splitwcs "$__shellconf" __tmpconf __wildconf if [ -n "$__wildconf" ]; then array_lsall __shellconfs "$__tmpconf" "$__wildconf" else __shellconfs=("$__shellconf") fi __args=() for __tmpconf in "${__shellconfs[@]}"; do [ -f "$__tmpconf" ] || { eerror "$__tmpconf: fichier introuvable. Il a été ignoré" continue } __refvar="$(source "$__tmpconf"; echo "${!__refvarname}")" __args=("${__args[@]}" "$__refvarname=$__refvar") done set -- "${__args[@]}" fi fi for __ref in "$@"; do __runs_splitref "$__ref" __name __value if __runs_check_varname "$__name"; then __found= if [ "${__value#/}" != "$__value" ]; then # un chemin absolu. corriger éventuellement avec RUNSROOTDIR # s'il existe, le garder tel quel if [ -n "$RUNSROOTDIR" ]; then # corriger le chemin à partir de RUNSROOTDIR __value="$RUNSROOTDIR$__value" fi [ -e "$__value" ] && __found=1 fi if [ -z "$__found" ]; then if withpath "$__value" && [ -e "$RUNSSCRIPTDIR/$__value" ]; then # d'abord dans le répertoire du script __value="$RUNSSCRIPTDIR/$__value" elif [ -e "$RUNSWORKDIR/$__value" ]; then # ensuite dans le répertoire partagé __value="$RUNSWORKDIR/$__value" else # puis chercher dans RUNS*DIRS local __file if __file="$(runs_find_datafile "$__value" "" "$RUNSSCRIPTDIR")"; then __value="$__file" elif [ -z "$__required" ]; then # par défaut, prendre dans le répertoire partagé __value="$RUNSWORKDIR/$__value" else die "Le fichier $__value ($__name) est requis" fi fi fi [ -z "$__rfile" -o -f "$__value" ] || die "Le fichier $__value ($__name) est requis" [ -z "$__rdir" -o -d "$__value" ] || die "Le répertoire $__value ($__name) est requis" runs_var "$__name=$__value" RUNSVARDESC= array_add RUNSREFS "$__name" fi done } function indref() { runs_indref "$@"; } function refcerts() { runs_refcerts "$@"; } function refapacheconfig() { runs_refapacheconfig "$@"; } function out() { for __out in "$@"; do if [[ "$__out" == *=* ]]; then __name="${__out%%=*}" __value="${__out#*=}" else __name="$__out" __value="$__out" fi if __runs_check_varname "$__name"; then __value="$RUNSWORKDIR/$__value" runs_var "$__name=$__value" mkdirof "$__value" >"$__value" # s'assurer que le fichier est vide fi done } function script:() { runs_set_lang runs_set_proxy runs_clvars "${RUNSCLVARS[@]}" runs_indvars runs_check_confs } runs_check_runsscript "$RUNSSCRIPT" || die runs_loadconfs if [ -d "$RUNSSCRIPT" ]; then # installer le répertoire avec uinst # les variables sont passées en ligne de commande, et les tableau sont # transformés en chaine avec chaque élément séparés par ':' ( runs_clvars "${RUNSCLVARS[@]}" runs_indvars for __name in "${RUNSVARS[@]}"; do __value="$(array_join "$__name" ":")" __vars=("${__vars[@]}" "$(set_var_cmd "$__name" "$__value")") done urequire uinst udir prefixes uinc # Il faut définir UINST, chemin vers le script uinst. UINST="$RUNSSRCDIR/uinst" uinst $(get_interaction_option) $(get_verbosity_option) \ "$RUNSSCRIPT" "${__vars[@]}" ); __state=$? else # lancer le script avec le protocole runs (source "$RUNSSCRIPT"); __state=$? fi runs_clearvars return $__state } function runs_action_export() { local -a RUNSVARS RUNSARRAYS RUNSVARSINDIRECT local -a RUNSCLVARS RUNSCONFS RUNSCONFFILES local RUNSVARDESC __state RUNSCLVARS=("$@") function desc() { :; } function unsupported_system() { runs_unsupported_system "$@"; } function require_sysinfos() { runs_require_sysinfos "$@"; } function vardesc() { RUNSVARDESC="$*"; } function var() { runs_var "$@"; RUNSVARDESC=; } function conf() { runs_conf "$@"; } function after() { runs_after "$@" || exit 1; } function ref() { local -a __args local __ref __name __value __copy __required __rdir __rfile __rsconf local __shellconfs __shellconf __refvarname=refdir __refvar __shellvars local __lineconfs __lineconf local __tmpconf __wildconf parse_opts + \ -r,--required,-m,--mandatory __required=1 \ -f,--required-file '$__required=1; __rfile=1' \ -d,--required-dir '$__required=1; __rdir=1' \ -s:,--sconf: __shellconf= \ --rs,--rsconf,--required-sconf __rsconf=1 \ -V:,--refvar: __refvarname= \ -v:,--vars: __shellvars= \ -l:,--lconf: __lineconf= \ @ __args -- "$@" && set -- "${__args[@]}" || die "$__args" if [ -n "$__shellconf" ]; then splitwcs "$__shellconf" __tmpconf __wildconf [ -n "$__wildconf" ] && __shellconf="$__tmpconf" if [ -e "$__shellconf" -a "${__shellconf#/}" != "$__shellconf" ]; then # un chemin absolu _existant_, le garder tel quel : elif withpath "$__shellconf" && [ -e "$RUNSSCRIPTDIR/$__shellconf" ]; then # d'abord dans le répertoire du script __shellconf="$RUNSSCRIPTDIR/$__shellconf" elif __tmpconf="$(runs_find_datafile "$__shellconf" "" "$RUNSSCRIPTDIR")"; then # puis chercher dans RUNS*DIRS __shellconf="$__tmpconf" fi if [ ! -e "$__shellconf" ]; then if [ -n "$__rsconf" ]; then die "$__shellconf: fichier introuvable" else ewarn "$__shellconf: fichier introuvable" fi fi [ -n "$__wildconf" ] && __shellconf="$__shellconf/$__wildconf" [ -n "$__lineconf" ] && ewarn "L'option -s étant spécifiée, l'option -l a été ignorée" if [ -z "$*" ]; then # Si aucun argument n'est spécifié, essayer de prendre la # configuration dans le fichier $__shellconf __runs_check_varname "$__refvarname" || die set_var "$__refvarname" "" splitwcs "$__shellconf" __tmpconf __wildconf if [ -n "$__wildconf" ]; then array_lsall __shellconfs "$__tmpconf" "$__wildconf" else __shellconfs=("$__shellconf") fi __args=() for __tmpconf in "${__shellconfs[@]}"; do [ -f "$__tmpconf" ] || { eerror "$__tmpconf: fichier introuvable. Il a été ignoré" continue } __refvar="$(source "$__tmpconf"; echo "${!__refvarname}")" __args=("${__args[@]}" "$__refvarname=$__refvar") done set -- "${__args[@]}" fi elif [ -n "$__lineconf" ]; then splitwcs "$__lineconf" __tmpconf __wildconf [ -n "$__wildconf" ] && __lineconf="$__tmpconf" if [ -e "$__lineconf" -a "${__lineconf#/}" != "$__lineconf" ]; then # un chemin absolu _existant_, le garder tel quel : elif withpath "$__lineconf" && [ -e "$RUNSSCRIPTDIR/$__lineconf" ]; then # d'abord dans le répertoire du script __lineconf="$RUNSSCRIPTDIR/$__lineconf" elif __tmpconf="$(runs_find_datafile "$__lineconf" "" "$RUNSSCRIPTDIR")"; then # puis chercher dans RUNS*DIRS __lineconf="$__tmpconf" fi [ -e "$__lineconf" ] || die "$__lineconf: fichier introuvable" [ -n "$__wildconf" ] && __lineconf="$__lineconf/$__wildconf" fi if [ -n "$__rfile" ]; then [ -n "$__shellconf" -o -n "$__lineconf" ] && die "-f est incompatible avec -s et -l" fi for __ref in "$@"; do if [ -n "$__shellconf" -a -z "$__shellvars" ]; then ewarn "L'option -v n'a pas été spécifiée pour $__ref" fi __copy=1 __runs_splitref "$__ref" __name __value if __runs_check_varname "$__name"; then if [ -e "$__value" -a "${__value#/}" != "$__value" ]; then # un chemin absolu _existant_, le garder tel quel : elif withpath "$__value" && [ -e "$RUNSSCRIPTDIR/$__value" ]; then # d'abord dans le répertoire du script __value="$RUNSSCRIPTDIR/$__value" elif [ -e "$RUNSWORKDIR/$__value" ]; then # puis dans le répertoire partagé __value="$RUNSWORKDIR/$__value" __copy= else # puis chercher dans RUNS*DIRS local __file if __file="$(runs_find_datafile "$__value" "" "$RUNSSCRIPTDIR")"; then __value="$__file" elif [ -z "$__required" ]; then # par défaut, prendre dans le répertoire partagé __value="$RUNSWORKDIR/$__value" __copy= else die "Le fichier $__value ($__name) est requis" fi fi [ -z "$__rfile" -o -f "$__value" ] || die "Le fichier $__value ($__name) est requis" [ -z "$__rdir" -o -d "$__value" ] || die "Le répertoire $__value ($__name) est requis" runs_var "$__name=$__value" RUNSVARDESC= array_add RUNSREFS "$__name" if [ -n "$__copy" ]; then if [ -n "$__shellconf" ]; then # copier les fichiers mentionnés dans le fichier de # configuration, d'après les valeurs de __shellvars estep "Vérification du répertoire $(ppath "$__value")" mkdirof "$RUNSROOTDIR$__value" if [ -n "$__shellvars" ]; then ( __CPNOVCS_RSYNC_ARGS=(--copy-unsafe-links) array_split __shellvars "$__shellvars" , splitwcs "$__shellconf" __tmpconf __wildconf if [ -n "$__wildconf" ]; then array_lsall __shellconfs "$__tmpconf" "$__wildconf" else __shellconfs=("$__shellconf") fi for __shellconf in "${__shellconfs[@]}"; do estep "Dans $(ppath "$__shellconf")" for __shellvar in "${__shellvars[@]}"; do # s'assurer que la variables sont vides # avant de sourcer $__shellconf set_var "$__shellvar" done source "$__shellconf" for __shellvar in "${__shellvars[@]}"; do [ -n "${!__shellvar}" ] || continue estep "... Copie de ${!__shellvar}" cpnovcs "$__value/${!__shellvar}" "$(dirname "$RUNSROOTDIR$__value/${!__shellvar}")" done done ) fi elif [ -n "$__lineconf" ]; then # copier les fichiers mentionnés dans le fichier de # configuration, à raison de un par ligne estep "Vérification du répertoire $(ppath "$__value")" mkdirof "$RUNSROOTDIR$__value" ( __CPNOVCS_RSYNC_ARGS=(--copy-unsafe-links) splitwcs "$__lineconf" __tmpconf __wildconf if [ -n "$__wildconf" ]; then array_lsall __lineconfs "$__tmpconf" "$__wildconf" else __lineconfs=("$__lineconf") fi for __lineconf in "${__lineconfs[@]}"; do estep "Dans $(ppath "$__lineconf")" array_from_lines __relpaths "$(<"$__lineconf" filter_conf)" for __relpath in "${__relpaths[@]}"; do estep "... Copie de $__relpath" cpnovcs "$__value/$__relpath" "$(dirname "$RUNSROOTDIR$__value/$__relpath")" done done ) elif [ ! -e "$RUNSROOTDIR$__value" ]; then # copie standard estep "Copie de $(ppath "$__value")" mkdirof "$RUNSROOTDIR$__value" cpnovcs "$__value" "$(dirname "$RUNSROOTDIR$__value")" fi fi fi done } function indref() { runs_indref "$@"; } function refcerts() { runs_refcerts "$@"; } function refapacheconfig() { runs_refapacheconfig "$@"; } function out() { for __out in "$@"; do if [[ "$__out" == *=* ]]; then __name="${__out%%=*}" __value="${__out#*=}" else __name="$__out" __value="$__out" fi if __runs_check_varname "$__name"; then __value="$RUNSWORKDIR/$__value" runs_var "$__name=$__value" mkdirof "$__value" >"$__value" # s'assurer que le fichier est vide fi done } function script:() { runs_set_lang runs_set_proxy runs_clvars "${RUNSCLVARS[@]}" runs_indvars local __recipe if array_contains RUNSCONFS local; then if array_contains RUNSCONFS root; then __recipe="$RUNSEXPORTDIR/localroot" else __recipe="$RUNSEXPORTDIR/localuser" fi else __recipe="$RUNSEXPORTDIR/remote" if array_contains RUNSCONFS root; then touch "$RUNSEXPORTDIR/remote-needs-root" fi fi if [ -n "$RUNSUPDATERECIPE" ]; then estep "Ajout de la ligne de recette" runs_clvars_cmd "$RUNSSCRIPTPATH" "${RUNSCLVARS[@]}" >>"$__recipe" fi if [ ! -e "$RUNSROOTDIR$RUNSSCRIPT" ]; then estep "Copie du script" mkdirof "$RUNSROOTDIR$RUNSSCRIPT" cpnovcs "$RUNSSCRIPT" "$(dirname "$RUNSROOTDIR$RUNSSCRIPT")" fi exit 0 } runs_check_runsscript "$RUNSSCRIPT" || die runs_loadconfs local __conffile for __conffile in "${RUNSCONFFILES[@]}"; do if [ ! -e "$RUNSROOTDIR$__conffile" ]; then estep "Copie de $(ppath "$__conffile")" mkdirof "$RUNSROOTDIR$__conffile" cpnovcs "$__conffile" "$(dirname "$RUNSROOTDIR$__conffile")" fi done if [ -d "$RUNSSCRIPT" ]; then # le répertoire sera installé avec uinst if [ -n "$RUNSUPDATERECIPE" ]; then estep "Ajout de la ligne de recette" runs_clvars_cmd "$RUNSSCRIPTPATH" "${RUNSCLVARS[@]}" >>"$RUNSEXPORTDIR/remote" fi if [ ! -e "$RUNSROOTDIR$RUNSSCRIPT" ]; then estep "Copie des fichiers" mkdir -p "$RUNSROOTDIR$RUNSSCRIPT" || return 1 cpdirnovcs "$RUNSSCRIPT" "$RUNSROOTDIR$RUNSSCRIPT" || return 1 fi else # le script sera lancé avec le protocole runs (source "$RUNSSCRIPT") || return 1 fi runs_clearvars return 0 } ################################################################################ # Les fonctions de cette section permettent de maintenir des informations sur # des opérations _effectuées avec l'utilisateur root_. Cela permet à un script de # savoir s'il a déjà été lancé. Le code typique sera celui-ci: # shouldrun || exit # ... # setdone # Les informations sont stockées sur la machine cible dans /var/lib/runs.state if is_root; then RUNSSTATE_FILE=/var/lib/runs.state else RUNSSTATE_FILE="$HOME/etc/runs.state" fi RUNSSTATE_LASTKEY= RUNSSTATE_LASTVALUE= # Vérifier que la clé $1 existe dans $RUNSSTATE_FILE, et si $2!="", qu'elle # existe avec la valeur $2. Si la clé existe, retourner faux (il ne faut pas # lancer le script). # Si $RUNSRESET est vrai, toujours retourner vrai. Ceci permet de forcer # l'installation pour un script # La clé est toujours automatiquement préfixée de $3(=$RUNSSCRIPTPATH) function shouldrun() { [ -f "$RUNSSTATE_FILE" ] || return 0 is_yes "$RUNSRESET" && return 0 local key="${3:-$RUNSSCRIPTPATH}${1:+-$1}" if ! quietgrep "^$key:$2" "$RUNSSTATE_FILE"; then RUNSSTATE_LASTKEY="$1" RUNSSTATE_LASTVALUE="$2" return 0 else return 1 fi } # Pour le script de chemin scriptpath $1, vérifier que la clé $2 existe dans # $RUNSSTATE_FILE, et si $3!="", qu'elle existe avec la valeur $3. # Cette fonction permet de ne lancer un traitement que si un autre script a été # lancé auparavant avec succès function checkdone() { [ -n "$1" ] || return 1 [ -f "$RUNSSTATE_FILE" ] || return 1 local key="$1${1:+-$2}" value="$3" quietgrep "^$key:$value" "$RUNSSTATE_FILE" } # Comme checkdone(), mais si la clé n'existe pas, afficher un message d'erreur # et quitter le script function requiredone() { if ! checkdone "$@"; then msg="Ce script requière que le script dépendant $1" if [ -n "$2" ]; then msg="${msg} soit dans l'état $2=${3:-done}" else msg="${msg} aie été installé avec succès" fi die "$msg. Pour résoudre ce problème, vous pouvez essayer de relancer le script dépendant" fi } # Ajouter la clé $1 avec la valeur $2(=done) dans $RUNSSTATE_FILE si elle n'y # existe pas déjà. Si $1 est vide, prendre par défaut la dernière clé utilisée # avec shouldrun() # La clé est toujours automatiquement préfixée de $3(=$RUNSSCRIPTPATH) function setdone() { local key="$1" value="$2" [ -n "$key" ] || key="$RUNSSTATE_LASTKEY" [ -n "$value" ] || value="$RUNSSTATE_LASTVALUE" key="${3:-$RUNSSCRIPTPATH}${key:+-$key}" [ -n "$value" ] || value=done local line="$key:$value" mkdirof "$RUNSSTATE_FILE" [ -f "$RUNSSTATE_FILE" ] || touch "$RUNSSTATE_FILE" if ! quietgrep "^$line" "$RUNSSTATE_FILE"; then echo "$line" >>"$RUNSSTATE_FILE" fi RUNSSTATE_LASTKEY= RUNSSTATE_LASTVALUE= } # Supprimer toutes les valeurs de la clé $1 dans $RUNSSTATE_FILE # La clé est toujours automatiquement préfixée de $2(=$RUNSSCRIPTPATH) function resetdone() { local key="${2:-$RUNSSCRIPTPATH}${1:+-$1}" [ -n "$key" ] || return [ -f "$RUNSSTATE_FILE" ] || return sedi "/^${key//\//\\/}:/d" "$RUNSSTATE_FILE" }