#!/bin/bash
VERSION='0.3.2'
#
# Copyright (C) 2002       Mark Lawrence <nomad@null.net>
# Copyright (C) 2002       Paul Sladen   <vserver@paul.sladen.org>
# Copyright (C) 2003-2005  Ola Lundqvist <opal@debian.org>
#
# This 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 2, or (at your option) any later
# version.
# 
# This 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 with
# the source package as the file COPYING.  If not, write to the Free
# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
# 02111-1307, USA.
#
# Install a virtual debian server (vserver) from a debian HTTP/FTP archive
#
# ----------------------------------------------------------------
# Configurable items:

# Root directory of your virtual servers (probably shouldn't change this)
VROOTDIR="/vservers"

# Packages to install in addition to the base defaults
# MUST INCLUDE ALL DEPENDENCIES (seperated by "," commas)
# Devfsd is very important.
INSTALL_PACKAGES="less,dnsutils,wget,bzip2,ssh,rsync,libssl0.9.6,libdns5,libisc4,liblwres1,bind9-host,zlib1g,libbz2-1.0,traceroute,devfsd,nfsbooted"

# Packages installed from within the vserver after it has
# been setup (seperated by "," commas)
#EXTRA_PACKAGES="emacs20,lynx-ssl"
EXTRA_PACKAGES="nfsbooted"

# Packages to remove from the base defaults (seperated by "," commas)
#REMOVE_PACKAGES="dhcp-client,lilo,makedev,pcmcia-cs,ppp,pppconfig,pppoe,pppoeconf,setserial,syslinux,nano,fdutils,iptables,libpcap0,pciutils"
REMOVE_PACKAGES="lilo,makedev,nfs-kernel-server,lpr"

# sysvinit services relating to hardware access to remove
#REMOVE_LINKS="klogd hwclock.sh setserial urandom networking umountfs halt reboot"

# Architecture: overide on non-Debian host such as Redhat
# otherwise dpkg will detect whether we are i386/powerpc/sparc/etc
ARCH=""

# Which debian distribution (warning: this has only been tested with woody)
DIST="woody"

# Local or nearest location of a debian mirror (must include the `/debian')
MIRROR="http://ftp.uk.debian.org/debian"

# Default network interface for vservers:
INTERFACE="eth0"

# Copy vreboot/vhalt/vshutdown utility into /usr/local/sbin/
COPY_VREBOOT="true"

if [ -r /etc/vservers/util-vserver-vars ] ; then
    . /etc/vservers/util-vserver-vars
fi

if [ -r /etc/vservers/newvserver-vars ] ; then
    . /etc/vservers/newvserver-vars    
fi

# Support for 0.30.203 and above of util-vserver
if [ -n "$DEFAULT_VSERVERDIR" ] ; then
    VROOTDIR = "$DEFAULT_VSERVERDIR"
fi

# ----------------------------------------------------------------
# Nothing from here on should need changing.
# ----------------------------------------------------------------

# NOTE: debootstrap handles multiple MIRRORs, so there is no reason why
# we shouldn't too--that way we could just let it build /etc/apt/sources.list

usage ()
{
    cat << EOF 1>&2
usage:	${0##*/} [OPTIONS] --hostname x --domain y.z --ip 1.2.3.4
	(see --help for more information)
EOF
}
full_usage ()
{
	cat << EOF
Usage:	${0##*/} [OPTIONS] --hostname x --domain y.z --ip 1.2.3.4 --nfs-server 2.3.4.5
Creates a new Debian vserver by downloading packages via HTTP/FTP

Options:
        -h, --help		this help
        -V, --version		copyright and version information
	--arch			set target architecture (eg. --arch "i386")
				(autodetected on Debian host if dpkg available)
	--copy-vreboot		install "vreboot/vshutdown/vhalt"
	--no-copy-vreboot	don't install "vreboot/vshutdown/vhalt"
	--dist			defaults to "woody", passed to debootstrap.
	--fakeinit		use "/sbin/init" to boot vserver
	--conffile		extra configuration file to load.
	--interface		interface for IP addresses (if not "eth0")
	--mirror		Debian HTTP/FTP mirror (including the /debian)
	--sshkeys		copy pub-keys to "/root/.ssh/authorized_keys"
	-v, --verbose		show extra output during setup
	--vsroot		location of "/vserver/" directory
Required:
        --nfs-server            The ip of the nfs server to use.
	--hostname		hostname for new vserver (eg. "alpha")
	--domain		dns domain for new vserver (eg. "example.com")
	--ip			IPv4 address for new vserver

EOF
}
full_version ()
{
    cat << EOF
${0##*/} version $VERSION
Copyright (c) 2002 Mark Lawrence <nomad@null.net>
Copyright (c) 2002 Paul Sladen   <vserver@paul.sladen.org>
Copyright (c) 2003 Ola Lundqvist <opal@debian.org>

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 2 of the License, or (at
your option) any later version.

EOF
}
parse_args ()
{
	while [ $# -gt 0 ] ; do
		#echo "parse_args() doing :$#: :$1: :$*:"
		case "$1" in
		    --help|-h)
			full_usage
			shift
			exit 0
			;;
		    --version|-V)
			full_version
			shift
			exit 0
			;;
		    --arch)
			case "$2" in
			    [a-z]*)
				ARCH="$2"
				;;
			    *)
				echo "${0##*/} error: $1 overrides dpkg autodetect on non-Debian host-servers" 1>&2
				echo 'e.g. "i386"' 1>&2
				exit 1
				;;
			esac
			shift 2
			;;
		    --conffile)
		        if [ -r "$2" ] ; then
			    . "$2"
			else
			    echo "Error, can not load config file $2."
			    exit 1
			fi
			shift 2
			;;
		    --copy-vreboot)
			COPY_VREBOOT="true"
			shift
			;;
		    --nfs-server)
		        NFS_SERVER=$2
			shift 2
			;;
		    --no-copy-vreboot)
			COPY_VREBOOT=""
			shift
			;;
		    --dist)
			case "$2" in
			    [a-z]*)
				DIST="$2"
				if [ "woody" != "$2" ]; then
				    echo "${0##*/} warning: I only know how to do \"woody\", be careful!" 1>&2
				fi
				;;
			    *)
				echo "${0##*/} error: $1 requires a Debian distribution" 1>&2
				echo 'e.g. "woody"' 1>&2
				exit 1
				;;
			esac
			shift 2
			;;
		    --domain)
			case "$2" in
			    [a-z]*[a-z])
				VDOMAIN="$2"
				;;
			    *)
				echo "${0##*/} error: $1 requires a dns domain-name" 1>&2
				echo 'e.g. "example.com"' 1>&2
				exit 1
				;;
			esac
			shift 2
			;;
		    --fakeinit)
		        # Note space at beginning--this gets tagged straight on
			FAKEINIT=" fakeinit"
			shift
			;;
		    --hostname)
			case "$2" in
			    [a-z]*[a-z0-9])
				VHOST="$2"
				;;
			    *)
				echo "${0##*/} error: $1 must be a hostname for the vserver" 1>&2
				echo 'e.g. "alpha"' 1>&2
				exit 1
				;;
			esac
			shift 2
			;;
		    --interface)
			case "$2" in
			    [a-z]*)
				INTERFACE="$2"
				;;
			    *)
				echo "${0##*/} error: $1 must be followed by a network interface" 1>&2
				echo 'e.g. "eth1"' 1>&2
				exit 1
				;;
			esac
			shift 2
			;;
		    --ip)
			# This does for an octet: ([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]) ;-)
			case "$2" in
			    [0-9]*.[0-9]*.[0-9]*.[0-9]*)
				IP="$2"
				;;
			    *)
				echo "${0##*/} error: $1 requires a single IPv4  e.g. \"192.168.100.1\"" 1>&2
				exit 1
				;;
			esac
			shift 2
			;;
		    --mirror)
			case "$2" in
			    [hf]*:/*)
				MIRROR="$2"
				;;
			    *)
				echo "${0##*/} error: $1 requires a debian mirror"  1>&2
				echo 'e.g. "http://ftp.uk.debian.org/debian"' 1>&2
				exit 1
				;;
			esac
			shift 2
			;;
		    --verbose|-v)
			export verbose="true"
			shift
			;;
		    --sshkeys)
			if [ -f "$2" ]; then
			    SSH_KEYS="$2"
			else
			    echo "${0##*/} error: $1 must be followed by a valid public-key-file!" 1>&2
			    echo 'e.g. "/root/.ssh/id_dsa.pub"' 1>&2
			    exit 1
			fi
			shift 2
			;;
		    --vsroot)
			case "$2" in
			    /*)
				if [ -d "$2" ]; then
				    VROOTDIR="$2"
				else
				    echo "${0##*/} error: $1 needs a valid absolute directory"  1>&2
				    echo 'e.g. "/vservers"' 1>&2
				    exit 1
				fi
				;;
			    *)
				echo "${0##*/} error: $1 needs a valid absolute directory"  1>&2
				echo 'e.g. "/vservers"' 1>&2
				exit 1
				;;
			esac
			shift 2
			;;
		    -*)
			usage
			exit 1
			;;
		    ?*)
			usage
			exit 1
			;;
		esac
	done
}

parse_args $@

if ! [ -n "$VHOST" -a -n "$VDOMAIN" -a -n "$IP" -a -n "$NFS_SERVER" ]; then
    echo "${0##*/} error: --hostname, --domain, --ip and --nfs-server are required" 1>&2
    usage
    exit 1
fi

# Strip final slashes off a couple of things
MIRROR="${MIRROR%/}"
VROOTDIR="${VROOTDIR%/}"

# Check we've got debootstrap available
if [ ! -x /usr/sbin/debootstrap ]; then
  cat << EOF 1>&2
${0##*/}: Requires the debootstrap package to bootstrap Debian
  Debian Host:	apt-get install debootstrap
  RPM Host:	rpm -i http://people.debian.org/~blade/install/debootstrap/debootstrap-0.1.17.3-2.i386.rpm
EOF
  exit 1
fi

if ! cat /proc/self/status | grep '^s_context:[^0-9]0$'; then
    echo "${0##*/} error:"
    echo "	Must be run from the host server (security context 0)" 1>&2
    echo '	on a "vserver/ctx-patch" enabled kernel' 1>&2
    echo '	See: http://www.solucorp.qc.ca/miscprj/s_context.hc' 1>&2
    exit 1
fi

if [ -x /usr/bin/id ] && [ `id -u` -ne 0 ]; then
  echo "${0##*/} error: Must be run as root!" 1>&2
  exit 1
fi

# check for /vserver/$VHOST/etc/ incase we are on pre-mounted LVM partition
# (used to just check for "/vserver/$VHOST/" existing
if [ -d "$VROOTDIR/$VHOST/etc/" -o -f "/etc/vservers/$VHOST.conf" ] ; then
  cat << EOF 1>&2
${0##*/} error: Virtual Server "$VHOST" appears to already exist
  check "/etc/vservers/$VHOST.conf" or "/vservers/$VHOST/etc/";
EOF
  exit 1
fi

# This is used to keep a cache of the downloaded .deb packges for next install
if [ -d "$VROOTDIR/ARCHIVES/$DIST" ]; then
    mkdir -p "$VROOTDIR/$VHOST/var/cache/apt/archives"
    cp -a "$VROOTDIR/ARCHIVES/$DIST/"*.deb "$VROOTDIR/$VHOST/var/cache/apt/archives"
fi

# We only want to pass the Architecture if we need to (autodectected otherwise)
if [ -n "$ARCH" ]; then
    ARCH_ARGUMENT="--arch $ARCH"
fi

# Fire off `debootstrap' to do all the hard work
# like downloading and installing
if ! /usr/sbin/debootstrap $ARCH_ARGUMENT \
	"--include=$INSTALL_PACKAGES" "--exclude=$REMOVE_PACKAGES" \
	"$DIST" "$VROOTDIR/$VHOST" "$MIRROR" ; then
    echo "${0##*/}: error: debootstrap failure. Cannot continue."
    exit 1
fi

# Make it so that apt and friends work
cat << EOF > "$VROOTDIR/$VHOST/etc/apt/sources.list"
deb $MIRROR/ $DIST main non-free contrib
deb-src $MIRROR/ $DIST main non-free contrib

deb http://non-us.debian.org/debian-non-US $DIST/non-US main contrib non-free
deb-src http://non-us.debian.org/debian-non-US $DIST/non-US main contrib non-free

deb http://security.debian.org $DIST/updates main contrib non-free

EOF

# Fix up the available device nodes, for security
if cd "$VROOTDIR/$VHOST/dev"; then
    tar cfp /var/cache/vserver/dev.tar.$$ full null ptmx random tty urandom zero
    rm -rf "$VROOTDIR/$VHOST/dev/"*
    tar xfp /var/cache/vserver/dev.tar.$$
    rm /var/cache/vserver/dev.tar.$$
    mkdir pts
    mkdir shm
fi

# Give the new host a hostname
echo "$VHOST" > "$VROOTDIR/$VHOST/etc/hostname"

# Set up the /etc/hosts file (needed for some parts of the base-config)
cat << EOF > "$VROOTDIR/$VHOST/etc/hosts"
# /etc/hosts

127.0.0.1	localhost
$IP	$VHOST.$VDOMAIN $VHOST

# The following lines are desirable for IPv6 capable hosts
# (added automatically by netbase upgrade)

::1	ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts

EOF

# Set up network interface
cat <<EOF > "$VROOTDIR/$VHOST/etc/hosts"
# /etc/network/interfaces -- configuration file for ifup(8), ifdown(8)

# The loopback interface
auto lo
iface lo inet loopback

# Do NEVER add the interface that you boot from here!

EOF

# grab DNS servers from the host-server for `resolv.conf'
(echo search $VDOMAIN ; grep '^nameserver' /etc/resolv.conf) \
    > "$VROOTDIR/$VHOST/etc/resolv.conf"

# If there is a proxy server statement in-use in the Host server, copy it across
if [ -f /etc/apt/apt.conf ]; then
    cp /etc/apt/apt.conf $VROOTDIR/$VHOST/etc/apt/apt.conf
fi

# This old uname was putting the name of the *host* into the vserver motd
# uname -a > $VROOTDIR/$VHOST/etc/motd

# Create a shorter motd (uname -a would give name of host-server)
echo "Debian GNU/Linux ($DIST/$(uname -m)) $VHOST.$VDOMAIN" \
    > "$VROOTDIR/$VHOST/etc/motd"

mkdir -p $VROOTDIR/$VHOST/nfsroot

# Create a dummy fstab
cat << EOF > "$VROOTDIR/$VHOST/etc/fstab"
# /etc/fstab: static file system information.
#
# <file system> <mount point>	<type>	<options>			<dump>	<pass>
$NFS_SERVER:$VROOTDIR/$VHOST /   nfs defaults,rw,nfsvers=3		0       0
$NFS_SERVER:$VROOTDIR/$VHOST /nfsroot nfs  defaults,ro,nfsvers=3	0       0
$NFS_SERVER:$VROOTDIR/usr    /usr   nfs defaults,ro,nfsvers=3	0	0
#$NFS_SERVER:$VROOTDIR/etc    /etc   nfs defaults,rw,nfsvers=3	0	0
$NFS_SERVER:$VROOTDIR/lib    /lib   nfs defaults,ro,nfsvers=3	0	0
$NFS_SERVER:$VROOTDIR/boot   /boot   nfs defaults,ro,nfsvers=3	0	0
$NFS_SERVER:$VROOTDIR/bin    /bin   nfs defaults,ro,nfsvers=3	0	0
$NFS_SERVER:$VROOTDIR/sbin   /sbin   nfs defaults,ro,nfsvers=3	0	0
$NFS_SERVER:$VROOTDIR/opt   /opt   nfs defaults,ro,nfsvers=3	0	0
proc		/proc		proc	defaults			0	0
EOF

# The new vserver patch now automatically mounts /proc
# Debian needs /dev/pts mounting "gid=5,mode=620" by vserver
cat << EOF > "$VROOTDIR/$VHOST/etc/mtab"
/dev/hdv1 / vfs none 0 0
proc /proc proc rw 0 0
devpts /dev/pts devpts rw,gid=5,mode=620 0 0
EOF

# Create default /etc/vservers entry
cat << EOF > /etc/vservers/$VHOST.conf
S_HOSTNAME="$VHOST"
IPROOT="$IP"
IPROOTDEV="$INTERFACE"
ONBOOT="yes"
S_NICE=""
S_FLAGS="lock nproc$FAKEINIT"
ULIMIT="-H -u 256 -n 1024"
S_CAPS="CAP_NET_RAW"

# *NOT* DNS domain name, for NIS only
S_DOMAINNAME=""

EOF

if [ -n "$EXTRA_PACKAGES" ]; then
    EXTRA_PACKAGES_INSTALL="apt-get --assume-yes install ${EXTRA_PACKAGES//,/ }"
fi

# ------------------------------------------------------------
# From here on we do things live in the server
# Generate the script that runs the rest of the setup from within the
# virtual server.
cat << EOF > $VROOTDIR/$VHOST/vserver-config.sh
#!/bin/sh

dselect update

tzsetup -y

dpkg-reconfigure passwd

tasksel

rm -f /etc/exim/exim.conf
eximconfig

# because the --exclude flag doesn\'t seem to work on debootstrap
apt-get --assume-yes --purge remove `echo $REMOVE_PACKAGES | sed -e 's/,/ /g'`

for link in $REMOVE_LINKS
do
    update-rc.d -f \$link remove
done

$EXTRA_PACKAGES_INSTALL

EOF

# Run the above commands from within the server
chmod 755 $VROOTDIR/$VHOST/vserver-config.sh
vserver $VHOST exec /vserver-config.sh
rm -f $VROOTDIR/$VHOST/vserver-config.sh

# If you need to install your SSH management keys into the vserver
if [ -f "$SSH_KEYS" ]; then
    mkdir -p "$VROOTDIR/$VHOST/root/.ssh"
    chmod 700 "$VROOTDIR/$VHOST/root/.ssh/"
    cat "$SSH_KEYS" >> "$VROOTDIR/$VHOST/root/.ssh/authorized_keys"
    chmod 600 "$VROOTDIR/$VHOST/root/.ssh/authorized_keys"
fi

# Install the vreboot/rebootmgr utility--hopefully this will disappear soon
VREBOOT_LOCATION="/usr/lib/util-vserver/vreboot"
if [ "$COPY_VREBOOT" == "true" -a -x "$VREBOOT_LOCATION" ]; then
    cp -a "$VREBOOT_LOCATION" "$VROOTDIR/$VHOST/usr/local/sbin/"
    chmod 750 "$VROOTDIR/$VHOST/usr/local/sbin/vreboot"
    # ln -s "$VROOTDIR/$VHOST/usr/local/sbin/"{vshutdown,vreboot}
    # ln -s "$VROOTDIR/$VHOST/usr/local/sbin/"{vhalt,vreboot}
fi

# Stop all the processes that were started inside the server
export PREVLEVEL=2
vserver $VHOST exec /etc/init.d/rc 0
vserver $VHOST stop

cat $VROOTDIR/$VHOST/etc/nfsbooted/fstab >> $VROOTDIR/$VHOST/etc/fstab

# Populate the archive for future virtual servers
if [ ! -d $VROOTDIR/ARCHIVES/$DIST ]; then
    mkdir -p $VROOTDIR/ARCHIVES/$DIST
fi
cp $VROOTDIR/$VHOST/var/cache/apt/archives/*.deb $VROOTDIR/ARCHIVES/$DIST

echo "Restarting rebootmgr."
invoke-rc.d rebootmgr restart
echo
echo "You should now adjust /etc/vservers/$VHOST.conf to suit your needs,"
echo "or else just go ahead and type \`vserver $VHOST start' to start"
echo "your new virtual server.  debian/rules!"
echo
echo "Also make sure that /var/lib/vservers/ is exported from /etc/exports."
echo
