##@cooked comments # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 ## Outils pour les distributions debian ##@cooked nocomments ##@require base ##@require sysinfos ##@require service uprovide debian urequire base sysinfos service ################################################################################ # Gestion des packages function pkg_check() { # Vérifier que les packages sont installés sur le système local pkg status for pkg in "$@"; do status="$(LANG=C dpkg -s "$pkg" 2>/dev/null | grep '^Status:')" || return 1 [[ "$status" == *not-installed* ]] && return 1 [[ "$status" == *deinstall* ]] && return 1 done return 0 } function pkg_update() { # Mettre à jour la liste des packages silencieusement sans confirmation LANG=C apt-get update -qy } function pkg_upgrade() { # Mettre à jour la liste des packages silencieusement sans confirmation LANG=C apt-get upgrade -qy } function pkg_install() { # Installer les packages silencieusement et sans confirmation LANG=C apt-get install -qy "$@" } function pkg_installm() { # Installer les packages silencieusement et sans confirmation # Retourner 0 si au moins un des packages a été installé. Sinon, les # packages n'ont pas été installés, soit parce qu'ils sont déjà installé, # soit parce qu'il y a eu une erreur. if ! pkg_check "$@"; then # essayer d'installer les packages pkg_install "$@" else # aucun package n'a été installé return 1 fi } ################################################################################ # Gestion des services function service_disable() { # Désactiver le service $1 pour qu'il ne se lance pas automatiquement au # démarrage update-rc.d -f "$1" remove } function service_enable() { # Activer le service $1 pour qu'il se lance automatiquement au démarrage update-rc.d "$1" defaults } ################################################################################ # Gestion des interfaces réseau function network_create_bridge() { # Modifier le fichier /etc/network/interfaces pour créer un nouveau pont # nommé $1 avec les paramètres $2. Si $2 est vide, sa valeur par défaut est # bridge_ports none # bridge_stp off # bridge_fd 2 # bridge_maxwait 0 # OPTIONS: # --static L'interface est statique plutôt que de recevoir son # adresse par DHCP # --auto Démarrer l'interface automatiquement # Une définition existante de l'interface n'est jamais écrasée. # Retourner vrai si la définition a été ajoutée dans /etc/network/interfaces local args method auto ifname lines method=dhcp parse_opts \ --static method=static \ --auto auto \ @ args -- "$@" && set -- "${args[@]}" || { eerror "$args" return 1 } ifname="${1:-br0}" array_from_lines lines "$2" lines="$(array_to_lines lines "" " ")" doinplacef /etc/network/interfaces awkrun ifname="$ifname" lines="$lines" method="$method" auto="$auto" ' BEGIN { foundif = 0 } $0 ~ "^iface " ifname " " { foundif = 1 } { print } END { if (foundif) { exit 1 } else { print "" if (auto != "") print "auto " ifname print "iface " ifname " inet " method if (lines != "") print lines exit 0 } }' } __DEBIAN_NETWORK_DEVEL_SHOW_MODIFS= function __network_backup() { local file="$1" local backup="$file.orig$(date +%Y%m%d)" [ -f "$backup" ] || cat "$file" >"$backup" 2>/dev/null } function __network_hostname() { local hostname="${1%%.*}" if [ -n "$__DEBIAN_NETWORK_DEVEL_SHOW_MODIFS" ]; then uecho "Setting /etc/hostname to $hostname" else __network_backup /etc/hostname echo "$hostname" >/etc/hostname fi } function __network_mailname() { local host="$1" if [ -n "$__DEBIAN_NETWORK_DEVEL_SHOW_MODIFS" ]; then uecho "Setting /etc/mailname to $host" else __network_backup /etc/mailname echo "$host" >/etc/mailname fi } function __network_fix_exim4() { local host="$1" local tmpfile; ac_set_tmpfile tmpfile "$tmpfile" if [ -n "$__DEBIAN_NETWORK_DEVEL_SHOW_MODIFS" ]; then if testdiff "$tmpfile" /etc/exim4/update-exim4.conf.conf; then uecho "Setting /etc/exim4/update-exim4.conf.conf to:" cat "$tmpfile" | sed 's/^/ /g' 1>&2 else uecho "/etc/exim4/update-exim4.conf.conf: pas de modifications" fi elif testdiff "$tmpfile" /etc/exim4/update-exim4.conf.conf; then __network_backup /etc/exim4/update-exim4.conf.conf cat "$tmpfile" >/etc/exim4/update-exim4.conf.conf update-exim4.conf fi ac_clean "$tmpfile" } function __network_hosts() { local host="$1" hostname="${1%%.*}" ip="$2" local shost="${host//./\\.}" local tmpfile; ac_set_tmpfile tmpfile awkrun "$tmpfile" host="$host" hostname="$hostname" ip="$ip" ' /^[^# \t]/ { gsub("[ \\t]+" host "[ \\t]*", " ") gsub("[ \\t]+" hostname "[ \\t]*", " ") if ($0 ~ /^[^ \t]+[ \t]*$/) next gsub(/[ \t]*$/, "") } { print }' local sip="${ip//./\\.}" shost="${host//./\\.}" if ! quietgrep "^$sip[ $TAB]\\+$shost[ $TAB]\\+$hostname" "$tmpfile"; then sed -i "/^$sip[ $TAB]/d" "$tmpfile" sed -i "1i\\ $ip$TAB$host $hostname" "$tmpfile" fi if [ -n "$__DEBIAN_NETWORK_DEVEL_SHOW_MODIFS" ]; then if testdiff "$tmpfile" /etc/hosts; then uecho "Setting /etc/hosts to:" cat "$tmpfile" | sed 's/^/ /g' 1>&2 else uecho "/etc/hosts: pas de modifications" fi elif testdiff "$tmpfile" /etc/hosts; then __network_backup /etc/hosts cat "$tmpfile" >/etc/hosts fi ac_clean "$tmpfile" } function __network_reset_interfaces() { local tmpfile; ac_set_tmpfile tmpfile echo >"$tmpfile" "\ # This file describes the network interfaces available on your system # and how to activate them. For more information, see interfaces(5). # The loopback network interface auto lo iface lo inet loopback " if [ -n "$__DEBIAN_NETWORK_DEVEL_SHOW_MODIFS" ]; then if testdiff "$tmpfile" /etc/network/interfaces; then uecho "Setting /etc/network/interfaces to:" cat "$tmpfile" | sed 's/^/ /g' 1>&2 else uecho "/etc/network/interfaces: pas de modifications" fi elif testdiff "$tmpfile" /etc/network/interfaces; then __network_backup /etc/network/interfaces cat "$tmpfile" >/etc/network/interfaces fi ac_clean "$tmpfile" } function __network_parse_confip() { # confip --> iface [ipspecs@] [ipspecs] local __npc_tmp splitfsep2 "$1" : "${2:-iface}" __npc_tmp [ -n "$3" ] && array_split "$3" "$__npc_tmp" , [ -n "$4" ] && set_var "$4" "$__npc_tmp" } function __network_parse_ipspec() { # ipspec --> ip suffix gateway local __npi_tmp splitfsep "$1" / "${2:-ip}" __npi_tmp splitfsep "$__npi_tmp" + "${3:-suffix}" "${4:-gateway}" } function __network_parse_confbr() { # confbr --> br [ifaces@] [ifaces] local __npc_tmp splitfsep "$1" : "${2:-br}" __npc_tmp [ -n "$3" ] && array_split "$3" "$__npc_tmp" , [ -n "$4" ] && set_var "$4" "$__npc_tmp" } function __network_fix_confips() { # confips@ local -a __nfc_confips __nfc_ipspecs __nfc_ifaces __nfc_ips local __nfc_confip __nfc_iface __nfc_ipspec __nfc_ip __nfc_suffix __nfc_gateway __nfc_first array_copy __nfc_confips "${1:-confips}" # recenser les interfaces et créer les tableaux __IFACE_ipspecs for __nfc_confip in "${__nfc_confips[@]}"; do __network_parse_confip "$__nfc_confip" __nfc_iface __nfc_ipspecs array_addu __nfc_ifaces "$__nfc_iface" eval "local -a __nfc_${__nfc_iface}_ipspecs" done # puis constuire la liste des adresses IP associées à chaque interface for __nfc_confip in "${__nfc_confips[@]}"; do __network_parse_confip "$__nfc_confip" __nfc_iface __nfc_ipspecs for __nfc_ipspec in "${__nfc_ipspecs[@]}"; do __network_parse_ipspec "$__nfc_ipspec" __nfc_ip __nfc_suffix __nfc_gateway if ! array_contains __nfc_ips "$__nfc_ip"; then [ -n "$__nfc_suffix" ] || __nfc_suffix=24 if [ -z "$__nfc_gateway" ] && array_isempty "__nfc_${__nfc_iface}_ipspecs"; then # premier ipspec, il faut spécifier le gateway __nfc_gateway="$(ipcalc_gateway "$__nfc_ip" "$__nfc_suffix")" fi eval "array_add __nfc_${__nfc_iface}_ipspecs \"$__nfc_ip/$__nfc_suffix${__nfc_gateway:++$__nfc_gateway}\"" array_add __nfc_ips "$__nfc_ip" fi done done # puis construire le tableau final array_new __nfc_confips for __nfc_iface in "${__nfc_ifaces[@]}"; do array_add __nfc_confips "$__nfc_iface:$(array_join "__nfc_${__nfc_iface}_ipspecs" ,)" done array_copy "${1:-confips}" __nfc_confips } function __network_fix_confbrs() { # confbrs@ local -a __nfc_confbrs __nfc_ifaces __nfc_brs __nfc_ips local __nfc_confbr __nfc_br __nfc_iface __nfc_ip __nfc_suffix __nfc_gateway array_copy __nfc_confbrs "${1:-confbrs}" # recenser les bridges et créer les tableaux __BR_ifaces for __nfc_confbr in "${__nfc_confbrs[@]}"; do __network_parse_confbr "$__nfc_confbr" __nfc_br __nfc_ifaces array_addu __nfc_brs "$__nfc_br" eval "local -a __nfc_${__nfc_br}_ifaces" done # puis constuire la liste des interfaces associées à chaque bridge for __nfc_confbr in "${__nfc_confbrs[@]}"; do __network_parse_confbr "$__nfc_confbr" __nfc_br __nfc_ifaces array_extendu "__nfc_${__nfc_br}_ifaces" __nfc_ifaces done # puis construire le tableau final array_new __nfc_confbrs for __nfc_br in "${__nfc_brs[@]}"; do array_add __nfc_confbrs "$__nfc_br:$(array_join "__nfc_${__nfc_br}_ifaces" ,)" done array_copy "${1:-confbrs}" __nfc_confbrs } function __network_resolve_mainiface() { local __nrm_mainiface="${1:-mainiface}" __nrm_mainconfbr="$2" __nrm_mainconfip="$3" [ -n "${!__nrm_mainiface}" ] || __network_parse_confbr "$__nrm_mainconfbr" "$__nrm_mainiface" [ -n "${!__nrm_mainiface}" ] || __network_parse_confip "$__nrm_mainconfip" "$__nrm_mainiface" [ -n "${!__nrm_mainiface}" ] || set_var "$__nrm_mainiface" eth0 } function __network_set_mainip() { # XXX modifier pour en faire set_confips, qui initialise confips en fonction # des adresses actuelles, principale et supplémentaire eval "$(ip addr show dev "$1" | awk "BEGIN { mainipvar = \"${2:-mainip}\" suffixvar = \"${3:-suffix}\" broadcastvar = \"${4:-broadcast}\" "' print mainipvar "=" print suffixvar "=" print broadcastvar "=" } $1 == "inet" { mainip = ""; suffix = ""; broadcast = "" if (match($0, "inet ([^ ]*)/([^ ]*) ", vs) != 0) { mainip = vs[1] suffix = vs[2] } if (match($0, "brd ([^ ]*) ", vs) != 0) { broadcast = vs[1] } print mainipvar "=\"" mainip "\"" print suffixvar "=\"" suffix "\"" print broadcastvar "=\"" broadcast "\"" exit 0 }')" } function __network_set_gateway() { eval "$(route -n | awk "BEGIN { iface = \"$1\" gatewayvar = \"${2:-gateway}\" "' print gatewayvar "=" } $1 == "0.0.0.0" && $8 == iface { print gatewayvar "=\"" $2 "\"" }')" } #BUG" function __network_update_bridge() { # vérifier la configuration (interfaces de ifaces[@] en manual, présence du # bridge, bridge en auto, adresse ip principale statique ou en dhcp, # adresses ip supplémentaires), puis si nécessaire, supprimer l'ancienne # configuration et créer la nouvelle. local inf="$1"; shift local outf="$1"; shift awkrun <"$inf" >"$outf" -f iface="$1" ipspecs[@]="${2:-ipspecs}" ifaces[@]="${3:-ifaces}" ' { print } ' } function __network_update_iface() { # vérifier la configuration (présence de l'interface, interface en auto, # adresse ip principale statique ou en dhcp, adresses ip supplémentaires), # puis si nécessaire, supprimer l'ancienne configuration et créer la # nouvelle. local inf="$1"; shift local outf="$1"; shift local IPSPECS_SCRIPT='BEGIN { method = "dhcp" ips_count = ipspecs_count array_new(ips) array_new(suffixes) array_new(gateways) array_new(have_ups) array_new(have_downs) for (i = 1; i <= ips_count; i++) { if (match(ipspecs[i], "^([^/]*)(/([^+]*))?(\\+(.*))?$", vs) != 0) { ip = vs[1] suffix = vs[3] gateway = vs[5] } else { ip = "" suffix = "" gateway = "" } if (i == 1) { if (ip == "dhcp") method = "dhcp" else method = "static" } if (suffix == "") suffix = "255.255.255.0" ips[i] = ip suffixes[i] = suffix gateways[i] = gateway have_ups[i] = 0 have_downs[i] = 0 } } function indexof_ip(ip, i) { for (i = 1; i = ips_count; i++) { if (ips[i] == ip) { return i } } return 0 } ' awkrun <"$inf" -f iface="$1" ipspecs[@]="${2:-ipspecs}" ' BEGIN { have_hotplug = 0 have_auto = 0 have_iface = 0 have_method = 0 # static ou dhcp in_iface = 0 have_mainip = 0 } '"$IPSPECS_SCRIPT"' ($0 " ") ~ ("^allow-hotplug.* " iface " ") { have_hotplug = 1; next } ($0 " ") ~ ("^(allow-)?auto.* " iface " ") { have_auto = 1; next } $0 ~ ("^iface " iface " inet ") { have_iface = 1 if (($0 " ") ~ (" " method " ")) have_method = 1 in_iface = 1 next } $0 ~ ("^iface ") { in_iface = 0; next; } in_iface && $0 ~ "^[ ]*address " mainip { have_mainip = 1 } in_iface && $0 ~ "^[ ]*up (/sbin/)?ip addr add " { if (match($0, "ip addr add (.*)/.* dev " iface, vs) != 0) { i = indexof_ip(vs[1]) if (i != 0) have_ups[i] = 1 } next } in_iface && $0 ~ "^[ ]*down (/sbin/)?ip addr del " { if (match($0, "ip addr del (.*)/.* dev " iface, vs) != 0) { i = indexof_ip(vs[1]) if (i != 0) have_downs[i] = 1 } next } END { check_hotplug = !have_hotplug check_auto = have_auto check_iface = have_iface && have_method && have_mainip check_supplips = 1 for (i = 1; i <= ipspecs_count; i++) { if (!have_ups[i] || !have_downs[i]) { check_supplips = 0 break } } if (!check_hotplug || !check_auto || !check_iface || !check_supplips) { exit 1 # il faut refaire la configuration } exit 0 # tout est ok } ' && return 1 # il faut refaire la configuration awkrun <"$inf" >"$outf" -f iface="$1" ipspecs[@]="${2:-ipspecs}" ' BEGIN { have_auto = 0 remove_auto = 0 replace_conf = 1 in_iface = 0 } '"$IPSPECS_SCRIPT"' function remove_hotplug_iface() { line = $0 " "; gsub(" " iface " ", " ", line) if (line ~ /^allow-hotplug *$/) { # une seule interface sur la ligne: la commenter print "#" $0 } else { # supprimer l"interface de la ligne sub(/ *$/, "", line) print line } } function remove_auto_iface() { line = $0 " "; gsub(" " iface " ", " ", line) if (line ~ /^(allow-)?auto *$/) { # une seule interface sur la ligne: la commenter print "#" $0 } else { # supprimer l"interface de la ligne sub(/ *$/, "", line) print line } } function get_netmask(suffix) { if (suffix == 0) return "0.0.0.0" else if (suffix == 8) return "255.0.0.0" else if (suffix == 16) return "255.255.0.0" else if (suffix == 24) return "255.255.255.0" else if (suffix == 32) return "255.255.255.255" else return suffix } function write_conf() { replace_conf = 0 if (!have_auto) print "auto " iface print "iface " iface " inet " method if (ips_count > 0 && method == "static") { print " address " ips[1] print " netmask " get_netmask(suffixes[1]) print " gateway " gateways[1] } serial = 0 for (i = 2; i <= ips_count; i++) { print " up ip addr add " ips[i] "/" suffixes[i] " dev " iface " label " iface ":" serial print " down ip addr del " ips[i] "/" suffixes[i] " dev " iface " label " iface ":" serial serial++ } } ($0 " ") ~ ("^allow-hotplug.* " iface " ") { remove_hotplug_iface(); next; } !remove_auto && ($0 " ") ~ ("^(allow-)?auto.* " iface " ") { have_auto = 1; remove_auto = 1; next; } remove_auto && ($0 " ") ~ ("^(allow-)?auto.* " iface " ") { remove_auto_iface(); next; } replace_conf && !in_iface && $0 ~ ("^iface " iface " inet ") { in_iface = 1; next; } replace_conf && in_iface && $0 ~ "^iface " { print; in_iface = 0; write_conf(); next; } { print } END { if (replace_conf) write_conf() } ' return 0 } function network_config() { # (Re)configurer le réseau sur l'hôte courant. # $1 (host) est le nom d'hôte. $2 (confips) est le nom d'un tableau # contenant la configuration des adresses ips pour les interfaces. $3 # (confbrs) est le nom d'un tableau contenant la configuration des bridges à # créer/mettre à jour. $4 (mainiface) est le nom de l'interface # principale. En principe, l'interface principale est le premier bridge # défini ou la première interface définie. # Si un des arguments n'est pas spécifié, il est ignoré. # Le tableau confips doit contenir des définitions d'une des formes # suivantes: # [iface:]address[/suffix][+gateway],... # [iface:]dhcp # La deuxième forme est pour spécifier qu'une interface est configurée par # DHCP. iface vaut par défaut eth0, sauf si une définition de bridge # existe, auquel cas il s'agit du premier bridge défini. Pour chaque # interface, seule la première spécification d'adresse IP tient compte de # l'argument gateway. Les autres spécifications définissent des adresses IP # supplémentaires pour l'interface. # Le tableau brs doit contenir des définitions de la forme suivante: # br:ifaces,... # br est le nom du bridge, e.g. br0. ifaces est une liste d'interfaces # séparées par une virgule. e.g. br0:eth0,eth1 # Bien que ce soit techniquement possible, ce script interdit que l'on # définisse une adresse IP pour une interface faisant partie d'un bridge. urequire ipcalc conf local -a __nc_confips __nc_confbrs [ -n "$2" ] && array_copy __nc_confips "$2" [ -n "$3" ] && array_copy __nc_confbrs "$3" local host="$1" mainiface="$4" local -a confips confbrs array_copy confips __nc_confips array_copy confbrs __nc_confbrs __network_fix_confips confips __network_fix_confbrs confbrs local mainbr # bridge principal local mainip # adresse IP principale de l'interface principale local confbr confip br iface suffix gateway local -a ipspecs __network_resolve_mainiface mainiface "${confbrs[0]}" "${confips[0]}" __network_parse_confbr "${confbrs[0]}" mainbr [ -n "$mainbr" ] || mainbr=br0 if [ -n "${confips[*]}" -o -n "${confbrs[*]}" ]; then # Vérifier qu'une interface avec une adresse IP ne figure pas dans les # bridges à configurer. en profiter pour classer les interfaces par # type: standard et bridges etitle "Configuration des interfaces réseau" local -a ifaces_with_ips ifaces local -a brifaces ifaces_with_ips=() for confip in "${confips[@]}"; do __network_parse_confip "$confip" iface array_addu ifaces_with_ips "$iface" done for confbr in "${confbrs[@]}"; do __network_parse_confbr "$confbr" br ifaces array_add brifaces "$br" for iface in "${ifaces[@]}"; do if array_contains ifaces_with_ips "$iface"; then eerror "$iface: Impossible de configurer une interface dans un bridge ET avec une adresse IP" return 1 fi done done local -a confstdips confbrips for confip in "${confips[@]}"; do __network_parse_confip "$confip" iface if array_contains brifaces "$iface"; then array_add confbrips "$confip" else array_add confstdips "$confip" fi done # maintenant, configurer /etc/network/interfaces: faire une copie de travail local interfaces workfile ac_set_tmpfile interfaces ac_set_tmpfile workfile cat /etc/network/interfaces >"$interfaces" # configurer chaque bridge local -a tmpifaces local tmpbr for confip in "${confbrips[@]}"; do __network_parse_confip "$confip" br ipspecs [ -n "$br" ] || br="$mainbr" if [ -z "$mainip" -a "$br" == "$mainiface" ]; then __network_parse_ipspec "${ipspecs[0]}" mainip suffix gateway fi ifaces=() for confbr in "${confbrs[@]}"; do __network_parse_confbr "$confbr" tmpbr tmpifaces if [ "$tmpbr" == "$br" ]; then array_copy ifaces "$tmpifaces" break fi done if __network_update_bridge "$interfaces" "$workfile" "$br" ipspecs ifaces; then cat "$workfile" >"$interfaces" fi done # configurer chaque interface classique for confip in "${confstdips[@]}"; do __network_parse_confip "$confip" iface ipspecs [ -n "$iface" ] || iface="$mainiface" if [ -z "$mainip" -a "$iface" == "$mainiface" ]; then __network_parse_ipspec "${ipspecs[0]}" mainip suffix gateway fi if __network_update_iface "$interfaces" "$workfile" "$iface" ipspecs; then cat "$workfile" >"$interfaces" fi done # Fin de traitement if [ -n "$__DEBIAN_NETWORK_DEVEL_SHOW_MODIFS" ]; then if testdiff "$interfaces" /etc/network/interfaces; then uecho "Setting /etc/network/interfaces to:" cat "$interfaces" | sed 's/^/ /g' 1>&2 else uecho "/etc/network/interfaces: pas de modifications" fi elif testdiff "$interfaces" /etc/network/interfaces; then __network_backup /etc/network/interfaces cat "$interfaces" >/etc/network/interfaces fi ac_clean "$interfaces" "$workfile" eend fi # configurer le nom d'hôte if [ -n "$host" ]; then host="$(ipcalc_fqdn_maybe "$host")" etitle "Configuration du nom d'hôte: $host" # si on ne spécifie pas l'adresse ip principale, la résoudre ici. ceci # est nécessaire pour mettre à jour /etc/hosts if [ "$mainip" == dhcp ]; then # si l'adresse obtenue est en DHCP, ne pas mettre à jour /etc/hosts mainip= elif [ -z "$mainip" ]; then local -a mainips resolv_ips mainips "$host" [ -n "${mainips[*]}" ] || { eerror "$host: Impossible de résoudre ce nom d'hôte" return 1 } mainip="${mainips[0]}" fi estep "/etc/hostname" __network_hostname "$host" estep "/etc/mailname" __network_mailname "$host" if [ -f "/etc/exim4/update-exim4.conf.conf" ]; then estep "/etc/exim4/update-exim4.conf.conf" __network_fix_exim4 "$host" fi # XXX tester la présence de postfix, et si nécessaire, modifier la # configuration if [ -n "$mainip" ]; then estep "/etc/hosts" __network_hosts "$host" "$mainip" fi eend fi return 0 }