#!/bin/bash
#******************************************************************
#$ident: @(#) netenv 0.94 Time-stamp: <03/07/31 21:35:06 bav> $
#******************************************************************
#                            Gerd Bavendiek  bav@epost.de  97-05-00
#
# This script is used to set up a file containing information about
# the actual network environment. This can be esp. useful for a
# laptop being used in multiple environments: at home, in the office,
# at a customer site.
#
# After the comment section has become quite large, I swapped it to a
# netenv.html file. 
#
# netenv will fail when there is no dialog(1) or gdialog found !
#
# As of version 0.81 netenv will try to use "math" when setting up a
# new environment. math is work of Bruce Perens <Bruce@Pixar.com>. To
# avoid conflicts with e.g. Mathematica it has been renamed to trpnc.
#-------------------------------------------------------------------
NETENV_VERSION="0.94"

# Security check
NO_NOT_BY_ROOT_OWNED_FILES=`find /etc/netenv ! -user 0 | wc -l | awk '{print $1}'`
NO_GROUP_WRITABLE_FILES=`find /etc/netenv -type f -perm -060 | wc -l | awk '{print $1}'`
NO_WORLD_WRITABLE_FILES=`find /etc/netenv -type f -perm -006 | wc -l | awk '{print $1}'`
if [ $NO_NOT_BY_ROOT_OWNED_FILES != "0" -o $NO_GROUP_WRITABLE_FILES != "0" -o $NO_WORLD_WRITABLE_FILES != "0" ]; then
   echo netenv: Security check failed, fix permissions in /etc/netenv !
   echo NO_NOT_BY_ROOT_OWNED_FILES=$NO_NOT_BY_ROOT_OWNED_FILES NO_GROUP_WRITABLE_FILES=$NO_GROUP_WRITABLE_FILES NO_WORLD_WRITABLE_FILES=$NO_WORLD_WRITABLE_FILES
   exit 1
fi

###set -x
# When located in /tmp, script must be called
# AFTER wiping out /tmp has been done ...
# Since this is not possible in Debian (bootmisc.sh called after
# networking), we put the file in /var/tmp, which is preserved upon boot.
NETENV_FIL="/var/tmp/netenv"
# Create the netenv file if it doesn't exist yet (only if called by root).
[ `id -u` = "0" ] && touch -a $NETENV_FIL
# Security check
if [ "X`stat -c %u $NETENV_FIL`" != "X0" -o ! -f $NETENV_FIL ]; then
  echo "netenv: $NETENV_FIL must be a regular file owned by root"
  exit 1
fi
if [ "X$((0`stat -c %a $NETENV_FIL` & 022))" != "X0" ]; then
  echo "netenv: Security check failed, fix $NETENV_FIL permissions"
  exit 1
fi

NETENV_BASE=/etc/netenv
COLS=68
TMPFIL=`tempfile -d /tmp -p dialo  2>/dev/null`
if [ $? -ne 0 ]; then TMPFIL=/tmp/netenv.tmp.$$; fi
# trpnc is no longer used in Debian
#MATH=/usr/bin/trpnc # Needed only when setting up a new environment, see
	            # function get_netenvdata()
DIALOG=dialog
NODE=`uname -n`
# dialog is in /usr, which might be on a separate partition. If this is not 
# mounted, we are in trouble. However, the user can specify a NETENV
# assignment at the boot prompt.
nodialog () {
	echo "dialog binary not found."
	echo
	echo "Probably the partition containing /usr could not be mounted"
	echo "To get a valid network setup, specify it at the boot prompt"
	echo "like this: linux NETENV=someplace"
	echo
	echo "For \"someplace\", put in one of the names after the dash"
	echo "in the files from the following listing (only the ones starting"
	echo "with $NODE name)."
	echo
	ls $NETENV_BASE
	echo
	echo "Press enter to continue"
	read -t 20
	exit 1
}

which dialog >/dev/null 2>&1 || DIALOG=nodialog

NETENV_TIMEOUT=0 # as a default, don't timeout at all
NETENV_RUN_INIT_SCRIPTS=""

# You can change the variables in the configuration file:
CONFFILE=/etc/netenv/netenv.conf


if [ -r $CONFFILE ]; then
  . $CONFFILE
fi

# In the configuration file, also NETENV_RUN_INIT_SCRIPTS may be set.
DEBIAN_RUN_INIT_SCRIPTS="networking $NETENV_RUN_INIT_SCRIPTS"


###TERM=linux; export TERM # We are called from init, so we need to know

# Some functions ...

ExitOnCancel() { echo "You have canceled the setup routine. No Network \
configuration will be done !"; exit 1; }
ExitOnError() { echo "An unknown error ocurred during setup. No Network \
configuration will be done !"; exit 1; }

first() { echo $* | cut -f1 -d" "; }
second() { echo $* | cut -f2 -d" "; }
third() { echo $* | cut -f3 -d" "; }
fourth() { echo $* | cut -f4 -d" "; }

CheckReturnVal()
{
   case $? in
      0) ;;
      1) ExitOnCancel;;
      *) ExitOnError;;
   esac
}
CheckNoInput()
{
   if [ "$1" = "" ]; then 
      echo "You did not respond with input. No Network \
       configuration will be done !"
      exit 1
   fi
}


get_netenvdata()
{   
   # Executable assigned by $MATH is being used. If it's not available,
   # don't panic, just a loss of convenience ...

	 # Debian uses bash's arithmetic evaluation instead of trpnc.

   while true
   do
      
      $DIALOG --inputbox "IP-Address\nEnter the current IP-address or the keyword dhcp ..." \
       9 $COLS $IPADDR 2>$TMPFIL
      CheckReturnVal
      IPADDR=`cat $TMPFIL`
      case $IPADDR in 
         [dD][hH][cC][pP])
            # Maybe this is SuSE specific. RedHat / Debian users feedback appreciated !
            BOOTPROTO='dhcp'
            # This is a rather crude attempt to distinguish between
            # onboard and PCMCIA-NIC's.
            STARTMODE='hotplug' # Default is PCMCIA-NIC
	    # cardmgr is (hopefully) not running at this time, so if
	    # there is a eth0, it must be onboard
            if dmesg | grep eth0 >/dev/null 2>&1; then STARTMODE='onboot'; fi
           ;;
         *)      
      CheckNoInput $IPADDR
      ipa=`echo $IPADDR | sed 's/\./ /g'`
      I1=`first $ipa`
      I2=`second $ipa`
      I3=`third $ipa`
      I4=`fourth $ipa`

      # Just a wild guess ...
      NETMASK=255.255.255.0
      
      $DIALOG --inputbox "Netmask\nEnter the netmask of the current subnet ..." \
       9 $COLS $NETMASK 2>$TMPFIL
      CheckReturnVal
      NETMASK=`cat $TMPFIL`
      CheckNoInput $NETMASK

      netm=`echo $NETMASK | sed 's/\./ /g'`
      N1=`first $netm`
      N2=`second $netm`
      N3=`third $netm`
      N4=`fourth $netm`
      let R1=$(($I1 & $N1))
      let R2=$(($I2 & $N2))
      let R3=$(($I3 & $N3))
      R4="0"
      NETWORK="$R1.$R2.$R3.$R4"
      
      $DIALOG --inputbox "Network\nEnter the IP-Address of the current network ..." \
       9 $COLS $NETWORK 2>$TMPFIL
      CheckReturnVal
      NETWORK=`cat $TMPFIL`
      CheckNoInput $NETWORK

      let R1=$((~N1 & 255 | I1))
      let R2=$((~N2 & 255 | I2))
      let R3=$((~N3 & 255 | I3))
      let R4=$((~N4 & 255 | I4))
      BROADCAST="$R1.$R2.$R3.$R4"
      
      $DIALOG --inputbox "Broadcast\nEnter the broadcast-Address of the current network ..." \
       9 $COLS $BROADCAST 2>$TMPFIL
      CheckReturnVal
      BROADCAST=`cat $TMPFIL`
      CheckNoInput $BROADCAST
			
      let R1=$(($I1 & $N1))
      let R2=$(($I2 & $N2))
      let R3=$(($I3 & $N3))
      GATEWAY="$R1.$R2.$R3.1"
      
      $DIALOG --inputbox "Gateway\nEnter the Gateway-Address of the current network ..." \
       9 $COLS $GATEWAY 2>$TMPFIL
      CheckReturnVal
      GATEWAY=`cat $TMPFIL`
      # Don't check for GATEWAY, no input simply means closed shop -
      # which may be useful.

      # As of 0.92 I remove PROFILE. It's confusing people ...

      # As of 0.92 add Nameserver. Comes in handy from time to
      # time. Useful only when GATEWAY is set.
      if [ -n "$GATEWAY" ]; then 
         $DIALOG --inputbox "Nameserver\nEnter the IP-address of the current nameserver ..." \
         9 $COLS $GATEWAY 2>$TMPFIL
         CheckReturnVal
         DNS_1=`cat $TMPFIL`
         # Don't check for input - there may be no nameserver
      fi      
      ;;
esac
      (
      echo "The current contents of your /tmp/netenv is as follows:"
      echo
      echo "# Networkenvironment: netask"

      if [ "$BOOTPROTO" = "dhcp" ] ; then
         echo export BOOTPROTO=$BOOTPROTO
         echo export STARTMODE=$STARTMODE
      else
         echo export IPADDR=$IPADDR
         echo export NETWORK=$NETWORK
         echo export NETMASK=$NETMASK
         echo export BROADCAST=$BROADCAST
         if [ ! -z "$GATEWAY" ]; then
            echo export GATEWAY=$GATEWAY
         fi
         if [ ! -z "$DNS_1" ]; then
            echo export DNS_1=$DNS_1
         fi
      fi
      echo -e "\n\n\n\n\n"
      echo "Answer <Yes> if that looks ok, otherwise <No>"
      echo
      ) > $TMPFIL
	 
      $DIALOG --yesno "`cat $TMPFIL`" 20 72
	 
      if [ $? -eq 0 ]; then
	 (
	 echo "# Networkenvironment: netask"
         if [ "$BOOTPROTO" = "dhcp" ] ; then
            echo export BOOTPROTO=$BOOTPROTO
            echo export STARTMODE=$STARTMODE
         else
	    echo export IPADDR=$IPADDR
	    echo export NETWORK=$NETWORK
	    echo export NETMASK=$NETMASK
	    echo export BROADCAST=$BROADCAST
	    if [ ! -z "$GATEWAY" ]; then
	       echo export GATEWAY=$GATEWAY
	    fi
 	    if [ ! -z "$DNS_1" ]; then
 	       echo export DNS_1=$DNS_1
 	    fi
         fi
	 ) > $NETENV_FIL
	 rm -f $TMPFIL
         break
      else	
	 $DIALOG --yesno "Do you want to repeat the setup process ? " 5 72
	 if [ $? -eq 1 ]; then exit 1; fi		   
      fi
   done

   chmod 644 $NETENV_FIL

   $DIALOG --yesno "Do you want to save this configuration ?" 5 72
   if [ $? -eq 0 ]; then
      $DIALOG --inputbox "Configuration name\nEnter the name under which\
 the configuration should be stored ..." \
       9 $COLS  2>$TMPFIL
      CheckReturnVal
      NETENV=`cat $TMPFIL`
# The scripts requires that there be a file named /etc/netenv/$NODE. 
# If it does not yet exist, we have to create it.
      if [ -e /etc/netenv/$NODE ]; then
				CheckNoInput $NETENV
				NETCONF_FIL="$NETENV_BASE/"$NODE"-"$NETENV
      else
# The file does not yet exist.
# If the user has let the name field empty, use "default". This is necessary
# in order to be able to bypass DIALOG by specifying a non-empty NETENV at the
# boot prompt (in case /usr/ has not been mounted. 
				if [ X$NETENV = X ]; then
					NETENV=default
				fi
				NETCONF_FIL="$NETENV_BASE/"$NODE"-"$NETENV
# the target of the link doesn't exit yet, but it will be created soon.
				(cd $NETENV_BASE; ln -s ${NODE}-${NETENV} $NODE)
      fi
      cp $NETENV_FIL $NETCONF_FIL # As of 0.92 omit -p (scripts clean /tmp)  
   fi
} # End Function get_confdata

choose_network_environment()
{
   ITEM_LIST="" # have to be reentrant as of 0.93 ...
   # The ls gives a list of files starting with the current node name,
   # excluding emacs backup files. The list starts with the default
   # and ends with ask.
   for i in `ls $NETENV_BASE/$NODE $NETENV_BASE/$NODE-*[0-9a-zA-Z]`
   do
      netenv_id="unknown"
      eval `grep netenv_id $i`
      SUFFIX=`basename $i | cut -d '-' -f2-`
      # Maybe something like lulu or foo-bar or lulu-no-eth or foo-bar-no-eth
      SUFFIX=`basename $i`
      if [ $SUFFIX != $NODE ]; then
         LENGTH=$((${#NODE}+1))
         SUFFIX=${SUFFIX:$LENGTH}
      fi
      ITEM_LIST=$ITEM_LIST" "$SUFFIX" "$netenv_id
   done
   
   ITEM_LIST=$ITEM_LIST" "new" "Set_up_new_environment
   CHOOSE_TITLE_TEXT="netenv $NETENV_VERSION on $NODE running `uname -s` `uname -r`\n\nChoose your current network-environment !"
   if [ $DIALOG = gdialog ]; then
     $DIALOG --menu "$CHOOSE_TITLE_TEXT" 20 $COLS 12 $ITEM_LIST 2>$TMPFIL
   else
     $DIALOG --timeout $NETENV_TIMEOUT --menu "$CHOOSE_TITLE_TEXT" 20 $COLS 12 $ITEM_LIST 2>$TMPFIL
   fi
} # End Function choose_network_environment

# End of function defining

# Let's do the work ...
case $1 in
   '-h'|'--help'|'-?'|'help')
      echo Version $NETENV_VERSION
      echo Docs and examples in /usr/share/doc/packages/netenv
      echo Latest versions see http://netenv.sourceforge.net
      exit 0
   ;;
esac

###NODE=foo-bar  # TODO
# The following block will hopefully get us a valid NETENV. It is
# skipped for those, who still prefer input at the boot prompt 
if [ -z "$NETENV" ]; then
   choose_network_environment
   if [ $? -ne 0 -a X`grep -v ^$ $TMPFIL` != Xtimeout ]; then
      $DIALOG --yesno "Do you want to enter expert mode ? " 5 72
      if [ $? -eq 0 ]; then
         /bin/sh
	 choose_network_environment
      else
         echo "You didn't choose a network environment - good luck !"
	 rm -f $TMPFIL
         exit 0
      fi
   else
#      NETENV=`cat $TMPFIL` && rm -f $TMPFIL
      NETENV=`grep -v ^$ $TMPFIL` && rm -f $TMPFIL
      # Deal with special cases
      if [ "$NETENV" = new ]; then
	 :
      elif [ "$NETENV" = $NODE -o "$NETENV" = timeout ]; then
	 NETENV=""
	 echo "netenv: File $NETENV_BASE/$NODE will be used for setting up the network environment ..."
      else	
	 echo "netenv: File $NETENV_BASE/$NODE"-"$NETENV will be used for setting up the network environment ..."
      fi
   fi
else
   # Continue here, if variable NETENV has been set up as boot argument
   echo "netenv: Using provided NETENV=$NETENV ..."
fi

# Define the file holding the current network-environment
# configuration. Directory is $NETENV_BASE, filename is e.g. foo-off
# for the current system named "foo" and the Variable NETENV=off
# (Laptop "foo" used in the office). If NETENV has not been defined,
# look for file <nodename>, e.g. "foo".

# Handle special case: ask for environment
if [ "$NETENV" = new ]; then
   NETCONF_FIL="$NETENV_BASE/"$NODE"-"$NETENV
   if [ -r $NETCONF_FIL ]; then
      . $NETCONF_FIL
   fi

   get_netenvdata

   # At this point we assume to have a valid NETENV_FIL, eventually a
   # NETCONF_FIL too. The latter is not a must. If the user decided
   # not to store the data it may be a once-and-never-again environment.

elif [ -z "$NETENV" ]; then
   # Variable NETENV has not been defined or assigned, probably on
   # home tower or something like that ...
   # This is the default !
   NETCONF_FIL="$NETENV_BASE/"$NODE
else
   NETCONF_FIL="$NETENV_BASE/"$NODE"-"$NETENV
fi	

if [ -r $NETCONF_FIL ]; then
   cp $NETCONF_FIL $NETENV_FIL # As of 0.92 omit -p (scripts clean /tmp)   
   chmod 644 $NETENV_FIL
else
   echo "netenv: Using temporary configuration data ..."
fi

. $NETENV_FIL

# If NETENV_SCRIPT has been defined, run it. Of course this is done as
# root. There is no NIC configured yet !
if [ -r "$NETENV_SCRIPT" ]; then
   if [ -z "$NETENV" ]; then
      PROFILE=default
   else
      PROFILE=$NETENV
   fi
   echo "executing $NETENV_SCRIPT $PROFILE ..."
   . $NETENV_SCRIPT $PROFILE
fi

rm -f $TMPFIL

# New as of 0.93. If netenv is not run during boot but on the fly, ask the user, if
# he wants to restart the network
if [ `tty` != /dev/console ]; then
   $DIALOG --yesno "Do you want to activate changes by restarting the network ? " 5 72
   if [ $? -eq 0 ]; then
      if grep 'Red Hat' /etc/issue > /dev/null; then
	 /etc/init.d/network restart 
      elif grep SuSE /etc/issue > /dev/null; then
         /etc/init.d/network restart
         # As of SuSE 8.0 route has gone. Still here for 7.3 Users ...
         if [ -x /etc/init.d/route ]; then /etc/init.d/route restart; fi
      elif grep Debian /etc/issue > /dev/null; then
	for script in $DEBIAN_RUN_INIT_SCRIPTS; do
	  /etc/init.d/$script restart;
	done
      fi
   fi
fi
