[libvirt] PATCH: 3/7: Main HAL & DeviceKit impl

Daniel P. Berrange berrange at redhat.com
Thu Nov 20 17:55:03 UTC 2008


The internal impl for HAL and devicekit

Changes since last time

 - Fix broken logic in devicekit NIC interfac checks
 - Disable DeviceKit by default

Daniel

diff -r c51d78efb84a configure.in
--- a/configure.in	Thu Nov 20 17:43:18 2008 +0000
+++ b/configure.in	Thu Nov 20 17:47:42 2008 +0000
@@ -1105,6 +1105,108 @@
 LV_LIBTOOL_OBJDIR=${lt_cv_objdir-.}
 AC_SUBST([LV_LIBTOOL_OBJDIR])
 
+dnl HAL or DeviceKit library for host device enumeration
+HAL_REQUIRED=0.0
+HAL_CFLAGS=
+HAL_LIBS=
+AC_ARG_WITH([hal],
+  [  --with-hal         use HAL for host device enumeration],
+  [],
+  [with_hal=check])
+
+if test "$with_libvirtd" = "no" ; then
+  with_hal=no
+fi
+if test "x$with_hal" = "xyes" -o "x$with_hal" = "xcheck"; then
+  PKG_CHECK_MODULES(HAL, hal >= $HAL_REQUIRED,
+    [with_hal=yes], [
+    if test "x$with_hal" = "xcheck" ; then
+       with_hal=no
+    else
+       AC_MSG_ERROR(
+         [You must install hal-devel >= $HAL_REQUIRED to compile libvirt])
+    fi
+  ])
+  if test "x$with_hal" = "xyes" ; then
+    AC_DEFINE_UNQUOTED([HAVE_HAL], 1,
+      [use HAL for host device enumeration])
+
+    old_CFLAGS=$CFLAGS
+    old_LDFLAGS=$LDFLAGS
+    CFLAGS="$CFLAGS $HAL_CFLAGS"
+    LDFLAGS="$LDFLAGS $HAL_LIBS"
+    AC_CHECK_FUNCS([libhal_get_all_devices],,[with_hal=no])
+    CFLAGS="$old_CFLAGS"
+    LDFLAGS="$old_LDFLAGS"
+  fi
+fi
+AM_CONDITIONAL([HAVE_HAL], [test "x$with_hal" = "xyes"])
+AC_SUBST([HAL_CFLAGS])
+AC_SUBST([HAL_LIBS])
+
+DEVKIT_REQUIRED=0.0
+DEVKIT_CFLAGS=
+DEVKIT_LIBS=
+AC_ARG_WITH([devkit],
+  [  --with-devkit      use DeviceKit for host device enumeration],
+  [],
+  [with_devkit=no])
+
+if test "$with_libvirtd" = "no" ; then
+  with_devkit=no
+fi
+
+dnl Extra check needed while devkit pkg-config info missing glib2 dependency
+PKG_CHECK_MODULES(GLIB2, glib-2.0 >= 0.0,,[
+  if test "x$with_devkit" = "xcheck"; then
+    with_devkit=no
+  elif test "x$with_devkit" = "xyes"; then
+    AC_MSG_ERROR([required package DeviceKit requires package glib-2.0])
+  fi])
+
+if test "x$with_devkit" = "xyes" -o "x$with_devkit" = "xcheck"; then
+  PKG_CHECK_MODULES(DEVKIT, devkit-gobject >= $DEVKIT_REQUIRED,
+    [with_devkit=yes], [
+    if test "x$with_devkit" = "xcheck" ; then
+       with_devkit=no
+    else
+       AC_MSG_ERROR(
+         [You must install DeviceKit-devel >= $DEVKIT_REQUIRED to compile libvirt])
+    fi
+  ])
+  if test "x$with_devkit" = "xyes" ; then
+    AC_DEFINE_UNQUOTED([HAVE_DEVKIT], 1,
+      [use DeviceKit for host device enumeration])
+
+    dnl Add glib2 flags explicitly while devkit pkg-config info missing glib2 dependency
+    DEVKIT_CFLAGS="$GLIB2_CFLAGS $DEVKIT_CFLAGS"
+    DEVKIT_LIBS="$GLIB2_LIBS $DEVKIT_LIBS"
+
+    dnl Add more flags apparently required for devkit to work properly
+    DEVKIT_CFLAGS="$DEVKIT_CFLAGS -D_POSIX_PTHREAD_SEMANTICS -D_REENTRANT"
+
+    old_CFLAGS=$CFLAGS
+    old_LDFLAGS=$LDFLAGS
+    CFLAGS="$CFLAGS $DEVKIT_CFLAGS"
+    LDFLAGS="$LDFLAGS $DEVKIT_LIBS"
+    AC_CHECK_FUNCS([devkit_client_connect],,[with_devkit=no])
+    CFLAGS="$old_CFLAGS"
+    LDFLAGS="$old_LDFLAGS"
+  fi
+fi
+AM_CONDITIONAL([HAVE_DEVKIT], [test "x$with_devkit" = "xyes"])
+AC_SUBST([DEVKIT_CFLAGS])
+AC_SUBST([DEVKIT_LIBS])
+
+with_nodedev=no;
+if test "$with_devkit" = "yes" -o "$with_hal" = "yes";
+then
+  with_nodedev=yes
+  AC_DEFINE_UNQUOTED([WITH_NODE_DEVICES], 1, [with node device driver])
+fi
+AM_CONDITIONAL([WITH_NODE_DEVICES], [test "$with_nodedev" = "yes"])
+
+
 # very annoying
 rm -f COPYING
 cp COPYING.LIB COPYING
@@ -1196,6 +1298,16 @@
 else
 AC_MSG_NOTICE([  xen: no])
 fi
+if test "$with_hal" = "yes" ; then
+AC_MSG_NOTICE([  hal: $HAL_CFLAGS $HAL_LIBS])
+else
+AC_MSG_NOTICE([  hal: no])
+fi
+if test "$with_devkit" = "yes" ; then
+AC_MSG_NOTICE([  devkit: $DEVKIT_CFLAGS $DEVKIT_LIBS])
+else
+AC_MSG_NOTICE([  devkit: no])
+fi
 AC_MSG_NOTICE([])
 AC_MSG_NOTICE([Test suite])
 AC_MSG_NOTICE([])
diff -r c51d78efb84a po/POTFILES.in
--- a/po/POTFILES.in	Thu Nov 20 17:43:18 2008 +0000
+++ b/po/POTFILES.in	Thu Nov 20 17:47:42 2008 +0000
@@ -13,6 +13,7 @@
 src/lxc_driver.c
 src/network_conf.c
 src/network_driver.c
+src/node_device.c
 src/openvz_conf.c
 src/openvz_driver.c
 src/proxy_internal.c
diff -r c51d78efb84a qemud/Makefile.am
--- a/qemud/Makefile.am	Thu Nov 20 17:43:18 2008 +0000
+++ b/qemud/Makefile.am	Thu Nov 20 17:47:42 2008 +0000
@@ -111,6 +111,10 @@
 if WITH_NETWORK
 libvirtd_LDADD += ../src/libvirt_driver_network.la
 endif
+
+if WITH_NODE_DEVICES
+libvirtd_LDADD += ../src/libvirt_driver_nodedev.la
+endif
 endif
 
 libvirtd_LDADD += ../src/libvirt.la
diff -r c51d78efb84a qemud/qemud.c
--- a/qemud/qemud.c	Thu Nov 20 17:43:18 2008 +0000
+++ b/qemud/qemud.c	Thu Nov 20 17:47:42 2008 +0000
@@ -78,6 +78,9 @@
 #endif
 #ifdef WITH_STORAGE_DIR
 #include "storage_driver.h"
+#endif
+#ifdef WITH_NODE_DEVICES
+#include "node_device.h"
 #endif
 #endif
 
@@ -763,6 +766,7 @@
     virDriverLoadModule("uml");
     virDriverLoadModule("network");
     virDriverLoadModule("storage");
+    virDriverLoadModule("nodedev");
 #else
 #ifdef WITH_QEMU
     qemuRegister();
@@ -778,6 +782,9 @@
 #endif
 #ifdef WITH_STORAGE_DIR
     storageRegister();
+#endif
+#if defined(HAVE_HAL) || defined(HAVE_DEVKIT)
+    nodedevRegister();
 #endif
 #endif
 
diff -r c51d78efb84a src/Makefile.am
--- a/src/Makefile.am	Thu Nov 20 17:43:18 2008 +0000
+++ b/src/Makefile.am	Thu Nov 20 17:47:42 2008 +0000
@@ -156,6 +156,14 @@
 STORAGE_HELPER_DISK_SOURCES =					\
 		parthelper.c
 
+
+NODE_DEVICE_DRIVER_SOURCES =					\
+		node_device.c node_device.h
+
+NODE_DEVICE_DRIVER_HAL_SOURCES =				\
+		node_device_hal.c
+NODE_DEVICE_DRIVER_DEVKIT_SOURCES =				\
+		node_device_devkit.c
 
 
 #########################
@@ -327,6 +335,36 @@
 libvirt_driver_storage_la_SOURCES += $(STORAGE_DRIVER_DISK_SOURCES)
 endif
 
+if WITH_NODE_DEVICES
+# Needed to keep automake quiet about conditionals
+if WITH_DRIVER_MODULES
+mod_LTLIBRARIES += libvirt_driver_nodedev.la
+else
+noinst_LTLIBRARIES += libvirt_driver_nodedev.la
+# Stateful, so linked to daemon instead
+#libvirt_la_LIBADD += libvirt_driver_nodedev.la
+endif
+libvirt_driver_nodedev_la_SOURCES = $(NODE_DEVICE_DRIVER_SOURCES)
+
+libvirt_driver_nodedev_la_CFLAGS =
+libvirt_driver_nodedev_la_LDFLAGS =
+if HAVE_HAL
+libvirt_driver_nodedev_la_SOURCES += $(NODE_DEVICE_DRIVER_HAL_SOURCES)
+libvirt_driver_nodedev_la_CFLAGS += $(HAL_CFLAGS)
+libvirt_driver_nodedev_la_LDFLAGS += $(HAL_LIBS)
+endif
+if HAVE_DEVKIT
+libvirt_driver_nodedev_la_SOURCES += $(NODE_DEVICE_DRIVER_DEVKIT_SOURCES)
+libvirt_driver_nodedev_la_CFLAGS += $(DEVKIT_CFLAGS)
+libvirt_driver_nodedev_la_LDFLAGS += $(DEVKIT_LIBS)
+endif
+
+if WITH_DRIVER_MODULES
+libvirt_driver_nodedev_la_LDFLAGS += -module -avoid-version
+endif
+endif
+
+
 # Add all conditional sources just in case...
 EXTRA_DIST +=							\
 		$(TEST_DRIVER_SOURCES)				\
@@ -341,7 +379,10 @@
 		$(STORAGE_DRIVER_FS_SOURCES)			\
 		$(STORAGE_DRIVER_LVM_SOURCES)			\
 		$(STORAGE_DRIVER_ISCSI_SOURCES)			\
-		$(STORAGE_DRIVER_DISK_SOURCES)
+		$(STORAGE_DRIVER_DISK_SOURCES)			\
+		$(NODE_DEVICE_DRIVER_SOURCES)			\
+		$(NODE_DEVICE_DRIVER_HAL_SOURCES)		\
+		$(NODE_DEVICE_DRIVER_DEVKIT_SOURCES)
 
 
 # Empty source list - it merely links a bunch of convenience libs together
diff -r c51d78efb84a src/libvirt_sym.version.in
--- a/src/libvirt_sym.version.in	Thu Nov 20 17:43:18 2008 +0000
+++ b/src/libvirt_sym.version.in	Thu Nov 20 17:47:42 2008 +0000
@@ -327,6 +327,7 @@
 	virGetNetwork;
 	virGetStoragePool;
 	virGetStorageVol;
+	virGetNodeDevice;
 	virUnrefDomain;
 
 
@@ -450,6 +451,7 @@
 	virRegisterNetworkDriver;
 	virRegisterStateDriver;
 	virRegisterStorageDriver;
+	virRegisterDeviceMonitor;
 
 
 	/* memory.h */
@@ -478,6 +480,16 @@
 
 	/* nodeinfo.h */
 	virNodeInfoPopulate;
+
+
+	/* node_device_conf.h */
+	virNodeDeviceObjRemove;
+	virNodeDevCapTypeToString;
+	virNodeDeviceFindByName;
+	virNodeDeviceObjListFree;
+	virNodeDeviceDefFree;
+	virNodeDevCapsDefFree;
+	virNodeDeviceDefFormat;
 
 
 	/* qparams.h */
@@ -538,6 +550,7 @@
 	virStrToLong_i;
 	virStrToLong_ll;
 	virStrToLong_ull;
+	virStrToLong_ui;
 	virFileLinkPointsTo;
 	saferead;
 	safewrite;
@@ -557,6 +570,7 @@
 	virFileOpenTty;
 	virFileReadLimFD;
 	virFileReadPid;
+	virFileLinkPointsTo;
 	virParseNumber;
 	virRun;
 	virSkipSpaces;
diff -r c51d78efb84a src/node_device.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/node_device.c	Thu Nov 20 17:47:42 2008 +0000
@@ -0,0 +1,213 @@
+/*
+ * node_device.c: node device enumeration
+ *
+ * Copyright (C) 2008 Virtual Iron Software, Inc.
+ * Copyright (C) 2008 David F. Lively
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: David F. Lively <dlively at virtualiron.com>
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <errno.h>
+
+#include "virterror_internal.h"
+#include "datatypes.h"
+#include "memory.h"
+
+#include "node_device_conf.h"
+#include "node_device.h"
+
+static int dev_has_cap(const virNodeDeviceObjPtr dev, const char *cap)
+{
+    virNodeDevCapsDefPtr caps = dev->def->caps;
+    while (caps) {
+        if (STREQ(cap, virNodeDevCapTypeToString(caps->type)))
+            return 1;
+        caps = caps->next;
+    }
+    return 0;
+}
+
+
+static int nodeNumOfDevices(virConnectPtr conn,
+                            const char *cap,
+                            unsigned int flags ATTRIBUTE_UNUSED)
+{
+    virDeviceMonitorStatePtr driver = conn->devMonPrivateData;
+    int ndevs = 0;
+    unsigned int i;
+
+    for (i = 0; i < driver->devs.count; i++)
+        if ((cap == NULL) ||
+            dev_has_cap(driver->devs.objs[i], cap))
+            ++ndevs;
+
+    return ndevs;
+}
+
+static int
+nodeListDevices(virConnectPtr conn,
+                const char *cap,
+                char **const names, int maxnames,
+                unsigned int flags ATTRIBUTE_UNUSED)
+{
+    virDeviceMonitorStatePtr driver = conn->devMonPrivateData;
+    int ndevs = 0;
+    unsigned int i;
+
+    for (i = 0; i < driver->devs.count && ndevs < maxnames; i++)
+        if (cap == NULL ||
+            dev_has_cap(driver->devs.objs[i], cap))
+            if ((names[ndevs++] = strdup(driver->devs.objs[i]->def->name)) == NULL)
+                goto failure;
+
+    return ndevs;
+
+ failure:
+    --ndevs;
+    while (--ndevs >= 0)
+        VIR_FREE(names[ndevs]);
+    return -1;
+}
+
+
+static virNodeDevicePtr nodeDeviceLookupByName(virConnectPtr conn,
+                                               const char *name)
+{
+    virDeviceMonitorStatePtr driver = conn->devMonPrivateData;
+    virNodeDeviceObjPtr obj = virNodeDeviceFindByName(&driver->devs, name);
+
+    if (!obj) {
+        virNodeDeviceReportError(conn, VIR_ERR_INVALID_NODE_DEVICE,
+                                 "%s", _("no node device with matching name"));
+        return NULL;
+    }
+
+    return virGetNodeDevice(conn, name);
+
+}
+
+static char *nodeDeviceDumpXML(virNodeDevicePtr dev,
+                               unsigned int flags ATTRIBUTE_UNUSED)
+{
+    virDeviceMonitorStatePtr driver = dev->conn->devMonPrivateData;
+    virNodeDeviceObjPtr obj = virNodeDeviceFindByName(&driver->devs, dev->name);
+
+    if (!obj) {
+        virNodeDeviceReportError(dev->conn, VIR_ERR_INVALID_NODE_DEVICE,
+                              "%s", _("no node device with matching name"));
+        return NULL;
+    }
+
+    return virNodeDeviceDefFormat(dev->conn, obj->def);
+}
+
+
+static char *nodeDeviceGetParent(virNodeDevicePtr dev)
+{
+    virDeviceMonitorStatePtr driver = dev->conn->devMonPrivateData;
+    virNodeDeviceObjPtr obj = virNodeDeviceFindByName(&driver->devs, dev->name);
+
+    if (!obj) {
+        virNodeDeviceReportError(dev->conn, VIR_ERR_INVALID_NODE_DEVICE,
+                              "%s", _("no node device with matching name"));
+        return NULL;
+    }
+
+    return obj->def->parent;
+}
+
+
+static int nodeDeviceNumOfCaps(virNodeDevicePtr dev)
+{
+    virDeviceMonitorStatePtr driver = dev->conn->devMonPrivateData;
+    virNodeDeviceObjPtr obj = virNodeDeviceFindByName(&driver->devs, dev->name);
+    virNodeDevCapsDefPtr caps;
+    int ncaps = 0;
+
+    if (!obj) {
+        virNodeDeviceReportError(dev->conn, VIR_ERR_INVALID_NODE_DEVICE,
+                              "%s", _("no node device with matching name"));
+        return -1;
+    }
+
+    for (caps = obj->def->caps; caps; caps = caps->next)
+        ++ncaps;
+
+    return ncaps;
+}
+
+
+static int
+nodeDeviceListCaps(virNodeDevicePtr dev, char **const names, int maxnames)
+{
+    virDeviceMonitorStatePtr driver = dev->conn->devMonPrivateData;
+    virNodeDeviceObjPtr obj = virNodeDeviceFindByName(&driver->devs, dev->name);
+    virNodeDevCapsDefPtr caps;
+    int ncaps = 0;
+
+    if (!obj) {
+        virNodeDeviceReportError(dev->conn, VIR_ERR_INVALID_NODE_DEVICE,
+                              "%s", _("no node device with matching name"));
+        return -1;
+    }
+
+    for (caps = obj->def->caps; caps && ncaps < maxnames; caps = caps->next) {
+        names[ncaps] = strdup(virNodeDevCapTypeToString(caps->type));
+        if (names[ncaps++] == NULL)
+            goto failure;
+    }
+
+    return ncaps;
+
+ failure:
+    --ncaps;
+    while (--ncaps >= 0)
+        VIR_FREE(names[ncaps]);
+    return -1;
+}
+
+
+void registerCommonNodeFuncs(virDeviceMonitorPtr driver)
+{
+    driver->numOfDevices = nodeNumOfDevices;
+    driver->listDevices = nodeListDevices;
+    driver->deviceLookupByName = nodeDeviceLookupByName;
+    driver->deviceDumpXML = nodeDeviceDumpXML;
+    driver->deviceGetParent = nodeDeviceGetParent;
+    driver->deviceNumOfCaps = nodeDeviceNumOfCaps;
+    driver->deviceListCaps = nodeDeviceListCaps;
+}
+
+
+int nodedevRegister(void) {
+#if defined(HAVE_HAL) && defined(HAVE_DEVKIT)
+    /* Register only one of these two - they conflict */
+    if (halNodeRegister() == -1)
+        return devkitNodeRegister();
+    return 0;
+#else
+#ifdef HAVE_HAL
+    return halNodeRegister();
+#endif
+#ifdef HAVE_DEVKIT
+    return devkitNodeRegister();
+#endif
+#endif
+}
diff -r c51d78efb84a src/node_device.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/node_device.h	Thu Nov 20 17:47:42 2008 +0000
@@ -0,0 +1,41 @@
+/*
+ * node_device.h: node device enumeration
+ *
+ * Copyright (C) 2008 Virtual Iron Software, Inc.
+ * Copyright (C) 2008 David F. Lively
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: David F. Lively <dlively at virtualiron.com>
+ */
+
+#ifndef __VIR_NODE_DEVICE_H__
+#define __VIR_NODE_DEVICE_H__
+
+#include "internal.h"
+#include "driver.h"
+
+#ifdef HAVE_HAL
+int halNodeRegister(void);
+#endif
+#ifdef HAVE_DEVKIT
+int devkitNodeRegister(void);
+#endif
+
+void registerCommonNodeFuncs(virDeviceMonitorPtr mon);
+
+int nodedevRegister(void);
+
+#endif /* __VIR_NODE_DEVICE_H__ */
diff -r c51d78efb84a src/node_device_conf.h
--- a/src/node_device_conf.h	Thu Nov 20 17:43:18 2008 +0000
+++ b/src/node_device_conf.h	Thu Nov 20 17:47:42 2008 +0000
@@ -161,6 +161,7 @@
 typedef struct _virDeviceMonitorState virDeviceMonitorState;
 typedef virDeviceMonitorState *virDeviceMonitorStatePtr;
 struct _virDeviceMonitorState {
+    int dbusWatch;
     virNodeDeviceObjList devs;		/* currently-known devices */
     void *privateData;			/* driver-specific private data */
 };
diff -r c51d78efb84a src/node_device_devkit.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/node_device_devkit.c	Thu Nov 20 17:47:42 2008 +0000
@@ -0,0 +1,429 @@
+/*
+ * node_device_devkit.c: node device enumeration - DeviceKit-based implementation
+ *
+ * Copyright (C) 2008 Virtual Iron Software, Inc.
+ * Copyright (C) 2008 David F. Lively
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: David F. Lively <dlively at virtualiron.com>
+ */
+
+#include <config.h>
+#include <devkit-gobject.h>
+
+#include "node_device_conf.h"
+#include "virterror_internal.h"
+#include "driver.h"
+#include "datatypes.h"
+#include "event.h"
+#include "memory.h"
+#include "uuid.h"
+
+#include "node_device.h"
+
+/*
+ * Host device enumeration (DeviceKit implementation)
+ */
+
+static virDeviceMonitorStatePtr driverState;
+
+#define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt, __VA_ARGS__)
+#define DEBUG0(msg) VIR_DEBUG(__FILE__, "%s", msg)
+
+#define CONN_DRV_STATE(conn) \
+        ((virDeviceMonitorStatePtr)((conn)->devMonPrivateData))
+#define DRV_STATE_DKCLIENT(ds) ((DevkitClient *)((ds)->privateData))
+#define CONN_DKCLIENT(conn) DRV_STATE_DKCLIENT(CONN_DRV_STATE(conn))
+
+#define NODE_DEV_DKDEV(obj) ((DevkitDevice *)((obj)->privateData)
+
+static int get_str_prop(DevkitDevice *dkdev, const char *prop, char **val_p)
+{
+    char *val = devkit_device_dup_property_as_str(dkdev, prop);
+
+    if (val) {
+        if (*val) {
+            *val_p = val;
+            return 0;
+        } else {
+            /* Treat empty strings as NULL values */
+            VIR_FREE(val);
+        }
+    }
+
+    return -1;
+}
+
+#if 0
+static int get_int_prop(DevkitDevice *dkdev, const char *prop, int *val_p)
+{
+    if (! devkit_device_has_property(dkdev, prop))
+        return -1;
+    *val_p = devkit_device_get_property_as_int(dkdev, prop);
+    return 0;
+}
+
+static int get_uint64_prop(DevkitDevice *dkdev, const char *prop,
+                           unsigned long long *val_p)
+{
+    if (! devkit_device_has_property(dkdev, prop))
+        return -1;
+    *val_p = devkit_device_get_property_as_uint64(dkdev, prop);
+    return 0;
+}
+#endif
+
+static int gather_pci_cap(DevkitDevice *dkdev,
+                          union _virNodeDevCapData *d)
+{
+    const char *sysfs_path = devkit_device_get_native_path(dkdev);
+
+    if (sysfs_path != NULL) {
+        char *p = strrchr(sysfs_path, '/');
+        if (p) {
+            (void)virStrToLong_ui(p+1, &p, 16, &d->pci_dev.domain);
+            (void)virStrToLong_ui(p+1, &p, 16, &d->pci_dev.bus);
+            (void)virStrToLong_ui(p+1, &p, 16, &d->pci_dev.slot);
+            (void)virStrToLong_ui(p+1, &p, 16, &d->pci_dev.function);
+        }
+    }
+    return 0;
+}
+
+
+static int gather_usb_cap(DevkitDevice *dkdev,
+                          union _virNodeDevCapData *d)
+{
+    (void)get_str_prop(dkdev, "ID_VENDOR", &d->usb_dev.vendor_name);
+    (void)get_str_prop(dkdev, "ID_MODEL", &d->usb_dev.product_name);
+    return 0;
+}
+
+
+static int gather_net_cap(DevkitDevice *dkdev,
+                          union _virNodeDevCapData *d)
+{
+    const char *sysfs_path = devkit_device_get_native_path(dkdev);
+    const char *interface;
+
+    if (sysfs_path == NULL)
+        return -1;
+    interface = strrchr(sysfs_path, '/');
+    if (!interface || !*interface || !*(++interface))
+        return -1;
+    if ((d->net.interface = strdup(interface)) == NULL)
+        return -1;
+
+    d->net.subtype = VIR_NODE_DEV_CAP_NET_LAST;
+
+    return 0;
+}
+
+
+static int gather_block_cap(DevkitDevice *dkdev,
+                          union _virNodeDevCapData *d)
+{
+    const char *device = devkit_device_get_device_file(dkdev);
+
+    if (device && ((d->block.device = strdup(device)) == NULL))
+        return -1;
+
+    return 0;
+}
+
+
+struct _caps_tbl_entry {
+    const char *cap_name;
+    enum virNodeDevCapType type;
+    int (*gather_fn)(DevkitDevice *dkdev,
+                     union _virNodeDevCapData *data);
+};
+
+typedef struct _caps_tbl_entry caps_tbl_entry;
+
+static caps_tbl_entry caps_tbl[] = {
+    { "pci",        VIR_NODE_DEV_CAP_PCI_DEV,   gather_pci_cap },
+    { "usb",        VIR_NODE_DEV_CAP_USB_DEV,   gather_usb_cap },
+    { "net",        VIR_NODE_DEV_CAP_NET,       gather_net_cap },
+    { "block",      VIR_NODE_DEV_CAP_BLOCK,     gather_block_cap },
+    // TODO: more caps!
+};
+
+
+/* qsort/bsearch string comparator */
+static int cmpstringp(const void *p1, const void *p2)
+{
+    /* from man 3 qsort */
+    return strcmp(* (char * const *) p1, * (char * const *) p2);
+}
+
+
+static int gather_capability(DevkitDevice *dkdev,
+                             const char *cap_name,
+                             virNodeDevCapsDefPtr *caps_p)
+{
+    size_t caps_tbl_len = sizeof(caps_tbl) / sizeof(caps_tbl[0]);
+    caps_tbl_entry *entry;
+
+    entry = bsearch(&cap_name, caps_tbl, caps_tbl_len,
+                    sizeof(caps_tbl[0]), cmpstringp);
+
+    if (entry) {
+        virNodeDevCapsDefPtr caps;
+        if (VIR_ALLOC(caps) < 0)
+            return ENOMEM;
+        caps->type = entry->type;
+        if (entry->gather_fn) {
+            int rv = (*entry->gather_fn)(dkdev, &caps->data);
+            if (rv != 0) {
+                virNodeDevCapsDefFree(caps);
+                return rv;
+            }
+        }
+        caps->next = *caps_p;
+        *caps_p = caps;
+    }
+
+    return 0;
+}
+
+
+static int gather_capabilities(DevkitDevice *dkdev,
+                               virNodeDevCapsDefPtr *caps_p)
+{
+    const char *subsys = devkit_device_get_subsystem(dkdev);
+    const char *bus_name = devkit_device_get_property(dkdev, "ID_BUS");
+    virNodeDevCapsDefPtr caps = NULL;
+    int rv;
+
+    if (subsys) {
+        rv = gather_capability(dkdev, subsys, &caps);
+        if (rv != 0) goto failure;
+    }
+
+    if (bus_name && (subsys == NULL || !STREQ(bus_name, subsys))) {
+        rv = gather_capability(dkdev, bus_name, &caps);
+        if (rv != 0) goto failure;
+    }
+
+    *caps_p = caps;
+    return 0;
+
+ failure:
+    while (caps) {
+        virNodeDevCapsDefPtr next = caps->next;
+        virNodeDevCapsDefFree(caps);
+        caps = next;
+    }
+    return rv;
+}
+
+static void dev_create(void *_dkdev, void *_dkclient ATTRIBUTE_UNUSED)
+{
+    DevkitDevice *dkdev = _dkdev;
+    const char *sysfs_path = devkit_device_get_native_path(dkdev);
+    virNodeDeviceObjPtr dev = NULL;
+    const char *name;
+    int rv;
+
+    if (sysfs_path == NULL)
+        /* Currently using basename(sysfs_path) as device name (key) */
+        return;
+
+    name = strrchr(sysfs_path, '/');
+    if (name == NULL)
+        name = sysfs_path;
+    else
+        ++name;
+
+    if (VIR_ALLOC(dev) < 0 || VIR_ALLOC(dev->def) < 0)
+        goto failure;
+
+    dev->privateData = dkdev;
+
+    if ((dev->def->name = strdup(name)) == NULL)
+        goto failure;
+
+    // TODO: Find device parent, if any
+
+    rv = gather_capabilities(dkdev, &dev->def->caps);
+    if (rv != 0) goto failure;
+
+    if (VIR_REALLOC_N(driverState->devs.objs, driverState->devs.count + 1) < 0)
+        goto failure;
+
+    driverState->devs.objs[driverState->devs.count++] = dev;
+
+    return;
+
+ failure:
+    DEBUG("FAILED TO ADD dev %s", name);
+    if (dev)
+        virNodeDeviceDefFree(dev->def);
+    VIR_FREE(dev);
+}
+
+
+static int devkitDeviceMonitorStartup(void)
+{
+    size_t caps_tbl_len = sizeof(caps_tbl) / sizeof(caps_tbl[0]);
+    DevkitClient *devkit_client = NULL;
+    GError *err = NULL;
+    GList *devs;
+    int i;
+
+    /* Ensure caps_tbl is sorted by capability name */
+    qsort(caps_tbl, caps_tbl_len, sizeof(caps_tbl[0]), cmpstringp);
+
+    if (VIR_ALLOC(driverState) < 0)
+        return -1;
+
+    // TODO: Is it really ok to call this multiple times??
+    //       Is there something analogous to call on close?
+    g_type_init();
+
+    /* Get new devkit_client and connect to daemon */
+    devkit_client = devkit_client_new(NULL);
+    if (devkit_client == NULL) {
+        DEBUG0("devkit_client_new returned NULL");
+        goto failure;
+    }
+    if (!devkit_client_connect(devkit_client, &err)) {
+        DEBUG0("devkit_client_connect failed");
+        goto failure;
+    }
+
+    /* Populate with known devices.
+     *
+     * This really should be:
+        devs = devkit_client_enumerate_by_subsystem(devkit_client, NULL, &err);
+        if (err) {
+            DEBUG0("devkit_client_enumerate_by_subsystem failed");
+            devs = NULL;
+            goto failure;
+        }
+        g_list_foreach(devs, dev_create, devkit_client);
+    * but devkit_client_enumerate_by_subsystem currently fails when the second
+    * arg is null (contrary to the API documentation).  So the following code
+    * (from Dan B) works around this by listing devices per handled subsystem.
+    */
+
+    for (i = 0 ; i < ARRAY_CARDINALITY(caps_tbl) ; i++) {
+        const char *caps[] = { caps_tbl[i].cap_name, NULL };
+        devs = devkit_client_enumerate_by_subsystem(devkit_client,
+                                                    caps,
+                                                    &err);
+        if (err) {
+            DEBUG0("devkit_client_enumerate_by_subsystem failed");
+            devs = NULL;
+            goto failure;
+        }
+        g_list_foreach(devs, dev_create, devkit_client);
+    }
+
+    driverState->privateData = devkit_client;
+
+    // TODO: Register to get DeviceKit events on device changes and
+    //       coordinate updates with queries and other operations.
+
+    return 0;
+
+ failure:
+    if (err) {
+        DEBUG("\terror[%d]: %s", err->code, err->message);
+        g_error_free(err);
+    }
+    if (devs) {
+        g_list_foreach(devs, (GFunc)g_object_unref, NULL);
+        g_list_free(devs);
+    }
+    if (devkit_client)
+        g_object_unref(devkit_client);
+    VIR_FREE(driverState);
+
+    return -1;
+}
+
+
+static int devkitDeviceMonitorShutdown(void)
+{
+    if (driverState) {
+        DevkitClient *devkit_client = DRV_STATE_DKCLIENT(driverState);
+        virNodeDeviceObjListFree(&driverState->devs);
+        if (devkit_client)
+            g_object_unref(devkit_client);
+        VIR_FREE(driverState);
+        return 0;
+    }
+    return -1;
+}
+
+
+static int devkitDeviceMonitorReload(void)
+{
+    (void)devkitDeviceMonitorShutdown();
+    return devkitDeviceMonitorStartup();
+}
+
+
+static int devkitDeviceMonitorActive(void)
+{
+    /* Always ready to deal with a shutdown */
+    return 0;
+}
+
+
+static virDrvOpenStatus
+devkitNodeDrvOpen(virConnectPtr conn,
+                  virConnectAuthPtr auth ATTRIBUTE_UNUSED,
+                  int flags ATTRIBUTE_UNUSED)
+{
+    if (driverState == NULL)
+        return VIR_DRV_OPEN_DECLINED;
+
+    conn->devMonPrivateData = driverState;
+
+    return VIR_DRV_OPEN_SUCCESS;
+}
+
+static int devkitNodeDrvClose(virConnectPtr conn ATTRIBUTE_UNUSED)
+{
+    conn->devMonPrivateData = NULL;
+    return 0;
+}
+
+
+static virDeviceMonitor devkitDeviceMonitor = {
+    .name = "devkitDeviceMonitor",
+    .open = devkitNodeDrvOpen,
+    .close = devkitNodeDrvClose,
+};
+
+
+static virStateDriver devkitStateDriver = {
+    .initialize = devkitDeviceMonitorStartup,
+    .cleanup = devkitDeviceMonitorShutdown,
+    .reload = devkitDeviceMonitorReload,
+    .active = devkitDeviceMonitorActive,
+};
+
+int devkitNodeRegister(void)
+{
+    registerCommonNodeFuncs(&devkitDeviceMonitor);
+    if (virRegisterDeviceMonitor(&devkitDeviceMonitor) < 0)
+        return -1;
+    return virRegisterStateDriver(&devkitStateDriver);
+}
diff -r c51d78efb84a src/node_device_hal.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/node_device_hal.c	Thu Nov 20 17:47:42 2008 +0000
@@ -0,0 +1,765 @@
+/*
+ * node_device_hal.c: node device enumeration - HAL-based implementation
+ *
+ * Copyright (C) 2008 Virtual Iron Software, Inc.
+ * Copyright (C) 2008 David F. Lively
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: David F. Lively <dlively at virtualiron.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <config.h>
+#include <libhal.h>
+
+#include "node_device_conf.h"
+#include "virterror_internal.h"
+#include "driver.h"
+#include "datatypes.h"
+#include "event.h"
+#include "memory.h"
+#include "uuid.h"
+#include "logging.h"
+#include "node_device.h"
+
+/*
+ * Host device enumeration (HAL implementation)
+ */
+
+static virDeviceMonitorStatePtr driverState;
+
+#define CONN_DRV_STATE(conn) \
+        ((virDeviceMonitorStatePtr)((conn)->devMonPrivateData))
+#define DRV_STATE_HAL_CTX(ds) ((LibHalContext *)((ds)->privateData))
+#define CONN_HAL_CTX(conn) DRV_STATE_HAL_CTX(CONN_DRV_STATE(conn))
+
+#define NODE_DEV_UDI(obj) ((const char *)((obj)->privateData)
+
+
+static const char *hal_name(const char *udi)
+{
+    const char *name = strrchr(udi, '/');
+    if (name)
+        return name+1;
+    return udi;
+}
+
+
+static int get_str_prop(LibHalContext *ctxt, const char *udi,
+                        const char *prop, char **val_p)
+{
+    char *val = libhal_device_get_property_string(ctxt, udi, prop, NULL);
+
+    if (val) {
+        if (*val) {
+            *val_p = val;
+            return 0;
+        } else {
+            /* Treat empty strings as NULL values */
+            VIR_FREE(val);
+        }
+    }
+
+    return -1;
+}
+
+static int get_int_prop(LibHalContext *ctxt, const char *udi,
+                        const char *prop, int *val_p)
+{
+    DBusError err;
+    int val;
+    int rv;
+
+    dbus_error_init(&err);
+    val = libhal_device_get_property_int(ctxt, udi, prop, &err);
+    rv = dbus_error_is_set(&err);
+    dbus_error_free(&err);
+    if (rv == 0)
+        *val_p = val;
+
+    return rv;
+}
+
+static int get_bool_prop(LibHalContext *ctxt, const char *udi,
+                         const char *prop, int *val_p)
+{
+    DBusError err;
+    int val;
+    int rv;
+
+    dbus_error_init(&err);
+    val = libhal_device_get_property_bool(ctxt, udi, prop, &err);
+    rv = dbus_error_is_set(&err);
+    dbus_error_free(&err);
+    if (rv == 0)
+        *val_p = val;
+
+    return rv;
+}
+
+static int get_uint64_prop(LibHalContext *ctxt, const char *udi,
+                           const char *prop, unsigned long long *val_p)
+{
+    DBusError err;
+    unsigned long long val;
+    int rv;
+
+    dbus_error_init(&err);
+    val = libhal_device_get_property_uint64(ctxt, udi, prop, &err);
+    rv = dbus_error_is_set(&err);
+    dbus_error_free(&err);
+    if (rv == 0)
+        *val_p = val;
+
+    return rv;
+}
+
+static int gather_pci_cap(LibHalContext *ctx, const char *udi,
+                          union _virNodeDevCapData *d)
+{
+    char *sysfs_path;
+
+    if (get_str_prop(ctx, udi, "pci.linux.sysfs_path", &sysfs_path) == 0) {
+        char *p = strrchr(sysfs_path, '/');
+        if (p) {
+            (void)virStrToLong_ui(p+1, &p, 16, &d->pci_dev.domain);
+            (void)virStrToLong_ui(p+1, &p, 16, &d->pci_dev.bus);
+            (void)virStrToLong_ui(p+1, &p, 16, &d->pci_dev.slot);
+            (void)virStrToLong_ui(p+1, &p, 16, &d->pci_dev.function);
+        }
+        VIR_FREE(sysfs_path);
+    }
+    (void)get_int_prop(ctx, udi, "pci.vendor_id", (int *)&d->pci_dev.vendor);
+    if (get_str_prop(ctx, udi, "pci.vendor", &d->pci_dev.vendor_name) != 0)
+        (void)get_str_prop(ctx, udi, "info.vendor", &d->pci_dev.vendor_name);
+    (void)get_int_prop(ctx, udi, "pci.product_id", (int *)&d->pci_dev.product);
+    if (get_str_prop(ctx, udi, "pci.product", &d->pci_dev.product_name) != 0)
+        (void)get_str_prop(ctx, udi, "info.product", &d->pci_dev.product_name);
+    return 0;
+}
+
+
+static int gather_usb_cap(LibHalContext *ctx, const char *udi,
+                          union _virNodeDevCapData *d)
+{
+    (void)get_int_prop(ctx, udi, "usb.interface.number",
+                       (int *)&d->usb_if.number);
+    (void)get_int_prop(ctx, udi, "usb.interface.class",
+                       (int *)&d->usb_if._class);
+    (void)get_int_prop(ctx, udi, "usb.interface.subclass",
+                       (int *)&d->usb_if.subclass);
+    (void)get_int_prop(ctx, udi, "usb.interface.protocol",
+                       (int *)&d->usb_if.protocol);
+    (void)get_str_prop(ctx, udi, "usb.interface.description",
+                       &d->usb_if.description);
+    return 0;
+}
+
+
+static int gather_usb_device_cap(LibHalContext *ctx, const char *udi,
+                          union _virNodeDevCapData *d)
+{
+    (void)get_int_prop(ctx, udi, "usb_device.bus_number",
+                       (int *)&d->usb_dev.bus);
+    (void)get_int_prop(ctx, udi, "usb_device.linux.device_number",
+                       (int *)&d->usb_dev.device);
+    (void)get_int_prop(ctx, udi, "usb_device.vendor_id",
+                       (int *)&d->usb_dev.vendor);
+    if (get_str_prop(ctx, udi, "usb_device.vendor",
+                     &d->usb_dev.vendor_name) != 0)
+        (void)get_str_prop(ctx, udi, "info.vendor", &d->usb_dev.vendor_name);
+    (void)get_int_prop(ctx, udi, "usb_device.product_id",
+                       (int *)&d->usb_dev.product);
+    if (get_str_prop(ctx, udi, "usb_device.product",
+                     &d->usb_dev.product_name) != 0)
+        (void)get_str_prop(ctx, udi, "info.product", &d->usb_dev.product_name);
+    return 0;
+}
+
+
+static int gather_net_cap(LibHalContext *ctx, const char *udi,
+                          union _virNodeDevCapData *d)
+{
+    (void)get_str_prop(ctx, udi, "net.interface", &d->net.interface);
+    (void)get_str_prop(ctx, udi, "net.address", &d->net.address);
+    if (get_uint64_prop(ctx, udi, "net.80203.mac_address",
+                        &d->net.data.ieee80203.mac_address) == 0)
+        d->net.subtype = VIR_NODE_DEV_CAP_NET_80203;
+    else if (get_uint64_prop(ctx, udi, "net.80211.mac_address",
+                        &d->net.data.ieee80211.mac_address) == 0)
+        d->net.subtype = VIR_NODE_DEV_CAP_NET_80211;
+    else
+        d->net.subtype = VIR_NODE_DEV_CAP_NET_LAST;
+
+    return 0;
+}
+
+
+static int gather_block_cap(LibHalContext *ctx, const char *udi,
+                            union _virNodeDevCapData *d)
+{
+    (void)get_str_prop(ctx, udi, "block.device", &d->block.device);
+    return 0;
+}
+
+
+static int gather_scsi_host_cap(LibHalContext *ctx, const char *udi,
+                                union _virNodeDevCapData *d)
+{
+    (void)get_int_prop(ctx, udi, "scsi_host.host", (int *)&d->scsi_host.host);
+    return 0;
+}
+
+
+static int gather_scsi_cap(LibHalContext *ctx, const char *udi,
+                           union _virNodeDevCapData *d)
+{
+    (void)get_int_prop(ctx, udi, "scsi.host", (int *)&d->scsi.host);
+    (void)get_int_prop(ctx, udi, "scsi.bus", (int *)&d->scsi.bus);
+    (void)get_int_prop(ctx, udi, "scsi.target", (int *)&d->scsi.target);
+    (void)get_int_prop(ctx, udi, "scsi.lun", (int *)&d->scsi.lun);
+    (void)get_str_prop(ctx, udi, "scsi.type", &d->scsi.type);
+    return 0;
+}
+
+
+static int gather_storage_cap(LibHalContext *ctx, const char *udi,
+                              union _virNodeDevCapData *d)
+{
+    int val;
+    (void)get_str_prop(ctx, udi, "storage.bus", &d->storage.bus);
+    (void)get_str_prop(ctx, udi, "storage.drive_type", &d->storage.drive_type);
+    (void)get_str_prop(ctx, udi, "storage.model", &d->storage.model);
+    (void)get_str_prop(ctx, udi, "storage.vendor", &d->storage.vendor);
+    if (get_bool_prop(ctx, udi, "storage.removable", &val) == 0 && val) {
+        d->storage.flags |= VIR_NODE_DEV_CAP_STORAGE_REMOVABLE;
+        if (get_bool_prop(ctx, udi,
+                          "storage.removable.media_available", &val) && val) {
+            d->storage.flags |=
+                VIR_NODE_DEV_CAP_STORAGE_REMOVABLE_MEDIA_AVAILABLE;
+            (void)get_uint64_prop(ctx, udi, "storage.removable.media_size",
+                                  &d->storage.removable_media_size);
+        }
+    } else {
+        (void)get_uint64_prop(ctx, udi, "storage.size", &d->storage.size);
+    }
+    if (get_bool_prop(ctx, udi, "storage.hotpluggable", &val) == 0 && val)
+        d->storage.flags |= VIR_NODE_DEV_CAP_STORAGE_HOTPLUGGABLE;
+    return 0;
+}
+
+
+static int gather_system_cap(LibHalContext *ctx, const char *udi,
+                             union _virNodeDevCapData *d)
+{
+    char *uuidstr;
+
+    (void)get_str_prop(ctx, udi, "system.product", &d->system.product_name);
+    (void)get_str_prop(ctx, udi, "system.hardware.vendor",
+                       &d->system.hardware.vendor_name);
+    (void)get_str_prop(ctx, udi, "system.hardware.version",
+                       &d->system.hardware.version);
+    (void)get_str_prop(ctx, udi, "system.hardware.serial",
+                       &d->system.hardware.serial);
+    if (get_str_prop(ctx, udi, "system.hardware.uuid", &uuidstr) == 0) {
+        (void)virUUIDParse(uuidstr, d->system.hardware.uuid);
+        VIR_FREE(uuidstr);
+    }
+    (void)get_str_prop(ctx, udi, "system.firmware.vendor",
+                       &d->system.firmware.vendor_name);
+    (void)get_str_prop(ctx, udi, "system.firmware.version",
+                       &d->system.firmware.version);
+    (void)get_str_prop(ctx, udi, "system.firmware.release_date",
+                       &d->system.firmware.release_date);
+    return 0;
+}
+
+
+struct _caps_tbl_entry {
+    const char *cap_name;
+    enum virNodeDevCapType type;
+    int (*gather_fn)(LibHalContext *ctx,
+                     const char *udi,
+                     union _virNodeDevCapData *data);
+};
+
+typedef struct _caps_tbl_entry caps_tbl_entry;
+
+static caps_tbl_entry caps_tbl[] = {
+    { "system",     VIR_NODE_DEV_CAP_SYSTEM,        gather_system_cap },
+    { "pci",        VIR_NODE_DEV_CAP_PCI_DEV,       gather_pci_cap },
+    { "usb",        VIR_NODE_DEV_CAP_USB_INTERFACE, gather_usb_cap },
+    { "usb_device", VIR_NODE_DEV_CAP_USB_DEV,       gather_usb_device_cap },
+    { "net",        VIR_NODE_DEV_CAP_NET,           gather_net_cap },
+    { "block",      VIR_NODE_DEV_CAP_BLOCK,         gather_block_cap },
+    { "scsi_host",  VIR_NODE_DEV_CAP_SCSI_HOST,     gather_scsi_host_cap },
+    { "scsi",       VIR_NODE_DEV_CAP_SCSI,          gather_scsi_cap },
+    { "storage",    VIR_NODE_DEV_CAP_STORAGE,       gather_storage_cap },
+};
+
+
+/* qsort/bsearch string comparator */
+static int cmpstringp(const void *p1, const void *p2)
+{
+    /* from man 3 qsort */
+    return strcmp(* (char * const *) p1, * (char * const *) p2);
+}
+
+
+static int gather_capability(LibHalContext *ctx, const char *udi,
+                             const char *cap_name,
+                             virNodeDevCapsDefPtr *caps_p)
+{
+    caps_tbl_entry *entry;
+
+    entry = bsearch(&cap_name, caps_tbl, ARRAY_CARDINALITY(caps_tbl),
+                    sizeof(caps_tbl[0]), cmpstringp);
+
+    if (entry) {
+        virNodeDevCapsDefPtr caps;
+        if (VIR_ALLOC(caps) < 0)
+            return ENOMEM;
+        caps->type = entry->type;
+        if (entry->gather_fn) {
+            int rv = (*entry->gather_fn)(ctx, udi, &caps->data);
+            if (rv != 0) {
+                virNodeDevCapsDefFree(caps);
+                return rv;
+            }
+        }
+        caps->next = *caps_p;
+        *caps_p = caps;
+    }
+
+    return 0;
+}
+
+
+static int gather_capabilities(LibHalContext *ctx, const char *udi,
+                               virNodeDevCapsDefPtr *caps_p)
+{
+    char *bus_name = NULL;
+    virNodeDevCapsDefPtr caps = NULL;
+    char **hal_cap_names;
+    int rv, i;
+
+    if (STREQ(udi, "/org/freedesktop/Hal/devices/computer")) {
+        rv = gather_capability(ctx, udi, "system", &caps);
+        if (rv != 0)
+            goto failure;
+    }
+
+    if (get_str_prop(ctx, udi, "info.subsystem", &bus_name) == 0) {
+        rv = gather_capability(ctx, udi, bus_name, &caps);
+        if (rv != 0)
+            goto failure;
+    }
+
+    hal_cap_names = libhal_device_get_property_strlist(ctx, udi,
+                                                       "info.capabilities",
+                                                       NULL);
+    if (hal_cap_names) {
+        for (i = 0; hal_cap_names[i]; i++) {
+            if (! (bus_name && STREQ(hal_cap_names[i], bus_name))) {
+                rv = gather_capability(ctx, udi, hal_cap_names[i], &caps);
+                if (rv != 0)
+                    goto failure;
+            }
+        }
+        for (i = 0; hal_cap_names[i]; i++)
+            VIR_FREE(hal_cap_names[i]);
+        VIR_FREE(hal_cap_names);
+    }
+    VIR_FREE(bus_name);
+
+    *caps_p = caps;
+    return 0;
+
+ failure:
+    VIR_FREE(bus_name);
+    if (hal_cap_names) {
+        for (i = 0; hal_cap_names[i]; i++)
+            VIR_FREE(hal_cap_names[i]);
+        VIR_FREE(hal_cap_names);
+    }
+    while (caps) {
+        virNodeDevCapsDefPtr next = caps->next;
+        virNodeDevCapsDefFree(caps);
+        caps = next;
+    }
+    return rv;
+}
+
+static void free_udi(void *udi)
+{
+    VIR_FREE(udi);
+}
+
+static void dev_create(char *udi)
+{
+    LibHalContext *ctx = DRV_STATE_HAL_CTX(driverState);
+    char *parent_key = NULL;
+    virNodeDeviceObjPtr dev;
+    const char *name = hal_name(udi);
+    int rv;
+
+    if (VIR_ALLOC(dev) < 0 || VIR_ALLOC(dev->def) < 0)
+        goto failure;
+
+    dev->privateData = udi;
+    dev->privateFree = free_udi;
+
+    if ((dev->def->name = strdup(name)) == NULL)
+        goto failure;
+
+    if (get_str_prop(ctx, udi, "info.parent", &parent_key) == 0) {
+        dev->def->parent = strdup(hal_name(parent_key));
+        VIR_FREE(parent_key);
+        if (dev->def->parent == NULL)
+            goto failure;
+    }
+
+    rv = gather_capabilities(ctx, udi, &dev->def->caps);
+    if (rv != 0) goto failure;
+
+    if (VIR_REALLOC_N(driverState->devs.objs, driverState->devs.count + 1) < 0)
+        goto failure;
+
+    driverState->devs.objs[driverState->devs.count++] = dev;
+
+    return;
+
+ failure:
+    DEBUG("FAILED TO ADD dev %s", name);
+    if (dev)
+        virNodeDeviceDefFree(dev->def);
+    VIR_FREE(dev);
+    VIR_FREE(udi);
+}
+
+
+static void device_added(LibHalContext *ctx ATTRIBUTE_UNUSED,
+                         const char *udi)
+{
+    DEBUG0(hal_name(udi));
+    dev_create(strdup(udi));
+}
+
+
+static void device_removed(LibHalContext *ctx ATTRIBUTE_UNUSED,
+                           const char *udi)
+{
+    const char *name = hal_name(udi);
+    virNodeDeviceObjPtr dev = virNodeDeviceFindByName(&driverState->devs,name);
+    DEBUG0(name);
+    if (dev)
+        virNodeDeviceObjRemove(&driverState->devs, dev);
+    else
+        DEBUG("no device named %s", name);
+}
+
+
+static void device_cap_added(LibHalContext *ctx,
+                             const char *udi, const char *cap)
+{
+    const char *name = hal_name(udi);
+    virNodeDeviceObjPtr dev = virNodeDeviceFindByName(&driverState->devs,name);
+    DEBUG("%s %s", cap, name);
+    if (dev)
+        (void)gather_capability(ctx, udi, cap, &dev->def->caps);
+    else
+        DEBUG("no device named %s", name);
+}
+
+
+static void device_cap_lost(LibHalContext *ctx ATTRIBUTE_UNUSED,
+                            const char *udi,
+                            const char *cap)
+{
+    const char *name = hal_name(udi);
+    virNodeDeviceObjPtr dev = virNodeDeviceFindByName(&driverState->devs,name);
+    DEBUG("%s %s", cap, name);
+    if (dev) {
+        /* Simply "rediscover" device -- incrementally handling changes
+         * to sub-capabilities (like net.80203) is nasty ... so avoid it.
+         */
+        virNodeDeviceObjRemove(&driverState->devs, dev);
+        dev_create(strdup(udi));
+    } else
+        DEBUG("no device named %s", name);
+}
+
+
+static void device_prop_modified(LibHalContext *ctx ATTRIBUTE_UNUSED,
+                                 const char *udi,
+                                 const char *key,
+                                 dbus_bool_t is_removed ATTRIBUTE_UNUSED,
+                                 dbus_bool_t is_added ATTRIBUTE_UNUSED)
+{
+    const char *name = hal_name(udi);
+    virNodeDeviceObjPtr dev = virNodeDeviceFindByName(&driverState->devs,name);
+    DEBUG("%s %s", key, name);
+    if (dev) {
+        /* Simply "rediscover" device -- incrementally handling changes
+         * to properties (which are mapped into caps in very capability-
+         * specific ways) is nasty ... so avoid it.
+         */
+        virNodeDeviceObjRemove(&driverState->devs, dev);
+        dev_create(strdup(udi));
+    } else
+        DEBUG("no device named %s", name);
+}
+
+
+static void dbus_watch_callback(int fdatch ATTRIBUTE_UNUSED,
+                                int fd ATTRIBUTE_UNUSED,
+                                int events, void *opaque)
+{
+    DBusWatch *watch = opaque;
+    LibHalContext *hal_ctx = DRV_STATE_HAL_CTX(driverState);
+    DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
+    int dbus_flags = 0;
+
+    if (events & VIR_EVENT_HANDLE_READABLE)
+        dbus_flags |= DBUS_WATCH_READABLE;
+    if (events & VIR_EVENT_HANDLE_WRITABLE)
+        dbus_flags |= DBUS_WATCH_WRITABLE;
+    if (events & VIR_EVENT_HANDLE_ERROR)
+        dbus_flags |= DBUS_WATCH_ERROR;
+    if (events & VIR_EVENT_HANDLE_HANGUP)
+        dbus_flags |= DBUS_WATCH_HANGUP;
+
+    (void)dbus_watch_handle(watch, dbus_flags);
+
+    while (dbus_connection_dispatch(dbus_conn) == DBUS_DISPATCH_DATA_REMAINS)
+        /* keep dispatching while data remains */;
+}
+
+
+static int xlate_dbus_watch_flags(int dbus_flags)
+{
+    unsigned int flags = 0;
+    if (dbus_flags & DBUS_WATCH_READABLE)
+        flags |= VIR_EVENT_HANDLE_READABLE;
+    if (dbus_flags & DBUS_WATCH_WRITABLE)
+        flags |= VIR_EVENT_HANDLE_WRITABLE;
+    if (dbus_flags & DBUS_WATCH_ERROR)
+        flags |= VIR_EVENT_HANDLE_ERROR;
+    if (dbus_flags & DBUS_WATCH_HANGUP)
+        flags |= VIR_EVENT_HANDLE_HANGUP;
+    return flags;
+}
+
+
+static dbus_bool_t add_dbus_watch(DBusWatch *watch,
+                                  void *data)
+{
+    int flags = 0;
+    virDeviceMonitorStatePtr state = data;
+
+    if (dbus_watch_get_enabled(watch))
+        flags = xlate_dbus_watch_flags(dbus_watch_get_flags(watch));
+
+    if ((state->dbusWatch =
+         virEventAddHandle(dbus_watch_get_unix_fd(watch), flags,
+                           dbus_watch_callback, watch, NULL)) < 0)
+        return 0;
+    return 1;
+}
+
+
+static void remove_dbus_watch(DBusWatch *watch ATTRIBUTE_UNUSED,
+                              void *data)
+{
+    virDeviceMonitorStatePtr state = data;
+
+    (void)virEventRemoveHandle(state->dbusWatch);
+}
+
+
+static void toggle_dbus_watch(DBusWatch *watch,
+                              void *data)
+{
+    int flags = 0;
+    virDeviceMonitorStatePtr state = data;
+
+    if (dbus_watch_get_enabled(watch))
+        flags = xlate_dbus_watch_flags(dbus_watch_get_flags(watch));
+
+    (void)virEventUpdateHandle(state->dbusWatch, flags);
+}
+
+
+static int halDeviceMonitorStartup(void)
+{
+    LibHalContext *hal_ctx = NULL;
+    DBusConnection *dbus_conn = NULL;
+    DBusError err;
+    char **udi = NULL;
+    int num_devs, i;
+
+    /* Ensure caps_tbl is sorted by capability name */
+    qsort(caps_tbl, ARRAY_CARDINALITY(caps_tbl), sizeof(caps_tbl[0]),
+          cmpstringp);
+
+    if (VIR_ALLOC(driverState) < 0)
+        return -1;
+
+    /* Allocate and initialize a new HAL context */
+    dbus_error_init(&err);
+    hal_ctx = libhal_ctx_new();
+    if (hal_ctx == NULL) {
+        fprintf(stderr, "%s: libhal_ctx_new returned NULL\n", __FUNCTION__);
+        goto failure;
+    }
+    dbus_conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
+    if (dbus_conn == NULL) {
+        fprintf(stderr, "%s: dbus_bus_get failed\n", __FUNCTION__);
+        goto failure;
+    }
+    if (!libhal_ctx_set_dbus_connection(hal_ctx, dbus_conn)) {
+        fprintf(stderr, "%s: libhal_ctx_set_dbus_connection failed\n",
+                __FUNCTION__);
+        goto failure;
+    }
+    if (!libhal_ctx_init(hal_ctx, &err)) {
+        fprintf(stderr, "%s: libhal_ctx_init failed\n", __FUNCTION__);
+        goto failure;
+    }
+
+    /* Register dbus watch callbacks */
+    if (!dbus_connection_set_watch_functions(dbus_conn,
+                                             add_dbus_watch,
+                                             remove_dbus_watch,
+                                             toggle_dbus_watch,
+                                             driverState, NULL)) {
+        fprintf(stderr, "%s: dbus_connection_set_watch_functions failed\n",
+                __FUNCTION__);
+        goto failure;
+    }
+
+    /* Register HAL event callbacks */
+    if (!libhal_ctx_set_device_added(hal_ctx, device_added) ||
+        !libhal_ctx_set_device_removed(hal_ctx, device_removed) ||
+        !libhal_ctx_set_device_new_capability(hal_ctx, device_cap_added) ||
+        !libhal_ctx_set_device_lost_capability(hal_ctx, device_cap_lost) ||
+        !libhal_ctx_set_device_property_modified(hal_ctx, device_prop_modified)) {
+        fprintf(stderr, "%s: setting up HAL callbacks failed\n", __FUNCTION__);
+        goto failure;
+    }
+
+    /* Populate with known devices */
+    driverState->privateData = hal_ctx;
+    udi = libhal_get_all_devices(hal_ctx, &num_devs, &err);
+    if (udi == NULL) {
+        fprintf(stderr, "%s: libhal_get_all_devices failed\n", __FUNCTION__);
+        goto failure;
+    }
+    for (i = 0; i < num_devs; i++)
+        dev_create(udi[i]);
+    free(udi);
+
+    return 0;
+
+ failure:
+    if (dbus_error_is_set(&err)) {
+        fprintf(stderr, "\t%s: %s\n", err.name, err.message);
+        dbus_error_free(&err);
+    }
+    virNodeDeviceObjListFree(&driverState->devs);
+    if (hal_ctx)
+        (void)libhal_ctx_free(hal_ctx);
+    if (udi) {
+        for (i = 0; i < num_devs; i++)
+            free(udi[i]);
+        free(udi);
+    }
+    VIR_FREE(driverState);
+
+    return -1;
+}
+
+
+static int halDeviceMonitorShutdown(void)
+{
+    if (driverState) {
+        LibHalContext *hal_ctx = DRV_STATE_HAL_CTX(driverState);
+        virNodeDeviceObjListFree(&driverState->devs);
+        (void)libhal_ctx_shutdown(hal_ctx, NULL);
+        (void)libhal_ctx_free(hal_ctx);
+        VIR_FREE(driverState);
+        return 0;
+    }
+    return -1;
+}
+
+
+static int halDeviceMonitorReload(void)
+{
+    (void)halDeviceMonitorShutdown();
+    return halDeviceMonitorStartup();
+}
+
+
+static int halDeviceMonitorActive(void)
+{
+    /* Always ready to deal with a shutdown */
+    return 0;
+}
+
+
+static virDrvOpenStatus halNodeDrvOpen(virConnectPtr conn,
+                                       virConnectAuthPtr auth ATTRIBUTE_UNUSED,
+                                       int flags ATTRIBUTE_UNUSED)
+{
+    if (driverState == NULL)
+        return VIR_DRV_OPEN_DECLINED;
+
+    conn->devMonPrivateData = driverState;
+
+    return VIR_DRV_OPEN_SUCCESS;
+}
+
+static int halNodeDrvClose(virConnectPtr conn ATTRIBUTE_UNUSED)
+{
+    conn->devMonPrivateData = NULL;
+    return 0;
+}
+
+
+static virDeviceMonitor halDeviceMonitor = {
+    .name = "halDeviceMonitor",
+    .open = halNodeDrvOpen,
+    .close = halNodeDrvClose,
+};
+
+
+static virStateDriver halStateDriver = {
+    .initialize = halDeviceMonitorStartup,
+    .cleanup = halDeviceMonitorShutdown,
+    .reload = halDeviceMonitorReload,
+    .active = halDeviceMonitorActive,
+};
+
+int halNodeRegister(void)
+{
+    registerCommonNodeFuncs(&halDeviceMonitor);
+    if (virRegisterDeviceMonitor(&halDeviceMonitor) < 0)
+        return -1;
+    return virRegisterStateDriver(&halStateDriver);
+}

-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|




More information about the libvir-list mailing list