[libvirt] [PATCH v4] lxc: Inherit namespace feature

ik.nitk ik.nitk at gmail.com
Thu Aug 20 13:46:17 UTC 2015


This patch adds feature for lxc containers to inherit namespaces.
This is very similar to what lxc-tools or docker provides.  Look
for "man lxc-start" and you will find that you can pass command
args as [ --share-[net|ipc|uts] name|pid ]. Or check out docker
networking option in which you can give --net=container:NAME_or_ID
as an option for sharing +namespace.

>From this patch you can add extra libvirt option to share
namespace in following way.

 <lxc:namespace>
   <lxc:sharenet type='netns' value='red'/>
   <lxc:shareipc type='pid' value='12345'/>
   <lxc:shareuts type='name' value='container1'/>
 </lxc:namespace>

The netns option is specific to sharenet. It can be used to
inherit from existing network namespace.

---
 docs/drvlxc.html.in                   |  21 +++++
 docs/schemas/domaincommon.rng         |  42 +++++++++
 po/POTFILES.in                        |   1 +
 src/Makefile.am                       |   7 +-
 src/lxc/lxc_conf.c                    |   2 +-
 src/lxc/lxc_container.c               |  71 +++++++++++++--
 src/lxc/lxc_container.h               |   2 +
 src/lxc/lxc_controller.c              |  57 +++++++++++-
 src/lxc/lxc_domain.c                  | 149 ++++++++++++++++++++++++++++++++
 src/lxc/lxc_domain.h                  |  26 ++++++
 src/lxc/lxc_process.c                 | 157 ++++++++++++++++++++++++++++++++++
 tests/lxcxml2xmldata/lxc-sharenet.xml |  33 +++++++
 tests/lxcxml2xmltest.c                |   1 +
 13 files changed, 560 insertions(+), 9 deletions(-)
 create mode 100644 tests/lxcxml2xmldata/lxc-sharenet.xml

diff --git a/docs/drvlxc.html.in b/docs/drvlxc.html.in
index a094bd9..d6c57c4 100644
--- a/docs/drvlxc.html.in
+++ b/docs/drvlxc.html.in
@@ -590,6 +590,27 @@ Note that allowing capabilities that are normally dropped by default can serious
 affect the security of the container and the host.
 </p>
 
+<h2><a name="share">Inherit namespaces</a></h2>
+
+<p>
+Libvirt allows you to inherit the namespace from container/process just like lxc tools
+or docker provides to share the network namespace. The following can be used to share
+required namespaces. If we want to share only one then the other namespaces can be ignored.
+The netns option is specific to sharenet. It can be used in cases we want to use existing network namespace
+rather than creating new network namespace for the container. In this case privnet option will be
+ignored.
+</p>
+<pre>
+<domain type='lxc' xmlns:lxc='http://libvirt.org/schemas/domain/lxc/1.0'>
+...
+<lxc:namespace>
+  <lxc:sharenet type='netns' value='red'/>
+  <lxc:shareuts type='name' value='container1'/>
+  <lxc:shareipc type='pid' value='12345'/>
+</lxc:namespace>
+</domain>
+</pre>
+
 <h2><a name="usage">Container usage / management</a></h2>
 
 <p>
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 043c975..fa026cd 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -68,6 +68,9 @@
           <ref name='qemucmdline'/>
         </optional>
         <optional>
+          <ref name='lxcsharens'/>
+        </optional>
+        <optional>
           <ref name='keywrap'/>
         </optional>
       </interleave>
@@ -5057,6 +5060,45 @@
     </element>
   </define>
 
+  <!--
+       Optional hypervisor extensions in their own namespace:
+       LXC
+    -->
+  <define name="lxcsharens">
+    <element name="namespace" ns="http://libvirt.org/schemas/domain/lxc/1.0">
+      <zeroOrMore>
+        <element name="sharenet">
+          <attribute name="type">
+            <choice>
+              <value>netns</value>
+              <value>name</value>
+              <value>pid</value>
+            </choice>
+          </attribute>
+          <attribute name='value'/>
+        </element>
+        <element name="shareipc">
+          <attribute name="type">
+            <choice>
+              <value>name</value>
+              <value>pid</value>
+            </choice>
+          </attribute>
+          <attribute name='value'/>
+        </element>
+        <element name="shareuts">
+          <attribute name="type">
+            <choice>
+              <value>name</value>
+              <value>pid</value>
+            </choice>
+          </attribute>
+          <attribute name='value'/>
+        </element>
+      </zeroOrMore>
+    </element>
+  </define>
+
   <define name="metadata">
     <element name="metadata">
       <zeroOrMore>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 1e52e6a..46220f7 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -85,6 +85,7 @@ src/lxc/lxc_native.c
 src/lxc/lxc_container.c
 src/lxc/lxc_conf.c
 src/lxc/lxc_controller.c
+src/lxc/lxc_domain.c
 src/lxc/lxc_driver.c
 src/lxc/lxc_process.c
 src/libxl/libxl_domain.c
diff --git a/src/Makefile.am b/src/Makefile.am
index c4d49a5..24d31e1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1320,7 +1320,12 @@ libvirt_driver_lxc_impl_la_CFLAGS = \
 		-I$(srcdir)/access \
 		-I$(srcdir)/conf \
 		$(AM_CFLAGS)
-libvirt_driver_lxc_impl_la_LIBADD = $(CAPNG_LIBS) $(LIBNL_LIBS) $(FUSE_LIBS)
+libvirt_driver_lxc_impl_la_LIBADD = \
+               $(CAPNG_LIBS) \
+               $(LIBNL_LIBS) \
+               $(LIBXML_LIBS) \
+               $(FUSE_LIBS)
+
 if WITH_BLKID
 libvirt_driver_lxc_impl_la_CFLAGS += $(BLKID_CFLAGS)
 libvirt_driver_lxc_impl_la_LIBADD += $(BLKID_LIBS)
diff --git a/src/lxc/lxc_conf.c b/src/lxc/lxc_conf.c
index b689b92..8ada531 100644
--- a/src/lxc/lxc_conf.c
+++ b/src/lxc/lxc_conf.c
@@ -213,7 +213,7 @@ lxcDomainXMLConfInit(void)
 {
     return virDomainXMLOptionNew(&virLXCDriverDomainDefParserConfig,
                                  &virLXCDriverPrivateDataCallbacks,
-                                 NULL);
+                                 &virLXCDriverDomainXMLNamespace);
 }
 
 
diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c
index 11e9514..8011ed0 100644
--- a/src/lxc/lxc_container.c
+++ b/src/lxc/lxc_container.c
@@ -27,6 +27,7 @@
 #include <config.h>
 
 #include <fcntl.h>
+#include <sched.h>
 #include <limits.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -38,7 +39,6 @@
 #include <mntent.h>
 #include <sys/reboot.h>
 #include <linux/reboot.h>
-
 /* Yes, we want linux private one, for _syscall2() macro */
 #include <linux/unistd.h>
 
@@ -111,6 +111,7 @@ struct __lxc_child_argv {
     size_t nttyPaths;
     char **ttyPaths;
     int handshakefd;
+    int *nsInheritFDs;
 };
 
 static int lxcContainerMountFSBlock(virDomainFSDefPtr fs,
@@ -2144,6 +2145,35 @@ static int lxcContainerDropCapabilities(virDomainDefPtr def ATTRIBUTE_UNUSED,
 
 
 /**
+ * lxcAttach_ns:
+ * @ns_fd: array of namespaces to attach
+ */
+static int lxcAttachNS(int *ns_fd)
+{
+    size_t i;
+    if (ns_fd)
+        for (i = 0; i < VIR_LXC_DOMAIN_NAMESPACE_LAST; i++) {
+            if (ns_fd[i] < 0)
+                continue;
+            VIR_DEBUG("Setting into namespace\n");
+            /* We get EINVAL if new NS is same as the current
+             * NS, or if the fd namespace doesn't match the
+             * type passed to setns()'s second param. Since we
+             * pass 0, we know the EINVAL is harmless
+             */
+            if (setns(ns_fd[i], 0) < 0 &&
+                errno != EINVAL) {
+                virReportSystemError(errno, _("failed to set namespace '%s'"),
+                                     virLXCDomainNamespaceTypeToString(i));
+                return -1;
+            }
+            VIR_FORCE_CLOSE(ns_fd[i]);
+        }
+    return 0;
+}
+
+
+/**
  * lxcContainerChild:
  * @data: pointer to container arguments
  *
@@ -2172,6 +2202,12 @@ static int lxcContainerChild(void *data)
         goto cleanup;
     }
 
+    if (lxcAttachNS(argv->nsInheritFDs) < 0) {
+        virReportError(VIR_ERR_SYSTEM_ERROR, "%s",
+                       _("failed to attach the namespace"));
+        return -1;
+    }
+
     /* Wait for controller to finish setup tasks, including
      * things like move of network interfaces, uid/gid mapping
      */
@@ -2342,6 +2378,7 @@ int lxcContainerStart(virDomainDefPtr def,
                       int *passFDs,
                       int control,
                       int handshakefd,
+                      int *nsInheritFDs,
                       size_t nttyPaths,
                       char **ttyPaths)
 {
@@ -2359,7 +2396,8 @@ int lxcContainerStart(virDomainDefPtr def,
         .monitor = control,
         .nttyPaths = nttyPaths,
         .ttyPaths = ttyPaths,
-        .handshakefd = handshakefd
+        .handshakefd = handshakefd,
+        .nsInheritFDs = nsInheritFDs,
     };
 
     /* allocate a stack for the container */
@@ -2368,7 +2406,7 @@ int lxcContainerStart(virDomainDefPtr def,
 
     stacktop = stack + stacksize;
 
-    cflags = CLONE_NEWPID|CLONE_NEWNS|CLONE_NEWUTS|CLONE_NEWIPC|SIGCHLD;
+    cflags = CLONE_NEWPID|CLONE_NEWNS|SIGCHLD;
 
     if (userns_required(def)) {
         if (userns_supported()) {
@@ -2381,10 +2419,31 @@ int lxcContainerStart(virDomainDefPtr def,
             return -1;
         }
     }
+    if (!nsInheritFDs || nsInheritFDs[VIR_LXC_DOMAIN_NAMESPACE_SHARENET] == -1) {
+        if (lxcNeedNetworkNamespace(def)) {
+            VIR_DEBUG("Enable network namespaces");
+            cflags |= CLONE_NEWNET;
+        }
+    } else {
+        if (lxcNeedNetworkNamespace(def)) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                           _("Config askes for inherit net namespace "
+                             "as well as private network interfaces"));
+            return -1;
+        }
+        VIR_DEBUG("Inheriting a net namespace");
+    }
+
+    if (!nsInheritFDs || nsInheritFDs[VIR_LXC_DOMAIN_NAMESPACE_SHAREIPC] == -1) {
+        cflags |= CLONE_NEWIPC;
+    } else {
+        VIR_DEBUG("Inheriting an IPC namespace");
+    }
 
-    if (lxcNeedNetworkNamespace(def)) {
-        VIR_DEBUG("Enable network namespaces");
-        cflags |= CLONE_NEWNET;
+    if (!nsInheritFDs || nsInheritFDs[VIR_LXC_DOMAIN_NAMESPACE_SHAREUTS] == -1) {
+        cflags |= CLONE_NEWUTS;
+    } else {
+        VIR_DEBUG("Inheriting a UTS namespace");
     }
 
     VIR_DEBUG("Cloning container init process");
diff --git a/src/lxc/lxc_container.h b/src/lxc/lxc_container.h
index 67292ab..33eaab4 100644
--- a/src/lxc/lxc_container.h
+++ b/src/lxc/lxc_container.h
@@ -25,6 +25,7 @@
 # define LXC_CONTAINER_H
 
 # include "lxc_conf.h"
+# include "lxc_domain.h"
 # include "security/security_manager.h"
 
 enum {
@@ -60,6 +61,7 @@ int lxcContainerStart(virDomainDefPtr def,
                       int *passFDs,
                       int control,
                       int handshakefd,
+                      int *nsInheritFDs,
                       size_t nttyPaths,
                       char **ttyPaths);
 
diff --git a/src/lxc/lxc_controller.c b/src/lxc/lxc_controller.c
index 48a3597..a94e819 100644
--- a/src/lxc/lxc_controller.c
+++ b/src/lxc/lxc_controller.c
@@ -119,6 +119,8 @@ struct _virLXCController {
     size_t npassFDs;
     int *passFDs;
 
+    int *nsFDs;
+
     size_t nconsoles;
     virLXCControllerConsolePtr consoles;
     char *devptmx;
@@ -287,6 +289,7 @@ static void virLXCControllerFree(virLXCControllerPtr ctrl)
 
     VIR_FREE(ctrl->nbdpids);
 
+    VIR_FREE(ctrl->nsFDs);
     virCgroupFree(&ctrl->cgroup);
 
     /* This must always be the last thing to be closed */
@@ -2391,6 +2394,7 @@ virLXCControllerRun(virLXCControllerPtr ctrl)
                                            ctrl->passFDs,
                                            control[1],
                                            containerhandshake[1],
+                                           ctrl->nsFDs,
                                            ctrl->nconsoles,
                                            containerTTYPaths)) < 0)
         goto cleanup;
@@ -2400,6 +2404,10 @@ virLXCControllerRun(virLXCControllerPtr ctrl)
     for (i = 0; i < ctrl->npassFDs; i++)
         VIR_FORCE_CLOSE(ctrl->passFDs[i]);
 
+    if (ctrl->nsFDs)
+        for (i = 0; i < VIR_LXC_DOMAIN_NAMESPACE_LAST; i++)
+            VIR_FORCE_CLOSE(ctrl->nsFDs[i]);
+
     if (virLXCControllerSetupCgroupLimits(ctrl) < 0)
         goto cleanup;
 
@@ -2468,6 +2476,7 @@ int main(int argc, char *argv[])
     const char *name = NULL;
     size_t nveths = 0;
     char **veths = NULL;
+    int ns_fd[VIR_LXC_DOMAIN_NAMESPACE_LAST];
     int handshakeFd = -1;
     bool bg = false;
     const struct option options[] = {
@@ -2478,6 +2487,9 @@ int main(int argc, char *argv[])
         { "passfd", 1, NULL, 'p' },
         { "handshakefd", 1, NULL, 's' },
         { "security", 1, NULL, 'S' },
+        { "share-net", 1, NULL, 'N' },
+        { "share-ipc", 1, NULL, 'I' },
+        { "share-uts", 1, NULL, 'U' },
         { "help", 0, NULL, 'h' },
         { 0, 0, 0, 0 },
     };
@@ -2489,6 +2501,9 @@ int main(int argc, char *argv[])
     size_t i;
     const char *securityDriver = "none";
 
+    for (i = 0; i < VIR_LXC_DOMAIN_NAMESPACE_LAST; i++)
+        ns_fd[i] = -1;
+
     if (setlocale(LC_ALL, "") == NULL ||
         bindtextdomain(PACKAGE, LOCALEDIR) == NULL ||
         textdomain(PACKAGE) == NULL ||
@@ -2504,7 +2519,7 @@ int main(int argc, char *argv[])
     while (1) {
         int c;
 
-        c = getopt_long(argc, argv, "dn:v:p:m:c:s:h:S:",
+        c = getopt_long(argc, argv, "dn:v:p:m:c:s:h:S:N:I:U:",
                         options, NULL);
 
         if (c == -1)
@@ -2552,6 +2567,30 @@ int main(int argc, char *argv[])
             }
             break;
 
+        case 'N':
+            if (virStrToLong_i(optarg, NULL, 10, &ns_fd[VIR_LXC_DOMAIN_NAMESPACE_SHARENET]) < 0) {
+                fprintf(stderr, "malformed --share-net argument '%s'",
+                        optarg);
+                goto cleanup;
+            }
+            break;
+
+        case 'I':
+            if (virStrToLong_i(optarg, NULL, 10, &ns_fd[VIR_LXC_DOMAIN_NAMESPACE_SHAREIPC]) < 0) {
+                fprintf(stderr, "malformed --share-ipc argument '%s'",
+                        optarg);
+                goto cleanup;
+            }
+            break;
+
+        case 'U':
+            if (virStrToLong_i(optarg, NULL, 10, &ns_fd[VIR_LXC_DOMAIN_NAMESPACE_SHAREUTS]) < 0) {
+                fprintf(stderr, "malformed --share-uts argument '%s'",
+                        optarg);
+                goto cleanup;
+            }
+            break;
+
         case 'S':
             securityDriver = optarg;
             break;
@@ -2569,6 +2608,9 @@ int main(int argc, char *argv[])
             fprintf(stderr, "  -v VETH, --veth VETH\n");
             fprintf(stderr, "  -s FD, --handshakefd FD\n");
             fprintf(stderr, "  -S NAME, --security NAME\n");
+            fprintf(stderr, "  -N FD, --share-net FD\n");
+            fprintf(stderr, "  -I FD, --share-ipc FD\n");
+            fprintf(stderr, "  -U FD, --share-uts FD\n");
             fprintf(stderr, "  -h, --help\n");
             fprintf(stderr, "\n");
             goto cleanup;
@@ -2621,6 +2663,19 @@ int main(int argc, char *argv[])
     ctrl->passFDs = passFDs;
     ctrl->npassFDs = npassFDs;
 
+    for (i = 0; i < VIR_LXC_DOMAIN_NAMESPACE_LAST; i++) {
+        if (ns_fd[i] != -1) {
+            if (!ctrl->nsFDs) {/*allocate only once */
+                size_t j = 0;
+                if (VIR_ALLOC_N(ctrl->nsFDs, VIR_LXC_DOMAIN_NAMESPACE_LAST) < 0)
+                    goto cleanup;
+                for (j = 0; j < VIR_LXC_DOMAIN_NAMESPACE_LAST; j++)
+                    ctrl->nsFDs[j] = -1;
+            }
+            ctrl->nsFDs[i] = ns_fd[i];
+        }
+    }
+
     for (i = 0; i < nttyFDs; i++) {
         if (virLXCControllerAddConsole(ctrl, ttyFDs[i]) < 0)
             goto cleanup;
diff --git a/src/lxc/lxc_domain.c b/src/lxc/lxc_domain.c
index 2f377d8..e3da9f0 100644
--- a/src/lxc/lxc_domain.c
+++ b/src/lxc/lxc_domain.c
@@ -26,8 +26,13 @@
 #include "viralloc.h"
 #include "virlog.h"
 #include "virerror.h"
+#include <libxml/xpathInternals.h>
+#include "virstring.h"
+#include "virutil.h"
+#include "virfile.h"
 
 #define VIR_FROM_THIS VIR_FROM_LXC
+#define LXC_NAMESPACE_HREF "http://libvirt.org/schemas/domain/lxc/1.0"
 
 VIR_LOG_INIT("lxc.lxc_domain");
 
@@ -41,6 +46,150 @@ static void *virLXCDomainObjPrivateAlloc(void)
     return priv;
 }
 
+VIR_ENUM_IMPL(virLXCDomainNamespace,
+              VIR_LXC_DOMAIN_NAMESPACE_LAST,
+              "sharenet",
+              "shareipc",
+              "shareuts")
+
+VIR_ENUM_IMPL(virLXCDomainNamespaceSource,
+              VIR_LXC_DOMAIN_NAMESPACE_SOURCE_LAST,
+              "none",
+              "name",
+              "pid",
+              "netns")
+
+static void
+lxcDomainDefNamespaceFree(void *nsdata)
+{
+    size_t i;
+    lxcDomainDefPtr lxcDef = nsdata;
+    for (i = 0; i < VIR_LXC_DOMAIN_NAMESPACE_LAST; i++)
+        VIR_FREE(lxcDef->ns_val[i]);
+    VIR_FREE(nsdata);
+}
+
+static int
+lxcDomainDefNamespaceParse(xmlDocPtr xml ATTRIBUTE_UNUSED,
+                           xmlNodePtr root ATTRIBUTE_UNUSED,
+                           xmlXPathContextPtr ctxt,
+                           void **data)
+{
+    lxcDomainDefPtr lxcDef = NULL;
+    xmlNodePtr *nodes = NULL;
+    bool uses_lxc_ns = false;
+    xmlNodePtr node;
+    int feature;
+    int n;
+    char *tmp = NULL;
+    size_t i;
+
+    if (xmlXPathRegisterNs(ctxt, BAD_CAST "lxc", BAD_CAST LXC_NAMESPACE_HREF) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Failed to register xml namespace '%s'"),
+                       LXC_NAMESPACE_HREF);
+        return -1;
+    }
+
+    if (VIR_ALLOC(lxcDef) < 0)
+        return -1;
+
+    node = ctxt->node;
+    if ((n = virXPathNodeSet("./lxc:namespace/*", ctxt, &nodes)) < 0)
+        goto error;
+    uses_lxc_ns |= n > 0;
+
+    for (i = 0; i < n; i++) {
+        if ((feature = virLXCDomainNamespaceTypeFromString(
+                 (const char *) nodes[i]->name)) < 0) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                            _("unsupported Namespace feature: %s"),
+                            nodes[i]->name);
+            goto error;
+        }
+
+        ctxt->node = nodes[i];
+
+        if (!(tmp = virXMLPropString(nodes[i], "type"))) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           "%s", _("No lxc environment type specified"));
+            goto error;
+        }
+        if ((lxcDef->ns_source[feature] =
+             virLXCDomainNamespaceSourceTypeFromString(tmp)) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Unknown LXC namespace source '%s'"),
+                           tmp);
+            VIR_FREE(tmp);
+            goto error;
+        }
+        VIR_FREE(tmp);
+
+        if (!(lxcDef->ns_val[feature] =
+              virXMLPropString(nodes[i], "value"))) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           "%s", _("No lxc environment type specified"));
+            goto error;
+        }
+    }
+    VIR_FREE(nodes);
+    ctxt->node = node;
+    if (uses_lxc_ns)
+        *data = lxcDef;
+    else
+        VIR_FREE(lxcDef);
+    return 0;
+ error:
+    VIR_FREE(nodes);
+    lxcDomainDefNamespaceFree(lxcDef);
+    return -1;
+}
+
+
+static int
+lxcDomainDefNamespaceFormatXML(virBufferPtr buf,
+                               void *nsdata)
+{
+    lxcDomainDefPtr lxcDef = nsdata;
+    size_t i;
+
+    if (!lxcDef)
+       return 0;
+
+    virBufferAddLit(buf, "<lxc:namespace>\n");
+    virBufferAdjustIndent(buf, 2);
+
+    for (i = 0; i < VIR_LXC_DOMAIN_NAMESPACE_LAST; i++) {
+        if (lxcDef->ns_source[i] == VIR_LXC_DOMAIN_NAMESPACE_SOURCE_NONE)
+            continue;
+
+        virBufferAsprintf(buf, "<lxc:%s type='%s' value='%s'/>\n",
+                          virLXCDomainNamespaceTypeToString(i),
+                          virLXCDomainNamespaceSourceTypeToString(
+                              lxcDef->ns_source[i]),
+                          lxcDef->ns_val[i]);
+    }
+
+    virBufferAdjustIndent(buf, -2);
+    virBufferAddLit(buf, "</lxc:namespace>\n");
+    return 0;
+}
+
+static const char *
+lxcDomainDefNamespaceHref(void)
+{
+    return "xmlns:lxc='" LXC_NAMESPACE_HREF "'";
+}
+
+
+virDomainXMLNamespace virLXCDriverDomainXMLNamespace = {
+    .parse = lxcDomainDefNamespaceParse,
+    .free = lxcDomainDefNamespaceFree,
+    .format = lxcDomainDefNamespaceFormatXML,
+    .href = lxcDomainDefNamespaceHref,
+};
+
+
 static void virLXCDomainObjPrivateFree(void *data)
 {
     virLXCDomainObjPrivatePtr priv = data;
diff --git a/src/lxc/lxc_domain.h b/src/lxc/lxc_domain.h
index 751aece..2119c78 100644
--- a/src/lxc/lxc_domain.h
+++ b/src/lxc/lxc_domain.h
@@ -27,6 +27,31 @@
 # include "lxc_conf.h"
 # include "lxc_monitor.h"
 
+typedef enum {
+    VIR_LXC_DOMAIN_NAMESPACE_SHARENET = 0,
+    VIR_LXC_DOMAIN_NAMESPACE_SHAREIPC,
+    VIR_LXC_DOMAIN_NAMESPACE_SHAREUTS,
+    VIR_LXC_DOMAIN_NAMESPACE_LAST,
+} virLXCDomainNamespace;
+
+typedef enum {
+    VIR_LXC_DOMAIN_NAMESPACE_SOURCE_NONE,
+    VIR_LXC_DOMAIN_NAMESPACE_SOURCE_NAME,
+    VIR_LXC_DOMAIN_NAMESPACE_SOURCE_PID,
+    VIR_LXC_DOMAIN_NAMESPACE_SOURCE_NETNS,
+
+    VIR_LXC_DOMAIN_NAMESPACE_SOURCE_LAST,
+} virLXCDomainNamespaceSource;
+
+VIR_ENUM_DECL(virLXCDomainNamespace)
+VIR_ENUM_DECL(virLXCDomainNamespaceSource)
+
+typedef struct _lxcDomainDef lxcDomainDef;
+typedef lxcDomainDef *lxcDomainDefPtr;
+struct _lxcDomainDef {
+    int ns_source[VIR_LXC_DOMAIN_NAMESPACE_LAST]; /* virLXCDomainNamespaceSource */
+    char *ns_val[VIR_LXC_DOMAIN_NAMESPACE_LAST];
+};
 
 typedef struct _virLXCDomainObjPrivate virLXCDomainObjPrivate;
 typedef virLXCDomainObjPrivate *virLXCDomainObjPrivatePtr;
@@ -41,6 +66,7 @@ struct _virLXCDomainObjPrivate {
     virCgroupPtr cgroup;
 };
 
+extern virDomainXMLNamespace virLXCDriverDomainXMLNamespace;
 extern virDomainXMLPrivateDataCallbacks virLXCDriverPrivateDataCallbacks;
 extern virDomainDefParserConfig virLXCDriverDomainDefParserConfig;
 
diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c
index e99b039..ade0ed7 100644
--- a/src/lxc/lxc_process.c
+++ b/src/lxc/lxc_process.c
@@ -359,6 +359,143 @@ char *virLXCProcessSetupInterfaceDirect(virConnectPtr conn,
     return ret;
 }
 
+static const char *nsInfoLocal[VIR_LXC_DOMAIN_NAMESPACE_LAST] = {
+    [VIR_LXC_DOMAIN_NAMESPACE_SHARENET] = "net",
+    [VIR_LXC_DOMAIN_NAMESPACE_SHAREIPC] = "ipc",
+    [VIR_LXC_DOMAIN_NAMESPACE_SHAREUTS] = "uts",
+};
+
+static int virLXCProcessSetupNamespaceName(virConnectPtr conn, int ns_type, const char *name)
+{
+    virLXCDriverPtr driver = conn->privateData;
+    int fd = -1;
+    virDomainObjPtr vm;
+    virLXCDomainObjPrivatePtr priv;
+    char *path;
+
+    vm = virDomainObjListFindByName(driver->domains, name);
+    if (!vm) {
+        virReportError(VIR_ERR_NO_DOMAIN,
+                       _("No domain with matching name '%s'"), name);
+        return -1;
+    }
+
+    priv = vm->privateData;
+    if (!priv->initpid) {
+        virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+                       _("Init pid is not yet available"));
+        goto cleanup;
+    }
+
+    if (virAsprintf(&path, "/proc/%lld/ns/%s",
+                    (long long int)priv->initpid,
+                    nsInfoLocal[ns_type]) < 0)
+        goto cleanup;
+
+    if ((fd = open(path, O_RDONLY)) < 0) {
+        virReportSystemError(errno,
+                             _("failed to open ns %s"),
+                             virLXCDomainNamespaceTypeToString(ns_type));
+        goto cleanup;
+    }
+
+ cleanup:
+    VIR_FREE(path);
+    virObjectUnlock(vm);
+    virObjectUnref(vm);
+    return fd;
+}
+
+
+static int virLXCProcessSetupNamespacePID(int ns_type, const char *name)
+{
+    int fd;
+    char *path;
+
+    if (virAsprintf(&path, "/proc/%s/ns/%s",
+                    name,
+                    nsInfoLocal[ns_type]) < 0)
+        return -1;
+    fd = open(path, O_RDONLY);
+    VIR_FREE(path);
+    if (fd < 0) {
+        virReportSystemError(errno,
+                             _("failed to open ns %s"),
+                             virLXCDomainNamespaceTypeToString(ns_type));
+        return -1;
+    }
+    return fd;
+}
+
+
+static int virLXCProcessSetupNamespaceNet(int ns_type, const char *name)
+{
+    char *path;
+    int fd;
+    if (ns_type != VIR_LXC_DOMAIN_NAMESPACE_SHARENET) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                       _("'netns' namespace source can only be "
+                         "used with sharenet"));
+        return -1;
+    }
+
+    if (virAsprintf(&path, "/var/run/netns/%s", name) < 0)
+        return  -1;
+    fd = open(path, O_RDONLY);
+    VIR_FREE(path);
+    if (fd < 0) {
+        virReportSystemError(errno,
+                             _("failed to open netns %s"), name);
+        return -1;
+    }
+    return fd;
+}
+
+
+/**
+ * virLXCProcessSetupNamespaces:
+ * @conn: pointer to connection
+ * @def: pointer to virtual machines namespaceData
+ * @nsFDs: out parameter to store the namespace FD
+ *
+ * Opens the specified namespace that needs to be shared and
+ * will moved into the container namespace later after clone has been called.
+ *
+ * Returns 0 on success or -1 in case of error
+ */
+static int virLXCProcessSetupNamespaces(virConnectPtr conn,
+                                        lxcDomainDefPtr lxcDef,
+                                        int *nsFDs)
+{
+    size_t i;
+
+    for (i = 0; i < VIR_LXC_DOMAIN_NAMESPACE_LAST; i++)
+        nsFDs[i] = -1;
+    /*If there are no namespace to be opened just return success*/
+    if (lxcDef == NULL)
+        return 0;
+
+    for (i = 0; i < VIR_LXC_DOMAIN_NAMESPACE_LAST; i++) {
+        switch (lxcDef->ns_source[i]) {
+        case VIR_LXC_DOMAIN_NAMESPACE_SOURCE_NONE:
+            continue;
+        case VIR_LXC_DOMAIN_NAMESPACE_SOURCE_NAME:
+            if ((nsFDs[i] = virLXCProcessSetupNamespaceName(conn, i, lxcDef->ns_val[i])) < 0)
+                return -1;
+            break;
+        case VIR_LXC_DOMAIN_NAMESPACE_SOURCE_PID:
+            if ((nsFDs[i] = virLXCProcessSetupNamespacePID(i, lxcDef->ns_val[i])) < 0)
+                return -1;
+            break;
+        case VIR_LXC_DOMAIN_NAMESPACE_SOURCE_NETNS:
+            if ((nsFDs[i] = virLXCProcessSetupNamespaceNet(i, lxcDef->ns_val[i])) < 0)
+                return -1;
+            break;
+        }
+    }
+
+    return 0;
+}
 
 /**
  * virLXCProcessSetupInterfaces:
@@ -764,6 +901,7 @@ virLXCProcessBuildControllerCmd(virLXCDriverPtr driver,
                                 char **veths,
                                 int *ttyFDs,
                                 size_t nttyFDs,
+                                int *nsInheritFDs,
                                 int *files,
                                 size_t nfiles,
                                 int handshakefd,
@@ -825,6 +963,19 @@ virLXCProcessBuildControllerCmd(virLXCDriverPtr driver,
         virCommandPassFD(cmd, files[i], 0);
     }
 
+    for (i = 0; i < VIR_LXC_DOMAIN_NAMESPACE_LAST; i++) {
+        if (nsInheritFDs[i] > 0) {
+            char *tmp = NULL;
+            if (virAsprintf(&tmp, "--share-%s",
+                            nsInfoLocal[i]) < 0)
+                goto cleanup;
+            virCommandAddArg(cmd, tmp);
+            virCommandAddArgFormat(cmd, "%d", nsInheritFDs[i]);
+            virCommandPassFD(cmd, nsInheritFDs[i], 0);
+            VIR_FREE(tmp);
+        }
+    }
+
     virCommandAddArgPair(cmd, "--security",
                          virSecurityManagerGetModel(driver->securityManager));
 
@@ -1032,6 +1183,7 @@ int virLXCProcessStart(virConnectPtr conn,
     off_t pos = -1;
     char ebuf[1024];
     char *timestamp;
+    int nsInheritFDs[VIR_LXC_DOMAIN_NAMESPACE_LAST];
     virCommandPtr cmd = NULL;
     virLXCDomainObjPrivatePtr priv = vm->privateData;
     virCapsPtr caps = NULL;
@@ -1204,6 +1356,10 @@ int virLXCProcessStart(virConnectPtr conn,
     if (virLXCProcessSetupInterfaces(conn, vm->def, &nveths, &veths) < 0)
         goto cleanup;
 
+    VIR_DEBUG("Setting up namespaces if any");
+    if (virLXCProcessSetupNamespaces(conn, vm->def->namespaceData, nsInheritFDs) < 0)
+        goto cleanup;
+
     VIR_DEBUG("Preparing to launch");
     if ((logfd = open(logfile, O_WRONLY | O_APPEND | O_CREAT,
              S_IRUSR|S_IWUSR)) < 0) {
@@ -1223,6 +1379,7 @@ int virLXCProcessStart(virConnectPtr conn,
                                                 vm,
                                                 nveths, veths,
                                                 ttyFDs, nttyFDs,
+                                                nsInheritFDs,
                                                 files, nfiles,
                                                 handshakefds[1],
                                                 &logfd,
diff --git a/tests/lxcxml2xmldata/lxc-sharenet.xml b/tests/lxcxml2xmldata/lxc-sharenet.xml
new file mode 100644
index 0000000..a2b8d1b
--- /dev/null
+++ b/tests/lxcxml2xmldata/lxc-sharenet.xml
@@ -0,0 +1,33 @@
+<domain type='lxc' xmlns:lxc='http://libvirt.org/schemas/domain/lxc/1.0'>
+  <name>jessie</name>
+  <uuid>e21987a5-e98e-9c99-0e35-803e4d9ad1fe</uuid>
+  <memory unit='KiB'>1048576</memory>
+  <currentMemory unit='KiB'>1048576</currentMemory>
+  <vcpu placement='static'>1</vcpu>
+  <resource>
+    <partition>/machine</partition>
+  </resource>
+  <os>
+    <type arch='x86_64'>exe</type>
+    <init>/sbin/init</init>
+  </os>
+  <clock offset='utc'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>restart</on_crash>
+  <devices>
+    <emulator>/usr/libexec/libvirt_lxc</emulator>
+    <filesystem type='mount' accessmode='passthrough'>
+      <source dir='/mach/jessie'/>
+      <target dir='/'/>
+    </filesystem>
+    <console type='pty'>
+      <target type='lxc' port='0'/>
+    </console>
+  </devices>
+  <lxc:namespace>
+    <lxc:sharenet type='netns' value='red'/>
+    <lxc:shareipc type='pid' value='12345'/>
+    <lxc:shareuts type='name' value='container1'/>
+  </lxc:namespace>
+</domain>
diff --git a/tests/lxcxml2xmltest.c b/tests/lxcxml2xmltest.c
index 3e00347..8d824b9 100644
--- a/tests/lxcxml2xmltest.c
+++ b/tests/lxcxml2xmltest.c
@@ -133,6 +133,7 @@ mymain(void)
     DO_TEST("filesystem-root");
     DO_TEST("idmap");
     DO_TEST("capabilities");
+    DO_TEST("sharenet");
 
     virObjectUnref(caps);
     virObjectUnref(xmlopt);
-- 
1.9.1




More information about the libvir-list mailing list