diff --git a/src/lxc/cgfsng.c b/src/lxc/cgfsng.c index a6767c520..4a492269a 100644 --- a/src/lxc/cgfsng.c +++ b/src/lxc/cgfsng.c @@ -1022,9 +1022,6 @@ static void cgfsng_destroy(void *hdata, struct lxc_conf *conf) struct cgroup_ops *cgfsng_ops_init(void) { - /* TODO - when cgroup_mount is implemented, drop this check */ - if (!file_exists("/proc/self/ns/cgroup")) - return NULL; return &cgfsng_ops; } @@ -1223,12 +1220,161 @@ static bool cgfsns_chown(void *hdata, struct lxc_conf *conf) return true; } +/* + * We've safe-mounted a tmpfs as parent, so we don't need to protect against + * symlinks any more - just use mount + */ + +/* mount cgroup-full if requested */ +static int mount_cgroup_full(int type, struct hierarchy *h, char *dest, + char *container_cgroup) +{ + if (type < LXC_AUTO_CGROUP_FULL_RO || type > LXC_AUTO_CGROUP_FULL_MIXED) + return 0; + if (mount(h->mountpoint, dest, "cgroup", MS_BIND, NULL) < 0) { + SYSERROR("Error bind-mounting %s cgroup onto %s", h->mountpoint, + dest); + return -1; + } + if (type != LXC_AUTO_CGROUP_FULL_RW) { + if (mount(NULL, dest, "cgroup", MS_BIND | MS_REMOUNT | MS_RDONLY, NULL) < 0) { + SYSERROR("Error remounting %s readonly", dest); + return -1; + } + } + + INFO("Bind mounted %s onto %s", h->mountpoint, dest); + if (type != LXC_AUTO_CGROUP_FULL_MIXED) + return 0; + + /* mount just the container path rw */ + char *source = must_make_path(h->mountpoint, h->base_cgroup, container_cgroup, NULL); + char *rwpath = must_make_path(dest, container_cgroup, NULL); + if (mount(source, rwpath, "cgroup", MS_BIND, NULL) < 0) + WARN("Failed to mount %s read-write: %m", rwpath); + INFO("Made %s read-write", rwpath); + free(rwpath); + free(source); + return 0; +} + +/* cgroup-full:* is done, no need to create subdirs */ +static bool cg_mount_needs_subdirs(int type) +{ + if (type >= LXC_AUTO_CGROUP_FULL_RO) + return false; + return true; +} + +/* + * After $rootfs/sys/fs/container/controller/the/cg/path has been + * created, remount controller ro if needed and bindmount the + * cgroupfs onto controll/the/cg/path + */ +static int +do_secondstage_mounts_if_needed(int type, struct hierarchy *h, + char *controllerpath, char *cgpath, + const char *container_cgroup) +{ + if (type == LXC_AUTO_CGROUP_RO || type == LXC_AUTO_CGROUP_MIXED) { + if (mount(controllerpath, controllerpath, "cgroup", MS_BIND, NULL) < 0) { + SYSERROR("Error bind-mounting %s", controllerpath); + return -1; + } + if (mount(controllerpath, controllerpath, "cgroup", + MS_REMOUNT | MS_BIND | MS_RDONLY, NULL) < 0) { + SYSERROR("Error remounting %s read-only", controllerpath); + return -1; + } + INFO("Remounted %s read-only", controllerpath); + } + char *sourcepath = must_make_path(h->mountpoint, h->base_cgroup, container_cgroup, NULL); + int flags = MS_BIND; + if (type == LXC_AUTO_CGROUP_RO) + flags |= MS_RDONLY; + INFO("Mounting %s onto %s", sourcepath, cgpath); + if (mount(sourcepath, cgpath, "cgroup", flags, NULL) < 0) { + free(sourcepath); + SYSERROR("Error mounting cgroup %s onto %s", h->controllers[0], + cgpath); + return -1; + } + free(sourcepath); + INFO("Completed second stage cgroup automounts for %s", cgpath); + return 0; +} + static bool cgfsng_mount(void *hdata, const char *root, int type) { + struct cgfsng_handler_data *d = hdata; + char *tmpfspath = NULL; + bool retval = false; + + if ((type & LXC_AUTO_CGROUP_MASK) == 0) + return true; + if (cgns_supported()) return true; - // TODO - implement this. Not needed for cgroup namespaces - return false; + + tmpfspath = must_make_path(root, "/sys/fs/cgroup", NULL); + + if (type == LXC_AUTO_CGROUP_NOSPEC) + type = LXC_AUTO_CGROUP_MIXED; + else if (type == LXC_AUTO_CGROUP_FULL_NOSPEC) + type = LXC_AUTO_CGROUP_FULL_MIXED; + + /* Mount tmpfs */ + if (safe_mount("cgroup_root", tmpfspath, "tmpfs", + MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_RELATIME, + "size=10240k,mode=755", + root) < 0) + goto bad; + + for (int i = 0; d->hierarchies[i]; i++) { + char *controllerpath, *path2; + struct hierarchy *h = d->hierarchies[i]; + char *controller = strrchr(h->mountpoint, '/'); + int r; + + if (!controller) + continue; + controller++; + controllerpath = must_make_path(tmpfspath, controller, NULL); + if (dir_exists(controllerpath)) { + free(controllerpath); + continue; + } + if (mkdir(controllerpath, 0755) < 0) { + SYSERROR("Error creating cgroup path: %s", controllerpath); + free(controllerpath); + goto bad; + } + if (mount_cgroup_full(type, h, controllerpath, d->container_cgroup) < 0) { + free(controllerpath); + goto bad; + } + if (!cg_mount_needs_subdirs(type)) { + free(controllerpath); + continue; + } + path2 = must_make_path(controllerpath, d->container_cgroup, NULL); + if (mkdir_p(path2, 0755) < 0) { + free(controllerpath); + goto bad; + } + + r = do_secondstage_mounts_if_needed(type, h, controllerpath, path2, + d->container_cgroup); + free(controllerpath); + free(path2); + if (r < 0) + goto bad; + } + retval = true; + +bad: + free(tmpfspath); + return retval; } static int recursive_count_nrtasks(char *dirname) diff --git a/src/lxc/liblxc.so.1 b/src/lxc/liblxc.so.1 new file mode 120000 index 000000000..824cc6c5e --- /dev/null +++ b/src/lxc/liblxc.so.1 @@ -0,0 +1 @@ +liblxc.so \ No newline at end of file diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index 5151dca80..68141f92b 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -48,7 +48,8 @@ bin_PROGRAMS = lxc-test-containertests lxc-test-locktests lxc-test-startone \ lxc-test-reboot lxc-test-list lxc-test-attach lxc-test-device-add-remove \ lxc-test-apparmor -bin_SCRIPTS = lxc-test-autostart lxc-test-cloneconfig lxc-test-createconfig +bin_SCRIPTS = lxc-test-automount lxc-test-autostart lxc-test-cloneconfig \ + lxc-test-createconfig if DISTRO_UBUNTU bin_SCRIPTS += \ @@ -79,6 +80,7 @@ EXTRA_DIST = \ locktests.c \ lxcpath.c \ lxc-test-lxc-attach \ + lxc-test-automount \ lxc-test-autostart \ lxc-test-apparmor-mount \ lxc-test-checkpoint-restore \ diff --git a/src/tests/lxc-test-automount b/src/tests/lxc-test-automount new file mode 100644 index 000000000..6a06d7acd --- /dev/null +++ b/src/tests/lxc-test-automount @@ -0,0 +1,217 @@ +#!/bin/bash + +# lxc: linux Container library + +# Authors: +# Serge Hallyn + +# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# At the moment this only tests cgroup automount. Testing proc and +# sys automounts would be worthwhile. + +[ -f /proc/self/ns/cgroup ] && exit 0 + +set -ex + +cleanup() { + set +e + rmdir /sys/fs/cgroup/freezer/xx + lxc-destroy -n lxc-test-automount -f + if [ $PHASE != "done" ]; then + echo "automount test failed at $PHASE" + exit 1 + fi + echo "automount test passed" + exit 0 +} + +PHASE=setup +trap cleanup EXIT + +rmdir /sys/fs/cgroup/freezer/xx || true +lxc-destroy -n lxc-test-automount -f || true +lxc-create -t busybox -n lxc-test-automount + +PHASE=no-cgroup +echo "Starting phase $PHASE" +config=/var/lib/lxc/lxc-test-automount/config +sed -i '/lxc.mount.auto/d' $config +echo "lxc.mount.auto = proc:mixed sys:mixed" >> $config + +lxc-start -n lxc-test-automount +pid=`lxc-info -n lxc-test-automount -p -H` +cg=`awk -F: '/freezer/ { print $3 }' /proc/$pid/cgroup` +notfound=0 +stat /proc/$pid/root/sys/fs/cgroup/freezer || notfound=1 +[ $notfound -ne 0 ] + +# Tests are as follows: +# 1. check that freezer controller is mounted +# 2. check that it is cgroupfs for cgroup-full (/cgroup.procs exists) or +# tmpfs for cgroup +# 3. check that root cgroup dir is readonly or not (try mkdir) +# 4. check that the container's cgroup dir is readonly or not +# 5. check that the container's cgroup dir is cgroupfs (/cgroup.procs exists) + +lxc-stop -n lxc-test-automount -k +PHASE=cgroup:mixed +echo "Starting phase $PHASE" +sed -i '/lxc.mount.auto/d' $config +echo "lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed" >> $config +lxc-start -n lxc-test-automount +pid=`lxc-info -n lxc-test-automount -p -H` +notfound=0 +stat /proc/$pid/root/sys/fs/cgroup/freezer || notfound=1 +[ $notfound -ne 1 ] +notfound=0 +stat /proc/$pid/root/sys/fs/cgroup/freezer/cgroup.procs || notfound=1 +[ $notfound -ne 0 ] +ro=0 +mkdir /proc/$pid/root/sys/fs/cgroup/freezer/xx || ro=1 +[ $ro -ne 0 ] +ro=0 +mkdir /proc/$pid/root/sys/fs/cgroup/freezer/$cg/xx || ro=1 +[ $ro -ne 1 ] +notfound=0 +stat /proc/$pid/root/sys/fs/cgroup/freezer/$cg/cgroup.procs || notfound=1 +[ $notfound -ne 1 ] + +lxc-stop -n lxc-test-automount -k +PHASE=cgroup:ro +echo "Starting phase $PHASE" +sed -i '/lxc.mount.auto/d' $config +echo "lxc.mount.auto = cgroup:ro proc:mixed sys:mixed" >> $config +lxc-start -n lxc-test-automount +pid=`lxc-info -n lxc-test-automount -p -H` +cg=`awk -F: '/freezer/ { print $3 }' /proc/$pid/cgroup` +notfound=0 +stat /proc/$pid/root/sys/fs/cgroup/freezer || notfound=1 +[ $notfound -ne 1 ] +notfound=0 +stat /proc/$pid/root/sys/fs/cgroup/freezer/cgroup.procs || notfound=1 +[ $notfound -ne 0 ] +ro=0 +mkdir /proc/$pid/root/sys/fs/cgroup/freezer/xx || ro=1 +[ $ro -ne 0 ] +ro=0 +mkdir /proc/$pid/root/sys/fs/cgroup/freezer/$cg/xx || ro=1 +[ $ro -ne 1 ] +notfound=0 +stat /proc/$pid/root/sys/fs/cgroup/freezer/$cg/cgroup.procs || notfound=1 +[ $notfound -ne 1 ] + +lxc-stop -n lxc-test-automount -k +PHASE=cgroup:rw +echo "Starting phase $PHASE" +sed -i '/lxc.mount.auto/d' $config +echo "lxc.mount.auto = cgroup:rw proc:mixed sys:mixed" >> $config +lxc-start -n lxc-test-automount +pid=`lxc-info -n lxc-test-automount -p -H` +cg=`awk -F: '/freezer/ { print $3 }' /proc/$pid/cgroup` +notfound=0 +stat /proc/$pid/root/sys/fs/cgroup/freezer || notfound=1 +[ $notfound -ne 1 ] +notfound=0 +stat /proc/$pid/root/sys/fs/cgroup/freezer/cgroup.procs || notfound=1 +[ $notfound -ne 0 ] +ro=0 +mkdir /proc/$pid/root/sys/fs/cgroup/freezer/xx || ro=1 +[ $ro -ne 1 ] +rmdir /proc/$pid/root/sys/fs/cgroup/freezer/xx +ro=0 +mkdir /proc/$pid/root/sys/fs/cgroup/freezer/$cg/xx || ro=1 +[ $ro -ne 1 ] +notfound=0 +stat /proc/$pid/root/sys/fs/cgroup/freezer/$cg/cgroup.procs || notfound=1 +[ $notfound -ne 1 ] + +# cgroup-full + +lxc-stop -n lxc-test-automount -k +PHASE=cgroup-full:mixed +echo "Starting phase $PHASE" +sed -i '/lxc.mount.auto/d' $config +echo "lxc.mount.auto = cgroup-full:mixed proc:mixed sys:mixed" >> $config +lxc-start -n lxc-test-automount +pid=`lxc-info -n lxc-test-automount -p -H` +cg=`awk -F: '/freezer/ { print $3 }' /proc/$pid/cgroup` +notfound=0 +stat /proc/$pid/root/sys/fs/cgroup/freezer || notfound=1 +[ $notfound -ne 1 ] +notfound=0 +stat /proc/$pid/root/sys/fs/cgroup/freezer/cgroup.procs || notfound=1 +[ $notfound -ne 1 ] +ro=0 +mkdir /proc/$pid/root/sys/fs/cgroup/freezer/xx || ro=1 +[ $ro -ne 0 ] +ro=0 +mkdir /proc/$pid/root/sys/fs/cgroup/freezer/$cg/xx || ro=1 +[ $ro -ne 1 ] +rmdir /proc/$pid/root/sys/fs/cgroup/freezer/$cg/xx +notfound=0 +stat /proc/$pid/root/sys/fs/cgroup/freezer/$cg/cgroup.procs || notfound=1 +[ $notfound -ne 1 ] + +lxc-stop -n lxc-test-automount -k +PHASE=cgroup-full:ro +echo "Starting phase $PHASE" +sed -i '/lxc.mount.auto/d' $config +echo "lxc.mount.auto = cgroup-full:ro proc:mixed sys:mixed" >> $config +lxc-start -n lxc-test-automount +pid=`lxc-info -n lxc-test-automount -p -H` +cg=`awk -F: '/freezer/ { print $3 }' /proc/$pid/cgroup` +notfound=0 +stat /proc/$pid/root/sys/fs/cgroup/freezer || notfound=1 +[ $notfound -ne 1 ] +notfound=0 +stat /proc/$pid/root/sys/fs/cgroup/freezer/cgroup.procs || notfound=1 +[ $notfound -ne 1 ] +ro=0 +mkdir /proc/$pid/root/sys/fs/cgroup/freezer/xx || ro=1 +[ $ro -ne 0 ] +ro=0 +mkdir /proc/$pid/root/sys/fs/cgroup/freezer/$cg/xy || ro=1 +[ $ro -ne 0 ] +notfound=0 +stat /proc/$pid/root/sys/fs/cgroup/freezer/$cg/cgroup.procs || notfound=1 +[ $notfound -ne 1 ] + +lxc-stop -n lxc-test-automount -k +PHASE=cgroup-full:rw +echo "Starting phase $PHASE" +sed -i '/lxc.mount.auto/d' $config +echo "lxc.mount.auto = cgroup-full:rw proc:mixed sys:mixed" >> $config +lxc-start -n lxc-test-automount +pid=`lxc-info -n lxc-test-automount -p -H` +cg=`awk -F: '/freezer/ { print $3 }' /proc/$pid/cgroup` +notfound=0 +stat /proc/$pid/root/sys/fs/cgroup/freezer || notfound=1 +[ $notfound -ne 1 ] +notfound=0 +stat /proc/$pid/root/sys/fs/cgroup/freezer/cgroup.procs || notfound=1 +[ $notfound -ne 1 ] +ro=0 +mkdir /proc/$pid/root/sys/fs/cgroup/freezer/xx || ro=1 +[ $ro -ne 1 ] +rmdir /proc/$pid/root/sys/fs/cgroup/freezer/xx +ro=0 +mkdir /proc/$pid/root/sys/fs/cgroup/freezer/$cg/xx || ro=1 +[ $ro -ne 1 ] +notfound=0 +/proc/$pid/root/sys/fs/cgroup/freezer/$cg/cgroup.procs || notfound=1 +[ $notfound -eq 1 ] + +PHASE=done