]> xenbits.xensource.com Git - libvirt.git/commitdiff
Run QEMU guests as an unprivileged user
authorDaniel P. Berrange <berrange@redhat.com>
Wed, 15 Jul 2009 21:25:01 +0000 (22:25 +0100)
committerDaniel P. Berrange <berrange@redhat.com>
Thu, 16 Jul 2009 16:06:55 +0000 (17:06 +0100)
* configure.in: Add --with-qemu-user and --with-qemu-group args
* libvirt.spec.in: use 'qemu' for user/group for Fedora >= 12
* qemud/libvirtd_qemu.arg, qemud/test_libvirtd_qemu.aug,
  src/qemu.conf: Add 'user' and 'group' args for configuration
* src/Makefile.am: Create %localstatedir/cache/libvirt/qemu
* src/qemu_conf.c, src/qemu_conf.h: Load user/group from config
* src/qemu_driver.c: Change user ID/group ID when launching QEMU
  guests. Change user/group ownership on disks/usb/pci devs.
  Put memory dumps in %localstatedir/cache/libvirt/qemu
* src/util.c, src/util.h: Add convenient APIs for converting
  username/groupname to user ID / group ID

12 files changed:
configure.in
libvirt.spec.in
qemud/libvirtd_qemu.aug
qemud/test_libvirtd_qemu.aug
src/Makefile.am
src/libvirt_private.syms
src/qemu.conf
src/qemu_conf.c
src/qemu_conf.h
src/qemu_driver.c
src/util.c
src/util.h

index 552089a8815bb5e708cc6644abe3bdce59c99462..634e812b987e10ac9283aea18668131ce995fde0 100644 (file)
@@ -1437,6 +1437,19 @@ AM_CONDITIONAL([WITH_NODE_DEVICES], [test "$with_nodedev" = "yes"])
 
 AM_CONDITIONAL([WITH_LINUX], [test `uname -s` = "Linux"])
 
+
+AC_ARG_WITH([qemu-user],
+  [  --with-qemu-user       username to run QEMU system instance as],
+  [QEMU_USER=${withval}],
+  [QEMU_USER=root])
+AC_ARG_WITH([qemu-group],
+  [  --with-qemu-group      groupname to run QEMU system instance as],
+  [QEMU_GROUP=${withval}],
+  [QEMU_GROUP=root])
+AC_DEFINE_UNQUOTED([QEMU_USER], ["$QEMU_USER"], [QEMU user account])
+AC_DEFINE_UNQUOTED([QEMU_GROUP], ["$QEMU_GROUP"], [QEMU group account])
+
+
 # Only COPYING.LIB is under version control, yet COPYING
 # is included as part of the distribution tarball.
 # Copy one to the other, but only if this is a srcdir-build.
@@ -1578,3 +1591,7 @@ AC_MSG_NOTICE([     Debug: $enable_debug])
 AC_MSG_NOTICE([  Warnings: $enable_compile_warnings])
 AC_MSG_NOTICE([  Readline: $lv_use_readline])
 AC_MSG_NOTICE([])
+AC_MSG_NOTICE([Privileges])
+AC_MSG_NOTICE([])
+AC_MSG_NOTICE([      QEMU: $QEMU_USER:$QEMU_GROUP])
+AC_MSG_NOTICE([])
\ No newline at end of file
index ffac3d4585700b53ece476805c0d44378744157d..a561aad92b6ef9b3feb674f1a6e5b489ba15b56f 100644 (file)
 %define with_capng     0%{!?_without_capng:1}
 %endif
 
+%if 0%{?fedora} >= 12
+%define qemu_user  qemu
+%define qemu_group  qemu
+%else
+%define qemu_user  root
+%define qemu_group  root
+%endif
+
 #
 # If building on RHEL switch on the specific support
 #
@@ -309,6 +317,8 @@ of recent versions of Linux (and other OSes).
            %{?_without_storage_iscsi} \
            %{?_without_storage_disk} \
            %{?_without_numactl} \
+           --with-qemu-user=%{qemu_user} \
+           --with-qemu-group=%{qemu_group} \
            --with-init-script=redhat \
            --with-remote-pid-file=%{_localstatedir}/run/libvirtd.pid
 make %{?_smp_mflags}
@@ -445,8 +455,9 @@ fi
 %dir %attr(0700, root, root) %{_localstatedir}/cache/libvirt/
 
 %if %{with_qemu}
-%dir %{_localstatedir}/run/libvirt/qemu/
-%dir %attr(0700, root, root) %{_localstatedir}/lib/libvirt/qemu/
+%dir %attr(0700, %{qemu_user}, %{qemu_group}) %{_localstatedir}/run/libvirt/qemu/
+%dir %attr(0700, %{qemu_user}, %{qemu_group}) %{_localstatedir}/lib/libvirt/qemu/
+%dir %attr(0700, %{qemu_user}, %{qemu_group}) %{_localstatedir}/cache/libvirt/qemu/
 %endif
 %if %{with_lxc}
 %dir %{_localstatedir}/run/libvirt/lxc/
index ff7e076fe4df36ef6389661b0a86b6ebd7808a68..8cf0461c5442ce427a4929d090b61bb9be67ff2d 100644 (file)
@@ -29,6 +29,8 @@ module Libvirtd_qemu =
                  | str_entry "vnc_password"
                  | bool_entry "vnc_sasl"
                  | str_entry "vnc_sasl_dir"
+                 | str_entry "user"
+                 | str_entry "group"
 
    (* Each enty in the config is one of the following three ... *)
    let entry = vnc_entry
index 6f38e47e3b840bc5b5b802d28e5f59b09b38a5ab..f62da0106f17b134a22b65b0418e8678989e1638 100644 (file)
@@ -79,6 +79,10 @@ vnc_sasl = 1
 # point to the directory, and create a qemu.conf in that location
 #
 vnc_sasl_dir = \"/some/directory/sasl2\"
+
+user = \"root\"
+
+group = \"root\"
 "
 
    test Libvirtd_qemu.lns get conf =
@@ -161,3 +165,7 @@ vnc_sasl_dir = \"/some/directory/sasl2\"
 { "#comment" = "point to the directory, and create a qemu.conf in that location" }
 { "#comment" = "" }
 { "vnc_sasl_dir" = "/some/directory/sasl2" }
+{ "#comment" = "" }
+{ "user"= "root" }
+{ "#comment" = "" }
+{ "group" = "root" }
\ No newline at end of file
index 889ede4b7dd44043540f25b21265ca091d295b29..9b662ae7023b7c86fc764ea0e61184a5a8928459 100644 (file)
@@ -684,6 +684,7 @@ install-exec-local:
 if WITH_QEMU
        $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/qemu"
        $(MKDIR_P) "$(DESTDIR)$(localstatedir)/run/libvirt/qemu"
+       $(MKDIR_P) "$(DESTDIR)$(localstatedir)/cache/libvirt/qemu"
 endif
 if WITH_LXC
        $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/lxc"
index 0534d53a48ed60f0bd439c3f6b7d9781444aee8e..59c78d53f898d6c8f84b9bc39a155b26a957fab8 100644 (file)
@@ -378,6 +378,8 @@ virRun;
 virSkipSpaces;
 virKillProcess;
 virGetUserDirectory;
+virGetUserID;
+virGetGroupID;
 
 
 # uuid.h
index c2a53c5cc0fbf242e102a66de9111b2c803e1dbf..300972548a8a91667740a261ba0f728ca58eb704 100644 (file)
 # to 'none' instead
 #
 # security_driver = "selinux"
+
+
+# The user ID for QEMU processes run by the system instance
+#user = "root"
+
+# The group ID for QEMU processes run by the system instance
+#group = "root"
index 0c28c20cceda7647305976582d070fd881198d7f..4043d706afd8ce6b4f6aeae7fbd8d94d85040611 100644 (file)
@@ -92,6 +92,8 @@ int qemudLoadDriverConfig(struct qemud_driver *driver,
                           const char *filename) {
     virConfPtr conf;
     virConfValuePtr p;
+    char *user;
+    char *group;
 
     /* Setup 2 critical defaults */
     if (!(driver->vncListen = strdup("127.0.0.1"))) {
@@ -186,6 +188,36 @@ int qemudLoadDriverConfig(struct qemud_driver *driver,
         }
     }
 
+    p = virConfGetValue (conf, "user");
+    CHECK_TYPE ("user", VIR_CONF_STRING);
+    if (!(user = strdup(p && p->str ? p->str : QEMU_USER))) {
+        virReportOOMError(NULL);
+        virConfFree(conf);
+        return -1;
+    }
+    if (virGetUserID(NULL, user, &driver->user) < 0) {
+        VIR_FREE(user);
+        virConfFree(conf);
+        return -1;
+    }
+    VIR_FREE(user);
+
+    p = virConfGetValue (conf, "group");
+    CHECK_TYPE ("group", VIR_CONF_STRING);
+    if (!(group = strdup(p && p->str ? p->str : QEMU_GROUP))) {
+        virReportOOMError(NULL);
+        virConfFree(conf);
+        return -1;
+    }
+
+
+    if (virGetGroupID(NULL, group, &driver->group) < 0) {
+        VIR_FREE(group);
+        virConfFree(conf);
+        return -1;
+    }
+    VIR_FREE(group);
+
     virConfFree (conf);
     return 0;
 }
index 175173d95a3245a676579d0692f1544cdf9ca296..fbf2ab91f620469af50f5ed58dd89655ad380e79 100644 (file)
@@ -66,6 +66,9 @@ struct qemud_driver {
 
     int privileged;
 
+    uid_t user;
+    gid_t group;
+
     unsigned int qemuVersion;
     int nextvmid;
 
index e41dfe8080255b8c10ee01629a7752088b4742c5..429235c1969bd7b0fdf16786ce7038e0935a858d 100644 (file)
 #define VIR_FROM_THIS VIR_FROM_QEMU
 
 /* For storing short-lived temporary files. */
-#define TEMPDIR LOCAL_STATE_DIR "/cache/libvirt"
+#define TEMPDIR LOCAL_STATE_DIR "/cache/libvirt/qemu"
 
 #define QEMU_CMD_PROMPT "\n(qemu) "
 #define QEMU_PASSWD_PROMPT "Password: "
 
-
 static int qemudShutdown(void);
 
 static void qemuDriverLock(struct qemud_driver *driver)
@@ -1367,6 +1366,205 @@ static int qemudDomainSetSecurityLabel(virConnectPtr conn, struct qemud_driver *
     return 0;
 }
 
+
+#ifdef __linux__
+static int qemuDomainSetHostdevUSBOwnership(virConnectPtr conn,
+                                            virDomainHostdevDefPtr def,
+                                            uid_t uid, gid_t gid)
+{
+    char *usbpath = NULL;
+
+    /* XXX what todo for USB devs assigned based on product/vendor ? Doom :-( */
+    if (!def->source.subsys.u.usb.bus ||
+        !def->source.subsys.u.usb.device)
+        return 0;
+
+    if (virAsprintf(&usbpath, "/dev/bus/usb/%03d/%03d",
+                    def->source.subsys.u.usb.bus,
+                    def->source.subsys.u.usb.device) < 0) {
+        virReportOOMError(conn);
+        return -1;
+    }
+
+    VIR_DEBUG("Setting ownership on %s to %d:%d", usbpath, uid, gid);
+    if (chown(usbpath, uid, gid) < 0) {
+        virReportSystemError(conn, errno, _("cannot set ownership on %s"), usbpath);
+        VIR_FREE(usbpath);
+        return -1;
+    }
+    VIR_FREE(usbpath);
+
+    return 0;
+}
+
+static int qemuDomainSetHostdevPCIOwnership(virConnectPtr conn,
+                                            virDomainHostdevDefPtr def,
+                                            uid_t uid, gid_t gid)
+{
+    char *pcidir = NULL;
+    char *file = NULL;
+    DIR *dir = NULL;
+    int ret = -1;
+    struct dirent *ent;
+
+    if (virAsprintf(&pcidir, "/sys/bus/pci/devices/%04x:%02x:%02x.%x",
+                    def->source.subsys.u.pci.domain,
+                    def->source.subsys.u.pci.bus,
+                    def->source.subsys.u.pci.slot,
+                    def->source.subsys.u.pci.function) < 0) {
+        virReportOOMError(conn);
+        goto cleanup;
+    }
+
+    if (!(dir = opendir(pcidir))) {
+        virReportSystemError(conn, errno,
+                             _("cannot open %s"), pcidir);
+        goto cleanup;
+    }
+
+    while ((ent = readdir(dir)) != NULL) {
+        /* QEMU device assignment requires:
+         *   $PCIDIR/config, $PCIDIR/resource, $PCIDIR/resourceNNN, $PCIDIR/rom
+         */
+        if (STREQ(ent->d_name, "config") ||
+            STRPREFIX(ent->d_name, "resource") ||
+            STREQ(ent->d_name, "rom")) {
+            if (virAsprintf(&file, "%s/%s", pcidir, ent->d_name) < 0) {
+                virReportOOMError(conn);
+                goto cleanup;
+            }
+            VIR_DEBUG("Setting ownership on %s to %d:%d", file, uid, gid);
+            if (chown(file, uid, gid) < 0) {
+                virReportSystemError(conn, errno, _("cannot set ownership on %s"), file);
+                goto cleanup;
+            }
+            VIR_FREE(file);
+        }
+    }
+
+    ret = 0;
+
+cleanup:
+    if (dir)
+        closedir(dir);
+    VIR_FREE(file);
+    VIR_FREE(pcidir);
+    return ret;
+}
+#endif
+
+
+static int qemuDomainSetHostdevOwnership(virConnectPtr conn,
+                                         virDomainHostdevDefPtr def,
+                                         uid_t uid, gid_t gid)
+{
+    if (def->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+        return 0;
+
+#ifdef __linux__
+    switch (def->source.subsys.type) {
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
+        return qemuDomainSetHostdevUSBOwnership(conn, def, uid, gid);
+
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
+        return qemuDomainSetHostdevPCIOwnership(conn, def, uid, gid);
+
+    }
+    return 0;
+#else
+    qemudReportError(conn, NULL, NULL, "%s",
+                     _("unable to set host device ownership on this platform"));
+    return -1;
+#endif
+
+}
+
+static int qemuDomainSetDiskOwnership(virConnectPtr conn,
+                                      virDomainDiskDefPtr def,
+                                      uid_t uid, gid_t gid)
+{
+
+    if (!def->src)
+        return 0;
+
+    VIR_DEBUG("Setting ownership on %s to %d:%d", def->src, uid, gid);
+    if (chown(def->src, uid, gid) < 0) {
+        virReportSystemError(conn, errno, _("cannot set ownership on %s"),
+                             def->src);
+        return -1;
+    }
+    return 0;
+}
+
+static int qemuDomainSetDeviceOwnership(virConnectPtr conn,
+                                        struct qemud_driver *driver,
+                                        virDomainDeviceDefPtr def,
+                                        int restore)
+{
+    uid_t uid;
+    gid_t gid;
+
+    if (!driver->privileged)
+        return 0;
+
+    /* short circuit case of root:root */
+    if (!driver->user && !driver->group)
+        return 0;
+
+    uid = restore ? 0 : driver->user;
+    gid = restore ? 0 : driver->group;
+
+    switch (def->type) {
+    case VIR_DOMAIN_DEVICE_DISK:
+        if (restore &&
+            (def->data.disk->readonly || def->data.disk->shared))
+            return 0;
+
+        return qemuDomainSetDiskOwnership(conn, def->data.disk, uid, gid);
+
+    case VIR_DOMAIN_DEVICE_HOSTDEV:
+        return qemuDomainSetHostdevOwnership(conn, def->data.hostdev, uid, gid);
+    }
+
+    return 0;
+}
+
+static int qemuDomainSetAllDeviceOwnership(virConnectPtr conn,
+                                           struct qemud_driver *driver,
+                                           virDomainDefPtr def,
+                                           int restore)
+{
+    int i;
+    uid_t uid;
+    gid_t gid;
+
+    if (!driver->privileged)
+        return 0;
+
+    /* short circuit case of root:root */
+    if (!driver->user && !driver->group)
+        return 0;
+
+    uid = restore ? 0 : driver->user;
+    gid = restore ? 0 : driver->group;
+
+    for (i = 0 ; i < def->ndisks ; i++) {
+        if (restore &&
+            (def->disks[i]->readonly || def->disks[i]->shared))
+            continue;
+
+        if (qemuDomainSetDiskOwnership(conn, def->disks[i], uid, gid) < 0)
+            return -1;
+    }
+
+    for (i = 0 ; i < def->nhostdevs ; i++) {
+        if (qemuDomainSetHostdevOwnership(conn, def->hostdevs[i], uid, gid) < 0)
+            return -1;
+    }
+
+    return 0;
+}
+
 static virDomainPtr qemudDomainLookupByName(virConnectPtr conn,
                                             const char *name);
 
@@ -1377,14 +1575,39 @@ struct gemudHookData {
 };
 
 static int qemudSecurityHook(void *data) {
-        struct gemudHookData *h = (struct gemudHookData *) data;
+    struct gemudHookData *h = (struct gemudHookData *) data;
+
+    if (qemudDomainSetSecurityLabel(h->conn, h->driver, h->vm) < 0) {
+        qemudReportError(h->conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                         _("Failed to set security label"));
+        return -1;
+    }
+
+    if (h->driver->privileged) {
+        DEBUG("Dropping privileges of VM to %d:%d", h->driver->user, h->driver->group);
+
+        if (qemuDomainSetAllDeviceOwnership(h->conn, h->driver, h->vm->def, 0) < 0)
+            return -1;
 
-        if (qemudDomainSetSecurityLabel(h->conn, h->driver, h->vm) < 0) {
-                qemudReportError(h->conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
-                                 _("Failed to set security label"));
+        if (h->driver->group) {
+            if (setregid(h->driver->group, h->driver->group) < 0) {
+                virReportSystemError(NULL, errno,
+                                     _("cannot change to '%d' group"),
+                                     h->driver->group);
                 return -1;
+            }
         }
-        return 0;
+        if (h->driver->user) {
+            if (setreuid(h->driver->user, h->driver->user) < 0) {
+                virReportSystemError(NULL, errno,
+                                     _("cannot change to '%d' user"),
+                                     h->driver->user);
+                return -1;
+            }
+        }
+    }
+
+    return 0;
 }
 
 static int
@@ -1662,6 +1885,10 @@ static void qemudShutdownVMDaemon(virConnectPtr conn ATTRIBUTE_UNUSED,
         VIR_FREE(vm->def->seclabel.imagelabel);
     }
 
+    if (qemuDomainSetAllDeviceOwnership(conn, driver, vm->def, 1) < 0)
+        VIR_WARN("Failed to restore all device ownership for %s",
+                 vm->def->name);
+
     if (qemudRemoveDomainStatus(conn, driver, vm) < 0) {
         VIR_WARN(_("Failed to remove domain status for %s"),
                  vm->def->name);
@@ -4329,12 +4556,20 @@ static int qemudDomainAttachDevice(virDomainPtr dom,
         case VIR_DOMAIN_DISK_DEVICE_FLOPPY:
             if (driver->securityDriver)
                 driver->securityDriver->domainSetSecurityImageLabel(dom->conn, vm, dev->data.disk);
+
+            if (qemuDomainSetDeviceOwnership(dom->conn, driver, dev, 0) < 0)
+                goto cleanup;
+
             ret = qemudDomainChangeEjectableMedia(dom->conn, vm, dev);
             break;
 
         case VIR_DOMAIN_DISK_DEVICE_DISK:
             if (driver->securityDriver)
                 driver->securityDriver->domainSetSecurityImageLabel(dom->conn, vm, dev->data.disk);
+
+            if (qemuDomainSetDeviceOwnership(dom->conn, driver, dev, 0) < 0)
+                goto cleanup;
+
             if (dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_USB) {
                 ret = qemudDomainAttachUsbMassstorageDevice(dom->conn, vm, dev);
             } else if (dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_SCSI ||
@@ -4357,6 +4592,9 @@ static int qemudDomainAttachDevice(virDomainPtr dom,
     } else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV &&
                dev->data.hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
                dev->data.hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) {
+        if (qemuDomainSetDeviceOwnership(dom->conn, driver, dev, 0) < 0)
+            goto cleanup;
+
         ret = qemudDomainAttachHostDevice(dom->conn, vm, dev);
     } else {
         qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
@@ -4369,8 +4607,11 @@ static int qemudDomainAttachDevice(virDomainPtr dom,
         ret = -1;
 
 cleanup:
-    if (ret < 0)
+    if (ret < 0) {
+        if (qemuDomainSetDeviceOwnership(dom->conn, driver, dev, 1) < 0)
+            VIR_WARN0("Fail to restore disk device ownership");
         virDomainDeviceDefFree(dev);
+    }
     if (vm)
         virDomainObjUnlock(vm);
     qemuDriverUnlock(driver);
@@ -4498,6 +4739,8 @@ static int qemudDomainDetachDevice(virDomainPtr dom,
         ret = qemudDomainDetachPciDiskDevice(dom->conn, vm, dev);
         if (driver->securityDriver)
             driver->securityDriver->domainRestoreSecurityImageLabel(dom->conn, dev->data.disk);
+        if (qemuDomainSetDeviceOwnership(dom->conn, driver, dev, 1) < 0)
+            VIR_WARN0("Fail to restore disk device ownership");
     }
     else
         qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
index 178ff0c81004c787d1143d216fbac1aa6ed586fe..2420f8bcb4e33c6391c495e0ff5c94a036b6e8a7 100644 (file)
@@ -55,6 +55,7 @@
 #include <netdb.h>
 #ifdef HAVE_GETPWUID_R
 #include <pwd.h>
+#include <grp.h>
 #endif
 #if HAVE_CAPNG
 #include <cap-ng.h>
@@ -1862,4 +1863,78 @@ char *virGetUserDirectory(virConnectPtr conn,
 
     return ret;
 }
+
+
+int virGetUserID(virConnectPtr conn,
+                 const char *name,
+                 uid_t *uid)
+{
+    char *strbuf;
+    struct passwd pwbuf;
+    struct passwd *pw = NULL;
+    size_t strbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+
+    if (VIR_ALLOC_N(strbuf, strbuflen) < 0) {
+        virReportOOMError(conn);
+        return -1;
+    }
+
+    /*
+     * From the manpage (terrifying but true):
+     *
+     * ERRORS
+     *  0 or ENOENT or ESRCH or EBADF or EPERM or ...
+     *        The given name or uid was not found.
+     */
+    if (getpwnam_r(name, &pwbuf, strbuf, strbuflen, &pw) != 0 || pw == NULL) {
+        virReportSystemError(conn, errno,
+                             _("Failed to find user record for name '%s'"),
+                             name);
+        VIR_FREE(strbuf);
+        return -1;
+    }
+
+    *uid = pw->pw_uid;
+
+    VIR_FREE(strbuf);
+
+    return 0;
+}
+
+
+int virGetGroupID(virConnectPtr conn,
+                  const char *name,
+                  gid_t *gid)
+{
+    char *strbuf;
+    struct group grbuf;
+    struct group *gr = NULL;
+    size_t strbuflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+
+    if (VIR_ALLOC_N(strbuf, strbuflen) < 0) {
+        virReportOOMError(conn);
+        return -1;
+    }
+
+    /*
+     * From the manpage (terrifying but true):
+     *
+     * ERRORS
+     *  0 or ENOENT or ESRCH or EBADF or EPERM or ...
+     *        The given name or uid was not found.
+     */
+    if (getgrnam_r(name, &grbuf, strbuf, strbuflen, &gr) != 0 || gr == NULL) {
+        virReportSystemError(conn, errno,
+                             _("Failed to find group record for name '%s'"),
+                             name);
+        VIR_FREE(strbuf);
+        return -1;
+    }
+
+    *gid = gr->gr_gid;
+
+    VIR_FREE(strbuf);
+
+    return 0;
+}
 #endif
index 6dd005fe6a42da77f8cd211730df7396b6bedaa7..1a7286cd660baf60681e97bc78f1294fa478c57c 100644 (file)
@@ -217,6 +217,12 @@ int virKillProcess(pid_t pid, int sig);
 #ifdef HAVE_GETPWUID_R
 char *virGetUserDirectory(virConnectPtr conn,
                           uid_t uid);
+int virGetUserID(virConnectPtr conn,
+                 const char *name,
+                 uid_t *uid);
+int virGetGroupID(virConnectPtr conn,
+                  const char *name,
+                  gid_t *gid);
 #endif
 
 int virRandomInitialize(unsigned int seed);