#!/bin/sh

# trivial-net-setup, by Seth David Schoen <schoen@linuxcare.com>
# (now <schoen@loyalty.org>)

# This version is a lot simpler than before because we got rid of the
# ssh and rc5des setup.  (openssh is already included now.)
#
# Now we use dialog for prettier interaction!

# Copyright (C) 1999, 2001, Linuxcare, Inc.
# Copyright (C) 2001, 2002, Seth Schoen

# This program is free software.  You may modify and/or redistribute it
# under the terms of the GNU General Public License, version 2, or, at
# your option, any later version.

# January 2003:
#  I made the following repair.  The part which asks a few details during
#  manual setup now asks for the broadcast explicitly.  This is essentially
#  a workaround for ifconfig, which is too stupid to figure out that if 
#  your network is smaller than class C, then your broadcast *cannot* be 
#  255, it must be within range after your netmask is applied.  The normal 
#  result of #  everything being correct but your broadcast, is complete
#  inability to reach your gateway and thence the network.
# -- Heather Stern <star@starshine.org>

SCRIPTNAME=trivial-net-setup

# These paths will be wrong for a non-rescue system.  If you think
# that everything will be in your PATH, you could change this to,
# e.g., IFCONFIG=`which ifconfig`

IFCONFIG=$(which ifconfig)
IPCALC=$(which ipcalc)
DHCPCD=$(which dhcpcd)
DOTQUAD=$(which dotquad)
ROUTE=$(which route)
AND=$(which and)
OR=$(which or)
LSPCI=$(which lspci)
MODPROBE=$(which modprobe)
FMT=$(which fmt)

BINARIES="IFCONFIG IPCALC DHCPCD DOTQUAD ROUTE AND OR LSPCI MODPROBE FMT"

for a in $BINARIES
do
	VALUE=`eval echo '$'$a`
	if [ ! -x "$VALUE" ]
	then
		echo "can't find required program $VALUE!"
		exit 1
	fi
done

# A list of PCI Ethernet drivers which can be loaded with modprobe.
# This list is based mostly on searching for <linux/pci.h> in kernel sources.
# Please change it if you know better.
PCIMODULES="eepro100 3c59x ne2k-pci tulip acenic de4x5 dgrs dmfe epic100"
PCIMODULES="$PCIMODULES hamachi hp100 lanstreamer olympic pcnet32"
PCIMODULES="$PCIMODULES rrunner sis900 tlan via-rhine yellowfin"


# Let's define a bunch of functions.  If you want to see the main script,
# search for "main script" below.

yesno(){

# Default to Yes

dialog --yesno "$*" 0 0
return $?

}

pcisetup(){


IF='You seem to have at least one PCI Ethernet interface in your system.  In this
version of the Bootable Business Card, network drivers are not automatically
loaded at boot time.  We can load drivers now, at your request.\n\n
Here are the PCI devices which appear to be Ethernet cards:\n\n'

IF="$IF"$($LSPCI | grep Ether | sed 's/$/\\n/')

dialog --msgbox "$IF" 0 0

RESPONSE=$(dialog --menu "Choose a PCI module." 0 0 0 auto "-- Autoinsert each of these" specify "-- Specify a different module" eepro100 "Intel EtherExpress Pro 100" 3c59x "3Com 590 and 950 series" ne2k-pci "NE2000 PCI" tulip "DECchip Tulip " acenic "Gigabit Alteon, NetGear, 3Com 3C985(B)" de4x5 "Digital DE425, DE434, DE435, DE450 and DE500" dgrs "Digi International RightSwitch" dmfe "Davicom DM9102(A)/DM9132/DM9801" epic100 "SMC EtherPower II 9432 or SMC83c17x" hamachi "Packet Engines 'Hamachi' Gigabit Ethernet" hp100 "[no description]" lanstreamer "IBM Token Ring Cards" olympic "all non-Lanstreamer IBM Token Ring Cards" pcnet32 "PCnet32 or PCnetPCI" rrunner "Roadrunner HIPPI ethernet" tlan "ThunderLAN (Netelligent, NetFlex and Olicom)" via-rhine "Rhine-I (3043) or Rhine-2 (VT86c100A)" yellowfin "Packet Engines 'Yellowfin' Gigabit Ethernet" 2>&1 >/dev/tty)

# cancel?
if [ $? -eq 1 ]
then
	exit 1
fi

#cat <<WHATNOW
#
#Here are the PCI Ethernet modules I know about.  (There may be others.)
#
#WHATNOW
#echo $PCIMODULES | $FMT -w 79
#
#cat <<WHATNOW
#
#Press Enter to try all of these modules (default), Ctrl+C or "quit" to quit,
#WHATNOW
#echo -n "or type the name of a specific module to load: "
#
#read RESPONSE

if [ "$RESPONSE" != auto ]
then 
	clear
	if [ "$RESPONSE" = specify ]
	then
		# specify
		RESPONSE=$(dialog --inputbox "Please type the name of a specific module to load." 0 0 mymodule 2>&1 >/dev/tty)
		# cancel?
		if [ $? -eq 1 ]
		then
			exit 1
		fi
	fi	
	if $MODPROBE $RESPONSE 
	then
		dialog --msgbox "That seems to have worked.  Checking for interfaces..." 0 0
		check_how_many_interfaces_and_do_something
	else
		dialog --msgbox "Loading $RESPONSE failed; try a different driver." 0 0
		nointerfaces
	fi
else
	SOMETHINGWORKED=false
	clear
	for CANDIDATE in $PCIMODULES
	do
		echo; echo "*** Trying $CANDIDATE."
		if $MODPROBE $CANDIDATE
		then
			SOMETHINGWORKED=true
			WHATWORKED=$CANDIDATE
			break
		fi
	done
	sleep 2
	if $SOMETHINGWORKED
	then
		dialog --msgbox "I had success loading the $WHATWORKED module." 0 0
		check_how_many_interfaces_and_do_something
	else
		dialog --msgbox "No module loaded successfully." 0 0
		exit 1
	fi
fi
}

nointerfaces(){

if $LSPCI | grep Ether > /dev/null
then 
	pcisetup
else
cat <<WHATNOW
No Ethernet interfaces have been detected by currently-loaded network drivers.
If you have an Ethernet card installed in your system and wish to use the
network, you must load an appropriate module.  The available modules on this
CD-ROM are located in /lib/modules/`uname -r`/kernel/drivers/net.

If you want to load one of these drivers, try
"cd /lib/modules/`uname -r`/kernel/drivers/net", and then
"modprobe module options", where "module" is the name of the module
you wish to load and "options" are any required options.  For instance,

	modprobe ne io=0x300

for an NE2000-compatible ISA card with I/O base address 0x300.  (See
the Bootprompt HOWTO and Ethernet HOWTO for documentation about the
significance of options to the Ethernet modules.)  Note that specifying
the wrong module or options may require you to reboot your system.  (If
you have ISA Plug-n-Play(tm) devices, you might need to use "pnpdump"
and "isapnp" to configure them first, before loading any modules.  This
should not be necessary for PCI devices.  For PCMCIA, do
"/etc/init.d/pcmcia start".)

If you have PCI network cards, try "lspci" to see which, and then use
modprobe to load associated drivers if they were not included in the kernel.
WHATNOW

exit 1
fi
}

oneinterface(){
INTERFACE=`$IFCONFIG -a | grep "Link encap:Ethernet" | awk '{print $1}'`
}

manyinterfaces(){
INTERFACE=$(/sbin/ifconfig -a | grep encap:Ethernet | awk '{print $1; print $2, $3, $4, $5, $6, $7, $8, $9} ' |sed 's/ /\\ /g' | xargs dialog --menu "The following Ethernet interfaces have been detected by currently-loaded drivers.\n\nThe $SCRIPTNAME program can only configure networking on a single Ethernet interface.  Which of these interfaces would you like to use?" 0 0 0 2>&1 >/dev/tty)

# cancel?
if [ $? -ne 0 ]
then
	exit 1
fi
}

check_how_many_interfaces_and_do_something(){
INTERFACES=`$IFCONFIG -a | grep "Link encap:Ethernet" | wc -l | awk '{print $1}'`

case $INTERFACES in
	0) nointerfaces;;
	1) oneinterface;;
	*) manyinterfaces;;
esac
}

dhcpsetup(){

if ps wax | grep dhcpcd | grep "$INTERFACE" >/dev/null
then
	# Must kill old dhcpcd first
	$DHCPCD -k "$INTERFACE"
fi

if $DHCPCD -d "$INTERFACE"
then
	return 0
else
	return 1
fi

}

validip(){

# uses dotquad.c to parse $1 as a dotted-quad IP address

if $DOTQUAD "$1" >/dev/null
then
	return 0
else
	return 1
fi

}

readaddr(){
while true
do

foo=$(dialog --inputbox "$1" 0 0 $2 2>&1 >/dev/tty)

# cancel?
if [ $? -ne 0 ]
then
	exit 1
fi
if validip "$foo"
then
	if [ "$foo" != "216.88.157.147" ]
	then
		RESULT="$foo"
		RESPONSE="$foo"
		return 0
	else
		dialog --msgbox '216.88.157.147 was provided only as an example.  Please enter your own IP address instead!' 0 0
	fi
fi
done
RESULT="$foo"
RESPONSE="$foo"
}

readaddr_blankok(){
while true
do

foo=$(dialog --inputbox "$1" 0 0 $2 2>&1 >/dev/tty)

# cancel?
if [ $? -ne 0 ]
then
	exit 1
fi
if [ -z "$foo" ]
then
	RESULT=""
	RESPONSE=""
	return 0
fi

if validip "$foo"
then
	if [ "$foo" != "216.88.157.147" ]
	then
		RESULT="$foo"
		return 0
	else
		dialog --msgbox '216.88.157.147 was provided only as an example. Please enter your own IP address instead!' 0 0
	fi
fi
done
RESULT="$foo"
RESPONSE="$foo"
}

manualsetup(){

dialog --msgbox "Configuring manually...\n\nIn the questions that follow, please enter all addresses in dotted-quad format (a.b.c.d); other formats will not be recognized." 0 0

while true
do
readaddr 'Enter your IP address (e.g. 216.88.157.147): '
IP="$RESULT"
DEFAULTMASK=$($IPCALC --netmask "$IP" | cut -d= -f2)
while [ -z "$NETMASK" ]
do
readaddr "Enter your netmask: " $DEFAULTMASK

if [ "x$RESPONSE" = x ]
then
	NETMASK="$DEFAULTMASK"
else
	if validip "$RESPONSE"	# This is wrong, because not all valid IP
				# addresses are valid netmasks.  But we can
				# catch some inconsistencies below.
	then
		NETMASK="$RESPONSE"
	else
		dialog --msgbox 'Invalid entry, try again.' 0 0
	fi
fi
done

BROADCAST=$($IPCALC -b $IP $NETMASK | cut -d= -f2)

cat <<NOGW
  (For the following question, just press Enter if you have no gateway.  Note
  that doing this will allow you to access your local-area network, but you'll
  need to add a route manually if you want to use the Internet.)
NOGW

# We don't want users to omit a gateway without knowing what they are getting
# into.

unset GATEWAY
GATEWAYOK=false
until $GATEWAYOK
do
unset RESULT
readaddr_blankok 'Enter your default router (gateway) address (Enter for none): '
if [ -z "$RESULT" ]
then
	GATEWAYOK=true
else
	if validip "$RESULT"
	then
		GATEWAY="$RESULT"
		GATEWAYOK=true
	else
		echo 'Invalid entry, try again.'
	fi
fi
done

# Now let's do the nameserver

unset NAMESERVER
NAMESERVEROK=false
until $NAMESERVEROK
do
unset RESULT
readaddr_blankok 'Enter the IP address of a nameserver for resolving DNS names to IP addresses (Enter for defaults, which should usually work on the public Internet):'
if [ -z "$RESULT" ]
then
	NAMESERVEROK=true
else
	if validip "$RESULT"
	then
		NAMESERVER="$RESULT"
		NAMESERVEROK=true
	else
		echo 'Invalid entry, try again.'
	fi
fi
done
	

# Check that network is right

HOSTNUM=$($DOTQUAD "$IP")
MASKNUM=$($DOTQUAD "$NETMASK")
GATENUM=$($DOTQUAD "$GATEWAY")
CASTNUM=$($DOTQUAD "$BROADCAST")

HOSTNET=$($AND "$MASKNUM" "$HOSTNUM")
GATENET=$($AND "$MASKNUM" "$GATENUM")

if [ -z "$GATEWAY" ]
then
	# It is legitimate not to have a gateway at all.  In that case, it
	# doesn't have a network. :-)
	unset HOSTNET
	unset GATENET
fi

if [ x"$HOSTNET" != x"$GATENET" ]
then
	dialog --msgbox "Your gateway $GATEWAY is not on this network!  Please try again.  (You may have entered your netmask incorrectly, for instance.)" 0 0  0 0
else
	# do the work
	if $IFCONFIG "$INTERFACE" inet "$IP" netmask "$NETMASK" broadcast "$BROADCAST" up
	then
		# Configure a nameserver, if we're supposed to.
		# This now replaces any existing resolv.conf, which
		# we will try to back up.
		if [ "$NAMESERVER" ]
		then
			mv -f /etc/resolv.conf /etc/resolv.conf.$$
			echo nameserver $NAMESERVER > /etc/resolv.conf
		fi

		# add default route, if we're supposed to
		if [ "$GATEWAY" ]
		then
			if $ROUTE add -net default gw "$GATEWAY"
			then
				dialog --msgbox "Default route set through $GATEWAY." 0 0
			else
				dialog --msgbox "Could not set default route through $GATEWAY.  Please try again." 0 0
				$IFCONFIG "$INTERFACE" down
			fi
		fi

		return 0
	else
		dialog --msgbox "Interface configuration failed; please try again." 0 0
		$IFCONFIG "$INTERFACE" down
	fi
	
fi

unset NETMASK	# Must unset NETMASK in order for the user to be prompted
		# for it next time through.

done

}


# Beginning of main script

ME=$(whoami)
if [ x"$ME" != "xroot" ]
then
	echo "$SCRIPTNAME" should only be run as root.
	exit 1
fi

dialog --msgbox "Welcome to the $SCRIPTNAME program.  This program will help you configure an Ethernet network interface, so that you will be able to access your network while running from this rescue CD-ROM." 0 0

check_how_many_interfaces_and_do_something

dialog --msgbox "OK, configuring networking on $INTERFACE." 0 0

cat <<DHCP

If you are on a network with a DHCP server which can automatically assign
you an IP address and other network settings, you may wish to use DHCP to
configure this interface automatically.  If you do not have a DHCP server
available, or would like to specify your network configuration manually,
say no to the following question.

DHCP

if yesno "Would you like to attempt to configure $INTERFACE automatically with DHCP? "
then
	DHCP=true
else
	DHCP=false
fi

if $DHCP
then
	dhcpsetup || exit 1
else
	if yesno "OK, how about manual configuration? "
	then
		manualsetup || exit 1
	else
		dialog --msgbox "Could not complete $INTERFACE configuration.  Please re-run $SCRIPTNAME if you would like to try again." 0 0
		exit 1
	fi
fi

# Reaching here means that we successfully configured the network, at least
# as far as software is concerned.

cd

dialog --msgbox "Now that network setup has completed, you should have access to your network.  If your network is connected to the Internet, you can download software and install it onto the RAM disk.\n\nYou may be able to use the \"list-packages\" program to download additional software provided by your distributor." 0 0
