#!/bin/bash

#
# template script for generating CentOS container for LXC

#
# lxc: linux Container library

# Authors:
# Daniel Lezcano <daniel.lezcano@free.fr>
# Ramez Hanna <rhanna@informatiq.org>
# Fajar A. Nugraha <github@fajar.net>
# Michael H. Warfield <mhw@WittsEnd.com>

# 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

#Configurations
default_path=@LXCPATH@

# Some combinations of the tuning knobs below do not exactly make sense.
# but that's ok.
#
# If the "root_password" is non-blank, use it, else set a default.
# This can be passed to the script as an environment variable and is
# set by a shell conditional assignment.  Looks weird but it is what it is.
#
# If the root password contains a ding ($) then try to expand it.
# That will pick up things like ${name} and ${RANDOM}.
# If the root password contains more than 3 consecutive X's, pass it as
# a template to mktemp and take the result.
#
# If root_display_password = yes, display the temporary root password at exit.
# If root_store_password = yes, store it in the configuration directory
# If root_prompt_password = yes, invoke "passwd" to force the user to change
# the root password after the container is created.
# If root_expire_password = yes, you will be prompted to change the root
# password at the first login.
#
# These are conditional assignments...  The can be overridden from the
# preexisting environment variables...
#
# Make sure this is in single quotes to defer expansion to later!
# :{root_password='Root-${name}-${RANDOM}'}
: ${root_password='Root-${name}-XXXXXX'}

# Now, it doesn't make much sense to display, store, and force change
# together.  But, we gotta test, right???
: ${root_display_password='no'}
: ${root_store_password='yes'}
# Prompting for something interactive has potential for mayhem
# with users running under the API...  Don't default to "yes"
: ${root_prompt_password='no'}

# Expire root password? Default to yes, but can be overridden from
# the environment variable
: ${root_expire_password='yes'}

# These are only going into comments in the resulting config...
lxc_network_type=veth
lxc_network_link=lxcbr0

# is this CentOS?
# Alow for weird remixes like the Raspberry Pi
#
# Use the Mitre standard CPE identifier for the release ID if possible...
# This may be in /etc/os-release or /etc/system-release-cpe.  We
# should be able to use EITHER.  Give preference to /etc/os-release for now.

# Detect use under userns (unsupported)
for arg in "$@"; do
    [ "$arg" = "--" ] && break
    if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then
        echo "This template can't be used for unprivileged containers." 1>&2
        echo "You may want to try the \"download\" template instead." 1>&2
        exit 1
    fi
done

# Make sure the usual locations are in PATH
export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin

if [ -e /etc/os-release ]
then
# This is a shell friendly configuration file.  We can just source it.
# What we're looking for in here is the ID, VERSION_ID and the CPE_NAME
    . /etc/os-release
    echo "Host CPE ID from /etc/os-release: ${CPE_NAME}"
fi

if [ "${CPE_NAME}" = "" -a -e /etc/system-release-cpe ]
then
    CPE_NAME=$(head -n1 /etc/system-release-cpe)
    CPE_URI=$(expr ${CPE_NAME} : '\([^:]*:[^:]*\)')
    if [ "${CPE_URI}" != "cpe:/o" ]
    then
        CPE_NAME=
    else
        # Probably a better way to do this but sill remain posix
        # compatible but this works, shrug...
        # Must be nice and not introduce convenient bashisms here.
        #
        # According to the official registration at Mitre and NIST,
        # this should have been something like this for CentOS:
        #    cpe:/o:centos:centos:6
        # or this:
        #    cpe:/o:centos:centos:6.5
        #
        ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:\([^:]*\)')
        # The "enterprise_linux" is a bone toss back to RHEL.
        # Since CentOS and RHEL are so tightly coupled, we'll
        # take the RHEL version if we're running on it and do the
        # equivalent version for CentOS.
        if [ ${ID} = "linux" -o ${ID} = "enterprise_linux" ]
        then
                # Instead we got this: cpe:/o:centos:linux:6
                ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:\([^:]*\)')
        fi

        VERSION_ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:[^:]*:\([^:]*\)')
        echo "Host CPE ID from /etc/system-release-cpe: ${CPE_NAME}"
    fi
fi

if [ "${CPE_NAME}" != "" -a "${ID}" = "centos" -a "${VERSION_ID}" != "" ]
then
    centos_host_ver=${VERSION_ID}
    is_centos=true
elif [ "${CPE_NAME}" != "" -a "${ID}" = "redhat" -o "${ID}" = "rhel" -a "${VERSION_ID}" != "" ]
then
    # RHEL 7+ /etc/os-release ID = 'rhel', which doesn't enter this elif without the added OR statement
    redhat_host_ver=${VERSION_ID}
    is_redhat=true
elif [ -e /etc/centos-release ]
then
    # Only if all other methods fail, try to parse the redhat-release file.
    centos_host_ver=$( sed -e '/^CentOS /!d' -e 's/CentOS.*\srelease\s*\([0-9][0-9.]*\)\s.*/\1/' < /etc/centos-release )
    if [ "$centos_host_ver" != "" ]
    then
        is_centos=true
    fi
fi

force_mknod()
{
    # delete a device node if exists, and create a new one
    rm -f $2 && mknod -m $1 $2 $3 $4 $5
}

configure_centos()
{

    # disable selinux in CentOS
    mkdir -p $rootfs_path/selinux
    echo 0 > $rootfs_path/selinux/enforce

    # Also kill it in the /etc/selinux/config file if it's there...
    if [ -f $rootfs_path/etc/selinux/config ]
    then
        sed -i '/^SELINUX=/s/.*/SELINUX=disabled/' $rootfs_path/etc/selinux/config
    fi

    # Nice catch from Dwight Engen in the Oracle template.
    # Wantonly plagerized here with much appreciation.
    if [ -f $rootfs_path/usr/sbin/selinuxenabled ]; then
        mv $rootfs_path/usr/sbin/selinuxenabled $rootfs_path/usr/sbin/selinuxenabled.lxcorig
        ln -s /bin/false $rootfs_path/usr/sbin/selinuxenabled
    fi

    # This is a known problem and documented in RedHat bugzilla as relating
    # to a problem with auditing enabled.  This prevents an error in
    # the container "Cannot make/remove an entry for the specified session"
    sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/login
    sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/sshd

    if [ -f ${rootfs_path}/etc/pam.d/crond ]
    then
        sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/crond
    fi

    # In addition to disabling pam_loginuid in the above config files
    # we'll also disable it by linking it to pam_permit to catch any
    # we missed or any that get installed after the container is built.
    #
    # Catch either or both 32 and 64 bit archs.
    if [ -f ${rootfs_path}/lib/security/pam_loginuid.so ]
    then
        ( cd ${rootfs_path}/lib/security/
        mv pam_loginuid.so pam_loginuid.so.disabled
        ln -s pam_permit.so pam_loginuid.so
        )
    fi

    if [ -f ${rootfs_path}/lib64/security/pam_loginuid.so ]
    then
        ( cd ${rootfs_path}/lib64/security/
        mv pam_loginuid.so pam_loginuid.so.disabled
        ln -s pam_permit.so pam_loginuid.so
        )
    fi

    # Set default localtime to the host localtime if not set...
    if [ -e /etc/localtime -a ! -e ${rootfs_path}/etc/localtime ]
    then
        # if /etc/localtime is a symlink, this should preserve it.
        cp -a /etc/localtime ${rootfs_path}/etc/localtime
    fi

    # Deal with some dain bramage in the /etc/init.d/halt script.
    # Trim it and make it our own and link it in before the default
    # halt script so we can intercept it.  This also preventions package
    # updates from interferring with our interferring with it.
    #
    # There's generally not much in the halt script that useful but what's
    # in there from resetting the hardware clock down is generally very bad.
    # So we just eliminate the whole bottom half of that script in making
    # ourselves a copy.  That way a major update to the init scripts won't
    # trash what we've set up.
    if [ -f ${rootfs_path}/etc/init.d/halt ]
    then
        sed -e '/hwclock/,$d' \
            < ${rootfs_path}/etc/init.d/halt \
            > ${rootfs_path}/etc/init.d/lxc-halt

        echo '$command -f' >> ${rootfs_path}/etc/init.d/lxc-halt
        chmod 755 ${rootfs_path}/etc/init.d/lxc-halt

        # Link them into the rc directories...
        (
             cd ${rootfs_path}/etc/rc.d/rc0.d
             ln -s ../init.d/lxc-halt S00lxc-halt
             cd ${rootfs_path}/etc/rc.d/rc6.d
             ln -s ../init.d/lxc-halt S00lxc-reboot
        )
    fi

    # configure the network using the dhcp
    cat <<EOF > ${rootfs_path}/etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE=eth0
BOOTPROTO=dhcp
ONBOOT=yes
HOSTNAME=${utsname}
NM_CONTROLLED=no
TYPE=Ethernet
MTU=${MTU}
DHCP_HOSTNAME=\`hostname\`
EOF

    # set the hostname
    cat <<EOF > ${rootfs_path}/etc/sysconfig/network
NETWORKING=yes
HOSTNAME=${utsname}
EOF

    # set minimal hosts
    cat <<EOF > $rootfs_path/etc/hosts
127.0.0.1 localhost $name
EOF

    # set minimal fstab
    cat <<EOF > $rootfs_path/etc/fstab
/dev/root               /                       rootfs   defaults        0 0
EOF

    # create lxc compatibility init script
    if [ "$release" = "6" ]; then
        cat <<EOF > $rootfs_path/etc/init/lxc-sysinit.conf
start on startup
env container

pre-start script
        if [ "x\$container" != "xlxc" -a "x\$container" != "xlibvirt" ]; then
                stop;
        fi

        rm -f /var/lock/subsys/*
        rm -f /var/run/*.pid
        [ -e /etc/mtab ] || ln -s /proc/mounts /etc/mtab
        mkdir -p /dev/shm
        mount -t tmpfs -o nosuid,nodev tmpfs /dev/shm

        initctl start tty TTY=console
        telinit 3
        exit 0
end script
EOF
    elif [ "$release" = "5" ]; then
        cat <<EOF > $rootfs_path/etc/rc.d/lxc.sysinit
#! /bin/bash
rm -f /etc/mtab /var/run/*.{pid,lock} /var/lock/subsys/*
rm -rf {/,/var}/tmp/*
echo "/dev/root               /                       rootfs   defaults        0 0" > /etc/mtab
exit 0
EOF
        chmod 755 $rootfs_path/etc/rc.d/lxc.sysinit
        sed -i 's|si::sysinit:/etc/rc.d/rc.sysinit|si::bootwait:/etc/rc.d/lxc.sysinit|'  $rootfs_path/etc/inittab
        # prevent mingetty from calling vhangup(2) since it fails with userns.
        # Same issue as oracle template: prevent mingetty from calling vhangup(2)
        # commit 2e83f7201c5d402478b9849f0a85c62d5b9f1589.
        sed -i 's|^1:|co:2345:respawn:/sbin/mingetty --nohangup console\n1:|' $rootfs_path/etc/inittab
        sed -i 's|^\([56]:\)|#\1|' $rootfs_path/etc/inittab
    fi

    dev_path="${rootfs_path}/dev"
    rm -rf $dev_path
    mkdir -p $dev_path
    mknod -m 666 ${dev_path}/null c 1 3
    mknod -m 666 ${dev_path}/zero c 1 5
    mknod -m 666 ${dev_path}/random c 1 8
    mknod -m 666 ${dev_path}/urandom c 1 9
    mkdir -m 755 ${dev_path}/pts
    mkdir -m 1777 ${dev_path}/shm
    mknod -m 666 ${dev_path}/tty c 5 0
    mknod -m 666 ${dev_path}/tty0 c 4 0
    mknod -m 666 ${dev_path}/tty1 c 4 1
    mknod -m 666 ${dev_path}/tty2 c 4 2
    mknod -m 666 ${dev_path}/tty3 c 4 3
    mknod -m 666 ${dev_path}/tty4 c 4 4
    mknod -m 600 ${dev_path}/console c 5 1
    mknod -m 666 ${dev_path}/full c 1 7
    mknod -m 600 ${dev_path}/initctl p
    mknod -m 666 ${dev_path}/ptmx c 5 2

    # setup console and tty[1-4] for login. note that /dev/console and
    # /dev/tty[1-4] will be symlinks to the ptys /dev/lxc/console and
    # /dev/lxc/tty[1-4] so that package updates can overwrite the symlinks.
    # lxc will maintain these links and bind mount ptys over /dev/lxc/*
    # since lxc.tty.dir is specified in the config.

    # allow root login on console, tty[1-4], and pts/0 for libvirt
    echo "# LXC (Linux Containers)" >>${rootfs_path}/etc/securetty
    echo "lxc/console"  >>${rootfs_path}/etc/securetty
    echo "lxc/tty1"     >>${rootfs_path}/etc/securetty
    echo "lxc/tty2"     >>${rootfs_path}/etc/securetty
    echo "lxc/tty3"     >>${rootfs_path}/etc/securetty
    echo "lxc/tty4"     >>${rootfs_path}/etc/securetty
    echo "# For libvirt/Virtual Machine Monitor" >>${rootfs_path}/etc/securetty
    echo "pts/0"        >>${rootfs_path}/etc/securetty

    # prevent mingetty from calling vhangup(2) since it fails with userns.
    # Same issue as oracle template: prevent mingetty from calling vhangup(2)
    # commit 2e83f7201c5d402478b9849f0a85c62d5b9f1589.
    sed -i 's|mingetty|mingetty --nohangup|' $rootfs_path/etc/init/tty.conf

    if [ ${root_display_password} = "yes" ]
    then
        echo "Setting root password to '$root_password'"
    fi
    if [ ${root_store_password} = "yes" ]
    then
        touch ${config_path}/tmp_root_pass
        chmod 600 ${config_path}/tmp_root_pass
        echo ${root_password} > ${config_path}/tmp_root_pass
        echo "Storing root password in '${config_path}/tmp_root_pass'"
    fi

    echo "root:$root_password" | chroot $rootfs_path chpasswd

    if [ ${root_expire_password} = "yes" ]
    then
        # Also set this password as expired to force the user to change it!
        chroot $rootfs_path passwd -e root
    fi

    # This will need to be enhanced for CentOS 7 when systemd
    # comes into play...   /\/\|=mhw=|\/\/

    return 0
}

configure_centos_init()
{
    sed -i 's|.sbin.start_udev||' ${rootfs_path}/etc/rc.sysinit
    sed -i 's|.sbin.start_udev||' ${rootfs_path}/etc/rc.d/rc.sysinit
    if [ "$release" = "6" ]; then
        chroot ${rootfs_path} chkconfig udev-post off
    fi
    chroot ${rootfs_path} chkconfig network on

    if [ -d ${rootfs_path}/etc/init ]
    then
       # This is to make upstart honor SIGPWR
        cat <<EOF >${rootfs_path}/etc/init/power-status-changed.conf
#  power-status-changed - shutdown on SIGPWR
#
start on power-status-changed

exec /sbin/shutdown -h now "SIGPWR received"
EOF
    fi
}

download_centos()
{

    # check the mini CentOS was not already downloaded
    INSTALL_ROOT=$cache/partial
    mkdir -p $INSTALL_ROOT
    if [ $? -ne 0 ]; then
    echo "Failed to create '$INSTALL_ROOT' directory"
    return 1
    fi

    # download a mini CentOS into a cache
    echo "Downloading CentOS minimal ..."
    YUM0="yum --installroot $INSTALL_ROOT -y --nogpgcheck"

    if yum -h | grep -q 'releasever=RELEASEVER'; then
       YUM="$YUM0 --releasever=$release"
    else
       YUM="$YUM0"
    fi
    PKG_LIST="yum initscripts passwd rsyslog vim-minimal openssh-server openssh-clients dhclient chkconfig rootfiles policycoreutils cronie"

    # use temporary repository definition
    # always prefer the repo given by the user
    # if no repo given, use mirrorlist.centos.org for i386 and x86_64
    # and http://mirror.centos.org/altarch/ otherwise
    REPO_FILE=$INSTALL_ROOT/etc/yum.repos.d/lxc-centos-temp.repo
    mkdir -p $(dirname $REPO_FILE)
    if [ -n "$repo" ]; then
	cat <<EOF > $REPO_FILE
[base]
name=local repository
baseurl="$repo"
EOF
    elif [ ${basearch} = 'i386' ] || [ ${basearch} = 'x86_64' ]; then
	cat <<EOF > $REPO_FILE
[base]
name=CentOS-$release - Base
mirrorlist=http://mirrorlist.centos.org/?release=$release&arch=$basearch&repo=os

[updates]
name=CentOS-$release - Updates
mirrorlist=http://mirrorlist.centos.org/?release=$release&arch=$basearch&repo=updates
EOF
    else
	cat <<EOF > $REPO_FILE
[base]
name=CentOS-$release - Base
baseurl=http://mirror.centos.org/altarch/7/os/$basearch/

[updates]
name=CentOS-$release - Updates
baseurl=http://mirror.centos.org/altarch/7/updates/$basearch/
EOF
    fi

    # create minimal device nodes, needed for "yum install" and "yum update" process
    mkdir -p $INSTALL_ROOT/dev
    force_mknod 666 $INSTALL_ROOT/dev/null c 1 3
    force_mknod 666 $INSTALL_ROOT/dev/urandom c 1 9

    $YUM install $PKG_LIST

    # create symlink for /var/run -> ../run 
    if [ "$release" = "7" ]; then
        mv $INSTALL_ROOT/var/run/* $INSTALL_ROOT/run/
        rmdir $INSTALL_ROOT/var/run
        ln -sf ../run $INSTALL_ROOT/var/run
    fi

    if [ $? -ne 0 ]; then
        echo "Failed to download the rootfs, aborting."
        return 1
    fi

    # use same nameservers as hosts, needed for "yum update later"
    cp /etc/resolv.conf $INSTALL_ROOT/etc/

    # check whether rpmdb is under $HOME
    if [ ! -e $INSTALL_ROOT/var/lib/rpm/Packages -a -e $INSTALL_ROOT/$HOME/.rpmdb/Packages ]; then
        echo "Fixing rpmdb location ..."
        mv $INSTALL_ROOT/$HOME/.rpmdb/[A-Z]* $INSTALL_ROOT/var/lib/rpm/
        rm -rf $INSTALL_ROOT/$HOME/.rpmdb
        chroot $INSTALL_ROOT rpm --rebuilddb 2>/dev/null
    fi

    # check whether rpmdb version is correct
    chroot $INSTALL_ROOT rpm --quiet -q yum 2>/dev/null
    ret=$?

    # if "rpm -q" doesn't work due to rpmdb version difference,
    # then we need to redo the process using the newly-installed yum
    if [ $ret -gt 0 ]; then
        echo "Reinstalling packages ..."
        mv $REPO_FILE $REPO_FILE.tmp
        mkdir $INSTALL_ROOT/etc/yum.repos.disabled
        mv $INSTALL_ROOT/etc/yum.repos.d/*.repo $INSTALL_ROOT/etc/yum.repos.disabled/
        mv $REPO_FILE.tmp $REPO_FILE
        mkdir -p $INSTALL_ROOT/$INSTALL_ROOT/etc
        cp /etc/resolv.conf $INSTALL_ROOT/$INSTALL_ROOT/etc/
        mkdir -p $INSTALL_ROOT/$INSTALL_ROOT/dev
        mknod -m 666 $INSTALL_ROOT/$INSTALL_ROOT/dev/null c 1 3
        mknod -m 666 $INSTALL_ROOT/$INSTALL_ROOT/dev/urandom c 1 9
        mkdir -p $INSTALL_ROOT/$INSTALL_ROOT/var/cache/yum
        cp -al $INSTALL_ROOT/var/cache/yum/* $INSTALL_ROOT/$INSTALL_ROOT/var/cache/yum/
        chroot $INSTALL_ROOT $YUM0 install $PKG_LIST
        if [ $? -ne 0 ]; then
            echo "Failed to download the rootfs, aborting."
            return 1
        fi
        mv $INSTALL_ROOT/$INSTALL_ROOT $INSTALL_ROOT.tmp
        rm -rf $INSTALL_ROOT
        mv $INSTALL_ROOT.tmp $INSTALL_ROOT
    fi

    rm -f $REPO_FILE
    rm -rf $INSTALL_ROOT/var/cache/yum/*

    mv "$INSTALL_ROOT" "$cache/rootfs"
    echo "Download complete."

    return 0
}

copy_centos()
{

    # make a local copy of the mini CentOS
    echo -n "Copying rootfs to $rootfs_path ..."
    #cp -a $cache/rootfs-$arch $rootfs_path || return 1
    # i prefer rsync (no reason really)
    mkdir -p $rootfs_path
    rsync -SHaAX $cache/rootfs/ $rootfs_path/
    echo
    return 0
}

update_centos()
{
    YUM="chroot $cache/rootfs yum -y --nogpgcheck"
    $YUM update
    if [ $? -ne 0 ]; then
        return 1
    fi
    $YUM clean packages
}

install_centos()
{
    mkdir -p /var/lock/subsys/
    (
    flock -x 9
    if [ $? -ne 0 ]; then
        echo "Cache repository is busy."
        return 1
    fi

    echo "Checking cache download in $cache/rootfs ... "
    if [ ! -e "$cache/rootfs" ]; then
        download_centos
        if [ $? -ne 0 ]; then
            echo "Failed to download 'CentOS base'"
            return 1
        fi
    else
        echo "Cache found. Updating..."
        update_centos
        if [ $? -ne 0 ]; then
            echo "Failed to update 'CentOS base', continuing with last known good cache"
        else
            echo "Update finished"
        fi
    fi

    echo "Copy $cache/rootfs to $rootfs_path ... "
    copy_centos
    if [ $? -ne 0 ]; then
        echo "Failed to copy rootfs"
        return 1
    fi

    return 0

    ) 9>/var/lock/subsys/lxc-centos

    return $?
}

create_hwaddr()
{
    openssl rand -hex 5 | sed -e 's/\(..\)/:\1/g; s/^/fe/'
}

copy_configuration()
{
    mkdir -p $config_path

    grep -q "^lxc.rootfs.path" $config_path/config 2>/dev/null || echo "
lxc.rootfs.path = $rootfs_path
" >> $config_path/config

    # The following code is to create static MAC addresses for each
    # interface in the container.  This code will work for multiple
    # interfaces in the default config.
    mv $config_path/config $config_path/config.def
    while read LINE
    do
        # This should catch variable expansions from the default config...
        if expr "${LINE}" : '.*\$' > /dev/null 2>&1
        then
                LINE=$(eval "echo \"${LINE}\"")
        fi

        # There is a tab and a space in the regex bracket below!
        # Seems that \s doesn't work in brackets.
        KEY=$(expr "${LINE}" : '\s*\([^	 ]*\)\s*=')

        if [[ "${KEY}" != "lxc.net.0.hwaddr" ]]
        then
            echo ${LINE} >> $config_path/config

            if [[ "${KEY}" == "lxc.net.0.link" ]]
            then
                echo "lxc.net.0.hwaddr = $(create_hwaddr)" >> $config_path/config
            fi
        fi
    done < $config_path/config.def

    rm -f $config_path/config.def

    if [ -e "@LXCTEMPLATECONFIG@/centos.common.conf" ]; then
        echo "
# Include common configuration
lxc.include = @LXCTEMPLATECONFIG@/centos.common.conf
" >> $config_path/config
    fi

    # Append things which require expansion here...
    cat <<EOF >> $config_path/config
lxc.arch = $arch
lxc.uts.name = $utsname

# When using LXC with apparmor, uncomment the next line to run unconfined:
#lxc.apparmor.profile = unconfined

# example simple networking setup, uncomment to enable
#lxc.net.0.type = $lxc_network_type
#lxc.net.0.flags = up
#lxc.net.0.link = $lxc_network_link
#lxc.net.0.name = eth0
# Additional example for veth network type
#    static MAC address,
#lxc.net.0.hwaddr = 00:16:3e:77:52:20
#    persistent veth device name on host side
#        Note: This may potentially collide with other containers of same name!
#lxc.net.0.veth.pair = v-$name-e0

EOF

    if [ $? -ne 0 ]; then
        echo "Failed to add configuration"
        return 1
    fi

    return 0
}

clean()
{

    if [ ! -e $cache ]; then
        exit 0
    fi

    # lock, so we won't purge while someone is creating a repository
    (
        flock -x 9
        if [ $? != 0 ]; then
            echo "Cache repository is busy."
            exit 1
        fi

        echo -n "Purging the download cache for CentOS-$release..."
        rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1
        exit 0

    ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-centos
}

usage()
{
    cat <<EOF
usage:
    $1 -n|--name=<container_name>
        [-p|--path=<path>] [-c|--clean] [-R|--release=<CentOS_release>] [-a|--arch=<arch of the container>]
        [-h|--help]
Mandatory args:
  -n,--name         container name, used to as an identifier for that container from now on
Optional args:
  -p,--path         path to where the container rootfs will be created, defaults to /var/lib/lxc/name.
  -c,--clean        clean the cache
  -R,--release      CentOS release for the new container. If the host is CentOS, then it will default to the host's release.
     --fqdn         fully qualified domain name (FQDN) for DNS and system naming
     --repo         repository to use (url)
  -a,--arch         Define what arch the container will be [i686,x86_64]
  -h,--help         print this help
EOF
    return 0
}

options=$(getopt -o a:hp:n:cR: -l help,path:,rootfs:,name:,clean,release:,repo:,arch:,fqdn: -- "$@")
if [ $? -ne 0 ]; then
    usage $(basename $0)
    exit 1
fi

arch=$(uname -m)
eval set -- "$options"
while true
do
    case "$1" in
        -h|--help)      usage $0 && exit 0;;
        -p|--path)      path=$2; shift 2;;
        --rootfs)       rootfs_path=$2; shift 2;;
        -n|--name)      name=$2; shift 2;;
        -c|--clean)     clean=1; shift 1;;
        -R|--release)   release=$2; shift 2;;
        --repo)         repo="$2"; shift 2;;
        -a|--arch)      newarch=$2; shift 2;;
        --fqdn)         utsname=$2; shift 2;;
        --)             shift 1; break ;;
        *)              break ;;
    esac
done

if [ ! -z "$clean" -a -z "$path" ]; then
    clean || exit 1
    exit 0
fi

basearch=${arch}
# Map a few architectures to their generic CentOS repository archs.
# The two ARM archs are a bit of a guesstimate for the v5 and v6
# archs.  V6 should have hardware floating point (Rasberry Pi).
# The "arm" arch is safer (no hardware floating point).  So
# there may be cases where we "get it wrong" for some v6 other
# than RPi.
case "$arch" in
i686) basearch=i386 ;;
armv3l|armv4l|armv5l) basearch=arm ;;
armv6l|armv7l|armv8l) basearch=armhfp ;;
*) ;;
esac

# Somebody wants to specify an arch.  This is very limited case.
#       i386/i586/i686 on i386/x86_64
#           - or -
#       x86_64 on x86_64
if [ "${newarch}" != "" -a "${newarch}" != "${arch}" ]
then
    case "${newarch}" in
        i386|i586|i686)
            if [ "${basearch}" = "i386" -o "${basearch}" = "x86_64" ]
            then
                # Make the arch a generic x86 32 bit...
                arch=${newarch}
                basearch=i386
            else
                basearch=bad
            fi
            ;;
        *)
            basearch=bad
            ;;
    esac

    if [ "${basearch}" = "bad" ]
    then
        echo "You cannot build a ${newarch} CentOS container on a ${arch} host.  Sorry!"
        exit 1
    fi
fi

# Allow the cache base to be set by environment variable
cache_base=${LXC_CACHE_PATH:-"@LOCALSTATEDIR@/cache/lxc"}/centos/$basearch

# Let's do something better for the initial root password.
# It's not perfect but it will defeat common scanning brute force
# attacks in the case where ssh is exposed.  It will also be set to
# expired, forcing the user to change it at first login.
if [ "${root_password}" = "" ]
then
    root_password=Root-${name}-${RANDOM}
else
    # If it's got a ding in it, try and expand it!
    if [ $(expr "${root_password}" : '.*$.') != 0 ]
    then
        root_password=$(eval echo "${root_password}")
    fi

    # If it has more than 3 consecutive X's in it, feed it
    # through mktemp as a template.
    if [ $(expr "${root_password}" : '.*XXXX') != 0 ]
    then
        root_password=$(mktemp -u ${root_password})
    fi
fi

if [ -z "${utsname}" ]; then
    utsname=${name}
fi

# This follows a standard "resolver" convention that an FQDN must have
# at least two dots or it is considered a local relative host name.
# If it doesn't, append the dns domain name of the host system.
#
# This changes one significant behavior when running
# "lxc_create -n Container_Name" without using the
# --fqdn option.
#
# Old behavior:
#    utsname and hostname = Container_Name
# New behavior:
#    utsname and hostname = Container_Name.Domain_Name

if [ $(expr "$utsname" : '.*\..*\.') = 0 ]; then
    if [[ "$(dnsdomainname)" != "" && "$(dnsdomainname)" != "localdomain" ]]; then
        utsname=${utsname}.$(dnsdomainname)
    fi
fi

type yum >/dev/null 2>&1
if [ $? -ne 0 ]; then
    echo "'yum' command is missing"
    exit 1
fi

if [ -z "$path" ]; then
    path=$default_path/$name
fi

if [ -z "$release" ]; then
    if [ "$is_centos" -a "$centos_host_ver" ]; then
        release=$centos_host_ver
    elif [ "$is_redhat" -a "$redhat_host_ver" ]; then
        # This is needed to clean out bullshit like 6workstation and 6server.
        release=$(expr $redhat_host_ver : '\([0-9.]*\)')
    else
        echo "This is not a CentOS or Red Hat host and release is missing, defaulting to 7, use -R|--release to specify release"
        release=7
    fi
fi

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

if [ -z "$rootfs_path" ]; then
    rootfs_path=$path/rootfs
    # check for 'lxc.rootfs.path' passed in through default config by lxc-create
    if grep -q '^lxc.rootfs.path' $path/config 2>/dev/null ; then
        rootfs_path=$(sed -e '/^lxc.rootfs.path\s*=/!d' -e 's/\s*#.*//' \
            -e 's/^lxc.rootfs.path\s*=\s*//' -e q $path/config)
    fi
fi
config_path=$path
cache=$cache_base/$release

revert()
{
    echo "Interrupted, so cleaning up"
    lxc-destroy -n $name
    # maybe was interrupted before copy config
    rm -rf $path
    echo "exiting..."
    exit 1
}

trap revert SIGHUP SIGINT SIGTERM

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

install_centos
if [ $? -ne 0 ]; then
    echo "failed to install CentOS"
    exit 1
fi

configure_centos
if [ $? -ne 0 ]; then
    echo "failed to configure CentOS for a container"
    exit 1
fi

configure_centos_init

if [ ! -z "$clean" ]; then
    clean || exit 1
    exit 0
fi
echo "
Container rootfs and config have been created.
Edit the config file to check/enable networking setup.
"

if [ ${root_display_password} = "yes" ]
then
    echo "The temporary password for root is: '$root_password'

You may want to note that password down before starting the container.
"
fi

if [ ${root_store_password} = "yes" ]
then
    echo "The temporary root password is stored in:

        '${config_path}/tmp_root_pass'
"
fi

if [ ${root_prompt_password} = "yes" ]
then
    echo "Invoking the passwd command in the container to set the root password.

        chroot ${rootfs_path} passwd
"
    chroot ${rootfs_path} passwd
else
    if [ ${root_expire_password} = "yes" ]
    then
        if ( mountpoint -q -- "${rootfs_path}" )
        then
            echo "To reset the root password, you can do:

        lxc-start -n ${name}
        lxc-attach -n ${name} -- passwd
        lxc-stop -n ${name}
"
        else
           echo "
The root password is set up as "expired" and will require it to be changed
at first login, which you should do as soon as possible.  If you lose the
root password or wish to change it without starting the container, you
can change it from the host by running the following command (which will
also reset the expired flag):

        chroot ${rootfs_path} passwd
"
        fi
    fi
fi
