]> git.proxmox.com Git - mirror_lxc.git/blobdiff - templates/lxc-busybox.in
start: call lxc_find_gateway_addresses early
[mirror_lxc.git] / templates / lxc-busybox.in
index f27efa9ef338b8e8ed48dcf749d509e36cc18ac5..c9f39872e3a04abacfa720827d8ebe3ac69aecc5 100644 (file)
-#!/bin/bash
+#!/bin/sh
 
+# Client script for LXC container images.
 #
-# 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.
+# Copyright @ Daniel Lezcano <daniel.lezcano@free.fr>
+# Copyright © 2018 Christian Brauner <christian.brauner@ubuntu.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.
+#  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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#  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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
+#  USA
 
 LXC_MAPPED_UID=
 LXC_MAPPED_GID=
-SSH=
+
+BUSYBOX_EXE=`which busybox`
 
 # Make sure the usual locations are in PATH
 export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin
 
-am_in_userns() {
-    [ -e /proc/self/uid_map ] || { echo no; return; }
-    [ "$(wc -l /proc/self/uid_map | awk '{ print $1 }')" -eq 1 ] || { echo yes; return; }
-    line=$(awk '{ print $1 " " $2 " " $3 }' /proc/self/uid_map)
-    [ "$line" = "0 0 4294967295" ] && { echo no; return; }
-    echo yes
-}
-
-in_userns=0
-[ $(am_in_userns) = "yes" ] && in_userns=1
-
-copy_binary()
-{
-    binary_path=`which $1`
-    if [ $? -ne 0 ]; then
-        echo "Unable to find $1 binary on the system"
-        return 1
-    fi
-
-    dir_path="${binary_path%/*}"
-    echo /{,usr/}{,s}bin | grep $dir_path >/dev/null 2>&1
-    if [ $? -ne 0 ]; then
-        echo "Binary $1 is located at $binary_path and will not be copied"
-        echo "($dir_path not supported)"
-        return 1
+in_userns() {
+  [ -e /proc/self/uid_map ] || { echo no; return; }
+  while read -r line; do
+    fields="$(echo "$line" | awk '{ print $1 " " $2 " " $3 }')"
+    if [ "${fields}" = "0 0 4294967295" ]; then
+      echo no;
+      return;
     fi
-
-    cp $binary_path $rootfs/$binary_path
-    if [ $? -ne 0 ]; then
-        echo "Failed to copy $binary_path to rootfs"
-        return 1
+    if echo "${fields}" | grep -q " 0 1$"; then
+      echo userns-root;
+      return;
     fi
+  done < /proc/self/uid_map
 
-    return 0
+  [ "$(cat /proc/self/uid_map)" = "$(cat /proc/1/uid_map)" ] && { echo userns-root; return; }
+  echo yes
 }
 
+USERNS="$(in_userns)"
+
 install_busybox()
 {
-    rootfs=$1
-    name=$2
-    res=0
-    tree="\
-$rootfs/selinux \
-$rootfs/dev \
-$rootfs/home \
-$rootfs/root \
-$rootfs/etc \
-$rootfs/etc/init.d \
-$rootfs/bin \
-$rootfs/usr/bin \
-$rootfs/sbin \
-$rootfs/usr/sbin \
-$rootfs/proc \
-$rootfs/sys \
-$rootfs/mnt \
-$rootfs/tmp \
-$rootfs/var/log \
-$rootfs/usr/share/udhcpc \
-$rootfs/dev/pts \
-$rootfs/dev/shm \
-$rootfs/lib \
-$rootfs/usr/lib \
-$rootfs/lib64 \
-$rootfs/usr/lib64"
-
-    mkdir -p $tree || return 1
-    chmod 755 $tree || return 1
-
-    pushd $rootfs/dev > /dev/null || return 1
-
-    # minimal devices needed for busybox
-    if [ $in_userns -eq 1 ]; then
-        for dev in tty console tty0 tty1 ram0 null urandom; do
-            echo "lxc.mount.entry = /dev/$dev dev/$dev    none bind,optional,create=file 0 0" >> $path/config
-        done
-    else
-        mknod -m 666 tty c 5 0       || res=1
-        mknod -m 666 console c 5 1   || res=1
-        mknod -m 666 tty0 c 4 0      || res=1
-        mknod -m 666 tty1 c 4 0      || res=1
-        mknod -m 666 tty5 c 4 0      || res=1
-        mknod -m 600 ram0 b 1 0      || res=1
-        mknod -m 666 null c 1 3      || res=1
-        mknod -m 666 zero c 1 5      || res=1
-        mknod -m 666 urandom c 1 9   || res=1
-    fi
-
-    popd > /dev/null
-
-    # root user defined
-    cat <<EOF >> $rootfs/etc/passwd
+  rootfs="${1}"
+  name="${2}"
+  res=0
+  fstree="\
+${rootfs}/selinux \
+${rootfs}/dev \
+${rootfs}/home \
+${rootfs}/root \
+${rootfs}/etc \
+${rootfs}/etc/init.d \
+${rootfs}/bin \
+${rootfs}/usr/bin \
+${rootfs}/sbin \
+${rootfs}/usr/sbin \
+${rootfs}/proc \
+${rootfs}/sys \
+${rootfs}/mnt \
+${rootfs}/tmp \
+${rootfs}/var/log \
+${rootfs}/var/run \
+${rootfs}/usr/share/udhcpc \
+${rootfs}/dev/pts \
+${rootfs}/dev/shm \
+${rootfs}/lib \
+${rootfs}/usr/lib \
+${rootfs}/lib64 \
+${rootfs}/usr/lib64"
+
+  # shellcheck disable=SC2086
+  mkdir -p ${fstree} || return 1
+  # shellcheck disable=SC2086
+  chmod 755 ${fstree} || return 1
+
+  # minimal devices needed for busybox
+  if [ "${USERNS}" = "yes" ]; then
+    for dev in tty console tty0 tty1 ram0 null urandom; do
+      echo "lxc.mount.entry = /dev/${dev} dev/${dev} none bind,optional,create=file 0 0" >> "${path}/config"
+    done
+  else
+    mknod -m 666 "${rootfs}/dev/tty" c 5 0       || res=1
+    mknod -m 666 "${rootfs}/dev/console" c 5 1   || res=1
+    mknod -m 666 "${rootfs}/dev/tty0" c 4 0      || res=1
+    mknod -m 666 "${rootfs}/dev/tty1" c 4 0      || res=1
+    mknod -m 666 "${rootfs}/dev/tty5" c 4 0      || res=1
+    mknod -m 600 "${rootfs}/dev/ram0" b 1 0      || res=1
+    mknod -m 666 "${rootfs}/dev/null" c 1 3      || res=1
+    mknod -m 666 "${rootfs}/dev/zero" c 1 5      || res=1
+    mknod -m 666 "${rootfs}/dev/urandom" c 1 9   || res=1
+  fi
+  
+  # make /tmp accessible to any user (with sticky bit)
+  chmod 1777 "${rootfs}/tmp" || return 1
+
+  # root user defined
+  cat <<EOF >> "${rootfs}/etc/passwd"
 root:x:0:0:root:/root:/bin/sh
 EOF
 
-    cat <<EOF >> $rootfs/etc/group
+  cat <<EOF >> "${rootfs}/etc/group"
 root:x:0:root
 EOF
 
     # mount everything
-    cat <<EOF >> $rootfs/etc/init.d/rcS
+  cat <<EOF >> "${rootfs}/etc/init.d/rcS"
 #!/bin/sh
 /bin/syslogd
 /bin/mount -a
 /bin/udhcpc
 EOF
 
-    # executable
-    chmod 744 $rootfs/etc/init.d/rcS || return 1
+  # executable
+  chmod 744 "${rootfs}/etc/init.d/rcS" || return 1
 
-    # launch rcS first then make a console available
-    # and propose a shell on the tty, the last one is
-    # not needed
-    cat <<EOF >> $rootfs/etc/inittab
+  # launch rcS first then make a console available
+  # and propose a shell on the tty, the last one is
+  # not needed
+  cat <<EOF >> "${rootfs}/etc/inittab"
 ::sysinit:/etc/init.d/rcS
 tty1::respawn:/bin/getty -L tty1 115200 vt100
 console::askfirst:/bin/sh
 EOF
-    # writable and readable for other
-    chmod 644 $rootfs/etc/inittab || return 1
+  # writable and readable for other
+  chmod 644 "${rootfs}/etc/inittab" || return 1
+
+  # Look for the pathname of "default.script" from the help of udhcpc
+  DEF_SCRIPT=`${BUSYBOX_EXE} udhcpc -h 2>&1 | grep -- '-s,--script PROG' | cut -d'/' -f2- | cut -d')' -f1`
+  DEF_SCRIPT_DIR=`dirname /${DEF_SCRIPT}`
+  mkdir -p ${rootfs}/${DEF_SCRIPT_DIR}
+  chmod 644 ${rootfs}/${DEF_SCRIPT_DIR} || return 1
 
-    cat <<EOF >> $rootfs/usr/share/udhcpc/default.script
+  cat <<EOF >> ${rootfs}/${DEF_SCRIPT}
 #!/bin/sh
 case "\$1" in
-    deconfig)
-        ip addr flush dev \$interface
-        ;;
-
-    renew|bound)
-        # flush all the routes
-        if [ -n "\$router" ]; then
-            ip route del default 2> /dev/null
-        fi
-
-        # check broadcast
-        if [ -n "\$broadcast" ]; then
-            broadcast="broadcast \$broadcast"
-        fi
-
-        # add a new ip address
-        ip addr add \$ip/\$mask \$broadcast dev \$interface
-
-        if [ -n "\$router" ]; then
-            ip route add default via \$router dev \$interface
-        fi
-
-        [ -n "\$domain" ] && echo search \$domain > /etc/resolv.conf
-        for i in \$dns ; do
-            echo nameserver \$i >> /etc/resolv.conf
-        done
-        ;;
-esac
-exit 0
-EOF
-
-    chmod 744 $rootfs/usr/share/udhcpc/default.script
+  deconfig)
+    ip addr flush dev \$interface
+    ;;
+
+  renew|bound)
+    # flush all the routes
+    if [ -n "\$router" ]; then
+      ip route del default 2> /dev/null
+    fi
 
-    return $res
-}
+    # check broadcast
+    if [ -n "\$broadcast" ]; then
+      broadcast="broadcast \$broadcast"
+    fi
 
-install_dropbear()
-{
-    # copy dropbear binary
-    copy_binary dropbear || return 1
-
-    # make symlinks to various ssh utilities
-    utils="\
-        $rootfs/usr/bin/dbclient \
-        $rootfs/usr/bin/scp \
-        $rootfs/usr/bin/ssh \
-        $rootfs/usr/sbin/dropbearkey \
-        $rootfs/usr/sbin/dropbearconvert \
-    "
-    echo $utils | xargs -n1 ln -s /usr/sbin/dropbear
-
-    # add necessary config files
-    mkdir $rootfs/etc/dropbear
-    dropbearkey -t rsa -f $rootfs/etc/dropbear/dropbear_rsa_host_key > /dev/null 2>&1
-    dropbearkey -t dss -f $rootfs/etc/dropbear/dropbear_dss_host_key > /dev/null 2>&1
-
-    echo "'dropbear' ssh utility installed"
-
-    return 0
-}
+    # add a new ip address
+    ip addr add \$ip/\$mask \$broadcast dev \$interface
 
-install_openssh()
-{
-    # tools to be installed
-    server_utils="sshd"
-    client_utils="\
-        ssh \
-        scp \
-        "
-    client_optional_utils="\
-        sftp \
-        ssh-add \
-        ssh-agent \
-        ssh-keygen \
-        ssh-keyscan \
-        ssh-argv0 \
-        ssh-copy-id \
-        "
-
-    # new folders used by ssh
-    ssh_tree="\
-$rootfs/etc/ssh \
-$rootfs/var/empty/sshd \
-$rootfs/var/lib/empty/sshd \
-$rootfs/var/run/sshd \
-"
-
-    # create folder structure
-    mkdir -p $ssh_tree
-    if [ $? -ne 0 ]; then
-        return 1
+    if [ -n "\$router" ]; then
+      ip route add default via \$router dev \$interface
     fi
 
-    # copy binaries
-    for bin in $server_utils $client_utils; do
-        copy_binary $bin || return 1
+    [ -n "\$domain" ] && echo search \$domain > /etc/resolv.conf
+    for i in \$dns ; do
+      grep "nameserver \$i" /etc/resolv.conf > /dev/null 2>&1
+      if [ \$? -ne 0 ]; then
+        echo nameserver \$i >> /etc/resolv.conf
+      fi
     done
-
-    for bin in $client_optional_utils; do
-        tool_path=`which $bin` && copy_binary $bin
-    done
-
-    # add user and group
-    cat <<EOF >> $rootfs/etc/passwd
-sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
-EOF
-
-    cat <<EOF >> $rootfs/etc/group
-sshd:x:74:
-EOF
-
-    # generate container keys
-    ssh-keygen -t rsa -N "" -f $rootfs/etc/ssh/ssh_host_rsa_key >/dev/null 2>&1
-    ssh-keygen -t dsa -N "" -f $rootfs/etc/ssh/ssh_host_dsa_key >/dev/null 2>&1
-
-    # by default setup root password with no password
-    cat <<EOF > $rootfs/etc/ssh/sshd_config
-Port 22
-Protocol 2
-HostKey /etc/ssh/ssh_host_rsa_key
-HostKey /etc/ssh/ssh_host_dsa_key
-UsePrivilegeSeparation yes
-KeyRegenerationInterval 3600
-ServerKeyBits 768
-SyslogFacility AUTH
-LogLevel INFO
-LoginGraceTime 120
-PermitRootLogin yes
-StrictModes yes
-RSAAuthentication yes
-PubkeyAuthentication yes
-IgnoreRhosts yes
-RhostsRSAAuthentication no
-HostbasedAuthentication no
-PermitEmptyPasswords yes
-ChallengeResponseAuthentication no
+    ;;
+esac
+exit 0
 EOF
 
-    echo "'OpenSSH' utility installed"
+ chmod 744 ${rootfs}/${DEF_SCRIPT}
 
-    return 0
+ return "${res}"
 }
 
 configure_busybox()
 {
-    rootfs=$1
-
-    which busybox >/dev/null 2>&1
-
-    if [ $? -ne 0 ]; then
-        echo "busybox executable is not accessible"
-        return 1
-    fi
+  rootfs="${1}"
 
-    # copy busybox in the rootfs
-    cp $(which busybox) $rootfs/bin
-    if [ $? -ne 0 ]; then
-        echo "failed to copy busybox in the rootfs"
-        return 1
-    fi
+  # copy busybox in the rootfs
+  if ! cp "${BUSYBOX_EXE}" "${rootfs}/bin"; then
+    echo "ERROR: Failed to copy busybox binary" 1>&2
+    return 1
+  fi
 
-    # symlink busybox for the commands it supports
-    # it would be nice to just use "chroot $rootfs busybox --install -s /bin"
-    # but that only works right in a chroot with busybox >= 1.19.0
-    pushd $rootfs/bin > /dev/null || return 1
-    ./busybox --help | grep 'Currently defined functions:' -A300 | \
-      grep -v 'Currently defined functions:' | tr , '\n' | \
-      xargs -n1 ln -s busybox
-    popd > /dev/null
+  # symlink busybox for the commands it supports
+  # it would be nice to just use "chroot $rootfs busybox --install -s /bin"
+  # but that only works right in a chroot with busybox >= 1.19.0
+  (
+    cd "${rootfs}/bin" || return 1
+    ./busybox --list | grep -v busybox | xargs -n1 ln -s busybox
+  )
 
-    # relink /sbin/init
-    ln $rootfs/bin/busybox $rootfs/sbin/init
+  # relink /sbin/init
+  ln "${rootfs}/bin/busybox" "${rootfs}/sbin/init"
 
-    # /etc/fstab must exist for "mount -a"
-    touch $rootfs/etc/fstab
+  # /etc/fstab must exist for "mount -a"
+  touch "${rootfs}/etc/fstab"
 
-    # passwd exec must be setuid
-    chmod +s $rootfs/bin/passwd
-    touch $rootfs/etc/shadow
+  # passwd exec must be setuid
+  chmod +s "${rootfs}/bin/passwd"
+  touch "${rootfs}/etc/shadow"
 
-    return 0
+  return 0
 }
 
 copy_configuration()
 {
-    path=$1
-    rootfs=$2
-    name=$3
+  path="${1}"
+  rootfs="${2}"
+  name="${3}"
 
-grep -q "^lxc.rootfs.path" $path/config 2>/dev/null || echo "lxc.rootfs.path = $rootfs" >> $path/config
-cat <<EOF >> $path/config
+grep -q "^lxc.rootfs.path" "${path}/config" 2>/dev/null || echo "lxc.rootfs.path = ${rootfs}" >> "${path}/config"
+cat <<EOF >> "${path}/config"
 lxc.signal.halt = SIGUSR1
-lxc.rebootsignal = SIGTERM
-lxc.uts.name = $name
+lxc.signal.reboot = SIGTERM
+lxc.uts.name = "${name}"
 lxc.tty.max = 1
 lxc.pty.max = 1
 lxc.cap.drop = sys_module mac_admin mac_override sys_time
@@ -355,130 +235,122 @@ lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed
 lxc.mount.entry = shm /dev/shm tmpfs defaults 0 0
 EOF
 
-    libdirs="\
-        lib \
-        usr/lib \
-        lib64 \
-        usr/lib64"
+  libdirs="\
+    lib \
+    usr/lib \
+    lib64 \
+    usr/lib64"
 
-    for dir in $libdirs; do
-        if [ -d "/$dir" ] && [ -d "$rootfs/$dir" ]; then
-            echo "lxc.mount.entry = /$dir $dir none ro,bind 0 0" >> $path/config
-        fi
-    done
-    echo "lxc.mount.entry = /sys/kernel/security sys/kernel/security none ro,bind,optional 0 0" >>$path/config
+  for dir in ${libdirs}; do
+    if [ -d "/${dir}" ] && [ -d "${rootfs}/${dir}" ]; then
+      echo "lxc.mount.entry = /${dir} ${dir} none ro,bind 0 0" >> "${path}/config"
+    fi
+  done
+  echo "lxc.mount.entry = /sys/kernel/security sys/kernel/security none ro,bind,optional 0 0" >> "${path}/config"
 }
 
 remap_userns()
 {
-    path=$1
+ path="${1}"
 
   if [ -n "$LXC_MAPPED_UID" ] && [ "$LXC_MAPPED_UID" != "-1" ]; then
-        chown $LXC_MAPPED_UID $path/config >/dev/null 2>&1
-        chown -R root $path/rootfs >/dev/null 2>&1
   fi
+ if [ -n "$LXC_MAPPED_UID" ] && [ "$LXC_MAPPED_UID" != "-1" ]; then
+   chown "${LXC_MAPPED_UID}" "${path}/config" > /dev/null 2>&1
+   chown -R root "${path}/rootfs" > /dev/null 2>&1
+ fi
 
   if [ -n "$LXC_MAPPED_GID" ] && [ "$LXC_MAPPED_GID" != "-1" ]; then
-        chgrp $LXC_MAPPED_GID $path/config >/dev/null 2>&1
-        chgrp -R root $path/rootfs >/dev/null 2>&1
   fi
+ if [ -n "$LXC_MAPPED_GID" ] && [ "$LXC_MAPPED_GID" != "-1" ]; then
+   chgrp "${LXC_MAPPED_GID}" "${path}/config" > /dev/null 2>&1
+   chgrp -R root "${path}/rootfs" > /dev/null 2>&1
+ fi
 }
 
-usage()
-{
-    cat <<EOF
-$1 -h|--help -p|--path=<path> -s|--ssh={dropbear,openssh}
+usage() {
+  cat <<EOF
+LXC busybox image builder
+
+Special arguments:
+
+  [ -h | --help ]: Print this help message and exit.
+
+LXC internal arguments:
+
+  [ --name <name> ]: The container name
+  [ --path <path> ]: The path to the container
+  [ --rootfs <rootfs> ]: The path to the container's rootfs (default: config or <path>/rootfs)
+  [ --mapped-uid <map> ]: A uid map (user namespaces)
+  [ --mapped-gid <map> ]: A gid map (user namespaces)
+
+BUSYBOX template specific arguments:
+
+  [ --busybox-path <path> ]: busybox pathname (default: ${BUSYBOX_EXE})
+
 EOF
-    return 0
+  return 0
 }
 
-options=$(getopt -o hp:n:s: -l help,rootfs:,path:,name:,mapped-uid:,mapped-gid:,ssh: -- "$@")
-if [ $? -ne 0 ]; then
-    usage $(basename $0)
-    exit 1
+if ! options=$(getopt -o hp:n: -l help,rootfs:,path:,name:,mapped-uid:,mapped-gid:,busybox-path: -- "$@"); then
+  usage
+  exit 1
 fi
 eval set -- "$options"
 
 while true
 do
-    case "$1" in
-        -h|--help)      usage $0 && exit 0;;
-        -p|--path)      path=$2; shift 2;;
-        --rootfs)       rootfs=$2; shift 2;;
-        -n|--name)      name=$2; shift 2;;
-        --mapped-uid)   LXC_MAPPED_UID=$2; shift 2;;
-        --mapped-gid)   LXC_MAPPED_GID=$2; shift 2;;
-        -s|--ssh)       SSH=$2; shift 2;;
-        --)             shift 1; break ;;
-        *)              break ;;
-    esac
+  case "$1" in
+    -h|--help)    usage && exit 0;;
+    -n|--name)    name=$2; shift 2;;
+    -p|--path)    path=$2; shift 2;;
+    --rootfs)     rootfs=$2; shift 2;;
+    --mapped-uid) LXC_MAPPED_UID=$2; shift 2;;
+    --mapped-gid) LXC_MAPPED_GID=$2; shift 2;;
+    --busybox-path) BUSYBOX_EXE=$2; shift 2;;
+    --)           shift 1; break ;;
+    *)            break ;;
+  esac
 done
 
-if [ "$(id -u)" != "0" ]; then
-    echo "This script should be run as 'root'"
+# Check that we have all variables we need
+if [ -z "${name}" ] || [ -z "${path}" ]; then
+    echo "ERROR: Please pass the name and path for the container" 1>&2
     exit 1
 fi
 
-if [ -z "$path" ]; then
-    echo "'path' parameter is required"
+# Make sure busybox is present
+if [ -z "${BUSYBOX_EXE}" ]; then
+    echo "ERROR: Please pass a pathname for busybox binary" 1>&2
+    exit 1
+fi
+if [ ! -x "${BUSYBOX_EXE}" ]; then
+    echo "ERROR: Failed to find busybox binary (${BUSYBOX_EXE})" 1>&2
     exit 1
 fi
 
 # detect rootfs
 config="$path/config"
 if [ -z "$rootfs" ]; then
-    if grep -q '^lxc.rootfs.path' $config 2>/dev/null ; then
-        rootfs=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' $config)
-    else
-        rootfs=$path/rootfs
-    fi
-fi
-
-install_busybox $rootfs $name
-if [ $? -ne 0 ]; then
-    echo "failed to install busybox's rootfs"
-    exit 1
+  if grep -q '^lxc.rootfs.path' "${config}" 2> /dev/null ; then
+    rootfs=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' "${config}")
+  else
+    rootfs="${path}/rootfs"
+  fi
 fi
 
-configure_busybox $rootfs
-if [ $? -ne 0 ]; then
-    echo "failed to configure busybox template"
-    exit 1
+if ! install_busybox "${rootfs}" "${name}"; then
+  echo "ERROR: Failed to install rootfs" 1>&2
+  exit 1
 fi
 
-copy_configuration $path $rootfs $name
-if [ $? -ne 0 ]; then
-    echo "failed to write configuration file"
-    exit 1
+if ! configure_busybox "${rootfs}"; then
+  echo "ERROR: Failed to configure busybox" 1>&2
+  exit 1
 fi
 
-remap_userns $path
-if [ $? -ne 0 ]; then
-    echo "failed to remap files to user"
-    exit 1
+if ! copy_configuration "${path}" "${rootfs}" "${name}"; then
+  echo "ERROR: Failed to write config file" 1>&2
+  exit 1
 fi
 
-if [ -n "$SSH" ]; then
-    case "$SSH" in
-        "dropbear")
-            install_dropbear
-            if [ $? -ne 0 ]; then
-                echo "Unable to install 'dropbear' ssh utility"
-                exit 1
-            fi ;;
-        "openssh")
-            install_openssh
-            if [ $? -ne 0 ]; then
-                echo "Unable to install 'OpenSSH' utility"
-                exit 1
-            fi ;;
-        *)
-            echo "$SSH: unrecognized ssh utility"
-            exit 1
-    esac
-else
-    which dropbear >/dev/null 2>&1
-    if [ $? -eq 0 ]; then
-        install_dropbear
-    fi
+if ! remap_userns "${path}"; then
+  echo "ERROR: Failed to change idmappings" 1>&2
+  exit 1
 fi