#!/bin/bash # # lxc: linux Container library # Authors: # Daniel Lezcano # template for slackware by ponce # some parts are taken from the debian one (used as model) # 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 SUITE=${SUITE:-13.37} cache=${cache:-/var/cache/lxc/slackware} # let's use a secondary mirror to avoid loading the primary MIRROR=${MIRROR:-http://www.slackware.at/data} if [ -z "$arch" ]; then case "$( uname -m )" in i?86) arch=i486 ;; arm*) arch=arm ;; *) arch=$( uname -m ) ;; esac fi configure_slackware() { rootfs=$1 hostname=$2 echo "Configuring..." ; echo # the next part contains excerpts taken from SeTconfig (written by # Patrick Volkerding) from the slackware setup disk. # but before pasting them just set a variable to use them as they are T_PX=$rootfs ( cd $T_PX ; chmod 755 ./ ) ( cd $T_PX ; chmod 755 ./var ) if [ -d $T_PX/usr/src/linux ]; then chmod 755 $T_PX/usr/src/linux fi if [ ! -d $T_PX/proc ]; then mkdir $T_PX/proc chown root.root $T_PX/proc fi if [ ! -d $T_PX/sys ]; then mkdir $T_PX/sys chown root.root $T_PX/sys fi chmod 1777 $T_PX/tmp if [ ! -d $T_PX/var/spool/mail ]; then mkdir -p $T_PX/var/spool/mail chmod 755 $T_PX/var/spool chown root.mail $T_PX/var/spool/mail chmod 1777 $T_PX/var/spool/mail fi echo "#!/bin/sh" > $T_PX/etc/rc.d/rc.keymap echo "# Load the keyboard map. More maps are in /usr/share/kbd/keymaps." \ >> $T_PX/etc/rc.d/rc.keymap echo "if [ -x /usr/bin/loadkeys ]; then" >> $T_PX/etc/rc.d/rc.keymap echo " /usr/bin/loadkeys us" >> $T_PX/etc/rc.d/rc.keymap echo "fi" >> $T_PX/etc/rc.d/rc.keymap chmod 755 $T_PX/etc/rc.d/rc.keymap # network configuration is left to the user # editing /etc/rc.d/rc.inet1.conf and /etc/resolv.conf of the container # just set the hostname cat < $rootfs/etc/HOSTNAME $hostname.example.net EOF cp $rootfs/etc/HOSTNAME $rootfs/etc/hostname # make needed devices, from Chris Willing's MAKEDEV.sh # http://www.vislab.uq.edu.au/howto/lxc/MAKEDEV.sh DEV=$rootfs/dev mkdir -p ${DEV} mknod -m 666 ${DEV}/null c 1 3 mknod -m 666 ${DEV}/zero c 1 5 mknod -m 666 ${DEV}/random c 1 8 mknod -m 666 ${DEV}/urandom c 1 9 mkdir -m 755 ${DEV}/pts mkdir -m 1777 ${DEV}/shm mknod -m 666 ${DEV}/tty c 5 0 mknod -m 600 ${DEV}/console c 5 1 mknod -m 666 ${DEV}/tty0 c 4 0 mknod -m 666 ${DEV}/tty1 c 4 1 mknod -m 666 ${DEV}/tty2 c 4 2 mknod -m 666 ${DEV}/tty3 c 4 3 mknod -m 666 ${DEV}/tty4 c 4 4 mknod -m 666 ${DEV}/tty5 c 4 5 mknod -m 666 ${DEV}/full c 1 7 mknod -m 600 ${DEV}/initctl p mknod -m 660 ${DEV}/loop0 b 7 0 mknod -m 660 ${DEV}/loop1 b 7 1 ln -s pts/ptmx ${DEV}/ptmx echo "Adding an etc/fstab that must be edited later" echo "with the full path of the container if you move it." cat >$rootfs/etc/fstab <$rootfs/tmp/rcs.patch <<'EOF' --- ./etc/rc.d/rc.S.orig 2011-07-20 10:18:27.956637848 +0200 +++ ./etc/rc.d/rc.S 2011-07-20 11:14:26.553649659 +0200 @@ -4,9 +4,18 @@ # # Mostly written by: Patrick J. Volkerding, # +# tweaks for an lxc container by ponce , +# based also on Chris Willing's modifications +# http://www.vislab.uq.edu.au/howto/lxc/rc.S +# a check for a container variable is made to jump unneeded sections + +CONTAINER=yes PATH=/sbin:/usr/sbin:/bin:/usr/bin +# container check +if [ ! $CONTAINER ]; then + # Try to mount /proc: /sbin/mount -v proc /proc -n -t proc 2> /dev/null @@ -250,16 +259,27 @@ read junk; fi # Done checking root filesystem +fi # end container check + # Any /etc/mtab that exists here is old, so we delete it to start over: /bin/rm -f /etc/mtab* + +# container check +if [ ! $CONTAINER ]; then + # Remounting the / partition will initialize the new /etc/mtab: /sbin/mount -w -o remount / +fi # end container check + # Read in the correct / filesystem complete with arguments so mount will # show them correctly. This does not stop those arguments from functioning # but does prevent a small bug with /etc/mtab. /bin/grep ' / ' /proc/mounts | grep -v "^rootfs" > /etc/mtab +# container check +if [ ! $CONTAINER ]; then + # Fix /etc/mtab to list sys and proc if they were not yet entered in # /etc/mtab because / was still mounted read-only: if [ -d /proc/sys ]; then @@ -337,6 +357,8 @@ # mounted read-write. /sbin/swapon -a 2> /dev/null +fi # end container check + # Clean up some temporary files: rm -f /var/run/* /var/run/*/* /var/run/*/*/* /etc/nologin \ /etc/dhcpc/*.pid /etc/forcefsck /etc/fastboot \ @@ -364,7 +386,7 @@ # if the first line of that file begins with the word 'Linux'. # You are free to modify the rest of the file as you see fit. if [ -x /bin/sed ]; then - /bin/sed -i "{1s/^Linux.*/$(/bin/uname -sr)\./}" /etc/motd + /bin/sed -i "{1s/^Linux.*/$(/bin/uname -sr) lxc container\./}" /etc/motd fi # If there are SystemV init scripts for this runlevel, run them. @@ -372,6 +394,9 @@ . /etc/rc.d/rc.sysvinit fi +# container check +if [ ! $CONTAINER ]; then + # Run serial port setup script: # CAREFUL! This can make some systems hang if the rc.serial script isn't # set up correctly. If this happens, you may have to edit the file from a @@ -380,6 +405,8 @@ sh /etc/rc.d/rc.serial start fi +fi # end container check + # Carry an entropy pool between reboots to improve randomness. if [ -f /etc/random-seed ]; then echo "Using /etc/random-seed to initialize /dev/urandom." --- ./etc/rc.d/rc.6.orig 2011-07-20 10:18:24.478681336 +0200 +++ ./etc/rc.d/rc.6 2011-07-20 11:14:26.553649659 +0200 @@ -9,6 +9,12 @@ # Author: Miquel van Smoorenburg # Modified by: Patrick J. Volkerding, # +# tweaks for an lxc container by ponce , +# based also on Chris Willing's modifications +# http://www.vislab.uq.edu.au/howto/lxc/rc.6 +# a check for a container variable is made to jump unneeded sections + +CONTAINER=yes # Set the path. PATH=/sbin:/etc:/bin:/usr/bin @@ -37,6 +43,9 @@ ;; esac +# container check +if [ ! $CONTAINER ]; then + # Save the system time to the hardware clock using hwclock --systohc. if [ -x /sbin/hwclock ]; then # Check for a broken motherboard RTC clock (where ioports for rtc are @@ -53,6 +62,8 @@ fi fi +fi # end container check + # Run any local shutdown scripts: if [ -x /etc/rc.d/rc.local_shutdown ]; then /etc/rc.d/rc.local_shutdown stop @@ -141,6 +152,9 @@ sleep 2 fi +# container check +if [ ! $CONTAINER ]; then + # Shut down PCMCIA devices: if [ -x /etc/rc.d/rc.pcmcia ]; then . /etc/rc.d/rc.pcmcia stop @@ -148,16 +162,23 @@ /bin/sleep 5 fi +fi # end container check + # Turn off process accounting: if [ -x /sbin/accton -a -r /var/log/pacct ]; then /sbin/accton off fi +# container check +if [ ! $CONTAINER ]; then + # Terminate acpid before syslog: if [ -x /etc/rc.d/rc.acpid -a -r /var/run/acpid.pid ]; then # quit . /etc/rc.d/rc.acpid stop fi +fi # end container check + # Kill all processes. # INIT is supposed to handle this entirely now, but this didn't always # work correctly without this second pass at killing off the processes. @@ -169,6 +190,9 @@ /sbin/killall5 -9 fi +# container check +if [ ! $CONTAINER ]; then + # Try to turn off quota. if /bin/grep -q quota /etc/fstab ; then if [ -x /sbin/quotaoff ]; then @@ -177,6 +201,8 @@ fi fi +fi # end container check + # Carry a random seed between reboots. echo "Saving random seed from /dev/urandom in /etc/random-seed." # Use the pool size from /proc, or 512 bytes: @@ -195,6 +221,9 @@ rm -f /var/lock/subsys/* fi +# container check +if [ ! $CONTAINER ]; then + # Turn off swap: echo "Turning off swap." /sbin/swapoff -a @@ -206,9 +235,14 @@ echo "Remounting root filesystem read-only." /bin/mount -v -n -o remount,ro / +fi # end container check + # This never hurts: /bin/sync +# container check +if [ ! $CONTAINER ]; then + # Close any volumes opened by cryptsetup: if [ -f /etc/crypttab -a -x /sbin/cryptsetup ]; then cat /etc/crypttab | grep -v "^#" | grep -v "^$" | while read line; do @@ -272,3 +306,8 @@ /sbin/poweroff fi +fi # end container check + +# confirm successful shutdown +echo ; echo "* container stopped. *" ; echo + --- ./etc/rc.d/rc.M.orig 2011-07-20 11:13:38.332252512 +0200 +++ ./etc/rc.d/rc.M 2011-07-20 13:22:23.206678301 +0200 @@ -10,6 +10,10 @@ # Author: Fred N. van Kempen, # Heavily modified by Patrick Volkerding # +# tweaks for an lxc container by ponce , +# a check for a CONTAINER variable is made to jump unneeded sections + +CONTAINER=yes # Tell the viewers what's going to happen. echo "Going multiuser..." @@ -20,6 +24,9 @@ /sbin/ldconfig & fi +# container check +if [ ! $CONTAINER ]; then + # Screen blanks after 15 minutes idle time, and powers down in one hour # if the kernel supports APM or ACPI power management: /bin/setterm -blank 15 -powersave powerdown -powerdown 60 @@ -33,6 +40,8 @@ /bin/hostname darkstar fi +fi # end container check + # Set the permissions on /var/log/dmesg according to whether the kernel # permits non-root users to access kernel dmesg information: if [ -r /proc/sys/kernel/dmesg_restrict ]; then --- ./etc/rc.d/rc.inet1.orig 2011-07-20 11:13:56.671023244 +0200 +++ ./etc/rc.d/rc.inet1 2011-07-20 11:14:26.553649659 +0200 @@ -3,6 +3,11 @@ # This script is used to bring up the various network interfaces. # # @(#)/etc/rc.d/rc.inet1 10.2 Sun Jul 24 12:45:56 PDT 2005 (pjv) +# +# tweaks for an lxc container by ponce , +# a check for a CONTAINER variable is made to jump unneeded sections + +CONTAINER=yes ############################ # READ NETWORK CONFIG FILE # @@ -82,6 +87,10 @@ [ "${IFNAME[$i]}" = "${1}" ] && break i=$(($i+1)) done + + # container check + if [ ! $CONTAINER ]; then + # If the interface isn't in the kernel yet (but there's an alias for it in # modules.conf), then it should be loaded first: if ! grep `echo ${1}: | cut -f 1 -d :`: /proc/net/dev 1> /dev/null ; then # no interface yet @@ -90,6 +99,9 @@ /sbin/modprobe ${1} fi fi + + fi # end container check + if grep `echo ${1}: | cut -f 1 -d :`: /proc/net/dev 1> /dev/null ; then # interface exists if ! /sbin/ifconfig | grep -w "${1}" 1>/dev/null || \ ! /sbin/ifconfig ${1} | grep "inet addr" 1> /dev/null ; then # interface not up or not configured EOF ( cd $rootfs ; patch -s -p1 < tmp/rcs.patch ; rm tmp/rcs.patch ) # restart rc.inet1 to have routing for the loop device echo "/etc/rc.d/rc.inet1 restart" >> $rootfs/etc/rc.d/rc.local # reduce the number of local consoles: two should be enough sed -i '/^c3\|^c4\|^c5\|^c6/s/^/# /' $rootfs/etc/inittab # add a message to rc.local that confirms successful container startup echo "echo ; echo \"* container $name started. *\" ; echo" >> $rootfs/etc/rc.d/rc.local # set a default combination for the luggage echo "root:root" | chroot $rootfs chpasswd echo "Root password is 'root', please change it!" return 0 } download_slackware() { subarch= if [ "$arch" == "x86_64" ]; then subarch=64 fi # thanks to Vincent Batts for this list of packages # (that I modified a little :P) # http://connie.slackware.com/~vbatts/minimal/ PACKAGES=${PACKAGES:-" \ a/aaa_base-$SUITE-$arch-3.txz \ a/aaa_elflibs-$SUITE-$arch-7.txz \ a/aaa_terminfo-5.8-$arch-1.txz \ a/bash-4.1.010-$arch-1.txz \ a/bin-11.1-$arch-1.txz \ a/bzip2-1.0.6-$arch-1.txz \ a/coreutils-8.11-$arch-1.txz \ n/dhcpcd-5.2.11-$arch-1.txz \ a/dialog-1.1_20100428-$arch-2.txz \ ap/diffutils-3.0-$arch-1.txz \ a/e2fsprogs-1.41.14-$arch-1.txz \ a/elvis-2.2_0-$arch-2.txz \ a/etc-13.013-$arch-1.txz \ a/findutils-4.4.2-$arch-1.txz \ a/gawk-3.1.8-$arch-1.txz \ a/glibc-solibs-2.13-$arch-4.txz \ n/gnupg-1.4.11-$arch-1.txz \ a/grep-2.7-$arch-1.txz \ a/gzip-1.4-$arch-1.tgz \ n/iputils-s20101006-$arch-1.txz \ a/logrotate-3.7.8-$arch-1.txz \ n/net-tools-1.60-$arch-3.txz \ n/network-scripts-13.0-noarch-3.txz \ n/openssh-5.8p1-$arch-1.txz \ a/openssl-solibs-0.9.8r-$arch-3.txz \ a/pkgtools-$SUITE-noarch-9.tgz \ a/procps-3.2.8-$arch-3.txz \ a/sed-4.2.1-$arch-1.txz \ a/shadow-4.1.4.3-$arch-2.txz \ a/sharutils-4.11-$arch-1.txz \ ap/slackpkg-2.82.0-noarch-5.tgz \ a/sysklogd-1.5-$arch-1.txz \ a/sysvinit-2.86-$arch-6.txz \ a/sysvinit-functions-8.53-$arch-2.txz \ a/sysvinit-scripts-1.2-noarch-43.txz \ a/tar-1.26-$arch-1.tgz \ a/udev-165-$arch-2.txz \ a/util-linux-2.19-$arch-1.txz \ n/wget-1.12-$arch-1.txz \ a/which-2.20-$arch-1.txz \ a/xz-5.0.2-$arch-1.tgz"} # check if the slackware packages are already downloaded mkdir -p "$cache/partial-$SUITE-$arch" if [ $? -ne 0 ]; then echo "Failed to create '$cache/partial-$SUITE-$arch' directory" return 1 fi # download the packages into a cache folder echo "Downloading some slackware minimal packages..." ( cd $cache/partial-$SUITE-$arch for package in $PACKAGES; do wget -nv -c $MIRROR/slackware$subarch-$SUITE/slackware$subarch/$package done ) if [ $? -ne 0 ]; then echo "Failed to download the packages, aborting." return 1 fi mv "$cache/partial-$SUITE-$arch" "$cache/cache-$SUITE-$arch" echo "Download complete." echo return 0 } copy_slackware() { rootfs=$1 # make a local copy of the installed filesystem echo -n "Copying rootfs to $rootfs..." cp -a "$cache/rootfs-$SUITE-$arch" $rootfs || return 1 # fix fstab with the actual path sed -i -e "s|$cache/rootfs-$SUITE-$arch|$rootfs|" $rootfs/etc/fstab return 0 } install_slackware() { rootfs=$1 mkdir -p /var/lock/subsys/ ( flock -n -x 200 if [ $? -ne 0 ]; then echo "Cache repository is busy." return 1 fi echo "Checking cache download in $cache/cache-$SUITE-$arch ... " if [ ! -e "$cache/cache-$SUITE-$arch" ]; then download_slackware if [ $? -ne 0 ]; then echo "Failed to download slackware base packages." return 1 fi fi echo "Installing packages in $cache/rootfs-$SUITE-$arch ... " if [ -e "$cache/rootfs-$SUITE-$arch" ]; then echo "cleaning up existing $cache/rootfs-$SUITE-$arch ... " rm -fR "$cache/rootfs-$SUITE-$arch" fi mkdir -p "$cache/rootfs-$SUITE-$arch" for package in $cache/cache-$SUITE-$arch/*.t?z ; do installpkg -root $cache/rootfs-$SUITE-$arch -terse -priority ADD $package done return 0 ) 200>/var/lock/subsys/lxc return $? } copy_configuration() { path=$1 rootfs=$2 name=$3 cat <> $path/config lxc.utsname = $name lxc.mount = $rootfs/etc/fstab 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 # we don't trust root user in the container, better safe than sorry. # comment out only if you know what you're doing. lxc.cap.drop = sys_module mknod lxc.cap.drop = mac_override kill sys_time lxc.cap.drop = setfcap setpcap sys_boot # if you want to be even more restrictive with your container's root # user comment the three lines above and uncomment the following one # lxc.cap.drop=sys_admin 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 -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 } set -e 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 type installpkg if [ $? -ne 0 ]; then echo "'installpkg' command is missing." exit 1 fi if [ -z "$path" ]; then echo "'path' parameter is required." exit 1 fi if [ "$(id -u)" != "0" ]; then echo "This script should be run as 'root'." exit 1 fi if [ -z "$name" ]; then # no name given? set a default one name=slackwarecontainer fi echo rootfs=$path/rootfs install_slackware $rootfs if [ $? -ne 0 ]; then echo "Failed to install slackware." exit 1 fi echo configure_slackware $cache/rootfs-$SUITE-$arch $name if [ $? -ne 0 ]; then echo "Failed to configure slackware for a container." exit 1 fi echo rootfs=$path/rootfs copy_slackware $rootfs if [ $? -ne 0 ]; then echo "Failed to copy rootfs." return 1 fi echo copy_configuration $path $rootfs $name if [ $? -ne 0 ]; then echo "Failed to write configuration file." exit 1 fi if [ ! -z $clean ]; then clean || exit 1 exit 0 fi