#!/bin/bash # # lxc: linux Container library # Authors: # Niels Rogalla # 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 die() { echo "ERROR: $@"; exit 1; } configure_magellan() { local rootfs="$1" local hostname="$2" echo "Setting up rootfs..." # fix locales first if [[ -z ${LANG} ]] && [[ -z ${LC_ALL} ]] then sed -i "s:^#\(en_US..*\):\1:g" ${rootfs}/etc/locale.gen LC_ALL=C chroot ${rootfs} /usr/sbin/locale-gen elif [[ ! -z ${LANG} ]] then sed -i "s:^#\(${LANG}\):\1:g" ${rootfs}/etc/locale.gen echo "LANG=${LANG}" > ${rootfs}/etc/env.d/01locale LC_ALL=C chroot ${rootfs} /usr/sbin/locale-gen LC_ALL=C chroot ${rootfs} /sbin/env-rebuild elif [[ ! -z ${LC_ALL} ]] then sed -i "s:^#\(${LC_ALL}\):\1:g" ${rootfs}/etc/locale.gen echo "LC_ALL=${LC_ALL}" > ${rootfs}/etc/env.d/01locale LC_ALL=C chroot ${rootfs} /usr/sbin/locale-gen LC_ALL=C chroot ${rootfs} /sbin/env-rebuild fi # configure the inittab cat < ${rootfs}/etc/inittab id:3:initdefault: si::sysinit:/etc/rc.d/init.d/rc sysinit l0:0:wait:/etc/rc.d/init.d/rc 0 l1:S1:wait:/etc/rc.d/init.d/rc 1 l2:2:wait:/etc/rc.d/init.d/rc 2 l3:3:wait:/etc/rc.d/init.d/rc 3 l4:4:wait:/etc/rc.d/init.d/rc 4 l5:5:wait:/etc/rc.d/init.d/rc 5 l6:6:wait:/etc/rc.d/init.d/rc 6 su:S016:once:/sbin/sulogin 1:2345:respawn:/sbin/agetty console 38400 2:2345:respawn:/sbin/agetty tty2 38400 linux 3:2345:respawn:/sbin/agetty tty3 38400 linux 4:2345:respawn:/sbin/agetty tty4 38400 linux 5:2345:respawn:/sbin/agetty tty5 38400 linux 6:2345:respawn:/sbin/agetty tty6 38400 linux x:a:once:/etc/X11/startxdm EOF # configure the network using dhcp cat < ${rootfs}/etc/conf.d/net.eth0 ONBOOT="yes" NETWORKING="dhcp" EOF # set the hostname cat < ${rootfs}/etc/hostname ${hostname} EOF # remove /, /boot, swap from fstab (\s = space) sed -i -e '/\s\/\s/d' -e '/\s\/boot\s/d' -e '/\sswap\s/d' ${rootfs}/etc/fstab # remove pointless services in a container chroot ${rootfs} /sbin/rc-config del checkfs chroot ${rootfs} /sbin/rc-config del setclock #echo "root:foobar" | chroot ${rootfs} chpasswd chroot ${rootfs} usermod -p $(openssl passwd -1 foobar) root echo "Root password is 'foobar', please change!" # create missing device nodes [[ ! -e ${rootfs}/dev/null ]] && mknod -m 666 ${rootfs}/dev/null c 1 3 [[ ! -e ${rootfs}/dev/zero ]] && mknod -m 666 ${rootfs}/dev/zero c 1 5 [[ ! -e ${rootfs}/dev/random ]] && mknod -m 666 ${rootfs}/dev/random c 1 8 [[ ! -e ${rootfs}/dev/urandom ]] && mknod -m 666 ${rootfs}/dev/urandom c 1 9 [[ ! -e ${rootfs}/dev/pts ]] && mkdir -m 755 ${rootfs}/dev/pts [[ ! -e ${rootfs}/dev/shm ]] && mkdir -m 1777 ${rootfs}/dev/shm [[ ! -e ${rootfs}/dev/tty ]] && mknod -m 666 ${rootfs}/dev/tty c 5 0 [[ ! -e ${rootfs}/dev/console ]] && mknod -m 600 ${rootfs}/dev/console c 5 1 [[ ! -e ${rootfs}/dev/tty0 ]] && mknod -m 666 ${rootfs}/dev/tty0 c 4 0 [[ ! -e ${rootfs}/dev/tty1 ]] && mknod -m 666 ${rootfs}/dev/tty1 c 4 1 [[ ! -e ${rootfs}/dev/tty2 ]] && mknod -m 666 ${rootfs}/dev/tty2 c 4 2 [[ ! -e ${rootfs}/dev/tty3 ]] && mknod -m 666 ${rootfs}/dev/tty3 c 4 3 [[ ! -e ${rootfs}/dev/tty4 ]] && mknod -m 666 ${rootfs}/dev/tty4 c 4 4 [[ ! -e ${rootfs}/dev/tty5 ]] && mknod -m 666 ${rootfs}/dev/tty5 c 4 5 [[ ! -e ${rootfs}/dev/tty6 ]] && mknod -m 666 ${rootfs}/dev/tty6 c 4 6 [[ ! -e ${rootfs}/dev/full ]] && mknod -m 666 ${rootfs}/dev/full c 1 7 [[ ! -e ${rootfs}/dev/initctl ]] && mknod -m 600 ${rootfs}/dev/initctl p [[ ! -e ${rootfs}/dev/ptmx ]] && mknod -m 666 ${rootfs}/dev/ptmx c 5 2 return 0 } download_magellan() { local cache="$1" local arch="$2" local distribution="unstable" local mageprofile="$(basename $(readlink /etc/mage-profile))" local extrapackages="dialog iproute2 openssh" # check the mini magellan was not already downloaded mkdir -p "${cache}/partial-${arch}" if [ $? -ne 0 ] then echo "Failed to create '${cache}/partial-${arch}' directory" return 1 fi # setup magerc cat < ${cache}/partial-${arch}/mage.rc.bootstrap ARCH="${arch}" MAGE_DISTRIBUTION="${distribution}" MAGEDIR="/usr/mage" PKGDIR="/var/cache/mage/packages" BUILDDIR="/var/tmp/magebuild" INSTALLDB="/var/db/mage" VIRTUALDB_DEFAULTS="/etc/mage-profile/virtuals.defaults" VIRTUALDB_FILE="\${INSTALLDB}/virtuals" VERBOSE="off" MAGEDEBUG="off" MIRRORS="http://magellan-linux.de/magellan/magellan-dev/svn/\${MAGE_DISTRIBUTION}" RSYNC="rsync://magellan-linux.de/mage-svn" PACKAGES_SERVER_PATH="packages/\${ARCH}" MAGE_UNINSTALL_TIMEOUT=0 EOF # download a mini magellan into a cache echo "Downloading magellan minimal ..." mage-bootstrap --profile ${mageprofile} --root ${cache}/partial-${arch} --magerc ${cache}/partial-${arch}/mage.rc.bootstrap --update-tarball if [ $? -ne 0 ] then echo "Failed to download the rootfs, aborting." return 1 fi # install extra packages for pkg in ${extrapackages} do MROOT=${cache}/partial-${arch} mage install ${pkg} if [ $? -ne 0 ] then echo "Failed to install package ${pkg}, aborting." return 1 fi done mv "${cache}/partial-${arch}" "${cache}/rootfs-${arch}" echo "Download complete." return 0 } copy_magellan() { local cache="$1" local arch="$2" local rootfs="$3" # make a local copy of the minimagellan echo "Copying rootfs to ${rootfs}..." cp -a ${cache}/rootfs-${arch} ${rootfs} || return 1 return 0 } install_magellan() { local cache="/var/cache/lxc/magellan" local rootfs="$1" mkdir -p /var/lock/subsys/ ( flock -n -x 200 if [ $? -ne 0 ] then echo "Cache repository is busy." return 1 fi arch=$(uname -m) echo "Checking cache download in ${cache}/rootfs-${arch} ... " if [ ! -e "${cache}/rootfs-${arch}" ] then download_magellan "${cache}" "${arch}" if [ $? -ne 0 ] then echo "Failed to download 'magellan base'" return 1 fi fi install -d $(dirname ${rootfs}) copy_magellan "${cache}" "${arch}" "${rootfs}" if [ $? -ne 0 ]; then echo "Failed to copy rootfs" return 1 fi return 0 ) 200>/var/lock/subsys/lxc return $? } copy_configuration() { local path="$1" local rootfs="$2" local name="$3" cat <> ${path}/config lxc.tty = 4 lxc.pts = 1024 lxc.rootfs = ${rootfs} lxc.cgroup.devices.deny = a # /dev/null and zero lxc.cgroup.devices.allow = c 1:3 rwm lxc.cgroup.devices.allow = c 1:5 rwm # consoles lxc.cgroup.devices.allow = c 5:1 rwm lxc.cgroup.devices.allow = c 5:0 rwm lxc.cgroup.devices.allow = c 4:0 rwm lxc.cgroup.devices.allow = c 4:1 rwm # /dev/{,u}random lxc.cgroup.devices.allow = c 1:9 rwm lxc.cgroup.devices.allow = c 1:8 rwm lxc.cgroup.devices.allow = c 136:* rwm lxc.cgroup.devices.allow = c 5:2 rwm # rtc lxc.cgroup.devices.allow = c 254:0 rwm # mounts point lxc.mount.entry=proc ${rootfs}/proc proc nodev,noexec,nosuid 0 0 lxc.mount.entry=devpts ${rootfs}/dev/pts devpts defaults 0 0 lxc.mount.entry=sysfs ${rootfs}/sys sysfs defaults 0 0 EOF if [ $? -ne 0 ] then echo "Failed to add configuration" return 1 fi return 0 } clean() { local cache="/var/cache/lxc/magellan" if [ ! -e ${cache} ] then exit 0 fi # lock, so we won't purge while someone is creating a repository ( flock -n -x 200 if [ $? != 0 ]; then echo "Cache repository is busy." exit 1 fi echo -n "Purging the download cache..." rm --preserve-root --one-file-system -rf ${cache} && echo "Done." || exit 1 exit 0 ) 200>/var/lock/subsys/lxc } usage() { cat < --clean EOF return 0 } options=$(getopt -o hp:n:c -l help,path:,name:,clean -- "$@") 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;; -c|--clean) clean=$2; shift 2;; --) shift 1; break ;; *) break ;; esac done if [ ! -z "${clean}" -a -z "${path}" ] then clean || exit 1 exit 0 fi # sanity checks [ "$(id -u)" != "0" ] && die "This script should be run as 'root'" type mage-bootstrap || die "'mage-bootstrap' command is missing" [ -z "${path}" ] && die "'path' parameter is required" rootfs=${path}/rootfs install_magellan ${rootfs} || die "failed to install magellan" configure_magellan ${rootfs} ${name} || die "failed to configure debian for a container" copy_configuration ${path} ${rootfs} || die "failed write configuration file" if [ ! -z ${clean} ] then clean || exit 1 exit 0 fi