#!/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

usage() {
    echo "usage: lxc-create -n <name> [-f configuration] [-t template] [-h] [fsopts] -- [template_options]"
    echo "   fsopts: -B none"
    echo "   fsopts: -B lvm [--lvname lvname] [--vgname vgname] [--fstype fstype] [--fssize fssize]"
    echo "   fsopts: -B btrfs"
    echo "           flag is not necessary, if possible btrfs support will be used"
#    echo "   fsopts: -B union [--uniontype overlayfs]"
#    echo "   fsopts: -B loop [--fstype fstype] [--fssize fssize]"
#    echo "   fsopts: -B qemu-nbd [--type qed|qcow2|raw] [--fstype fstype] [--fssize fssize] # Qemu qed disk format"
}

help() {
    usage
    echo
    echo "creates a lxc system object."
    echo
    echo "Options:"
    echo "name         : name of the container"
    echo "configuration: lxc configuration"
    echo "template     : lxc-template is an accessible template script"
    echo
    echo "The container backing store can be altered using '-B'.  By default it"
    echo "is 'none', which is a simple directory tree under /var/lib/lxc/<name>/rootfs"
    echo "Otherwise, the following option values may be relevant:"
    echo "lvname       : [for -lvm] name of lv in which to create lv,"
    echo "                container-name by default"
    echo "vgname       : [for -lvm] name of vg in which to create lv, 'lxc' by default"
    echo "fstype       : name of filesystem to create, ext4 by default"
    echo "fssize       : size of filesystem to create, 500M by default"
    echo
    if [ -z $lxc_template ]; then
        echo "for template-specific help, specify a template, for instance:"
        echo "lxc-create -t ubuntu -h"
        exit 0
    fi
    type ${templatedir}/lxc-$lxc_template >/dev/null
    echo
    echo "template-specific help follows: (these options follow '--')"
    if [ $? -eq 0 ]; then
        ${templatedir}/lxc-$lxc_template -h
    fi
}

shortoptions='hn:f:t:B:'
longoptions='help,name:,config:,template:,backingstore:,fstype:,lvname:,vgname:,fssize:'
localstatedir=/var
lxc_path=${localstatedir}/lib/lxc
bindir=/usr/bin
libdir=/usr/lib
templatedir=${libdir}/lxc/templates
backingstore=_unset
fstype=ext4
fssize=500M
vgname=lxc

getopt=$(getopt -o $shortoptions --longoptions  $longoptions -- "$@")
if [ $? != 0 ]; then
    usage
    exit 1;
fi

eval set -- "$getopt"

while true; do
        case "$1" in
	    -h|--help)
		help
		exit 1
		;;
	    -n|--name)
		shift
		lxc_name=$1
		shift
		;;
	    -f|--config)
		shift
		lxc_config=$1
		shift
		;;
	    -t|--template)
		shift
		lxc_template=$1
		shift
		;;
	    -B|--backingstore)
		shift
		backingstore=$1
		shift
		;;
	    --lvname)
		shift
		lvname=$1
		shift
		;;
	    --vgname)
		shift
		vgname=$1
		shift
		;;
	    --fstype)
		shift
		fstype=$1
		shift
		;;
	    --fssize)
		shift
		fssize=$1
		shift
		;;
            --)
		shift
		break;;
            *)
		echo $1
		usage
		exit 1
		;;
        esac
done

if [ -z "$lxc_path" ]; then
    echo "no configuration path defined !"
    exit 1
fi

if [ ! -r $lxc_path ]; then
    echo "configuration path '$lxc_path' not found"
    exit 1
fi

if [ -z "$lxc_name" ]; then
    echo "no container name specified"
    usage
    exit 1
fi

if [ -z "$lvname" ]; then
    lvname="$lxc_name"
fi

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

case "$backingstore" in
    lvm|none|btrfs|_unset) :;;
    *) echo "'$backingstore' is not known ('none', 'lvm', 'btrfs')"
        usage
        exit 1
        ;;
esac

if [ -d "$lxc_path/$lxc_name" ]; then
    echo "'$lxc_name' already exists"
    exit 1
fi

rootfs="$lxc_path/$lxc_name/rootfs"

if [ "$backingstore" = "_unset" -o "$backingstore" = "btrfs" ]; then
# if no backing store was given, then see if btrfs would work
    if which btrfs >/dev/null 2>&1 &&
        btrfs filesystem df "$lxc_path/" >/dev/null 2>&1; then
        backingstore="btrfs"
    else
        if [ "$backingstore" = "btrfs" ]; then
            echo "missing 'btrfs' command or $lxc_path is not btrfs";
            exit 1;
        fi
        backingstore="none"
    fi
fi

if [ $backingstore = "lvm" ]; then
    which vgscan > /dev/null
    if [ $? -ne 0 ]; then
        echo "vgscan not found.  Please install lvm2 package"
        exit 1
    fi
    grep -q "\<$fstype\>" /proc/filesystems
    if [ $? -ne 0 ]; then
        echo "$fstype is not listed in /proc/filesystems"
        usage
        exit 1
    fi

    vgscan | grep -q "Found volume group \"$vgname\""
    if [ $? -ne 0 ]; then
        echo "Could not find volume group \"$vgname\""
        usage
        exit 1
    fi

    rootdev=/dev/$vgname/$lvname
    lvdisplay $rootdev > /dev/null 2>&1
    if [ $? -eq 0 ]; then
        echo "backing store already exists: $rootdev"
        echo "please delete it (using \"lvremove $rootdev\") and try again"
        exit 1
    fi
elif [ "$backingstore" = "btrfs" ]; then
    mkdir "$lxc_path/$lxc_name"
    if ! out=$(btrfs subvolume create "$rootfs" 2>&1); then
        echo "failed to create subvolume in $rootfs: $out";
        exit 1;
    fi
fi

cleanup() {
    if [ $backingstore = "lvm" ]; then
        umount $rootfs
        lvremove -f $rootdev
    fi
    ${bindir}/lxc-destroy -n $lxc_name
    echo aborted
    exit 1
}

trap cleanup SIGHUP SIGINT SIGTERM

mkdir -p $lxc_path/$lxc_name

if [ -z "$lxc_config" ]; then
    touch $lxc_path/$lxc_name/config
else
    if [ ! -r "$lxc_config" ]; then
	echo "'$lxc_config' configuration file not found"
	exit 1
    fi

    cp $lxc_config $lxc_path/$lxc_name/config
fi

# Create the fs as needed
[ -d "$rootfs" ] || mkdir $rootfs
if [ $backingstore = "lvm" ]; then
    lvcreate -L $fssize -n $lvname $vgname || exit 1
    udevadm settle
    mkfs -t $fstype $rootdev || exit 1
    mount -t $fstype $rootdev $rootfs
fi

if [ ! -z $lxc_template ]; then

    type ${templatedir}/lxc-$lxc_template >/dev/null
    if [ $? -ne 0 ]; then
        echo "unknown template '$lxc_template'"
        cleanup
    fi

    if [ -z "$lxc_config" ]; then
	echo
	echo "Warning:"
	echo "-------"
	echo "Usually the template option is called with a configuration"
	echo "file option too, mostly to configure the network."
	echo "eg. lxc-create -n foo -f lxc.conf -t debian"
	echo "The configuration file is often:"
	echo
	echo "lxc.network.type=macvlan"
	echo "lxc.network.link=eth0"
	echo "lxc.network.flags=up"
	echo
	echo "or alternatively:"
	echo
	echo "lxc.network.type=veth"
	echo "lxc.network.link=br0"
	echo "lxc.network.flags=up"
	echo
	echo "For more information look at lxc.conf (5)"
	echo
	echo "At this point, I assume you know what you do."
	echo "Press <enter> to continue ..."
	read dummy
    fi

    ${templatedir}/lxc-$lxc_template --path=$lxc_path/$lxc_name --name=$lxc_name $*
    if [ $? -ne 0 ]; then
        echo "failed to execute template '$lxc_template'"
        cleanup
    fi

    echo "'$lxc_template' template installed"
fi

if [ $backingstore = "lvm" ]; then
    echo "Unmounting LVM"
    umount $rootfs

    # TODO: make the templates set this right from the start?
    sed -i '/lxc.rootfs/d' $lxc_path/$lxc_name/config
    echo "lxc.rootfs = $rootdev" >> $lxc_path/$lxc_name/config
fi

echo "'$lxc_name' created"
