#!/bin/sh
#
# Set up diskless/stateless workstations for Debian Edu, using LTSP 
#
# Author: Finn-Arne Johansen
# Date:   2006-01-25

# Make sure we terminate on the first error instead of making a mess
set -e

echo
echo "info: See http://wiki.debian.org/DebianEdu/HowTo/LtspDisklessWorkstation for"
echo "info: instructions on how to set up and configure a diskless workstation"
echo "info: using this script."
echo

target=

while [ $# -gt 0 ] ; do 
  case "$1" in 
    --target) target="$2" ; shift ;;
  esac
  shift
done

# Make the installation a bit more quiet when started from the command line
# The locale is not setup in the thin client chroot (yet?)
LC_ALL=C
export LC_ALL

#avoid running deamons in the chroot
LTSP_HANDLE_DAEMONS=false
export LTSP_HANDLE_DAEMONS

if [ 'amd64' = $(/usr/bin/dpkg --print-architecture) ] ; then
    arch=i386
else
    arch=$(dpkg --print-architecture)
fi

test "$target" || target=/opt/ltsp/$arch
dist=sarge

if [ ! -d $target ] ; then
    echo "error: $target do not exist."
    echo "error: You need to run debian-edu-ltsp first."
    exit 1
fi

optsize=$(LANG=C df -mP $target | grep -v Filesystem|awk '{print $2}')
sizeneeded=4096
if [ "$sizeneeded" -gt "$optsize" ] ; then
    echo "warning: The diskless workstation setup need at least $sizeneeded MiB disk space in total"
    echo "warning: in /opt/ltsp/$arch/.  Only $optsize MiB was detected."
    exit 1
fi

chroot $target aptitude update

# Check if server uses mirror or cd to install
if grep -v '^#' $target/etc/apt/sources.list | grep -q file:///cdrom ||
   chroot $target apt-cache policy ltsp-server | grep -q "cdrom://" ; then 
  mirror=file:///cdrom
  echo "error: Unable to use CD/DVD as source for converting the ltsp chroot"
  echo "error: to a diskless workstation."
  echo "error: The reason is that the CD/DVD is not a signed APT repository."
  echo "error: Edit $target/etc/apt/sources.list to fix this."
  exit 1
else
  mirror=http://ftp.debian.org/debian
fi

# Insert commands to be executed when the script is terminated
exit_handler() {
    # Unmount if anything is mounted
    for dir in $umounts ; do
	echo "Unmounting $dir"
	umount $dir || true
    done

    if [ true = "$run_successfull" ] ; then
	echo "info: conversion ended successfully"
    else
	echo "error: conversion ended abnormally"
    fi
}

trap exit_handler EXIT INT

# Mount the CD ROM if needed
case $mirror in
    file:///cdrom)
        umounts="/cdrom"
        mount /cdrom
	;;
    file:///media/cdrom)
        umounts="/media/cdrom"
        mount /media/cdrom
	;;
    *)
        ;;
esac


if [ "$umounts" ] ; then 
  umounts="$target/cdrom $umounts"
  mount --bind /cdrom  $target/cdrom
fi

umounts="$target/proc $umounts"
mount -t proc proc  $target/proc

# Add preseeding values
(
    cat /usr/lib/debian-edu-install/defaults.common \
        /usr/lib/debian-edu-install/defaults.networked \
        /usr/lib/debian-edu-install/defaults.workstation
    debconf-get-selections | egrep "^locales|^popularity-contest"
) | chroot $target debconf-set-selections

if [ -f /etc/locale.gen -a ! -f $target/locale.gen ] ; then 
  cp /etc/locale.gen $target/etc/locale.gen 
fi

if [ -f /etc/environment -a ! -f $target/envrionment ] ; then 
  cp /etc/environment $target/etc/environment
fi

# set the timezone
cp -d /etc/localtime $target/etc 
cp -d /etc/timezone $target/etc 

# Make the install Silent
DEBIAN_FRONTEND=noninteractive
export DEBIAN_FRONTEND


# Bind-mount /var/cache/apt/archives/ on $target/var/cache/apt/archives/,
# to avoid having to download packages twice.
umounts="$target/var/cache/apt/archives $umounts"
mount --bind /var/cache/apt/archives $target/var/cache/apt/archives

# install some more packages

chroot $target aptitude install -y -q education-tasks debian-edu-archive-keyring

# Install all edu-dependencies
if chroot $target tasksel install education-workstation ; then
  :
else
# An alternative if the previous command fail for no apparent reason
  chroot $target sh -c 'aptitude install -y -q $(tasksel --task-packages education-workstation)'
fi

ADDPKGS=""

# FIXME Add Samba when we have working thin client smb.conf
#ADDPKGS="$ADDPKGS samba"

# Why are these added to the installation?  For SMB
# mounted home directories? [pere 2007-07-11]
ADDPKGS="$ADDPKGS libpam-mount smbfs"

# Make it possible to localize the installation
ADDPKGS="$ADDPKGS localization-config"

# All Packages may not exist on the CD, so we
# install one at a time, to get as many as possible
for PKG in $ADDPKGS ; do
  chroot $target aptitude install -y -q $PKG || /bin/true
done

# Configure locale specific settings.
chroot $target update-locale-config $LANG

# Kill processes that are currently running on $target
if [ "$CHROOTPART" = "$target" ] ; then 
  fuser -mkv $target/ || /bin/true
fi

# check which network the install is on, and start either as diskless
# workstation or thin client
cat <<EOF > $target/etc/init.d/ltsp_set_runlevel
#!/bin/sh
### BEGIN INIT INFO
# Provides:          ltsp_set_runlevel
# Required-Start:    $local_fs
# Required-Stop:     $local_fs
# Should-Start:      console-screen xfree86-common
# Default-Start:     S
# Default-Stop:      
# Short-Description: Select runlevel for LTSP client at boot time
# Description:       Based on current IP address, select runlevel
#                    for the LTSP client at boot time.  This allow it
#                    to switch dynamically between thin client and
#                    diskless workstation profile.
### END INIT INFO

case "\$1" in
  start) ;;
  *) exit 0 ;;
esac

IP_ETH=\$(/sbin/ifconfig eth0 | sed -ne 's/ *inet addr:\([0-9.]*\) .*/\1/p')

case "\$IP_ETH" in
  10.*)
    echo "Detected 10.* network.  Selecting diskless workstation LTSP setup."
    telinit 3 ;;
  192.*)
    echo "Detected 192.* network.  Selecting thin client LTSP setup."
    telinit 4 ;;
esac

exit 0
EOF
chmod 0755 $target/etc/init.d/ltsp_set_runlevel
chroot $target update-rc.d ltsp_set_runlevel start 99 S .

only_run_on_rclevel() {
  runlevel=$1 # Use level N to disable on all levels
  shift
  for service in $@ ; do
      for link in $(cd $target/etc; ls rc[S2345].d/[SK]*$service || true); do
	  # link now look like 'rcS.d/S99foo'
	  set `echo $link|sed "s%rc\(.\).d\/\([SK]\)\(..\)$service%\1 \2 \3%"`
	  lvl=$1
	  action=$2
	  seq=$3
	  if [ "K" = "$action" ] && [ "$runlevel" = "$lvl" ]; then
	      mv $target/etc/$link $target/etc/rc$lvl.d/S$seq$service
	  fi
	  if [ "S" = "$action" ] && [ "$runlevel" != "$lvl" ]; then
	      mv $target/etc/$link $target/etc/rc$lvl.d/K$seq$service
	  fi
      done
  done	
}

# Needed to get the loopback interface
only_run_on_rclevel S ifupdown networking

# a lot of services should only be started in runlevel 3, eg, when
# running in workstation mode
only_run_on_rclevel 3 kdm xfs nscd cupsys autofs

# a lot of services should only be started in runlevel 4, eg. when running
# in thin client mode
only_run_on_rclevel 4 ltsp-client samba

# Some services should not be started on the terminals
only_run_on_rclevel N munin-node cron report-reboot open-backdoor enable-nat \
    boot_xconf cfengine2 start-wlan inetd

# Make sure udev can write to the directories it want to use
if grep -q "/var/lib/dbus /media" $target/etc/default/ltsp-client-setup ; then
  :
else
  (
    cat $target/etc/default/ltsp-client-setup
    echo '# Make sure udev can write to the directories it is using'
    echo 'rw_dirs="$rw_dirs /var/lib/dbus /media"' 
  ) >> $target/etc/default/ltsp-client-setup.new &&
     mv $target/etc/default/ltsp-client-setup.new $target/etc/default/ltsp-client-setup
fi

## set up samba for thinclients
# Fixme - provide a working smb.conf
#ln -sf smb-thinclient-debian-edu.conf $target/etc/samba/smb.conf

# set up usbmount for terminals
ln -sf usbmount-debian-edu.conf $target/etc/usbmount/usbmount.conf

# Create /skole, since the chroot will be read-only
mkdir -p $target/skole

# Make the ldap users availible in the chroot
chroot $target cfengine-debian-edu -Dinstallation

# Fetch the LDAP SSL cert while the file system is writable.
if [ -x $target/etc/init.d/fetch-ldap-cert ] ; then
  $target/etc/init.d/fetch-ldap-cert start
fi

# Create a new resolv.conf based on the one on the host
# FixME, this will not work if server have no net during setup, and is
# not a main-server
rm -rf $target/etc/resolv.conf
sed -e 's:127.0.0.1:10.0.2.2:g' /etc/resolv.conf | \
   grep -v "^#" > $target/etc/resolv.conf
chmod 0644 $target/etc/resolv.conf

# Set up a working hosts file
rm -rf $target/etc/hosts
cp /etc/hosts $target/etc/hosts

# it's nice to have a running ssh for the admin
rm -f $target/etc/ssh/sshd_not_to_be_run

if [ -f $target/etc/kde3/kdm/kdmrc ] ; then
    # Set up randomdevice for kdm if it is installed
    update-ini-file $target/etc/kde3/kdm/kdmrc 'General' RandomDevice \
	/dev/urandom

    #Include pammount config in /etc/pam.d/kdm
    KDM_PAM=$target/etc/pam.d/kdm
    KDM_ADD=$(grep -n ^auth $KDM_PAM | head -1 | cut -f1 -d:)

    cat << EOF | patch -Np0 $KDM_PAM
--- $KDM_PAM
+++ $KDM_PAM.new
@@ -$KDM_ADD,0 +$KDM_ADD,2 @@
+@include common-pammount
+
EOF
fi

# Need to set up some boot info in dhcpd.conf
# FIXME I guess this also will fail if done on a thin-client-server
# without proper connection
# But then again - the boot information should then be located on the
# main server
NETIF=eth0
DHCP_OLD=/etc/dhcp3/dhcpd-debian-edu.conf
DHCP_NEW=/etc/dhcp3/dhcpd-stateless-debian-edu.conf
DHCP_ADD=
IP_ETH=$(/sbin/ifconfig $NETIF | sed -ne 's/ *inet addr:\([0-9.]*\) .*/\1/p')
NET_ETH=$(/sbin/route -n | awk "/$NETIF/ { print \$1 }"| head -1 )
MASK_ETH=$(/sbin/route -n | awk "/$NETIF/ { print \$3 }"| head -1 )
INTERNAL_NET=$(grep -n "shared-network INTERNAL" $DHCP_OLD | cut -f1 -d:)
THIN_NET=$(grep -n "shared-network THINCLIENTS"  $DHCP_OLD | cut -f1 -d:)

OPTION_END=$(head -$THIN_NET $DHCP_OLD | tail -n +$INTERNAL_NET  | \
             grep -ne "^ *option" | tail -1 | cut -f1 -d:)
PXEFILE="$(tail -n +$THIN_NET $DHCP_OLD | grep -e '^ *filename.*pxe' | head -1)"

head -$THIN_NET $DHCP_OLD | tail -n +$INTERNAL_NET | \
  grep -qe "^ *filename" || DHCP_ADD="$DHCP_ADD$PXEFILE\n"
head -$THIN_NET $DHCP_OLD | tail -n +$INTERNAL_NET | \
  grep -qe "^ *option root-path" || \
  DHCP_ADD="$DHCP_ADD  option root-path \"$IP_ETH:$target\";\n"

if [ "$DHCP_ADD" ] ; then
  DHCP_SPLIT=$(expr $INTERNAL_NET + $OPTION_END)
  head -$DHCP_SPLIT $DHCP_OLD > $DHCP_NEW
  echo "$DHCP_ADD" >> $DHCP_NEW
  tail -n +$DHCP_SPLIT $DHCP_OLD >> $DHCP_NEW
  test -h /etc/dhcp3/dhcpd.conf && \
    ln -sf dhcpd-stateless-debian-edu.conf /etc/dhcp3/dhcpd.conf
  /usr/sbin/invoke-rc.d dhcp3-server restart || true # Is this a fatal error?
fi

# Export filesystem to the stateless machines if it isn't exported already
basedir=`dirname $target` # Get /opt/ltsp from /opt/ltsp/$arch
if grep -q "^$basedir " /etc/exports ; then
    :
else
    echo "$basedir $NET_ETH/$MASK_ETH(ro,async,no_root_squash,subtree_check)" >> /etc/exports
    /usr/sbin/invoke-rc.d nfs-kernel-server restart
fi

run_successfull=true # report success to exit_handler()
