[lxc-devel] [PATCH v3 7/7] added the unmount-namespace hook
Stéphane Graber
stgraber at ubuntu.com
Wed Oct 7 07:45:30 UTC 2015
On Tue, Oct 06, 2015 at 06:18:06PM +0000, Serge Hallyn wrote:
> Quoting Wolfgang Bumiller (w.bumiller at proxmox.com):
> > Signed-off-by: Wolfgang Bumiller <w.bumiller at proxmox.com>
>
> Hi Wolfgang,
>
> Thanks for resending.
>
> I'm a very forgetful person. If you could (in the future - don't resend
> this one) add a changelog showing what's changed, that would make my
> reviews a lot faster :)
>
> Looks good,
>
> Acked-by: Serge E. Hallyn <serge.hallyn at ubuntu.com>
>
> Stéphane, this is going to need packaging jujitsu for multiarch
> I assume, I'm not sure how we'll handle that.
So, as it turns out, hooks are currently in the main lxc package which
is arch-dependent, so while that's quite possibly the wrong place for
them to be, this change won't need any packaging change right now :)
>
> > ---
> > 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.bumiller at 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 at lists.linuxcontainers.org
> > http://lists.linuxcontainers.org/listinfo/lxc-devel
> _______________________________________________
> lxc-devel mailing list
> lxc-devel at lists.linuxcontainers.org
> http://lists.linuxcontainers.org/listinfo/lxc-devel
--
Stéphane Graber
Ubuntu developer
http://www.ubuntu.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20151007/a24213ae/attachment.sig>
More information about the lxc-devel
mailing list