--- /dev/null
+#!/bin/bash
+# update-virtualhosts Virtual hosts updater
+# (C) 2019 Nirgal Vourgère <nirgal@debian.org>
+# 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:
+