From 1279b6a5b134e17f44b2db2febf4b2fc02a0116c Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Thu, 30 Jan 2014 13:11:23 +0000 Subject: [PATCH] Add helper for running code in separate namespaces Implement virProcessRunInMountNamespace, which runs callback of type virProcessNamespaceCallback in a container namespace. This uses a child process to run the callback, since you can't change the mount namespace of a thread. This implies that callbacks have to be careful about what code they run due to async safety rules. Idea by Dan Berrange, based on an initial report by Reco at http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=732394 Signed-off-by: Daniel Berrange Signed-off-by: Eric Blake (cherry picked from commit 7c72ef6f555f1f9844d51be2f38f078bc908652c) --- src/libvirt_private.syms | 1 + src/util/virprocess.c | 106 +++++++++++++++++++++++++++++++++++++++ src/util/virprocess.h | 11 ++++ 3 files changed, 118 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 53e2804611..21effbe108 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1639,6 +1639,7 @@ virProcessGetNamespaces; virProcessGetStartTime; virProcessKill; virProcessKillPainfully; +virProcessRunInMountNamespace; virProcessSetAffinity; virProcessSetMaxFiles; virProcessSetMaxMemLock; diff --git a/src/util/virprocess.c b/src/util/virprocess.c index 9fc3207528..868dd045d1 100644 --- a/src/util/virprocess.c +++ b/src/util/virprocess.c @@ -46,6 +46,7 @@ #include "virlog.h" #include "virutil.h" #include "virstring.h" +#include "vircommand.h" #define VIR_FROM_THIS VIR_FROM_NONE @@ -847,3 +848,108 @@ int virProcessGetStartTime(pid_t pid, return 0; } #endif + + +#ifdef HAVE_SETNS +static int virProcessNamespaceHelper(int errfd, + pid_t pid, + virProcessNamespaceCallback cb, + void *opaque) +{ + char *path; + int fd = -1; + int ret = -1; + + if (virAsprintf(&path, "/proc/%llu/ns/mnt", (unsigned long long)pid) < 0) + goto cleanup; + + if ((fd = open(path, O_RDONLY)) < 0) { + virReportSystemError(errno, "%s", + _("Kernel does not provide mount namespace")); + goto cleanup; + } + + if (setns(fd, 0) < 0) { + virReportSystemError(errno, "%s", + _("Unable to enter mount namespace")); + goto cleanup; + } + + ret = cb(pid, opaque); + + cleanup: + if (ret < 0) { + virErrorPtr err = virGetLastError(); + if (err) { + size_t len = strlen(err->message) + 1; + ignore_value(safewrite(errfd, err->message, len)); + } + } + VIR_FREE(path); + VIR_FORCE_CLOSE(fd); + return ret; +} + +/* Run cb(opaque) in the mount namespace of pid. Return -1 with error + * message raised if we fail to run the child, if the child dies from + * a signal, or if the child has status 1; otherwise return the exit + * status of the child. The callback will be run in a child process + * so must be careful to only use async signal safe functions. + */ +int +virProcessRunInMountNamespace(pid_t pid, + virProcessNamespaceCallback cb, + void *opaque) +{ + int ret = -1; + pid_t child = -1; + int errfd[2] = { -1, -1 }; + + if (pipe(errfd) < 0) { + virReportSystemError(errno, "%s", + _("Cannot create pipe for child")); + return -1; + } + + ret = virFork(&child); + + if (ret < 0 || child < 0) { + if (child == 0) + _exit(1); + + /* parent */ + virProcessAbort(child); + goto cleanup; + } + + if (child == 0) { + VIR_FORCE_CLOSE(errfd[0]); + ret = virProcessNamespaceHelper(errfd[1], pid, + cb, opaque); + VIR_FORCE_CLOSE(errfd[1]); + _exit(ret < 0 ? 1 : 0); + } else { + char *buf = NULL; + VIR_FORCE_CLOSE(errfd[1]); + + ignore_value(virFileReadHeaderFD(errfd[0], 1024, &buf)); + ret = virProcessWait(child, NULL); + VIR_FREE(buf); + } + +cleanup: + VIR_FORCE_CLOSE(errfd[0]); + VIR_FORCE_CLOSE(errfd[1]); + return ret; +} +#else /* !HAVE_SETNS */ +int +virProcessRunInMountNamespace(pid_t pid ATTRIBUTE_UNUSED, + virProcessNamespaceCallback cb ATTRIBUTE_UNUSED, + void *opaque ATTRIBUTE_UNUSED) +{ + virReportSystemError(ENOSYS, "%s", + _("Mount namespaces are not available on this platform")); + return -1; +} +#endif diff --git a/src/util/virprocess.h b/src/util/virprocess.h index 9f77bc5491..5c173b0302 100644 --- a/src/util/virprocess.h +++ b/src/util/virprocess.h @@ -60,4 +60,15 @@ int virProcessSetNamespaces(size_t nfdlist, int virProcessSetMaxMemLock(pid_t pid, unsigned long long bytes); int virProcessSetMaxProcesses(pid_t pid, unsigned int procs); int virProcessSetMaxFiles(pid_t pid, unsigned int files); + +/* Callback to run code within the mount namespace tied to the given + * pid. This function must use only async-signal-safe functions, as + * it gets run after a fork of a multi-threaded process. The return + * value of this function is passed to _exit(), except that a + * negative value is treated as an error. */ +typedef int (*virProcessNamespaceCallback)(pid_t pid, void *opaque); + +int virProcessRunInMountNamespace(pid_t pid, + virProcessNamespaceCallback cb, + void *opaque); #endif /* __VIR_PROCESS_H__ */ -- GitLab