]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
implement lxc.mount.auto = cgroup for cgfsng
authorSerge Hallyn <serge.hallyn@ubuntu.com>
Wed, 16 Mar 2016 06:01:42 +0000 (23:01 -0700)
committerSerge Hallyn <serge.hallyn@ubuntu.com>
Wed, 16 Mar 2016 22:55:19 +0000 (15:55 -0700)
Also add testcase for each of the cgroup{,-full}:{rw,ro,mixed} cases.

Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>
src/lxc/cgfsng.c
src/lxc/liblxc.so.1 [new symlink]
src/tests/Makefile.am
src/tests/lxc-test-automount [new file with mode: 0644]

index a6767c5201487a4a4759f6f8bb8cead49afe29b0..4a492269aac7afce5f1bf15323b396bf8f99bc97 100644 (file)
@@ -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 (symlink)
index 0000000..824cc6c
--- /dev/null
@@ -0,0 +1 @@
+liblxc.so
\ No newline at end of file
index 5151dca801e5f1753abed7d244d2eefad74c1eb0..68141f92b0576471d8d4ac624fa3e5d4abda9bcf 100644 (file)
@@ -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 (file)
index 0000000..6a06d7a
--- /dev/null
@@ -0,0 +1,217 @@
+#!/bin/bash
+
+# lxc: linux Container library
+
+# Authors:
+# Serge Hallyn <serge.hallyn@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.
+
+# 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