Signed-off-by: Wolfgang Bumiller <w.bumil...@proxmox.com>
---
 hooks/Makefile.am         |   6 ++
 hooks/unmount-namespace.c | 213 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 219 insertions(+)
 create mode 100644 hooks/unmount-namespace.c

diff --git a/hooks/Makefile.am b/hooks/Makefile.am
index be55601..ef82083 100644
--- a/hooks/Makefile.am
+++ b/hooks/Makefile.am
@@ -6,4 +6,10 @@ hooks_SCRIPTS = \
        ubuntu-cloud-prep \
        squid-deb-proxy-client
 
+hooks_PROGRAMS = \
+       unmount-namespace
+
+unmount_namespace_SOURCES = \
+       unmount-namespace.c
+
 EXTRA_DIST=$(hooks_SCRIPTS)
diff --git a/hooks/unmount-namespace.c b/hooks/unmount-namespace.c
new file mode 100644
index 0000000..488c9cc
--- /dev/null
+++ b/hooks/unmount-namespace.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright © 2015 Wolfgang Bumiller <w.bumil...@proxmox.com>.
+ * Copyright © 2015 Proxmox Server Solutions GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * --
+ *
+ * This stop-hook unmounts everything in the container's namespace, and thereby
+ * waits for all calls commands to finish. This is useful when one needs to be
+ * sure that network filesystems are finished unmounting in the namespace
+ * before continuing with other tasks. Without this hook the cleanup of mounts
+ * is done by the kernel in the background after all the references to the
+ * namespaces are gone.
+ */
+
+#define _GNU_SOURCE    /* setns */
+#include <stdio.h>     /* fdopen, getmntent, endmntent */
+#include <stdlib.h>    /* malloc, qsort */
+#include <unistd.h>    /* close */
+#include <string.h>    /* strcmp, strncmp, strdup, strerror */
+#include <sched.h>     /* setns */
+#include <sys/mount.h> /* umount2 */
+#include <sys/types.h> /* openat, open */
+#include <sys/stat.h>  /* openat, open */
+#include <fcntl.h>     /* openat, open */
+#include <mntent.h>    /* getmntent, endmntent */
+#include <errno.h>     /* errno */
+
+struct mount {
+       char *src; /* currently not used */
+       char *dst;
+       char *fs; /* currently not used */
+};
+
+static void mount_free(struct mount *mnt) {
+       free(mnt->src);
+       free(mnt->dst);
+       free(mnt->fs);
+}
+
+static int mount_cmp_dst(const void *a_, const void *b_) {
+       struct mount *a = (struct mount*)a_;
+       struct mount *b = (struct mount*)b_;
+       return strcmp(b->dst, a->dst); /* swapped order */
+}
+
+/* Unmounting /dev/pts fails, and  so /dev also fails, but /dev is not what
+ * we're interested in. (There might also still be /dev/cgroup mounts).
+ */
+static int mount_should_error(const struct mount *mnt) {
+       const char *dst = mnt->dst;
+       return !(strncmp(dst, "/dev", 4) == 0 && (dst[4] == 0 || dst[4] == 
'/'));
+}
+
+/* Read mounts from 'self/mounts' relative to a directory filedescriptor.
+ * Before entering the container we open a handle to /proc on the host as we
+ * need to access /proc/self/mounts and the container's /proc doesn't contain
+ * our /self. We then use openat(2) to avoid having to mount a temporary /proc.
+ */
+static int read_mounts(int procfd, struct mount **mp, size_t *countp) {
+       int fd;
+       struct mntent *ent;
+       FILE *mf;
+       size_t capacity = 32;
+       size_t count = 0;
+       struct mount *mounts = (struct mount*)malloc(capacity * 
sizeof(*mounts));
+
+       if (!mounts) {
+               errno = ENOMEM;
+               return 0;
+       }
+
+       *mp = NULL;
+       *countp = 0;
+
+       fd = openat(procfd, "self/mounts", O_RDONLY);
+       if (fd < 0)
+               return 0;
+
+       mf = fdopen(fd, "r");
+       if (!mf) {
+               int error = errno;
+               close(fd);
+               errno = error;
+               return 0;
+       }
+       while ((ent = getmntent(mf))) {
+               struct mount *new;
+               if (count == capacity) {
+                       capacity *= 2;
+                       new = (struct mount*)realloc(mounts, capacity * 
sizeof(*mounts));
+                       if (!new)
+                               goto out_alloc_entry;
+                       mounts = new;
+               }
+               new = &mounts[count++];
+               new->src = strdup(ent->mnt_fsname);
+               new->dst = strdup(ent->mnt_dir);
+               new->fs  = strdup(ent->mnt_type);
+               if (!new->src || !new->dst || !new->fs)
+                       goto out_alloc_entry;
+       }
+       endmntent(mf);
+
+       *mp = mounts;
+       *countp = count;
+
+       return 1;
+
+out_alloc_entry:
+       endmntent(mf);
+       while (count--) {
+               free(mounts[count].src);
+               free(mounts[count].dst);
+               free(mounts[count].fs);
+       }
+       free(mounts);
+       errno = ENOMEM;
+       return 0;
+}
+
+int main(int argc, char **argv) {
+       int i, procfd, ctmntfd;
+       struct mount *mounts;
+       size_t zi, count = 0;
+       const char *mntns = NULL;
+
+       if (argc < 4 || strcmp(argv[2], "lxc") != 0) {
+               fprintf(stderr, "%s: usage error, expected LXC hook 
arguments\n", argv[0]);
+               return 2;
+       }
+
+       if (strcmp(argv[3], "stop") != 0)
+               return 0;
+
+       for (i = 4; i != argc; ++i) {
+               if (!strncmp(argv[i], "mnt:", 4)) {
+                       mntns = argv[i] + 4;
+                       break;
+               }
+       }
+
+       if (!mntns) {
+               fprintf(stderr, "%s: no mount namespace provided\n", argv[0]);
+               return 3;
+       }
+
+       /* Open a handle to /proc on the host as we need to access 
/proc/self/mounts
+        * and the container's /proc doesn't contain our /self. See 
read_mounts().
+        */
+       procfd = open("/proc", O_RDONLY | O_DIRECTORY | O_PATH);
+       if (procfd < 0) {
+               fprintf(stderr, "%s: failed to open /proc: %s\n", argv[0], 
strerror(errno));
+               return 4;
+       }
+
+       /* Open the mount namespace and enter it. */
+       ctmntfd = open(mntns, O_RDONLY);
+       if (ctmntfd < 0) {
+               fprintf(stderr, "%s: failed to open mount namespace: %s\n",
+                       argv[0], strerror(errno));
+               close(procfd);
+               return 5;
+       }
+
+       if (setns(ctmntfd, CLONE_NEWNS) != 0) {
+               fprintf(stderr, "%s: failed to attach to namespace: %s\n",
+                       argv[0], strerror(errno));
+               close(ctmntfd);
+               close(procfd);
+               return 6;
+       }
+       close(ctmntfd);
+
+       /* Now read [[procfd]]/self/mounts */
+       if (!read_mounts(procfd, &mounts, &count)) {
+               fprintf(stderr, "%s: failed to read mountpoints: %s\n",
+                       argv[0], strerror(errno));
+               close(procfd);
+               return 7;
+       }
+       close(procfd);
+
+       /* Just sort to get a sane unmount-order... */
+       qsort(mounts, count, sizeof(*mounts), &mount_cmp_dst);
+
+       for (zi = 0; zi != count; ++zi) {
+               /* fprintf(stderr, "Unmount: %s\n", mounts[zi].dst); */
+               if (umount2(mounts[zi].dst, 0) != 0) {
+                       int error = errno;
+                       if (mount_should_error(&mounts[zi])) {
+                               fprintf(stderr, "%s: failed to unmount %s: 
%s\n",
+                                       argv[0], mounts[zi].dst, 
strerror(error));
+                       }
+               }
+               mount_free(&mounts[zi]);
+       }
+       free(mounts);
+
+       return 0;
+}
-- 
2.1.4


_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to