mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-08-16 09:01:09 +00:00
added the unmount-namespace hook
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com> Acked-by: Serge E. Hallyn <serge.hallyn@ubuntu.com>
This commit is contained in:
parent
0a2b5ab1e7
commit
b73f115f41
@ -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)
|
||||
|
213
hooks/unmount-namespace.c
Normal file
213
hooks/unmount-namespace.c
Normal file
@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright © 2015 Wolfgang Bumiller <w.bumiller@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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user