#!/bin/bash

#
# lxc: linux Container library

# Authors:
# Serge Hallyn <serge.hallyn@ubuntu.com>
# 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

set -e

usage() {
    echo "usage: lxc-clone -o <orig> -n <new> [-s] [-h] [-L fssize] [-v vgname] [-p lxc_lv_prefix] [-t fstype]"
}

help() {
    usage
    echo
    echo "creates a lxc system object."
    echo
    echo "Options:"
    echo "orig        : name of the original container"
    echo "new         : name of the new container"
    echo "-s          : make the new rootfs a snapshot of the original"
    echo "fssize      : size if creating a new fs.  By default, 2G"
    echo "vgname      : lvm volume group name, lxc by default"
    echo "lvprefix    : lvm volume name prefix, none by default, e.g. --lvprefix=lxc_ then new lxc lv name will be lxc_newname"
    echo "fstype      : new container file system type, ext3 by default (only works for non-snapshot lvm)"
}

shortoptions='ho:n:sL:v:p:t:'
longoptions='help,orig:,name:,snapshot,fssize:,vgname:,lvprefix:,fstype:'
localstatedir=/var
lxc_path=${localstatedir}/lib/lxc
bindir=/usr/bin
snapshot=no
lxc_size=2G
lxc_vg=lxc
lxc_lv_prefix=""
fstype=ext3

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
            ;;
        -s|--snapshot)
            shift
            snapshot=yes
            snapshot_opt="-s"
            ;;
        -o|--orig)
            shift
            lxc_orig=$1
            shift
            ;;
        -L|--fssize)
            shift
            lxc_size=$1
            shift
            ;;
        -v|--vgname)
            shift
            lxc_vg=$1
            shift
            ;;
        -n|--new)
            shift
            lxc_new=$1
            shift
            ;;
        -p|--lvprefix)
            shift
            lxc_lv_prefix=$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_orig" ]; then
    echo "no original container name specified"
    usage
    exit 1
fi

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

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

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

if [ ! -d "$lxc_path/$lxc_orig" ]; then
    echo "'$lxc_orig' does not exist"
    exit 1
fi

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

mounted=0
frozen=0
oldroot=`grep lxc.rootfs $lxc_path/$lxc_orig/config | awk -F= '{ print $2 '}`

cleanup() {
    if [ -b $oldroot ]; then
        if [ $mounted -eq 1 ]; then
            umount $rootfs || true
        fi
        lvremove -f $rootdev || true
    fi
    ${bindir}/lxc-destroy -n $lxc_new || true
    if [ $frozen -eq 1 ]; then
        lxc-unfreeze -n $lxc_orig
    fi
    echo aborted
    exit 1
}
trap cleanup SIGHUP SIGINT SIGTERM

mkdir -p $lxc_path/$lxc_new
hostname=$lxc_new

echo "Tweaking configuration"
cp $lxc_path/$lxc_orig/config $lxc_path/$lxc_new/config
sed -i '/lxc.utsname/d' $lxc_path/$lxc_new/config
echo "lxc.utsname = $hostname" >> $lxc_path/$lxc_new/config

grep "lxc.mount =" $lxc_path/$lxc_new/config >/dev/null 2>&1 && { sed -i '/lxc.mount =/d' $lxc_path/$lxc_new/config; echo "lxc.mount = $lxc_path/$lxc_new/fstab" >> $lxc_path/$lxc_new/config; }

if [ -e  $lxc_path/$lxc_orig/fstab ];then
    cp $lxc_path/$lxc_orig/fstab $lxc_path/$lxc_new/fstab
    sed -i "s@$lxc_path/$lxc_orig@$lxc_path/$lxc_new@" $lxc_path/$lxc_new/fstab
fi

echo "Copying rootfs..."
rootfs=$lxc_path/$lxc_new/rootfs

container_running=True
lxc-info -s -n $lxc_orig|grep RUNNING >/dev/null 2>&1 || container_running=False

sed -i '/lxc.rootfs/d' $lxc_path/$lxc_new/config
oldroot=`grep lxc.rootfs $lxc_path/$lxc_orig/config | awk -F= '{ print $2 '}`
if [ -b $oldroot ]; then
    type vgscan || { echo "Please install lvm"; false; }
    lvdisplay $oldroot > /dev/null 2>&1 || { echo "non-lvm blockdev cloning not supported"; false; }
    lvm=TRUE
    # ok, create a snapshot of the lvm device
    if [ $container_running = "True" ]; then
        lxc-freeze -n $lxc_orig
        frozen=1
    fi
    lvcreate -s -L $lxc_size -n ${lxc_lv_prefix}${lxc_new}_snapshot $oldroot
    if [ $container_running = "True" ]; then
        lxc-unfreeze -n $lxc_orig
        frozen=0
    fi
    if [ $snapshot = "no" ]; then
        #mount snapshot
        mkdir -p ${rootfs}_snapshot
        mount /dev/$lxc_vg/${lxc_lv_prefix}${lxc_new}_snapshot ${rootfs}_snapshot || { echo "failed to mount new rootfs_snapshot"; false; }
        #create a new lv
        lvcreate -L $lxc_size $lxc_vg -n ${lxc_lv_prefix}$lxc_new
        echo "lxc.rootfs = /dev/$lxc_vg/${lxc_lv_prefix}$lxc_new" >> $lxc_path/$lxc_new/config
        # and mount it so we can tweak it
        mkdir -p $lxc_path/$lxc_new/rootfs
        mkfs -t $fstype /dev/$lxc_vg/${lxc_lv_prefix}$lxc_new
        mount /dev/$lxc_vg/${lxc_lv_prefix}$lxc_new $rootfs || { echo "failed to mount new rootfs"; false; }
        mounted=1
        rsync -ax ${rootfs}_snapshot/ ${rootfs}/ || { echo "copy of data to new lv failed"; false; }
        umount ${rootfs}_snapshot
        rmdir ${rootfs}_snapshot
        lvremove -f $lxc_vg/${lxc_lv_prefix}${lxc_new}_snapshot
    else
        lvrename $lxc_vg/${lxc_lv_prefix}${lxc_new}_snapshot $lxc_vg/${lxc_lv_prefix}$lxc_new
        echo "lxc.rootfs = /dev/$lxc_vg/${lxc_lv_prefix}$lxc_new" >> $lxc_path/$lxc_new/config
        # and mount it so we can tweak it
        mkdir -p $lxc_path/$lxc_new/rootfs
        mount /dev/$lxc_vg/${lxc_lv_prefix}$lxc_new $rootfs || { echo "failed to mount new rootfs"; false; }
        mounted=1
    fi

elif out=$(btrfs subvolume list "$lxc_path/$lxc_orig/rootfs" 2>&1); then

    out=$(btrfs subvolume snapshot "$lxc_path/$lxc_orig/rootfs" "$rootfs" 2>&1) || { echo "failed btrfs snapshot"; false; }
    echo "lxc.rootfs = $rootfs" >> "$lxc_path/$lxc_new/config"

else
    if [ $snapshot = "yes" ]; then
        echo "Can't snapshot a directory"
        cleanup
    fi
    if [ $container_running = "True" ]; then
        lxc-freeze -n $lxc_orig
        frozen=1
    fi
    mkdir -p $lxc_path/$lxc_new/rootfs/
    rsync -ax $lxc_path/$lxc_orig/rootfs/ $lxc_path/$lxc_new/rootfs/
    echo "lxc.rootfs = $rootfs" >> $lxc_path/$lxc_new/config
    if [ $container_running = "True" ]; then
        lxc-unfreeze -n $lxc_orig
        frozen=0
    fi
fi

echo "Updating rootfs..."

# so you can 'ssh $hostname.' or 'ssh $hostname.local'
if [ -f $rootfs/etc/dhcp/dhclient.conf ]; then
    sed -i "s/send host-name.*$/send host-name \"$hostname\";/" $rootfs/etc/dhcp/dhclient.conf
fi

# set the hostname
cat <<EOF > $rootfs/etc/hostname
$hostname
EOF
# set minimal hosts
cat <<EOF > $rootfs/etc/hosts
127.0.0.1 localhost $hostname
EOF

# if this was a block device, then umount it now
if [ $mounted -eq 1 ]; then
    umount $rootfs
fi

echo "'$lxc_new' created"
