#!/bin/bash # update-virtualhosts Virtual hosts updater # (C) 2019 Nirgal Vourgère # GPL-3 DIRS=() VERBOSE=1 # 0=only_warnings 1=notice 2=debug COMMANDLINE="$0 $@" OUTPUT="" parsearg() { _OPT="${1%=?*}" _VAL="${1#?*=}" } usage() { #echo $'\n' $@ $'\n' echo "Usage: $0 [options] basedir..." echo " -h|--help Display that help" echo " --verbose-lvl=level Define verbosity level. Defaults to $VERBOSE." echo " 0: Only warnings" echo " 1: Include notices" echo " 2: debug" echo " Fell free to ignore STDERR" echo " -q|--quiet Identical to --verbose-lvl=0" echo " -d|--debug Identical to --verbose-lvl=2" } for arg in "$@"; do parsearg $arg case $_OPT in --verbose-lvl) VERBOSE=$_VAL continue ;; -q|--quiet) VERBOSE=0 continue ;; -d|--debug) VERBOSE=2 continue ;; -h|--help) usage exit 0 ;; -*) echo "Unknown option $_OPT" >&2 usage >&2 exit 22 ;; *) DIRS+=("$_OPT") continue ;; esac done NORMAL=`tput sgr0 2>/dev/null` RED=`tput setaf 1 2>/dev/null` GREEN=`tput setaf 2 2>/dev/null` log() { echo "$@" >&2 } log_error() { log $RED"$@"$NORMAL } log_info() { if (( $VERBOSE > 0 )) then log "$@" fi } log_debug() { if (( $VERBOSE > 1 )) then log "$@" fi } output() { if [[ -n "$OUTPUT" ]] then echo "$@" >> $OUTPUT else echo "$@" # STDOUT fi } main() { basedir="$1" # ====================================================================== # Step 1 # Parse $INPUT that reads like # www1.lan < example.com www.example.com # 192.168.0.11 < example.net www.example.net # fdce:266b:c77a::b < example.net www.example.net # and fill these 6 vars: redirections4_src=() # ( "example.com www.example.com" "example.net www.example.net" ) redirections4_dst=() # ( "192.168.0.10" "192.168.0.11" ) redirections4_dstname=() # ( "www1.lan" "192.168.0.11" ) redirections6_src=() # ( "example.com www.example.com" "example.net www.example.net" ) redirections6_dst=() # ( "fdce:266b:c77a::a" "fdce:266b:c77a::b" ) redirections6_dstname=() # ( "www1.lan" "fdce:266b:c77a::b" ) redirectionsn_src=() # ( "example.com www.example.com" ) redirectionsn_dst=() # ( "www1.lan" ) if test -r "$basedir/config" then log_info "Reading $basedir/config" source "$basedir/config" else log_error "Cannot read $basedir/config. Skiping." return fi if [[ -z "$INPUT" ]] then log_error "Variable INPUT is empty. Skiping $basedir." return fi while read line do if [[ "$line" =~ ^([^\>]*)[[:space:]]*\>[[:space:]]*(.*)$ ]] then sources="${BASH_REMATCH[1]}" destination=${BASH_REMATCH[2]} if [[ "$destination" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] then log_info "Virtual host(s) $sources redirect(s) to ipv4 $destination, no ipv6." redirections4_src+=("$sources") redirections4_dst+=($destination) redirections4_dstname+=($destination) elif [[ "$destination" =~ ^[0-9a-f:]*:[0-9a-f:]* ]] then log_info "Virtual host(s) $sources redirect(s) to ipv6 $destination, no ipv4." redirections6_src+=("$sources") redirections6_dst+=("$destination") redirections6_dstname+=("$destination") else msg="Virtual host(s) $sources redirect(s) to host $destination: " redirectionsn_src+=("$sources") redirectionsn_dst+=("$destination") ipv4=$(getent ahostsv4 "$destination" | cut -d ' ' -f 1 | sort -u) ipv6=$(getent ahostsv6 "$destination" | cut -d ' ' -f 1 | sort -u) if (( $(echo "$ipv4" | wc -l) > 1 )) then log_error "ERROR: $destination has several ipv4 addresses. This is not supported. ipv4 disabled for this domain." ipv4="" fi if (( $(echo "$ipv6" | wc -l) > 1 )) then log_error "ERROR: $destination has several ipv6 addresses. This is not supported. ipv6 disabled for this domain." ipv6="" fi if [[ -n "$ipv4" ]] then msg+="ipv4=$ipv4, " redirections4_src+=("$sources") redirections4_dst+=($ipv4) redirections4_dstname+=($destination) else msg+="no ipv4," fi if [[ -n "$ipv6" ]] then msg+="ipv6=$ipv6." redirections6_src+=("$sources") redirections6_dst+=($ipv6) redirections6_dstname+=($destination) else msg+="no ipv6." fi log_info "$msg" if [[ -z "$ipv4" && -z "$ipv6" && ! -d "$basedir/hostname" ]] then log_error "ERROR: $destination has no ipv4 nor ipv6." fi fi else log_error "ERROR: syntax error while parsing $line" fi done < "$INPUT" #log_debug "=============================================================" # #for (( i=0; i<${#redirections4_dst[@]}; i++ )) #do # log_debug "${redirections4_dst[$i]}" # sources=${redirections4_src[$i]} # for s in ${sources[*]} # do # log_debug " if $s" # done #done # ====================================================================== # Step 2: write the file if [[ -n "$OUTPUT" ]] then log_debug "Backup'ing $OUTPUT" mv "$OUTPUT" "$OUTPUT.bak" log_info "Writing to $OUTPUT." echo -n > "$OUTPUT" fi log_debug "=============================================================" output "############################################################################" output "# DO NOT EDIT THAT FILE" output "# Notice: That file was generated using:" output "# $COMMANDLINE" output "# See $basedir/config" output "############################################################################" if [[ -f "$basedir/prolog" ]] then fragment=$(cat "$basedir/prolog") output "${fragment}" fi if [[ -d "$basedir/ipv4" ]] then for fragmentdir in $( find "$basedir/ipv4" -mindepth 1 -maxdepth 1 -type d | sort ) do log_debug "Processing $fragmentdir" if test -f "$fragmentdir/prolog" then fragment=$(cat "$fragmentdir/prolog") output "${fragment}" fi for (( i=0; i<${#redirections4_dst[@]}; i++ )) do destination="${redirections4_dst[$i]}" dstname="${redirections4_dstname[$i]}" log_debug "Processing $fragmentdir for destination $destination" if test -f "$fragmentdir/destination" then fragment=$(cat "$fragmentdir/destination") fragment="${fragment//[$]destination/$destination}" fragment="${fragment//[$]dstname/$dstname}" output "${fragment}" fi sources=${redirections4_src[$i]} for source in ${sources[*]} do if test -f "$fragmentdir/line_$source" then fragment=$(cat "$fragmentdir/line_$source") fragment="${fragment//[$]destination/$destination}" fragment="${fragment//[$]dstname/$dstname}" fragment="${fragment//[$]source/$source}" output "${fragment}" elif test -f "$fragmentdir/line" then fragment=$(cat "$fragmentdir/line") fragment="${fragment//[$]destination/$destination}" fragment="${fragment//[$]dstname/$dstname}" fragment="${fragment//[$]source/$source}" output "${fragment}" fi done done done fi if [[ -d "$basedir/ipv6" ]] then for fragmentdir in $( find "$basedir/ipv6" -mindepth 1 -maxdepth 1 -type d | sort ) do log_debug "processing $fragmentdir" if test -f "$fragmentdir/prolog" then fragment=$(cat "$fragmentdir/prolog") output "${fragment}" fi for (( i=0; i<${#redirections6_dst[@]}; i++ )) do destination="${redirections6_dst[$i]}" dstname="${redirections6_dstname[$i]}" log_debug "processing $fragmentdir for destination $destination" if test -f "$fragmentdir/destination" then fragment=$(cat "$fragmentdir/destination") fragment="${fragment//[$]destination/$destination}" fragment="${fragment//[$]dstname/$dstname}" output "${fragment}" fi sources=${redirections6_src[$i]} for source in ${sources[*]} do if test -f "$fragmentdir/line_$source" then fragment=$(cat "$fragmentdir/line_$source") fragment="${fragment//[$]destination/$destination}" fragment="${fragment//[$]dstname/$dstname}" fragment="${fragment//[$]source/$source}" output "${fragment}" elif test -f "$fragmentdir/line" then fragment=$(cat "$fragmentdir/line") fragment="${fragment//[$]destination/$destination}" fragment="${fragment//[$]dstname/$dstname}" fragment="${fragment//[$]source/$source}" output "${fragment}" fi done done done fi if [[ -d "$basedir/hostname" ]] then for fragmentdir in $( find "$basedir/hostname" -mindepth 1 -maxdepth 1 -type d | sort ) do log_debug "processing $fragmentdir" if test -f "$fragmentdir/prolog" then fragment=$(cat "$fragmentdir/prolog") output "${fragment}" fi for (( i=0; i<${#redirectionsn_dst[@]}; i++ )) do destination="${redirectionsn_dst[$i]}" log_debug "processing $fragmentdir for destination $destination" if test -f "$fragmentdir/destination" then fragment=$(cat "$fragmentdir/destination") fragment="${fragment//[$]destination/$destination}" output "${fragment}" fi sources=${redirectionsn_src[$i]} for source in ${sources[*]} do if test -f "$fragmentdir/line_$source" then fragment=$(cat "$fragmentdir/line_$source") fragment="${fragment//[$]destination/$destination}" fragment="${fragment//[$]source/$source}" output "${fragment}" elif test -f "$fragmentdir/line" then fragment=$(cat "$fragmentdir/line") fragment="${fragment//[$]destination/$destination}" fragment="${fragment//[$]source/$source}" output "${fragment}" fi done done done fi # ====================================================================== # Step 3: run the hook if [[ -n "$HOOK" ]] then read -p "$HOOK ? (y/n): " -n 1 confirm echo >&2 if [[ "$confirm" = "y" ]] then log_info "Running hook: $HOOK" $HOOK else log_debug "Skipping hook: $HOOK" fi fi } for dir in "${DIRS[@]}" do # Reset some vars that are supposed to be in $dir/condig # We don't want to keep the value from previous dir INPUT="" OUTPUT="" HOOK="" main "$dir" done # vim: set ts=4 noet: