diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index c155e2ae060a1651397aec338d7b4bbdd222ca48..7f4bc1d21274f3fe405357a995187d75f8bbf48b 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -334,6 +334,13 @@
To set a custom work directory for the init, use the initdir
element.
+
+ To run the init command as a given user or group, use the inituser
+ or initgroup
elements respectively. Both elements can be provided
+ either a user (resp. group) id or a name. Prefixing the user or group id with
+ a +
will force it to be considered like a numeric value. Without
+ this, it will be first tried as a user or group name.
+
<os>
@@ -343,6 +350,8 @@
<initarg>emergency.service</initarg>
<initenv name='MYENV'>some value</initenv>
<initdir>/my/custom/cwd</initdir>
+ <inituser>tester</inituser>
+ <initgroup>1000</initgroup>
</os>
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 412dba0b35d3457307245e436e7cc75ee5dbcecd..77136108adb36c0ca2e9a9bed5cd1344c887e083 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -400,6 +400,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index c264d00737a1718bfae884de875971685b891eea..59771e48ba1e7426dccb267a6e580d4ecb9cf024 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -2877,6 +2877,8 @@ void virDomainDefFree(virDomainDefPtr def)
for (i = 0; def->os.initenv && def->os.initenv[i]; i++)
VIR_FREE(def->os.initenv[i]);
VIR_FREE(def->os.initdir);
+ VIR_FREE(def->os.inituser);
+ VIR_FREE(def->os.initgroup);
VIR_FREE(def->os.initenv);
VIR_FREE(def->os.kernel);
VIR_FREE(def->os.initrd);
@@ -17070,6 +17072,8 @@ virDomainDefParseBootOptions(virDomainDefPtr def,
def->os.init = virXPathString("string(./os/init[1])", ctxt);
def->os.cmdline = virXPathString("string(./os/cmdline[1])", ctxt);
def->os.initdir = virXPathString("string(./os/initdir[1])", ctxt);
+ def->os.inituser = virXPathString("string(./os/inituser[1])", ctxt);
+ def->os.initgroup = virXPathString("string(./os/initgroup[1])", ctxt);
if ((n = virXPathNodeSet("./os/initarg", ctxt, &nodes)) < 0)
goto error;
@@ -24958,6 +24962,11 @@ virDomainDefFormatInternal(virDomainDefPtr def,
if (def->os.initdir)
virBufferEscapeString(buf, "%s \n",
def->os.initdir);
+ if (def->os.inituser)
+ virBufferAsprintf(buf, "%s \n", def->os.inituser);
+ if (def->os.initgroup)
+ virBufferAsprintf(buf, "%s \n", def->os.initgroup);
+
if (def->os.loader)
virDomainLoaderDefFormat(buf, def->os.loader);
virBufferEscapeString(buf, "%s \n",
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 0be5506694ebbbb0355b9d1f7bae3fd64738137a..a9b079cf290caf9e0ca4068be4f173bc91a72d39 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -1870,6 +1870,8 @@ struct _virDomainOSDef {
char **initargv;
virDomainOSEnvPtr *initenv;
char *initdir;
+ char *inituser;
+ char *initgroup;
char *kernel;
char *initrd;
char *cmdline;
diff --git a/src/lxc/lxc_container.c b/src/lxc/lxc_container.c
index 8d8e1a735cdb21a371841f8784a46f59cb266fd9..6309abe4b5bbe5b0bece7c294cdcf50be6eb556f 100644
--- a/src/lxc/lxc_container.c
+++ b/src/lxc/lxc_container.c
@@ -2110,6 +2110,55 @@ static int lxcAttachNS(int *ns_fd)
return 0;
}
+/**
+ * lxcContainerSetUserGroup:
+ * @cmd: command to update
+ * @vmDef: domain definition for the container
+ * @ttyPath: guest path to the tty
+ *
+ * Set the command UID and GID. As this function attempts at
+ * converting the user/group name into uid/gid, it needs to
+ * be called after the pivot root is done.
+ *
+ * The owner of the tty is also changed to the given user.
+ */
+static int lxcContainerSetUserGroup(virCommandPtr cmd,
+ virDomainDefPtr vmDef,
+ const char *ttyPath)
+{
+ uid_t uid;
+ gid_t gid;
+
+ if (vmDef->os.inituser) {
+ if (virGetUserID(vmDef->os.inituser, &uid) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, _("User %s doesn't exist"),
+ vmDef->os.inituser);
+ return -1;
+ }
+ virCommandSetUID(cmd, uid);
+
+ /* Change the newly created tty owner to the inituid for
+ * shells to have job control. */
+ if (chown(ttyPath, uid, -1) < 0) {
+ virReportSystemError(errno,
+ _("Failed to change ownership of tty %s"),
+ ttyPath);
+ return -1;
+ }
+ }
+
+ if (vmDef->os.initgroup) {
+ if (virGetGroupID(vmDef->os.initgroup, &gid) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, _("Group %s doesn't exist"),
+ vmDef->os.initgroup);
+ return -1;
+ }
+ virCommandSetGID(cmd, gid);
+ }
+
+ return 0;
+}
+
/**
* lxcContainerChild:
@@ -2208,6 +2257,9 @@ static int lxcContainerChild(void *data)
goto cleanup;
}
+ if (lxcContainerSetUserGroup(cmd, vmDef, argv->ttyPaths[0]) < 0)
+ goto cleanup;
+
/* rename and enable interfaces */
if (lxcContainerRenameAndEnableInterfaces(vmDef,
argv->nveths,
diff --git a/tests/lxcxml2xmldata/lxc-inituser.xml b/tests/lxcxml2xmldata/lxc-inituser.xml
new file mode 100644
index 0000000000000000000000000000000000000000..08338a2b76d533ae2621c9a60b47c94c4391d422
--- /dev/null
+++ b/tests/lxcxml2xmldata/lxc-inituser.xml
@@ -0,0 +1,31 @@
+
+ jessie
+ e21987a5-e98e-9c99-0e35-803e4d9ad1fe
+ 1048576
+ 1048576
+ 1
+
+ /machine
+
+
+ exe
+ /sbin/sh
+ tester
+ 1234
+
+
+ destroy
+ restart
+ restart
+
+ /usr/libexec/libvirt_lxc
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/lxcxml2xmltest.c b/tests/lxcxml2xmltest.c
index c81b0eace7e66d1bc000accbc37186646638814f..9b9314cf846e0c4a324d4049a98066662c2d92c5 100644
--- a/tests/lxcxml2xmltest.c
+++ b/tests/lxcxml2xmltest.c
@@ -100,6 +100,7 @@ mymain(void)
VIR_DOMAIN_DEF_PARSE_SKIP_OSTYPE_CHECKS);
DO_TEST("initenv");
DO_TEST("initdir");
+ DO_TEST("inituser");
virObjectUnref(caps);
virObjectUnref(xmlopt);