#!/bin/bash # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 source "$(dirname "$0")/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