This patch tries to add the similar option to libvirt lxc. So to inherit namespace from
name
container c2.
add this into xml.
<lxc:namespace>
<sharenet type='name' value='c2'/>
</lxc:namespace>
And to inherit namespace from a pid.
add this into xml.
<lxc:namespace>
<sharenet type='pid' value='10245'/>
</lxc:namespace>
And to inherit namespace from a netns.
add this into xml.
<lxc:namespace>
<sharenet type='netns' value='red'/>
</lxc:namespace>
Similar options for ipc/uts.
<shareipc /> , <shareuts />
The reasong lxc xml namespace is added because this feature is very specific to lxc.
Therfore wanted to
keep it seperated from actual libvirt xml domain.
-imran
---
src/Makefile.am | 5 +-
src/lxc/lxc_conf.c | 2 +-
src/lxc/lxc_conf.h | 23 +++++
src/lxc/lxc_container.c | 191 ++++++++++++++++++++++++++++++++++--
src/lxc/lxc_domain.c | 254 +++++++++++++++++++++++++++++++++++++++++++++++-
src/lxc/lxc_domain.h | 1 +
6 files changed, 463 insertions(+), 13 deletions(-)
diff --git a/src/Makefile.am b/src/Makefile.am
index 579421d..1a78fde 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1293,7 +1293,8 @@ 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)
+libvirt_driver_lxc_impl_la_LDFLAGS = libvirt-lxc.la
if WITH_BLKID
libvirt_driver_lxc_impl_la_CFLAGS += $(BLKID_CFLAGS)
libvirt_driver_lxc_impl_la_LIBADD += $(BLKID_LIBS)
@@ -2652,6 +2653,8 @@ libvirt_lxc_LDADD = \
libvirt-net-rpc.la \
libvirt_security_manager.la \
libvirt_conf.la \
+ libvirt.la \
+ libvirt-lxc.la \
libvirt_util.la \
../gnulib/lib/libgnu.la
if WITH_DTRACE_PROBES
diff --git a/src/lxc/lxc_conf.c b/src/lxc/lxc_conf.c
index c393cb5..96a0f47 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_conf.h b/src/lxc/lxc_conf.h
index 8340b1f..59002e5 100644
--- a/src/lxc/lxc_conf.h
+++ b/src/lxc/lxc_conf.h
@@ -67,6 +67,29 @@ struct _virLXCDriverConfig {
bool securityRequireConfined;
};
+
+typedef enum {
+ VIR_DOMAIN_NAMESPACE_SHARENET = 0,
+ VIR_DOMAIN_NAMESPACE_SHAREIPC,
+ VIR_DOMAIN_NAMESPACE_SHAREUTS,
+ VIR_DOMAIN_NAMESPACE_LAST,
+} virDomainNamespace;
+
+struct ns_info {
+ const char *proc_name;
+ int clone_flag;
+};
+
+extern const struct ns_info ns_info[VIR_DOMAIN_NAMESPACE_LAST];
+
+typedef struct _lxcDomainDef lxcDomainDef;
+typedef lxcDomainDef *lxcDomainDefPtr;
+struct _lxcDomainDef {
+ int ns_inherit_fd[VIR_DOMAIN_NAMESPACE_LAST];
+ char *ns_type[VIR_DOMAIN_NAMESPACE_LAST];
+ char *ns_val[VIR_DOMAIN_NAMESPACE_LAST];
+};
+
struct _virLXCDriver {
virMutex lock;
diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c
index 9a9ae5c..a9a7ba0 100644
--- a/src/lxc/lxc_container.c
+++ b/src/lxc/lxc_container.c
@@ -25,8 +25,8 @@
*/
#include <config.h>
-
#include <fcntl.h>
+#include <sched.h>
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
@@ -38,7 +38,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>
@@ -99,6 +98,50 @@ VIR_LOG_INIT("lxc.lxc_container");
typedef char lxc_message_t;
#define LXC_CONTINUE_MSG 'c'
+#ifdef __linux__
+/*
+ * Workaround older glibc. While kernel may support the setns
+ * syscall, the glibc wrapper might not exist. If that's the
+ * case, use our own.
+ */
+# ifndef __NR_setns
+# if defined(__x86_64__)
+# define __NR_setns 308
+# elif defined(__i386__)
+# define __NR_setns 346
+# elif defined(__arm__)
+# define __NR_setns 375
+# elif defined(__aarch64__)
+# define __NR_setns 375
+# elif defined(__powerpc__)
+# define __NR_setns 350
+# elif defined(__s390__)
+# define __NR_setns 339
+# endif
+# endif
+
+# ifndef HAVE_SETNS
+# if defined(__NR_setns)
+# include <sys/syscall.h>
+
+static inline int setns(int fd, int nstype)
+{
+ return syscall(__NR_setns, fd, nstype);
+}
+# else /* !__NR_setns */
+# error Please determine the syscall number for setns on your architecture
+# endif
+# endif
+#else /* !__linux__ */
+static inline int setns(int fd ATTRIBUTE_UNUSED, int nstype ATTRIBUTE_UNUSED)
+{
+ virReportSystemError(ENOSYS, "%s",
+ _("Namespaces are not supported on this platform."));
+ return -1;
+}
+#endif
+
+
typedef struct __lxc_child_argv lxc_child_argv_t;
struct __lxc_child_argv {
virDomainDefPtr config;
@@ -2233,7 +2276,6 @@ static int lxcContainerChild(void *data)
vmDef->os.init);
goto cleanup;
}
-
/* rename and enable interfaces */
if (lxcContainerRenameAndEnableInterfaces(vmDef,
argv->nveths,
@@ -2321,6 +2363,99 @@ virArch lxcContainerGetAlt32bitArch(virArch arch)
return VIR_ARCH_NONE;
}
+/* Used only for containers,same as the one defined in
+ * domain_conf.c. But used locally
+ */
+static const struct ns_info ns_info_local[VIR_DOMAIN_NAMESPACE_LAST] = {
+ [VIR_DOMAIN_NAMESPACE_SHARENET] = {"net", CLONE_NEWNET},
+ [VIR_DOMAIN_NAMESPACE_SHAREIPC] = {"ipc", CLONE_NEWIPC},
+ [VIR_DOMAIN_NAMESPACE_SHAREUTS] = {"uts", CLONE_NEWUTS}
+};
+
+
+static void close_ns(int ns_fd[VIR_DOMAIN_NAMESPACE_LAST])
+{
+ int i;
+ for (i = 0; i < VIR_DOMAIN_NAMESPACE_LAST; i++) {
+ if (ns_fd[i] > -1) {
+ if (VIR_CLOSE(ns_fd[i]) < 0)
+ virReportSystemError(errno, "%s", _("failed to close
file"));
+ ns_fd[i] = -1;
+ }
+ }
+}
+
+
+/**
+ * lxcPreserve_ns:
+ * @ns_fd: array to store current namespace
+ * @clone_flags: namespaces that need to be preserved
+ */
+static int lxcPreserve_ns(int ns_fd[VIR_DOMAIN_NAMESPACE_LAST], int clone_flags)
+{
+ int i, saved_errno;
+ char *path = NULL;
+
+ for (i = 0; i < VIR_DOMAIN_NAMESPACE_LAST; i++)
+ ns_fd[i] = -1;
+
+ if (access("/proc/self/ns", X_OK)) {
+ virReportSystemError(errno, "%s",
+ _("Kernel does not support attach; preserve_ns
ignored"));
+ return 0;
+ }
+
+ for (i = 0; i < VIR_DOMAIN_NAMESPACE_LAST; i++) {
+ if ((clone_flags & ns_info_local[i].clone_flag) == 0)
+ continue;
+ if (virAsprintf(&path, "/proc/self/ns/%s",
+ ns_info_local[i].proc_name) < 0)
+ goto error;
+ ns_fd[i] = open(path, O_RDONLY | O_CLOEXEC);
+ if (ns_fd[i] < 0)
+ goto error;
+ }
+ VIR_FREE(path);
+ return 0;
+ error:
+ saved_errno = errno;
+ close_ns(ns_fd);
+ errno = saved_errno;
+ VIR_FREE(path);
+ virReportSystemError(errno, _("failed to open '%s'"), path);
+ return -1;
+}
+
+/**
+ * lxcAttach_ns:
+ * @ns_fd: array of namespaces to attach
+ */
+static int lxcAttach_ns(const int ns_fd[VIR_DOMAIN_NAMESPACE_LAST])
+{
+ int i;
+
+ for (i = 0; i < VIR_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)
+ goto error;
+ }
+ return 0;
+
+ error:
+ virReportSystemError(errno, _("failed to set namespace '%s'")
+ , ns_info_local[i].proc_name);
+ return -1;
+}
+
/**
* lxcContainerStart:
@@ -2346,9 +2481,12 @@ int lxcContainerStart(virDomainDefPtr def,
char **ttyPaths)
{
pid_t pid;
- int cflags;
+ int cflags, i;
int stacksize = getpagesize() * 4;
char *stack, *stacktop;
+ int saved_ns_fd[VIR_DOMAIN_NAMESPACE_LAST];
+ int preserve_mask = 0;
+ lxcDomainDefPtr lxcDef;
lxc_child_argv_t args = {
.config = def,
.securityDriver = securityDriver,
@@ -2368,7 +2506,14 @@ int lxcContainerStart(virDomainDefPtr def,
stacktop = stack + stacksize;
- cflags = CLONE_NEWPID|CLONE_NEWNS|CLONE_NEWUTS|CLONE_NEWIPC|SIGCHLD;
+ lxcDef = def->namespaceData;
+ for (i = 0; i < VIR_DOMAIN_NAMESPACE_LAST; i++)
+ if (lxcDef && lxcDef->ns_inherit_fd[i] != -1)
+ preserve_mask |= ns_info_local[i].clone_flag;
+
+
+
+ cflags = CLONE_NEWPID|CLONE_NEWNS|SIGCHLD;
if (userns_required(def)) {
if (userns_supported()) {
@@ -2381,10 +2526,36 @@ int lxcContainerStart(virDomainDefPtr def,
return -1;
}
}
+ if (!lxcDef || (lxcDef &&
lxcDef->ns_inherit_fd[VIR_DOMAIN_NAMESPACE_SHARENET] == -1)) {
+ if (lxcNeedNetworkNamespace(def)) {
+ VIR_DEBUG("Enable network namespaces");
+ cflags |= CLONE_NEWNET;
+ }
+ } else {
+ VIR_DEBUG("Inheriting a net namespace");
+ }
- if (lxcNeedNetworkNamespace(def)) {
- VIR_DEBUG("Enable network namespaces");
- cflags |= CLONE_NEWNET;
+ if (!lxcDef || (lxcDef &&
lxcDef->ns_inherit_fd[VIR_DOMAIN_NAMESPACE_SHAREIPC] == -1)) {
+ cflags |= CLONE_NEWIPC;
+ } else {
+ VIR_DEBUG("Inheriting an IPC namespace");
+ }
+
+ if (!lxcDef || (lxcDef &&
lxcDef->ns_inherit_fd[VIR_DOMAIN_NAMESPACE_SHAREUTS] == -1)) {
+ cflags |= CLONE_NEWUTS;
+ } else {
+ VIR_DEBUG("Inheriting a UTS namespace");
+ }
+
+ if (lxcDef && lxcPreserve_ns(saved_ns_fd, preserve_mask) < 0) {
+ virReportError(VIR_ERR_SYSTEM_ERROR, "%s",
+ _("failed to preserve the namespace"));
+ return -1;
+ }
+ if (lxcDef && lxcAttach_ns(lxcDef->ns_inherit_fd) < 0) {
+ virReportError(VIR_ERR_SYSTEM_ERROR, "%s",
+ _("failed to attach the namespace"));
+ return -1;
}
VIR_DEBUG("Cloning container init process");
@@ -2397,6 +2568,10 @@ int lxcContainerStart(virDomainDefPtr def,
_("Failed to run clone container"));
return -1;
}
+ if (lxcDef && lxcAttach_ns(saved_ns_fd)) {
+ virReportError(VIR_ERR_SYSTEM_ERROR, "%s",
+ _("failed to restore saved namespaces"));
+ }
return pid;
}
diff --git a/src/lxc/lxc_domain.c b/src/lxc/lxc_domain.c
index c2180cb..6e4a19a 100644
--- a/src/lxc/lxc_domain.c
+++ b/src/lxc/lxc_domain.c
@@ -20,14 +20,18 @@
*/
#include <config.h>
-
#include "lxc_domain.h"
-
#include "viralloc.h"
#include "virlog.h"
#include "virerror.h"
+#include <fcntl.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 +45,251 @@ static void *virLXCDomainObjPrivateAlloc(void)
return priv;
}
+
+static int open_ns(const char *nnsname_pid, const char *ns_proc_name)
+{
+ int fd = -1;
+ virDomainPtr dom = NULL;
+ virConnectPtr conn = NULL;
+ pid_t pid;
+ int nfdlist;
+ int *fdlist;
+ char *path = NULL;
+ char *eptr;
+ pid = strtol(nnsname_pid, &eptr, 10);
+ if (*eptr != '\0' || pid < 1) {
+ /* check if the domain is running, then set the namespaces
+ * to that container
+ */
+ size_t i = 0;
+ const char *ns[] = { "user", "ipc", "uts",
"net", "pid", "mnt" };
+ conn = virConnectOpen("lxc:///");
+ if (!conn)
+ goto cleanup;
+ dom = virDomainLookupByName(conn, nnsname_pid);
+ if (!dom)
+ goto cleanup;
+ if ((nfdlist = virDomainLxcOpenNamespace(dom, &fdlist, 0)) < 0)
+ goto cleanup;
+ /* Internally above function calls virProcessGetNamespaces
+ * function which opens ns
+ * in the order { "user", "ipc", "uts",
"net", "pid", "mnt" }
+ */
+ for (i = 0; i < ARRAY_CARDINALITY(ns); i++) {
+ if (STREQ(ns[i], ns_proc_name)) {
+ fd = fdlist[i];
+ break;
+ }
+ }
+ if (nfdlist > 0)
+ VIR_FREE(fdlist);
+ } else {
+ if (virAsprintf(&path, "/proc/%d/ns/%s", pid, ns_proc_name) <
0)
+ goto cleanup;
+ fd = open(path, O_RDONLY);
+ }
+cleanup:
+ if (dom)
+ virDomainFree(dom);
+ VIR_FREE(path);
+ (fd < 0)? VIR_ERROR(
+ _("failed to open ns %s"), nnsname_pid):
+ VIR_DEBUG("OPENED NAMESPACE : fd %d\n", fd);
+ return fd;
+}
+
+
+/* Used only for containers */
+const struct ns_info ns_info[VIR_DOMAIN_NAMESPACE_LAST] = {
+ [VIR_DOMAIN_NAMESPACE_SHARENET] = {"net", CLONE_NEWNET},
+ [VIR_DOMAIN_NAMESPACE_SHAREIPC] = {"ipc", CLONE_NEWIPC},
+ [VIR_DOMAIN_NAMESPACE_SHAREUTS] = {"uts", CLONE_NEWUTS}
+};
+
+VIR_ENUM_DECL(virDomainNamespace)
+VIR_ENUM_IMPL(virDomainNamespace, VIR_DOMAIN_NAMESPACE_LAST,
+ N_("sharenet"),
+ N_("shareipc"),
+ N_("shareuts"))
+
+static void
+lxcDomainDefNamespaceFree(void *nsdata)
+{
+ int j;
+ lxcDomainDefPtr lxcDef = nsdata;
+ for (j = 0; j < VIR_DOMAIN_NAMESPACE_LAST; j++) {
+ if (lxcDef->ns_inherit_fd[j] > 0) {
+ VIR_FREE(lxcDef->ns_type);
+ VIR_FREE(lxcDef->ns_val);
+#if 0
+ if (VIR_CLOSE(lxcDef->ns_inherit_fd[j]) < 0)
+ virReportSystemError(errno, "%s", _("failed to close
file"));
+#endif
+ }
+ }
+ 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;
+ pid_t fd = -1;
+
+ 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;
+
+ /* Init ns_herit_fd for namespaces */
+ for (i = 0; i < VIR_DOMAIN_NAMESPACE_LAST; i++) {
+ lxcDef->ns_inherit_fd[i] = -1;
+ lxcDef->ns_type[i] = NULL;
+ lxcDef->ns_val[i] = NULL;
+ }
+
+ node = ctxt->node;
+ if ((n = virXPathNodeSet("./lxc:namespace/*", ctxt, &nodes)) < 0)
+ goto error;
+ uses_lxc_ns |= n > 0;
+
+ for (i = 0; i < n; i++) {
+ feature =
+ virDomainNamespaceTypeFromString((const char *) nodes[i]->name);
+ if (feature < 0) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("unsupported Namespace feature: %s"),
+ nodes[i]->name);
+ goto error;
+ }
+
+ ctxt->node = nodes[i];
+
+ switch ((virDomainNamespace) feature) {
+ case VIR_DOMAIN_NAMESPACE_SHARENET:
+ case VIR_DOMAIN_NAMESPACE_SHAREIPC:
+ case VIR_DOMAIN_NAMESPACE_SHAREUTS:
+ {
+ tmp = virXMLPropString(nodes[i], "type");
+ if (tmp == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("No lxc environment type
specified"));
+ goto error;
+ }
+ /* save the tmp so that its needed while writing to xml */
+ lxcDef->ns_type[feature] = tmp;
+ tmp = virXMLPropString(nodes[i], "value");
+ if (tmp == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("No lxc environment type
specified"));
+ goto error;
+ }
+ lxcDef->ns_val[feature] = tmp;
+ /*netns option is only for VIR_DOMAIN_NAMESPACE_SHARENET*/
+ if (STREQ("netns",
lxcDef->ns_type[VIR_DOMAIN_NAMESPACE_SHARENET])) {
+ char *path = NULL;
+ if (virAsprintf(&path, "/var/run/netns/%s", tmp) <
0)
+ goto error;
+ fd = open(path, O_RDONLY);
+ VIR_FREE(path);
+ } else {
+ fd = open_ns(tmp, ns_info[feature].proc_name);
+ if (fd < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("unable to open %s namespace for "
+ "namespace feature '%s'"),
tmp,
+ nodes[i]->name);
+ goto error;
+ }
+ }
+ lxcDef->ns_inherit_fd[feature] = fd;
+ }
+ break;
+ case VIR_DOMAIN_NAMESPACE_LAST:
+ break;
+ }
+ }
+ 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 j;
+
+ if (!lxcDef)
+ return 0;
+
+ virBufferAddLit(buf, "<lxc:namespace>\n");
+ virBufferAdjustIndent(buf, 2);
+
+ for (j = 0; j < VIR_DOMAIN_NAMESPACE_LAST; j++) {
+ switch ((virDomainNamespace) j) {
+ case VIR_DOMAIN_NAMESPACE_SHAREIPC:
+ case VIR_DOMAIN_NAMESPACE_SHAREUTS:
+ case VIR_DOMAIN_NAMESPACE_SHARENET:
+ {
+ if (lxcDef->ns_inherit_fd[j] > 0) {
+ virBufferAsprintf(buf, "<%s type='%s'
value='%s'/>\n",
+ virDomainNamespaceTypeToString(j),
+ lxcDef->ns_type[j],
+ lxcDef->ns_val[j]);
+ }
+ }
+ break;
+ case VIR_DOMAIN_NAMESPACE_LAST:
+ break;
+ }
+ }
+
+ 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;
@@ -73,7 +322,6 @@ static int virLXCDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, void
*data)
} else {
priv->initpid = thepid;
}
-
return 0;
}
diff --git a/src/lxc/lxc_domain.h b/src/lxc/lxc_domain.h
index 751aece..25df999 100644
--- a/src/lxc/lxc_domain.h
+++ b/src/lxc/lxc_domain.h
@@ -41,6 +41,7 @@ struct _virLXCDomainObjPrivate {
virCgroupPtr cgroup;
};
+extern virDomainXMLNamespace virLXCDriverDomainXMLNamespace;
extern virDomainXMLPrivateDataCallbacks virLXCDriverPrivateDataCallbacks;
extern virDomainDefParserConfig virLXCDriverDomainDefParserConfig;
--
1.9.1