diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index faa5e462d16521da08859d1029a1ba46013ff487..863377ca857325b2ed72331cfdfbc65d82021d3b 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -250,6 +250,28 @@
installation media source / kickstart file
+
+
+
+ When booting a domain using container based virtualization, instead
+ of a kernel / boot image, a path to the init binary is required, using
+ the init
element. By default this will be launched with
+ no arguments. To specify the initial argv, use the initarg
+ element, repeated as many time as is required. The cmdline
+ element, if set will be used to provide an equivalent to /proc/cmdline
+ but will not effect init argv.
+
+
+
+ <os>
+ <type arch='x86_64'>exe</type>
+ <init>/bin/systemd</init>
+ <initarg>--unit</initarg>
+ <initarg>emergency.service</initarg>
+ </os>
+
+
+
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 1f5232ef15a765ffde59142bbe3d762f901e3771..730f3d8e8ba339d19f24d13a95eb1f5793f3e58d 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -378,6 +378,11 @@
+
+
+
+
+
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index e235c13581d19e31e041e5f712da8b04178ae9bb..ea558bbcef81bc2944e94566d34c2d3fd17442bc 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -1559,6 +1559,9 @@ void virDomainDefFree(virDomainDefPtr def)
VIR_FREE(def->os.arch);
VIR_FREE(def->os.machine);
VIR_FREE(def->os.init);
+ for (i = 0 ; def->os.initargv && def->os.initargv[i] ; i++)
+ VIR_FREE(def->os.initargv[i]);
+ VIR_FREE(def->os.initargv);
VIR_FREE(def->os.kernel);
VIR_FREE(def->os.initrd);
VIR_FREE(def->os.cmdline);
@@ -8188,6 +8191,25 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps,
}
}
def->os.cmdline = virXPathString("string(./os/cmdline[1])", ctxt);
+
+ if ((n = virXPathNodeSet("./os/initarg", ctxt, &nodes)) < 0) {
+ goto error;
+ }
+
+ if (VIR_ALLOC_N(def->os.initargv, n+1) < 0)
+ goto no_memory;
+ for (i = 0 ; i < n ; i++) {
+ if (!nodes[i]->children ||
+ !nodes[i]->children->content) {
+ virDomainReportError(VIR_ERR_XML_ERROR, "%s",
+ _("No data supplied for element"));
+ goto error;
+ }
+ if (!(def->os.initargv[i] = strdup((const char*)nodes[i]->children->content)))
+ goto no_memory;
+ }
+ def->os.initargv[n] = NULL;
+ VIR_FREE(nodes);
}
if (STREQ(def->os.type, "xen") ||
@@ -12171,6 +12193,7 @@ virDomainDefFormatInternal(virDomainDefPtr def,
char uuidstr[VIR_UUID_STRING_BUFLEN];
const char *type = NULL;
int n, allones = 1;
+ int i;
bool blkio = false;
virCheckFlags(DUMPXML_FLAGS |
@@ -12332,7 +12355,6 @@ virDomainDefFormatInternal(virDomainDefPtr def,
virBufferAsprintf(buf, " %lld \n",
def->cputune.quota);
if (def->cputune.vcpupin) {
- int i;
for (i = 0; i < def->cputune.nvcpupin; i++) {
virBufferAsprintf(buf, " cputune.vcpupin[i]->vcpuid);
@@ -12408,6 +12430,9 @@ virDomainDefFormatInternal(virDomainDefPtr def,
virBufferEscapeString(buf, " %s \n",
def->os.init);
+ for (i = 0 ; def->os.initargv && def->os.initargv[i] ; i++)
+ virBufferEscapeString(buf, " %s \n",
+ def->os.initargv[i]);
virBufferEscapeString(buf, " %s \n",
def->os.loader);
virBufferEscapeString(buf, " %s \n",
@@ -12462,7 +12487,6 @@ virDomainDefFormatInternal(virDomainDefPtr def,
virBufferAddLit(buf, " \n");
if (def->features) {
- int i;
virBufferAddLit(buf, " \n");
for (i = 0 ; i < VIR_DOMAIN_FEATURE_LAST ; i++) {
if (def->features & (1 << i)) {
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 10030a494e0460665700ef0b60b1046186899c2d..3fcb02644c8e581677f02542d776dfb64c6a88a4 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -1354,6 +1354,7 @@ struct _virDomainOSDef {
int bootDevs[VIR_DOMAIN_BOOT_LAST];
int bootmenu;
char *init;
+ char **initargv;
char *kernel;
char *initrd;
char *cmdline;
diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c
index bb64b606f73d13d6472f727768c69becf99c047f..0755b3cc5028bf7fefd6a65b11b655421ea83dcb 100644
--- a/src/lxc/lxc_container.c
+++ b/src/lxc/lxc_container.c
@@ -122,6 +122,9 @@ static virCommandPtr lxcContainerBuildInitCmd(virDomainDefPtr vmDef)
cmd = virCommandNew(vmDef->os.init);
+ if (vmDef->os.initargv && vmDef->os.initargv[0])
+ virCommandAddArgSet(cmd, (const char **)vmDef->os.initargv);
+
virCommandAddEnvString(cmd, "PATH=/bin:/sbin");
virCommandAddEnvString(cmd, "TERM=linux");
virCommandAddEnvString(cmd, "container=lxc-libvirt");
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 4755a3e6ea2ae431513d104230dd53ae8aa724db..4a0686ff4bf524e78d88478ea94f51a785ed34e5 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -60,6 +60,7 @@ EXTRA_DIST = \
domainsnapshotxml2xmlin \
domainsnapshotxml2xmlout \
interfaceschemadata \
+ lxcxml2xmldata \
networkschematest \
networkxml2xmlin \
networkxml2xmlout \
@@ -115,6 +116,10 @@ check_PROGRAMS += qemuxml2argvtest qemuxml2xmltest qemuxmlnstest \
qemumonitortest
endif
+if WITH_LXC
+check_PROGRAMS += lxcxml2xmltest
+endif
+
if WITH_OPENVZ
check_PROGRAMS += openvzutilstest
endif
@@ -245,6 +250,10 @@ TESTS += qemuxml2argvtest qemuxml2xmltest qemuxmlnstest qemuargv2xmltest \
qemumonitortest
endif
+if WITH_LXC
+TESTS += lxcxml2xmltest
+endif
+
if WITH_OPENVZ
TESTS += openvzutilstest
endif
@@ -383,6 +392,18 @@ EXTRA_DIST += qemuxml2argvtest.c qemuxml2xmltest.c qemuargv2xmltest.c \
qemumonitortest.c testutilsqemu.c testutilsqemu.h
endif
+if WITH_LXC
+
+lxc_LDADDS = ../src/libvirt_driver_lxc.la
+
+lxcxml2xmltest_SOURCES = \
+ lxcxml2xmltest.c testutilslxc.c testutilslxc.h \
+ testutils.c testutils.h
+lxcxml2xmltest_LDADD = $(lxc_LDADDS) $(LDADDS)
+else
+EXTRA_DIST += lxcxml2xmltest.c testutilslxc.c testutilslxc.h
+endif
+
if WITH_OPENVZ
openvzutilstest_SOURCES = \
openvzutilstest.c \
diff --git a/tests/lxcxml2xmldata/lxc-systemd.xml b/tests/lxcxml2xmldata/lxc-systemd.xml
new file mode 100644
index 0000000000000000000000000000000000000000..bf239c25772c768dfc22b554648adcd68ca9c7fd
--- /dev/null
+++ b/tests/lxcxml2xmldata/lxc-systemd.xml
@@ -0,0 +1,30 @@
+
+ demo
+ 8369f1ac-7e46-e869-4ca5-759d51478066
+ 500000
+ 500000
+ 1
+
+ exe
+ /bin/systemd
+ --unit
+ emergency.service
+
+
+ destroy
+ restart
+ destroy
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/lxcxml2xmltest.c b/tests/lxcxml2xmltest.c
new file mode 100644
index 0000000000000000000000000000000000000000..558bd012c955db690add94c8ac1bf019fca37805
--- /dev/null
+++ b/tests/lxcxml2xmltest.c
@@ -0,0 +1,141 @@
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#ifdef WITH_LXC
+
+# include "internal.h"
+# include "testutils.h"
+# include "lxc/lxc_conf.h"
+# include "testutilslxc.h"
+
+static virCapsPtr caps;
+
+static int
+testCompareXMLToXMLFiles(const char *inxml, const char *outxml, bool live)
+{
+ char *inXmlData = NULL;
+ char *outXmlData = NULL;
+ char *actual = NULL;
+ int ret = -1;
+ virDomainDefPtr def = NULL;
+
+ if (virtTestLoadFile(inxml, &inXmlData) < 0)
+ goto fail;
+ if (virtTestLoadFile(outxml, &outXmlData) < 0)
+ goto fail;
+
+ if (!(def = virDomainDefParseString(caps, inXmlData,
+ 1 << VIR_DOMAIN_VIRT_LXC,
+ live ? 0 : VIR_DOMAIN_XML_INACTIVE)))
+ goto fail;
+
+ if (!(actual = virDomainDefFormat(def, VIR_DOMAIN_XML_SECURE)))
+ goto fail;
+
+ if (STRNEQ(outXmlData, actual)) {
+ virtTestDifference(stderr, outXmlData, actual);
+ goto fail;
+ }
+
+ ret = 0;
+ fail:
+ VIR_FREE(inXmlData);
+ VIR_FREE(outXmlData);
+ VIR_FREE(actual);
+ virDomainDefFree(def);
+ return ret;
+}
+
+struct testInfo {
+ const char *name;
+ int different;
+ bool inactive_only;
+};
+
+static int
+testCompareXMLToXMLHelper(const void *data)
+{
+ const struct testInfo *info = data;
+ char *xml_in = NULL;
+ char *xml_out = NULL;
+ int ret = -1;
+
+ if (virAsprintf(&xml_in, "%s/lxcxml2xmldata/lxc-%s.xml",
+ abs_srcdir, info->name) < 0 ||
+ virAsprintf(&xml_out, "%s/lxcxml2xmloutdata/lxc-%s.xml",
+ abs_srcdir, info->name) < 0)
+ goto cleanup;
+
+ if (info->different) {
+ ret = testCompareXMLToXMLFiles(xml_in, xml_out, false);
+ } else {
+ ret = testCompareXMLToXMLFiles(xml_in, xml_in, false);
+ }
+ if (!info->inactive_only) {
+ if (info->different) {
+ ret = testCompareXMLToXMLFiles(xml_in, xml_out, true);
+ } else {
+ ret = testCompareXMLToXMLFiles(xml_in, xml_in, true);
+ }
+ }
+
+cleanup:
+ VIR_FREE(xml_in);
+ VIR_FREE(xml_out);
+ return ret;
+}
+
+
+static int
+mymain(void)
+{
+ int ret = 0;
+
+ if ((caps = testLXCCapsInit()) == NULL)
+ return (EXIT_FAILURE);
+
+# define DO_TEST_FULL(name, is_different, inactive) \
+ do { \
+ const struct testInfo info = {name, is_different, inactive}; \
+ if (virtTestRun("LXC XML-2-XML " name, \
+ 1, testCompareXMLToXMLHelper, &info) < 0) \
+ ret = -1; \
+ } while (0)
+
+# define DO_TEST(name) \
+ DO_TEST_FULL(name, 0, false)
+
+# define DO_TEST_DIFFERENT(name) \
+ DO_TEST_FULL(name, 1, false)
+
+ /* Unset or set all envvars here that are copied in lxcdBuildCommandLine
+ * using ADD_ENV_COPY, otherwise these tests may fail due to unexpected
+ * values for these envvars */
+ setenv("PATH", "/bin", 1);
+
+ DO_TEST("systemd");
+
+ virCapabilitiesFree(caps);
+
+ return (ret==0 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+VIRT_TEST_MAIN(mymain)
+
+#else
+# include "testutils.h"
+
+int
+main(void)
+{
+ return EXIT_AM_SKIP;
+}
+
+#endif /* WITH_LXC */
diff --git a/tests/testutilslxc.c b/tests/testutilslxc.c
new file mode 100644
index 0000000000000000000000000000000000000000..e6193afcf04644929c33e557ddd874087860e865
--- /dev/null
+++ b/tests/testutilslxc.c
@@ -0,0 +1,63 @@
+#include
+#ifdef WITH_LXC
+# include
+
+# include "testutilslxc.h"
+# include "testutils.h"
+# include "memory.h"
+# include "domain_conf.h"
+
+
+static int testLXCDefaultConsoleType(const char *ostype ATTRIBUTE_UNUSED)
+{
+ return VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_LXC;
+}
+
+
+virCapsPtr testLXCCapsInit(void) {
+ virCapsPtr caps;
+ virCapsGuestPtr guest;
+
+ if ((caps = virCapabilitiesNew("x86_64",
+ 0, 0)) == NULL)
+ return NULL;
+
+ caps->defaultConsoleTargetType = testLXCDefaultConsoleType;
+
+ if ((guest = virCapabilitiesAddGuest(caps, "exe", "i686", 32,
+ "/usr/libexec/libvirt_lxc", NULL,
+ 0, NULL)) == NULL)
+ goto error;
+
+ if (!virCapabilitiesAddGuestDomain(guest, "lxc", NULL, NULL, 0, NULL))
+ goto error;
+
+
+ if ((guest = virCapabilitiesAddGuest(caps, "exe", "x86_64", 64,
+ "/usr/libexec/libvirt_lxc", NULL,
+ 0, NULL)) == NULL)
+ goto error;
+
+ if (!virCapabilitiesAddGuestDomain(guest, "lxc", NULL, NULL, 0, NULL))
+ goto error;
+
+
+ if (virTestGetDebug()) {
+ char *caps_str;
+
+ caps_str = virCapabilitiesFormatXML(caps);
+ if (!caps_str)
+ goto error;
+
+ fprintf(stderr, "LXC driver capabilities:\n%s", caps_str);
+
+ VIR_FREE(caps_str);
+ }
+
+ return caps;
+
+error:
+ virCapabilitiesFree(caps);
+ return NULL;
+}
+#endif
diff --git a/tests/testutilslxc.h b/tests/testutilslxc.h
new file mode 100644
index 0000000000000000000000000000000000000000..ee8056f1f595af1e9c0322a06d9eb11aa34b2186
--- /dev/null
+++ b/tests/testutilslxc.h
@@ -0,0 +1,4 @@
+
+#include "capabilities.h"
+
+virCapsPtr testLXCCapsInit(void);