#!/bin/bash
VERSION='0.7'
#
# Copyright (C) 2002-2010  Ola Lundqvist <ola@inguza.com>
# Copyright (C) 2006       Miroslav Kure <kurem@upcase.inf.upol.cz>
# Copyright (C) 2002       Mark Lawrence <nomad@null.net>
# Copyright (C) 2002       Paul Sladen   <vserver@paul.sladen.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., 51 Franklin St, Fifth Floor, Boston,
# MA 02110-1301, USA.
#
# Strip a server so it can function as a virtual Debian server (vserver)
#
# ----------------------------------------------------------------------
# Configurable items:

shopt -s extglob

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

# Packages to install in addition to the base defaults
# MUST INCLUDE ALL DEPENDENCIES (seperated by "," commas)
INSTALL_PACKAGES=""

if [ -n "$LANG" ] && [ "$LANG" != "C" ] ; then
    INSTALL_PACKAGES="$INSTALL_PACKAGES locales"
fi

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

# 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="dhcp-client,lilo,makedev,pcmcia-cs,ppp,pppconfig,pppoe,pppoeconf,setserial,syslinux,fdutils,iptables,libpcap0,pciutils,ntpd,ntpdate,ntp-simple,initrd-tools,isapnptools,modutils,modconf"
#REMOVE_PACKAGES="dhcp-client,lilo,makedev,pcmcia-cs,ppp,pppconfig,pppoe,pppoeconf,setserial,syslinux,fdutils,iptables,libpcap0,pciutils"

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

# 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
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			Deprecated option.
	--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:
	--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-2010  Ola Lundqvist <ola@inguza.com>
Copyright (C) 2006       Miroslav Kure <kurem@upcase.inf.upol.cz>
Copyright (C) 2002       Mark Lawrence <nomad@null.net>
Copyright (C) 2002       Paul Sladen   <vserver@paul.sladen.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
			;;
		    --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
			;;
		    --no-copy-vreboot)
			COPY_VREBOOT=""
			shift
			;;
		    --dist)
		        echo -- "--dist is a depciated option for stripserver."
			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-z0-9]?(*([a-z0-9_-])[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
			;;
		    --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 or /etc/vservers/.defaults/vdirbase"' 1>&2
				    exit 1
				fi
				;;
			    *)
				echo "${0##*/} error: $1 needs a valid absolute directory"  1>&2
				echo 'e.g. "/vservers or /etc/vservers/.defaults/vdirbase"' 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" ]; then
    echo "${0##*/} error: --hostname, --domain and --ip are required" 1>&2
    usage
    exit 1
fi

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

if ! cat /proc/self/status | grep '^\(VxID:[^0-9]0\|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 have already stripped once.
if [ -f "/etc/vservers/$VHOST.conf" ] ; then
  cat << EOF 1>&2
${0##*/} error: Virtual Server "$VHOST" appears to already exist
  check "/etc/vservers/$VHOST.conf";
EOF
  exit 1
fi

# check for /vserver/$VHOST/etc/ incase we have already stripped once.
if [ ! -d "$VROOTDIR/$VHOST/etc" ] ; then
  cat << EOF 1>&2
${0##*/} error: Virtual Server "$VHOST" dir do not exist
  check "$VROOTDIR/$VHOST/etc"
EOF
  exit 1
fi

# 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

# Create a locale.gen if needed.
if [ -n "$LANG" ] && [ "$LANG" != "C" ] ; then
    if [ ! -e "$VROOTDIR/$VHOST/etc/locale.gen" ] ; then
	echo $LANG $(locale charmap) > "$VROOTDIR/$VHOST/etc/locale.gen"
    elif ! grep $LANG "$VROOTDIR/$VHOST/etc/locale.gen" > /dev/null ; then
	echo $LANG $(locale charmap) >> "$VROOTDIR/$VHOST/etc/locale.gen"
    fi
fi

# Give the new host a hostname
if ! grep $VHOST "$VROOTDIR/$VHOST/etc/hostname" > /dev/null ; then
    echo "Hostname not found in hostname file, rewriting and creating backup."
    mv "$VROOTDIR/$VHOST/etc/hostname" "$VROOTDIR/$VHOST/etc/hostname.backup"
    echo "$VHOST" > "$VROOTDIR/$VHOST/etc/hostname"
fi

if ! grep $VHOST "$VROOTDIR/$VHOST/etc/hosts" > /dev/null ; then
    echo "Hostname not found in hosts file, rewriting and creating backup."
    mv "$VROOTDIR/$VHOST/etc/hosts" "$VROOTDIR/$VHOST/etc/hostname.hosts.backup"
# 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
fi
# grab DNS servers from the host-server for `resolv.conf'
#(echo search $VDOMAIN ; grep '^nameserver' /etc/resolv.conf) \
#    > "$VROOTDIR/$VHOST/etc/resolv.conf"

# Create a dummy fstab
cat << EOF > "$VROOTDIR/$VHOST/etc/fstab"
# /etc/fstab: static file system information.
#
# <file system> <mount point>	<type>	<options>			<dump>	<pass>
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 a reduced inittab that doesn't start getty on the consoles
cat << EOF > "$VROOTDIR/$VHOST/etc/inittab"
# /etc/inittab: init(8) configuration.

# The default runlevel.
id:2:initdefault:

# Boot-time system configuration/initialization script.
# This is run first except when booting in emergency (-b) mode.
si::sysinit:/etc/init.d/rcS

# What to do in single-user mode.
~~:S:wait:/sbin/sulogin

# /etc/init.d executes the S and K scripts upon change
# of runlevel.
#
# Runlevel 0 is halt.
# Runlevel 1 is single-user.
# Runlevels 2-5 are multi-user.
# Runlevel 6 is reboot.

l0:0:wait:/etc/init.d/rc 0
l1:1:wait:/etc/init.d/rc 1
l2:2:wait:/etc/init.d/rc 2
l3:3:wait:/etc/init.d/rc 3
l4:4:wait:/etc/init.d/rc 4
l5:5:wait:/etc/init.d/rc 5
l6:6:wait:/etc/init.d/rc 6

EOF

# By default the Debian Install script runs zillions of cron jobs at
# 0625 every morning.  On a system with lots of vservers all trying to
# scan the disk at the same time this causes $MAJOR disk-thrash.  So
# we randomize it a bit so that they run evenly between 1am and 7am,
# avoiding the 5minutes either side of the hour when other stuff tends
# to be scheduled.  (BTW, this solution is Overkill!)

# This looks over complicated--and it probably is...:
#
# a) We want the DAILY jobs to run between :05 and :55 minutes past
# b) We want the WEEKLY job 3-5 minutes after the DAILY.
# c) And the MONTHLY job 3-5 minutes after that.
# d) Make sure all three jobs are started by 55minutes past (five-to)
#    ...if they were ever to all run on the same day!

d1=$(($RANDOM % 3 + 3));      #	  between  3 and  5
d2=$(($RANDOM % 3 + 3));      #	  between  3 and  5
dt=$((50 - $d1 - $d2));	      #	  between  0 and 44

DAILY=$(($RANDOM % $dt + 5))  #	  between  5 and 49
WEEKLY=$(($DAILY + $d1))      #	  between  8 and 52
MONTHLY=$(($WEEKLY + $d2))    #	  between 11 and 55

HOUR=$(($RANDOM % 6 + 1))     #	  between  1 and  7 (AM localtime)

# Create replacement /etc/crontab with randomized times above
cat << EOF > "$VROOTDIR/$VHOST/etc/crontab"
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the \`crontab\'
# command to install the new version when you edit this file.
# This file also has a username field, that none of the other crontabs do.

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# m h dom mon dow user	command
$DAILY $HOUR	* * *	root	test -e /usr/sbin/anacron || run-parts --report /etc/cron.daily
$WEEKLY $HOUR	* * 7	root	test -e /usr/sbin/anacron || run-parts --report /etc/cron.weekly
$MONTHLY $HOUR	1 * *	root	test -e /usr/sbin/anacron || run-parts --report /etc/cron.monthly

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

[ -x /usr/sbin/locale-gen ] && /usr/sbin/locale-gen

apt-get 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

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

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
