#!/bin/bash

#
# lxc: linux Container library

# Authors:
# Daniel Lezcano <daniel.lezcano@free.fr>

# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.

# This library 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
# Lesser General Public License for more details.

# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

install_busybox()
{
    rootfs=$1
    name=$2
    res=0
    tree="\
$rootfs/selinux \
$rootfs/dev \
$rootfs/home \
$rootfs/root \
$rootfs/etc \
$rootfs/etc/init.d \
$rootfs/bin \
$rootfs/sbin \
$rootfs/proc \
$rootfs/mnt \
$rootfs/tmp \
$rootfs/var/log \
$rootfs/usr/share/udhcpc \
$rootfs/dev/pts \
$rootfs/dev/shm \
$rootfs/lib \
$rootfs/usr/lib \
$rootfs/lib64 \
$rootfs/usr/lib64"

    mkdir -p $tree || return 1
    chmod 755 $tree || return 1

    pushd $rootfs/dev > /dev/null || return 1

    # minimal devices needed for busybox
    mknod tty c 5 0       || res=1
    mknod console c 5 1   || res=1
    chmod 666 tty console || res=1
    mknod tty0 c 4 0      || res=1
    mknod tty1 c 4 0      || res=1
    mknod tty5 c 4 0      || res=1
    chmod 666 tty0        || res=1
    mknod ram0 b 1 0      || res=1
    chmod 600 ram0        || res=1
    mknod null c 1 3      || res=1
    chmod 666 null        || res=1

    popd > /dev/null

    # root user defined
    cat <<EOF >> $rootfs/etc/passwd
root:x:0:0:root:/root:/bin/sh
EOF

    cat <<EOF >> $rootfs/etc/group
root:x:0:root
EOF

    # mount everything
    cat <<EOF >> $rootfs/etc/init.d/rcS
#!/bin/sh
/bin/syslogd
/bin/mount -a
/bin/udhcpc
EOF

    # executable
    chmod 744 $rootfs/etc/init.d/rcS || return 1

    # mount points
    cat <<EOF >> $rootfs/etc/fstab
proc  /proc      proc    defaults     0      0
shm   /dev/shm   tmpfs   defaults     0      0
EOF

    # writable and readable for other
    chmod 644 $rootfs/etc/fstab || return 1

    # launch rcS first then make a console available
    # and propose a shell on the tty, the last one is
    # not needed
    cat <<EOF >> $rootfs/etc/inittab
::sysinit:/etc/init.d/rcS
tty1::respawn:/bin/getty -L tty1 115200 vt100
console::askfirst:/bin/sh
EOF
    # writable and readable for other
    chmod 644 $rootfs/etc/inittab || return 1

    cat <<EOF >> $rootfs/usr/share/udhcpc/default.script
#!/bin/sh

case "\$1" in
	deconfig)
		ip addr flush dev \$interface
                ;;

	renew|bound)

                # flush all the routes
		if [ -n "\$router" ]; then
                        ip route del default 2> /dev/null
		fi

                # check broadcast
                if [ -n "\$broadcast" ]; then
                        broadcast="broadcast \$broadcast"
                fi

                # add a new ip address
		ip addr add \$ip/\$mask \$broadcast dev \$interface

		if [ -n "\$router" ]; then
                        ip route add default via \$router dev \$interface
                fi

		[ -n "\$domain" ] && echo search \$domain > /etc/resolv.conf
		for i in \$dns ; do
			echo nameserver \$i >> /etc/resolv.conf
		done
		;;
esac
exit 0
EOF

    chmod 744 $rootfs/usr/share/udhcpc/default.script

    return $res
}

configure_busybox()
{
    rootfs=$1

    functions="\
	[ [[ addgroup adduser adjtimex ar arp arping ash awk basename \
	brctl bunzip2 bzcat bzip2 cal cat catv chattr chgrp chmod \
	chown chpasswd chpst chroot chrt chvt cksum clear cmp comm \
	cp cpio crond crontab cryptpw cut date dc dd deallocvt \
	delgroup deluser df dhcprelay diff dirname dmesg dnsd dos2unix \
	du dumpkmap dumpleases echo ed egrep eject env envdir envuidgid \
	ether-wake expand expr fakeidentd false fbset fdformat fdisk \
	fetchmail fgrep find findfs fold free freeramdisk fsck \
	fsck.minix ftpget ftpput fuser getopt getty grep gunzip gzip \
	halt hdparm head hexdump hostid hostname httpd hwclock id \
	ifconfig ifdown ifenslave ifup inetd init insmod install ip \
	ipaddr ipcalc ipcrm ipcs iplink iproute iprule iptunnel \
	kbd_mode kill killall killall5 klogd last length less linux32 \
	linux64 linuxrc ln loadfont loadkmap logger login logname \
	logread losetup lpd lpq lpr ls lsattr lsmod lzmacat makedevs \
	md5sum mdev mesg microcom mkdir mkfifo mkfs.minix mknod mkswap \
	mktemp modprobe more mount mountpoint msh mt mv nameif nc \
	netstat nice nmeter nohup nslookup od openvt passwd patch \
	pgrep pidof ping ping6 pipe_progress pivot_root pkill poweroff \
	printenv printf ps pscan pwd raidautorun rdate readahead \
	readlink readprofile realpath reboot renice reset resize rm \
	rmdir rmmod route rpm rpm2cpio run-parts runlevel runsv \
	runsvdir rx script sed sendmail seq setarch setconsole \
	setkeycodes setlogcons setsid setuidgid sh sha1sum slattach \
	sleep softlimit sort split start-stop-daemon stat strings \
	stty su sulogin sum sv svlogd swapoff swapon switch_root \
	sync sysctl syslogd tac tail tar taskset tcpsvd tee telnet \
	telnetd test tftp tftpd time top touch tr traceroute \
	true tty ttysize udhcpc udhcpd udpsvd umount uname uncompress \
	unexpand uniq unix2dos unlzma unzip uptime usleep uudecode \
	uuencode vconfig vi vlock watch watchdog wc wget which \
	who whoami xargs yes zcat zcip"

    type busybox >/dev/null

    if [ $? -ne 0 ]; then
	echo "busybox executable is not accessible"
	return 1
    fi

    file $(which busybox) | grep -q "statically linked"
    if [ $? -ne 0 ]; then
	echo "warning : busybox is not statically linked."
	echo "warning : The template script may not correctly"
	echo "warning : setup the container environment."
    fi

    # copy busybox in the rootfs
    cp $(which busybox) $rootfs/bin
    if [ $? -ne 0 ]; then
	echo "failed to copy busybox in the rootfs"
	return 1
    fi

    # do hardlink to busybox for the different commands
    for i in $functions; do ln $rootfs/bin/busybox $rootfs/bin/$i; done

    # relink /sbin/init
    ln $rootfs/bin/busybox $rootfs/sbin/init

    # passwd exec must be setuid
    chmod +s $rootfs/bin/passwd
    touch $rootfs/etc/shadow
    chroot $rootfs /bin/passwd -d root

    echo "No password for 'root', please change !"

    return 0
}

copy_configuration()
{
    path=$1
    rootfs=$2
    name=$3

cat <<EOF >> $path/config
lxc.utsname = $name
lxc.tty = 1
lxc.pts = 1
lxc.rootfs = $rootfs
# uncomment the next line to run the container unconfined:
#lxc.aa_profile = unconfined
EOF

if [ -d "$rootfs/lib" ]; then
cat <<EOF >> $path/config
lxc.mount.entry=/lib $rootfs/lib none ro,bind 0 0
lxc.mount.entry=/usr/lib $rootfs/usr/lib none ro,bind 0 0
EOF
fi

if [ -d "/lib64" ] && [ -d "$rootfs/lib64" ]; then
cat <<EOF >> $path/config
lxc.mount.entry=/lib64 $rootfs/lib64 none ro,bind 0 0
EOF
fi

if [ -d "/usr/lib64" ] && [ -d "$rootfs/usr/lib64" ]; then
cat <<EOF >> $path/config
lxc.mount.entry=/usr/lib64 $rootfs/usr/lib64 none ro,bind 0 0
EOF
fi
}

usage()
{
    cat <<EOF
$1 -h|--help -p|--path=<path>
EOF
    return 0
}

options=$(getopt -o hp:n: -l help,path:,name: -- "$@")
if [ $? -ne 0 ]; then
        usage $(basename $0)
	exit 1
fi
eval set -- "$options"

while true
do
    case "$1" in
        -h|--help)      usage $0 && exit 0;;
        -p|--path)      path=$2; shift 2;;
	-n|--name)      name=$2; shift 2;;
        --)             shift 1; break ;;
        *)              break ;;
    esac
done

if [ "$(id -u)" != "0" ]; then
    echo "This script should be run as 'root'"
    exit 1
fi

if [ -z "$path" ]; then
    echo "'path' parameter is required"
    exit 1
fi

rootfs=$path/rootfs

install_busybox $rootfs $name
if [ $? -ne 0 ]; then
    echo "failed to install busybox's rootfs"
    exit 1
fi

configure_busybox $rootfs
if [ $? -ne 0 ]; then
    echo "failed to configure busybox template"
    exit 1
fi

copy_configuration $path $rootfs $name
if [ $? -ne 0 ]; then
    echo "failed to write configuration file"
    exit 1
fi
