#!/usr/bin/bash
#
# Server Setup
# Copyright (C) 2013-2024 by Thomas Dreibholz
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# Contact: dreibh@simula.no


# ###### Get existing interface configuration (if available) ################
MAY_BE_CONTROLBOX=0
parse-interface-configuration ()
{
   local inputFile="$1"
   local interface="$2"
   local outputFile="$3"

   local found=0
   local writeMode=0
   while read inputLine ; do
      if [[ $inputLine =~ ^auto([ \t]*)${interface}$ ]] ; then
         echo -e "\n# ====== Additional Interface $interface ======" >>$outputFile
         echo "$inputLine" >>$outputFile
         found=1
      else
         # ====== New interface? =========================================
         if [[ $inputLine =~ ^iface([ \t]*)|^([ \t]*)$ ]] ; then
            if [[ $inputLine =~ ^iface([ \t]*)${interface}(\.[0-9]*)([ \t]) ]] ; then
               found=2   # Found the interface, but the interface has VLANs => do nothing!
            elif [[ $inputLine =~ ^iface([ \t]*)${interface}([ \t]) ]] ; then
               writeMode=1
               echo "$inputLine" | sed -e "s/^[ \t][ \t]*/\t/g" >>$outputFile
            else
               if [ $writeMode -eq 1 ] ; then
                  writeMode=0
               fi
            fi

         # ====== Write interface configuration ==========================
         elif [ $writeMode -eq 1 ] ; then
            if [[ $inputLine =~ ^([ \t]*)metric([ \t]*)([0-9]*)([ \t]*)(.*)$ ]] ; then
               metric=${BASH_REMATCH[3]}
               if [ "$metric" -le 5 ] ; then
                  # Non-NorNet metric (< default provider) => may be control box!
                  result=2
                  MAY_BE_CONTROLBOX=1
               fi
            elif [[ $inputLine =~ ^([ \t]*)gateway([ \t]*)([0-9]*)([ \t]*)(.*)$ ]] ; then
               # Interface has a gateway => may be control box!
               MAY_BE_CONTROLBOX=1
            fi
            echo -e "\t$inputLine" >>$outputFile
         fi
      fi
   done <$inputFile

   return $found
}



# ###### Main program #######################################################

# ====== Get arguments from storage, if they are not provided ===============
LAST_PARAMETERS_FILE="/etc/nornet/Server-Setup.args"
if [ $# -lt 8 -o \
     "$3" != "-site" -o \
     "$5" != "-interface" -o \
     "$7" != "-providers" ] ; then
   echo >&2 "Usage: $0 FQDN node_index -site site_index -interface interface_name -providers default_provider[,provider2,...] [-controlbox] [-bridge interface_name] [-ntp server1[,server2,...]]"
   if [ -e "$LAST_PARAMETERS_FILE" ] ; then
      LAST_PARAMETERS_ARGS=`cat $LAST_PARAMETERS_FILE`
      echo -en "\x1b[33mRun: sudo $0 $LAST_PARAMETERS_ARGS? [yes/no]?\x1b[0m "
      read -er applyChanges
      if [ "$applyChanges" == "yes" -o "$applyChanges" == "y" ] ; then
         bash -c "$0 $LAST_PARAMETERS_ARGS"
         exit $?
      fi
   fi
   exit 1
fi

# ====== Find distribution variant ==========================================
Variant=""
if [ -e /usr/bin/apt-get ] ; then
   Variant="Debian"
elif [ -e /usr/bin/dnf ] ; then
   Variant="Fedora"
elif [ -e /etc/freebsd-update.conf ] ; then
   Variant="FreeBSD"
else
   echo >&2 "ERROR: Unknown/unsupported operating system? Cannot determine how to configure network!"
   exit 1
fi

# ====== Initialise =========================================================
AllParameters="$@"
FQDN="$1"
NodeIndex="$2"
SiteIndex="$4"
InterfaceName="$6"
ProviderList="$8"
ControlBoxMode=""
BridgeInterface=""
NTPServers=""
MakeServerConfigurationPath=""
shift ; shift ; shift ; shift ; shift ; shift ; shift ; shift
while [ $# -gt 0 ] ; do
   if [ "$1" = "-controlbox" ] ; then
      ControlBoxMode="-controlbox"
   elif [ "$1" = "-bridge" ] ; then
      BridgeInterface="$2"
      if [ "$BridgeInterface" == "" ] ; then
         echo >&2 "ERROR: No bridge interface given!"
         exit 1
      fi
      shift
   elif [ "$1" = "-ntp" ] ; then
      NTPServers="$2"
      if [ "$NTPServers" == "" ] ; then
         echo >&2 "ERROR: No NTP server(s) given!"
         exit 1
      fi
      shift
   elif [ "$1" = "-from-current-dir" ] ; then
      MakeServerConfigurationPath="`pwd`/"
      echo "NOTE: Running Make-Server-Configuration from $MakeServerConfigurationPath!"
   else
      echo >&2 "ERROR: Bad option $1!"
      exit 1
   fi
   shift
done

# ====== Check for /etc/nornet and /etc/nornet/nornetapi-constants ==========
if [ ! -d /etc/nornet/ -o ! -e /etc/nornet/nornetapi-constants ] ; then
   if [ "$MakeServerConfigurationPath" != "" ] ; then
      if [ -e nornetapi-constants ] ; then
         echo -en "\x1b[33mInstall /etc/nornet/nornetapi-constants? [yes/no]?\x1b[0m "
         read -er applyChanges
         if [ "$applyChanges" == "yes" -o "$applyChanges" == "y" ] ; then
            mkdir /etc/nornet
            cp nornetapi-constants /etc/nornet/nornetapi-constants
         fi
      fi
   fi
   if [ ! -d /etc/nornet/ -o ! -e /etc/nornet/nornetapi-constants ] ; then
      echo >&2 "ERROR: /etc/nornet/ or /etc/nornet/nornetapi-constants do not exist!"
      exit 1
   fi
fi

# ====== Create network configuration =======================================
cd /root   # Ensure that configurations a written in /root!

options=""
if [ "$BridgeInterface" != "" ] ; then
   options="$options -bridge $BridgeInterface"
fi
if [ "$NTPServers" != "" ] ; then
   options="$options -ntp $NTPServers"
fi
${MakeServerConfigurationPath}Make-Server-Configuration "$Variant" "$FQDN" "$NodeIndex" -site "$SiteIndex" -interface "$InterfaceName" -providers "$ProviderList" $ControlBoxMode $options || exit 1

if [ "$Variant" = "Debian" ] ; then
   AllEthernetInterfaces=`ip link show | awk '/^([0-9]*:) ([a-zA-Z0-9\-\.@]+):/ { print $2 }' | grep "^eth" | sed -e "s/:$//" -e "s/@.*$//" | xargs`
   for interface in $AllEthernetInterfaces ; do
      if [ "$interface" != "lo" -a "$interface" != "$InterfaceName" ] ; then
         if parse-interface-configuration /etc/network/interfaces $interface interfaces.new ; then
            (
               echo -e "\n# ====== Additional Interface $interface ======"
               echo "auto $interface"
               echo "iface $interface inet static"
               echo -e "\taddress 0.0.0.0"
               echo -e "\tnetmask 0.0.0.0"
            ) >>interfaces.new
         fi
      fi
   done
fi

if [ $MAY_BE_CONTROLBOX -ne 0 ] ; then
   if [ "$ControlBoxMode" != "-controlbox" ] ; then
      echo -e  "\n\x1b[31mWARNING: Node has non-NorNet interface with gateway or small metric!\x1b[0m"
      echo -en "\x1b[31mWARNING: Are you really sure that this node is NOT a Control Box? [confirm/no]\x1b[0m? "
      read -er applyChanges
      if [ "$applyChanges" != "confirm" -a "$applyChanges" != "c" ] ; then
         exit 1
      fi
   fi
fi


# ====== Create other configurations ========================================
if [ "$Variant" != "FreeBSD" ] ; then
   ( cat /etc/modules | grep -v "^dummy" ; echo "dummy numdummies=1" ) >modules.new
   ( cat /etc/default/rcS | sed -e "s/^FSCKFIX=no/FSCKFIX=yes/g" ) >rcS.new
fi


# ====== Compare new configurations to old ones =============================
echo "------ BEGIN OF DIFFS ------"

if [ "$Variant" != "FreeBSD" ] ; then
   echo "------ /etc/hostname:"
   colordiff /etc/hostname hostname.new
   if [ -e /etc/modules ] ; then
      echo "------ /etc/modules:"
      colordiff /etc/modules modules.new
   else
      cat modules.new
   fi
   if [ -e /etc/default/rcS ] ; then
      echo "------ /etc/default/rcS:"
      colordiff /etc/default/rcS rcS.new
   fi
   echo "------ /etc/sysctl.d/90-nornet.conf:"
   if [ -e /etc/sysctl.d/90-nornet.conf ] ; then
      colordiff /etc/sysctl.d/90-nornet.conf sysctl.new
   else
      cat sysctl.new
   fi
   echo "------ /etc/profile.d/proxy.sh:"
   if [ -e /etc/profile.d/proxy.sh ] ; then
      colordiff /etc/profile.d/proxy.sh proxy.sh.new
   else
      cat proxy.sh.new
   fi
   echo "------ /etc/profile.d/proxy.csh:"
   if [ -e /etc/profile.d/proxy.csh ] ; then
      colordiff /etc/profile.d/proxy.csh proxy.csh.new
   else
      cat proxy.csh.new
   fi
fi

echo "------ /etc/hosts:"
colordiff /etc/hosts hosts.new

if [ "$Variant" = "Debian" ] ; then
   echo "------ /etc/apt/apt.conf.d/10-proxy:"
   if [ -e /etc/apt/apt.conf.d/10-proxy ] ; then
      colordiff /etc/apt/apt.conf.d/10-proxy apt-proxy.new
   else
      cat apt-proxy.new
   fi
   if [ -e interfaces.new -a  -e /etc/network/interfaces ] ; then
      echo "------ /etc/network/interfaces:"
      colordiff /etc/network/interfaces interfaces.new
   fi
elif [ "$Variant" = "Fedora" ] ; then
   if [ -e ifcfg.new -a  -e /etc/sysconfig/network-scripts/ifcfg-$InterfaceName ] ; then
      echo "------ /etc/sysconfig/network-scripts/ifcfg-$InterfaceName:"
      colordiff /etc/sysconfig/network-scripts/ifcfg-$InterfaceName ifcfg.new
   fi
   if [ -e ifcfg-bridge.new -a  -e /etc/sysconfig/network-scripts/ifcfg-$BridgeInterface ] ; then
      echo "------ /etc/sysconfig/network-scripts/ifcfg-$BridgeInterface:"
      colordiff /etc/sysconfig/network-scripts/ifcfg-$BridgeInterface ifcfg-bridge.new
      if [ -e route.new -a  -e /etc/sysconfig/network-scripts/route-$BridgeInterface ] ; then
         echo "------ /etc/sysconfig/network-scripts/route-$BridgeInterface:"
         colordiff /etc/sysconfig/network-scripts/route-$BridgeInterface route.new
      fi
      if [ -e route6.new -a  -e /etc/sysconfig/network-scripts/route6-$BridgeInterface ] ; then
         echo "------ /etc/sysconfig/network-scripts/route6-$BridgeInterface:"
         colordiff /etc/sysconfig/network-scripts/route6-$BridgeInterface route6.new
      fi
   else
      if [ -e route.new -a  -e /etc/sysconfig/network-scripts/route-$InterfaceName ] ; then
         echo "------ /etc/sysconfig/network-scripts/route-$InterfaceName:"
         colordiff /etc/sysconfig/network-scripts/route-$InterfaceName route.new
      fi
      if [ -e route6.new -a  -e /etc/sysconfig/network-scripts/route6-$InterfaceName ] ; then
         echo "------ /etc/sysconfig/network-scripts/route6-$InterfaceName:"
         colordiff /etc/sysconfig/network-scripts/route6-$InterfaceName route6.new
      fi
   fi
elif [ "$Variant" = "FreeBSD" ] ; then
   echo "------ /etc/resolv.conf:"
   if [ -e /etc/resolv.conf ] ; then
      colordiff /etc/resolv.conf resolv.conf.new
   else
      cat resolv.conf.new
   fi

   echo "------ /etc/rc.conf:"
   SED_FILE=`mktemp`
   rm -f $SED_FILE rc.conf.updated
   cat rc.conf.new | (
      while read line ; do
         if [[ "$line" =~ ^([a-zA-Z0-9_]*)=(.*)$ ]] ; then
            echo "/${BASH_REMATCH[1]}/d" >>$SED_FILE
         fi
      done
   )
   cp rc.conf.new rc.conf.updated && sed -f $SED_FILE </etc/rc.conf >>rc.conf.updated || rm -f rc.conf.updated
   if [ ! -e rc.conf.updated ] ; then
      echo >&2 "ERROR: Merging rc.conf updates failed!"
      exit 1
   fi
   rm -f $SED_FILE
   colordiff /etc/rc.conf rc.conf.updated
fi

echo "------ /etc/ntp.conf:"
if [ -e /etc/ntp.conf ] ; then
   colordiff /etc/ntp.conf ntp.new
else
   cat ntp.new
fi

if [ "$Variant" != "FreeBSD" ] ; then
   echo "------ /etc/auto.master:"
   if [ -e /etc/auto.master ] ; then
      colordiff /etc/auto.master auto.master
   else
      cat auto.master
   fi
else
   echo "------ /etc/auto_master:"
   if [ -e /etc/auto_master ] ; then
      colordiff /etc/auto_master auto.master
   else
      cat auto.master
   fi
fi
echo "------ /etc/auto.nfs:"
if [ -e /etc/auto.nfs ] ; then
   colordiff /etc/auto.nfs auto.nfs
else
   cat auto.nfs
fi
echo "------ END OF DIFFS ------"


# ====== Ask for confirmation and apply changes =============================
echo -en "\x1b[33mApply changes (current configuration files will be backuped to <file>~)?\x1b[0m [yes/no]? "
read -er applyChanges
if [ "$applyChanges" != "yes" -a "$applyChanges" != "y" ] ; then
   echo "Skipped!"
else
   if [ "$Variant" != "FreeBSD" ] ; then
      cp /etc/hostname /etc/hostname~
      cp  hostname.new /etc/hostname
      hostname `cat /etc/hostname`
   fi

   cp /etc/hosts /etc/hosts~
   cp  hosts.new /etc/hosts

   if [ -e /etc/ntp.conf ] ; then
      cp /etc/ntp.conf /etc/ntp.conf~
   fi
   cp ntp.new /etc/ntp.conf

   if [ "$Variant" != "FreeBSD" ] ; then
      if [ -e /etc/profile.d/proxy.sh ] ; then
         cp /etc/profile.d/proxy.sh /etc/profile.d/proxy.sh~
      fi
      cp proxy.sh.new /etc/profile.d/proxy.sh
      if [ -e /etc/profile.d/proxy.csh ] ; then
         cp /etc/profile.d/proxy.csh /etc/profile.d/proxy.csh~
      fi
      cp proxy.csh.new /etc/profile.d/proxy.csh
   fi

   if [ "$Variant" = "Debian" ] ; then
      cp modules.new /etc/modules
      cp rcS.new /etc/default/rcS
      cp sysctl.new /etc/sysctl.d/90-nornet.conf
      cp /etc/network/interfaces /etc/network/interfaces~
      cp interfaces.new /etc/network/interfaces

      if [ -e /etc/apt/apt.conf.d/10-proxy ] ; then
         cp /etc/apt/apt.conf.d/10-proxy /etc/apt/apt.conf.d/10-proxy~
      fi
      cp apt-proxy.new /etc/apt/apt.conf.d/10-proxy
      if [ -e /etc/udev/rules.d/70-persistent-net.rules ] ; then
         if ! ifconfig eth0 >/dev/null 2>&1 ; then
            echo "eth0 does not exist => Deleting /etc/udev/rules.d/70-persistent-net.rules"
            rm -f /etc/udev/rules.d/70-persistent-net.rules
            echo "*** Interfaces should be remapping during next boot! ***"
         fi
      fi
   elif [ "$Variant" = "Fedora" ] ; then
      cp sysctl.new /etc/sysctl.d/90-nornet.conf
      if [ -e /etc/sysconfig/network-scripts/ifcfg-$InterfaceName ] ; then
         cp /etc/sysconfig/network-scripts/ifcfg-$InterfaceName /etc/sysconfig/network-scripts/ifcfg-$InterfaceName~
      fi
      if [ "$BridgeInterface" != "" ] ; then
         if [ -e /etc/sysconfig/network-scripts/ifcfg-$BridgeInterface ] ; then
            cp /etc/sysconfig/network-scripts/ifcfg-$BridgeInterface /etc/sysconfig/network-scripts/ifcfg-$BridgeInterface~
         fi
         if [ -e /etc/sysconfig/network-scripts/route-$BridgeInterface ] ; then
            cp /etc/sysconfig/network-scripts/route-$BridgeInterface /etc/sysconfig/network-scripts/route-$BridgeInterface~
         fi
         if [ -e /etc/sysconfig/network-scripts/route6-$BridgeInterface ] ; then
            cp /etc/sysconfig/network-scripts/route6-$BridgeInterface /etc/sysconfig/network-scripts/route6-$BridgeInterface~
         fi
      else
         if [ -e /etc/sysconfig/network-scripts/route-$InterfaceName ] ; then
            cp /etc/sysconfig/network-scripts/route-$InterfaceName /etc/sysconfig/network-scripts/route-$InterfaceName~
         fi
         if [ -e /etc/sysconfig/network-scripts/route6-$InterfaceName ] ; then
            cp /etc/sysconfig/network-scripts/route6-$InterfaceName /etc/sysconfig/network-scripts/route6-$InterfaceName~
         fi
      fi
      rm -f /etc/sysconfig/network-scripts/ifcfg-$InterfaceName* >/dev/null 2>&1 || true
      cp ifcfg.new /etc/sysconfig/network-scripts/ifcfg-$InterfaceName
      if [ "$BridgeInterface" != "" ] ; then
         rm -f /etc/sysconfig/network-scripts/ifcfg-$BridgeInterface* >/dev/null 2>&1 || true
         cp ifcfg-bridge.new /etc/sysconfig/network-scripts/ifcfg-$BridgeInterface
         cp route.new  /etc/sysconfig/network-scripts/route-$BridgeInterface
         cp route6.new /etc/sysconfig/network-scripts/route6-$BridgeInterface
      else
         cp route.new  /etc/sysconfig/network-scripts/route-$InterfaceName
         cp route6.new /etc/sysconfig/network-scripts/route6-$InterfaceName
      fi
   elif [ "$Variant" = "FreeBSD" ] ; then
      cp rc.conf.updated /etc/rc.conf

      cp /etc/resolv.conf /etc/resolv.conf~
      cp resolv.conf.new /etc/resolv.conf

      cp /etc/auto_master /etc/auto_master~
      cp auto.master /etc/auto_master
   fi

   if [ "$Variant" != "FreeBSD" ] ; then
      cp /etc/auto.master /etc/auto.master~
      cp auto.master /etc/auto.master
   fi
   if [ -e /etc/auto.nfs ] ; then
      cp /etc/auto.nfs /etc/auto.nfs~
   fi
   cp auto.nfs /etc/auto.nfs

   if [ "$Variant" != "FreeBSD" ] ; then
      sysctl -f
      sysctl -f /etc/sysctl.d/90-nornet.conf
   fi
   if [ "$Variant" = "Fedora" ] ; then
      chkconfig network on
      systemctl start network.service
   fi
   service networking restart >/dev/null 2>&1

   if [ "$Variant" = "Debian" ] ; then
      service ntp restart >/dev/null 2>&1
   elif [ "$Variant" = "Fedora" ] ; then
      rpm -q ntp >/dev/null || yum install -y ntp
      systemctl enable ntpd.service
      systemctl start ntpd.service
   elif [ "$Variant" = "FreeBSD" ] ; then
      /etc/netstart
   fi

   if [ "$Variant" = "Fedora" ] ; then
      rpm -q autofs  >/dev/null || yum install -y autofs
      systemctl enable autofs.service
   fi
   service autofs restart >/dev/null 2>&1

   if [ "$Variant" = "Fedora" ] ; then
      rpm -q openssh-server  >/dev/null || yum install -y openssh-server
      systemctl enable sshd.service
      systemctl start sshd.service
   fi
fi
rm -f hostname.new hosts.new ntp.new modules.new rcS.new sysctl.new interfaces.new ifcfg.new ifcfg-bridge.new route.new route6.new auto.master auto.nfs

# Store parameters for later re-run.
echo "$AllParameters" >$LAST_PARAMETERS_FILE


# ====== Keyboard configuration =============================================
if [ "$Variant" != "FreeBSD" ] ; then
   echo -en "\x1b[33mConfigure keyboard? [yes/no]\x1b[0m? "
   read -er applyChanges
   if [ "$applyChanges" != "yes" -a "$applyChanges" != "y" ] ; then
      echo "Skipped!"
   else
      if [ "$Variant" = "Debian" ] ; then
         dpkg-reconfigure keyboard-configuration
      elif [ "$Variant" = "Fedora" ] ; then
         rpm -q system-config-keyboard || yum install -y system-config-keyboard
         system-config-keyboard
      fi
   fi
fi


# ====== System maintenance =================================================
echo -en "\x1b[33mRun system maintenance script? [yes/no]\x1b[0m? "
read -er applyChanges
if [ "$applyChanges" != "yes" -a "$applyChanges" != "y" ] ; then
   echo "Skipped!"
else
   ${MakeServerConfigurationPath}System-Maintenance
fi
