315 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			315 lines
		
	
	
		
			9.4 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 DEFAULTS
 | 
						|
 | 
						|
function display_help() {
 | 
						|
    uecho "$scriptname: se connecter par ssh à un ou plusieurs hôtes
 | 
						|
 | 
						|
USAGE
 | 
						|
    $scriptname [options] hosts
 | 
						|
    $scriptname [options] @hostsfile
 | 
						|
    $scriptname -r hosts
 | 
						|
    $scriptname --parse hosts
 | 
						|
 | 
						|
OPTIONS
 | 
						|
    hosts
 | 
						|
    @hostsfile
 | 
						|
        Spécifier un ou plusieurs hôtes distants sur lequels faire la connexion.
 | 
						|
        Pour spécifier plusieurs hôtes, il faut les séparer par un espace ou le
 | 
						|
        caractère ':', e.g. 'host1 host2' ou 'host1:host2'. Si la spécification
 | 
						|
        contient les caractères { et }, l'expansion est effectuée, e.g
 | 
						|
            'root@{host1,host2}.univ.run'
 | 
						|
        La forme @hostsfile permet de lire la liste des hôtes depuis le fichier
 | 
						|
        hostsfile, à raison d'un hôte par ligne.
 | 
						|
 | 
						|
Toutes les options de ssh sont reconnues. Les options longues suivantes sont
 | 
						|
reconnues comme alias de certaines options courtes de ssh:
 | 
						|
    --quiet
 | 
						|
        alias de -q, activer le mode non verbeux
 | 
						|
    --tty
 | 
						|
        alias de -t, forcer l'allocation d'un TTY
 | 
						|
    --login USER
 | 
						|
        alias de -l, spécifier le user avec lequel se connecter
 | 
						|
    --port PORT
 | 
						|
        alias de -p, spécifier le port sur lequel se connecter
 | 
						|
 | 
						|
Les options suivantes sont exclusives à ce script:
 | 
						|
    -d, --domain DOMAIN
 | 
						|
        Spécifier un domaine par défaut pour les hôtes qui sont spécifiés sans
 | 
						|
        domaine.
 | 
						|
    -z, --ssh SSH
 | 
						|
        Spécifier l'exécutable à utiliser pour lancer ssh.
 | 
						|
    -r, --remove
 | 
						|
        Lancer 'ssh-keygen -R' pour chacun des hôtes spécifiés avant de s'y
 | 
						|
        connecter. Par exemple:
 | 
						|
            $scriptname -r host.tld
 | 
						|
        est équivalent à:
 | 
						|
            ssh-keygen -R host.tld
 | 
						|
            ssh-keygen -R host
 | 
						|
            ssh-keygen -R 10.10.1.5
 | 
						|
            ssh host.tld
 | 
						|
        si l'adresse ip de host.tld est 10.10.1.5
 | 
						|
        Quand cette option est spécifié, l'option -j est reconnue et permet de
 | 
						|
        NE PAS se reconnecter à l'hôte juste après avoir nettoyé les clés. Avec
 | 
						|
        l'option -j, TOUS les arguments sont des noms d'hôte puisqu'aucune
 | 
						|
        connexion n'est effectuée.
 | 
						|
    --exec
 | 
						|
    --no-exec
 | 
						|
        Avec --exec, si un seul hôte est spécifié, lancer le processus ssh avec
 | 
						|
        exec, pour éviter d'encombrer la mémoire. C'est l'option par défaut.
 | 
						|
        Avec --no-exec, ne jamais utiliser exec pour lancer ssh.
 | 
						|
    --parse
 | 
						|
        Afficher la définition des variables ssh, options, hosts et args qui
 | 
						|
        permettent d'effectuer la connexion à partir d'un autre script. Exemple:
 | 
						|
            "'eval "$(ussh --parse args...)"
 | 
						|
            for host in "${hosts[@]}"; do
 | 
						|
                ${exec:+exec} "$ssh" "${options[@]}" "$host" "${args[@]}"
 | 
						|
            done'"
 | 
						|
 | 
						|
Si la variable UTOOLS_USSH_RSYNC_SUPPORT contient une valeur non vide, l'analyse
 | 
						|
des arguments s'arrête à la première valeur qui n'est pas une option, afin de
 | 
						|
permettre l'utilisation de ce script avec l'option -e de rsync."
 | 
						|
}
 | 
						|
 | 
						|
__PARSED_HOSTS=()
 | 
						|
__PARSED_FILES=()
 | 
						|
function parse_hostsfile() {
 | 
						|
    # Lire chacun des fichiers $* et initialiser __PARSED_HOSTS avec la liste
 | 
						|
    # des hôtes mentionnés dans les fichiers.
 | 
						|
    local inputfile basedir inputs input
 | 
						|
    for inputfile in "$@"; do
 | 
						|
        inputfile="$(abspath "$inputfile")"
 | 
						|
        array_contains __PARSED_FILES "$inputfile" && {
 | 
						|
            ewarn "$(ppath "$inputfile"): inclusion récursive"
 | 
						|
            continue
 | 
						|
        }
 | 
						|
        array_add __PARSED_FILES "$inputfile"
 | 
						|
        basedir="$(dirname "$inputfile")"
 | 
						|
        
 | 
						|
        array_from_lines inputs "$(<"$inputfile" filter_conf)" || {
 | 
						|
            ewarn "$inputfile: fichier ingnoré"
 | 
						|
            continue
 | 
						|
        }
 | 
						|
        for input in "${inputs[@]}"; do
 | 
						|
            if [ "${input#@}" != "$input" ]; then
 | 
						|
                # fichier inclus
 | 
						|
                parse_hostsfile "$(abspath "${input#@}" "$basedir")"
 | 
						|
            else
 | 
						|
                array_addu __PARSED_HOSTS "$input"
 | 
						|
            fi
 | 
						|
        done
 | 
						|
    done
 | 
						|
}
 | 
						|
function __expand_braces() {
 | 
						|
    if [[ "$1" == *{* ]] && [[ "$1" == *}* ]]; then
 | 
						|
        eval "echo $1"
 | 
						|
    else
 | 
						|
        echo "$1"
 | 
						|
    fi
 | 
						|
}
 | 
						|
function __dot_is_localhost() { [ "$1" == "." ] && echo "localhost" || echo "$1"; }
 | 
						|
function __fix_domain() {
 | 
						|
    local user host
 | 
						|
    splituserhost "$1" user host
 | 
						|
    if [ "$host" == localhost ]; then
 | 
						|
        : # ne pas corriger localhost
 | 
						|
    elif [ "${host%.}" != "$host" ]; then
 | 
						|
        # si le nom se termine par ., le prendre tel quel
 | 
						|
        host="${host%.}"
 | 
						|
    elif ! [[ "$host" == *.* ]]; then
 | 
						|
        # sinon rajouter le domaine par défaut le cas échéant
 | 
						|
        host="$host${DOMAIN:+.$DOMAIN}"
 | 
						|
    fi
 | 
						|
    echo "${user:+$user@}$host"
 | 
						|
}
 | 
						|
function fix_hosts() {
 | 
						|
    # Si hosts contient des éléments multiple, comme a:b, séparer ces
 | 
						|
    # éléments. i.e (a b:c "d e") --> (a b c d e)
 | 
						|
    # Supporter la syntaxe @hostsfile qui permet de charger la liste des hôtes
 | 
						|
    # depuis un fichier.
 | 
						|
    # Remplacer aussi les '.' par 'localhost'
 | 
						|
    array_map hosts __expand_braces
 | 
						|
    array_fix_paths hosts ":"
 | 
						|
    array_fix_paths hosts " "
 | 
						|
 | 
						|
    local -a _hosts _tmphosts host
 | 
						|
    for host in "${hosts[@]}"; do
 | 
						|
        host="${host%/}"
 | 
						|
        if [ "${host#@}" != "$host" ]; then
 | 
						|
            __PARSED_HOSTS=()
 | 
						|
            parse_hostsfile "${host#@}"
 | 
						|
            array_fix_paths __PARSED_HOSTS
 | 
						|
            array_extendu _hosts __PARSED_HOSTS
 | 
						|
        else
 | 
						|
            array_addu _hosts "$host"
 | 
						|
        fi
 | 
						|
    done
 | 
						|
    array_map _hosts __dot_is_localhost
 | 
						|
    array_map _hosts __fix_domain
 | 
						|
    array_copy hosts _hosts
 | 
						|
}
 | 
						|
 | 
						|
function remove_key() {
 | 
						|
    estep "$1"
 | 
						|
    ssh-keygen -R "$1" >&/dev/null
 | 
						|
}
 | 
						|
function remove_keys() {
 | 
						|
    urequire ipcalc
 | 
						|
 | 
						|
    local -a __hosts; array_copy __hosts hosts
 | 
						|
    local -a allhosts hosts ips; local host hostname user ip
 | 
						|
    array_copy allhosts __hosts
 | 
						|
 | 
						|
    etitle "Suppression des entrées dans ~/.ssh/known_hosts"
 | 
						|
    for host in "${allhosts[@]}"; do
 | 
						|
        splituserhost "$host" user host
 | 
						|
        if ipcalc_checkip "$host" >/dev/null; then
 | 
						|
            ip="$host"
 | 
						|
            remove_key "$ip"
 | 
						|
 | 
						|
            resolv_hosts hosts "$ip"
 | 
						|
            for host in "${hosts[@]}"; do
 | 
						|
                remove_key "$host"
 | 
						|
                hostname="${host%%.*}"
 | 
						|
                [ "$hostname" != "$host" ] && remove_key "$hostname"
 | 
						|
            done
 | 
						|
        else
 | 
						|
            remove_key "$host"
 | 
						|
            hostname="${host%%.*}"
 | 
						|
            [ "$hostname" != "$host" ] && remove_key "$hostname"
 | 
						|
 | 
						|
            resolv_ips ips "$host"
 | 
						|
            for ip in "${ips[@]}"; do
 | 
						|
                remove_key "$ip"
 | 
						|
            done
 | 
						|
        fi
 | 
						|
    done
 | 
						|
    eend
 | 
						|
}
 | 
						|
 | 
						|
function __have_ssh_config() {
 | 
						|
    local host="$1" config="$2"
 | 
						|
    [ -f "$config" ] || return 1
 | 
						|
    awkrun host="$host" '
 | 
						|
BEGIN {
 | 
						|
  in_host = 0
 | 
						|
  ec = 1
 | 
						|
}
 | 
						|
tolower($1) == "host" {
 | 
						|
  in_host = 0
 | 
						|
  for (i = 2; i <= NF; i++) {
 | 
						|
    if ($i == host) {
 | 
						|
      in_host = 1
 | 
						|
      break
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
in_host && tolower($1) == "user" {
 | 
						|
  ec = 0
 | 
						|
  exit 0
 | 
						|
}
 | 
						|
END {
 | 
						|
  exit ec
 | 
						|
}
 | 
						|
' -- "$config"
 | 
						|
}
 | 
						|
 | 
						|
function __update_sshopts_l() {
 | 
						|
    # Ajouter l'option -l USSH_USER au tableau sshopts si aucune définition pour
 | 
						|
    # l'hôte $1 n'existe dans l'un des fichiers de configuration de ssh
 | 
						|
    if [ -n "$1" ]; then
 | 
						|
        __have_ssh_config "$1" ~/.ssh/config && return
 | 
						|
        __have_ssh_config "$1" /etc/ssh/ssh_config && return
 | 
						|
    fi
 | 
						|
    # Rajouter l'option au début pour que l'utilisateur puisse la surcharger
 | 
						|
    sshopts=(${USSH_USER:+-l "$USSH_USER"} "${sshopts[@]}")
 | 
						|
}
 | 
						|
 | 
						|
function show_vars() {
 | 
						|
    [ "${#hosts[*]}" -gt 1 ] && exec=
 | 
						|
 | 
						|
    local -a sshopts
 | 
						|
    array_copy sshopts SSHOPTS
 | 
						|
    __update_sshopts_l "${hosts[0]}"
 | 
						|
 | 
						|
    set_var_cmd ssh "$SSH"
 | 
						|
    set_var_cmd exec "$exec"
 | 
						|
    set_array_cmd options sshopts
 | 
						|
    set_array_cmd hosts
 | 
						|
    set_array_cmd args @ "$@"
 | 
						|
}
 | 
						|
 | 
						|
function do_ssh() {
 | 
						|
    local -a sshopts
 | 
						|
    local onehost r
 | 
						|
    if [ "${#hosts[*]}" -gt 1 ]; then
 | 
						|
        onehost=
 | 
						|
        exec=
 | 
						|
    else
 | 
						|
        onehost=1
 | 
						|
    fi
 | 
						|
    for host in "${hosts[@]}"; do
 | 
						|
        [ -z "$onehost" ] && etitle "$host"
 | 
						|
 | 
						|
        array_copy sshopts SSHOPTS
 | 
						|
        __update_sshopts_l "$host"
 | 
						|
        ${exec:+exec} "$SSH" "${sshopts[@]}" "$host" "$@" || r=$?
 | 
						|
 | 
						|
        [ -z "$onehost" ] && eend
 | 
						|
    done
 | 
						|
    return "${r:-0}"
 | 
						|
}
 | 
						|
 | 
						|
function __update_SSHOPTS() { SSHOPTS=("${SSHOPTS[@]}" "$@"); }
 | 
						|
 | 
						|
# charger USSH_DOMAIN et USSH_USER
 | 
						|
set_defaults nutools
 | 
						|
 | 
						|
DOMAIN="$USSH_DOMAIN"
 | 
						|
SSH=
 | 
						|
remove=
 | 
						|
remove_only=
 | 
						|
exec=1
 | 
						|
parse=
 | 
						|
parse_opts ${UTOOLS_USSH_RSYNC_SUPPORT:++} "${PRETTYOPTS[@]}" \
 | 
						|
    --help '$exit_with display_help' \
 | 
						|
    -1,-2,-4,-6,-A,-a,-C,-f,-g,-K,-k,-M,-N,-n,-q,-s,-T,-t,-V,-v,-X,-x,-Y,-y '$__update_SSHOPTS "$option_"' \
 | 
						|
    -b:,-c:,-D:,-e:,-F:,-I:,-i:,-L:,-l:,-m:,-O:,-o:,-p:,-R:,-S:,-W:,-w: '$__update_SSHOPTS "$option_" "$value_"' \
 | 
						|
    --quiet '$__update_SSHOPTS -q' \
 | 
						|
    --tty '$__update_SSHOPTS -t' \
 | 
						|
    --login: '$__update_SSHOPTS -l "$value_"' \
 | 
						|
    --port: '$__update_SSHOPTS -p "$value_"' \
 | 
						|
    -d:,--domain: DOMAIN= \
 | 
						|
    -z:,--ssh: SSH= \
 | 
						|
    -r,--remove '$remove=1; parse=' \
 | 
						|
    -j remove_only=1 \
 | 
						|
    --exec exec=1 \
 | 
						|
    --no-exec exec= \
 | 
						|
    --parse '$parse=1; remove=' \
 | 
						|
    @ args -- "$@" && set -- "${args[@]}" || die "$args"
 | 
						|
 | 
						|
if [ -n "$remove" ]; then
 | 
						|
    [ -n "$*" ] || die "Vous devez spécifier les hôtes à supprimer de ~/.ssh/known_hosts"
 | 
						|
    if [ -n "$remove_only" ]; then
 | 
						|
        hosts=("$@")
 | 
						|
        fix_hosts
 | 
						|
        remove_keys
 | 
						|
        exit 0
 | 
						|
    fi
 | 
						|
 | 
						|
    hosts=("$1"); shift
 | 
						|
    fix_hosts
 | 
						|
    remove_keys
 | 
						|
else
 | 
						|
    hosts=("$1"); shift
 | 
						|
    fix_hosts
 | 
						|
fi
 | 
						|
 | 
						|
[ -n "$SSH" ] || SSH=ssh
 | 
						|
if [ -n "$parse" ]; then
 | 
						|
    show_vars "$@"
 | 
						|
else
 | 
						|
    do_ssh "$@"
 | 
						|
fi
 |