[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

Re: [libvirt] [PATCH] 0/7 host ("node") device enumeration - completed submission



Daniel(s) -
  In the attached set of patches, I've merged in DanB's reworking of the
headers, etc. and adjusted my code accordingly.  I've also renamed
"NodeDriver" to "DeviceMonitor", as discussed with DanV, and added a
--cap option to the virsh node-list-devices command.

Dave


On Tue, 2008-11-04 at 14:30 +0100, Daniel Veillard wrote:
> On Mon, Oct 27, 2008 at 02:46:47PM -0400, David Lively wrote:
> > Since the version of this I posted last week still had some outstaning
> > TODO items that I have since finished, I'm re-submitting the whole
> > thing.  I consider this a "complete submission" since I don't think it's
> > necessary to implement any additional functionality to make this
> > acceptable.  (The two remaining unimplemented items are
> > virNodeDeviceCreate / Destroy and a real Devkit impl, neither of which
> > needs to be done immediately, for reasons discussed earlier.)
> > 
> > These patches implement the node device enumeration functionality, as
> > discussed here:
> > https://www.redhat.com/archives/libvir-list/2008-September/msg00398.html
> > 
> > I've broken it into the following pieces:
> >   1-public-api                  additions to the public API
> >   2-internal-api                additions to the internal API
> >   3-local-node-drivers          the HAL & DeviceKit implementations
> >   4-remote-node-driver          the remote driver
> >   5-virsh-support               virsh support
> >   6-python-bindings             python bindings
> >   7-java-bindings		Java bindings (for libvirt-java)
> 
>   Hi Dave,
> 
> Just in case you're wondering why there is no comment on those,
> I don't think there is a problem with the patches, my take is that we
> need to commit first the modularization fron Dan Berrange before pushing
> your patches due to HAL licencing.
> I intend to push them before the 0.5.0 release next week, so unless
> we get more comment on your patches, the current state will be used as
> the base.
> If you could just check that commit done in the meantime don't clash
> or change behaviour that would be nice,
> 
>   thanks in advance !
> 
> Daniel
> 
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index 52b7cdb..f7fdaac 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -995,6 +995,74 @@ virDomainPtr            virDomainCreateLinux    (virConnectPtr conn,
                                                  unsigned int flags);
 
 /*
+ * Host device enumeration
+ */
+
+/**
+ * virNodeDevice:
+ *
+ * A virNodeDevice contains a node (host) device details.
+ */
+
+typedef struct _virNodeDevice virNodeDevice;
+
+/**
+ * virNodeDevicePtr:
+ *
+ * A virNodeDevicePtr is a pointer to a virNodeDevice structure.  Get
+ * one via virNodeDeviceLookupByKey, virNodeDeviceLookupByName, or
+ * virNodeDeviceCreate.  Be sure to Call virNodeDeviceFree when done
+ * using a virNodeDevicePtr obtained from any of the above functions to
+ * avoid leaking memory.
+ */
+
+typedef virNodeDevice *virNodeDevicePtr;
+
+
+int                     virNodeNumOfDevices     (virConnectPtr conn,
+                                                 unsigned int flags);
+
+int                     virNodeListDevices      (virConnectPtr conn,
+                                                 char **const names,
+                                                 int maxnames,
+                                                 unsigned int flags);
+
+int                     virNodeNumOfDevicesByCap (virConnectPtr conn,
+                                                  const char *cap,
+                                                  unsigned int flags);
+
+int                     virNodeListDevicesByCap (virConnectPtr conn,
+                                                 const char *cap,
+                                                 char **const names,
+                                                 int maxnames,
+                                                 unsigned int flags);
+
+virNodeDevicePtr        virNodeDeviceLookupByName (virConnectPtr conn,
+                                                   const char *name);
+
+const char *            virNodeDeviceGetName     (virNodeDevicePtr dev);
+
+const char *            virNodeDeviceGetParent   (virNodeDevicePtr dev);
+
+int                     virNodeDeviceNumOfCaps   (virNodeDevicePtr dev);
+
+int                     virNodeDeviceListCaps    (virNodeDevicePtr dev,
+                                                  char **const names,
+                                                  int maxnames);
+
+char *                  virNodeDeviceGetXMLDesc (virNodeDevicePtr dev,
+                                                 unsigned int flags);
+
+virNodeDevicePtr        virNodeDeviceCreate     (virConnectPtr conn,
+                                                 const char *xml,
+                                                 unsigned int flags);
+
+int                     virNodeDeviceDestroy    (virNodeDevicePtr dev,
+                                                 unsigned int flags);
+
+int                     virNodeDeviceFree       (virNodeDevicePtr dev);
+
+/*
  * Domain Event Notification
  */
 
diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h
index 8e24708..057b047 100644
--- a/include/libvirt/virterror.h
+++ b/include/libvirt/virterror.h
@@ -58,6 +58,7 @@ typedef enum {
     VIR_FROM_STORAGE,   /* Error from storage driver */
     VIR_FROM_NETWORK,   /* Error from network config */
     VIR_FROM_DOMAIN,    /* Error from domain config */
+    VIR_FROM_DEVMONITOR,/* Error from node device monitor */
 } virErrorDomain;
 
 
@@ -148,6 +149,9 @@ typedef enum {
     VIR_WAR_NO_STORAGE, /* failed to start storage */
     VIR_ERR_NO_STORAGE_POOL, /* storage pool not found */
     VIR_ERR_NO_STORAGE_VOL, /* storage pool not found */
+    VIR_WAR_NO_NODE, /* failed to start node driver */
+    VIR_ERR_INVALID_NODE_DEVICE,/* invalid node device object */
+    VIR_ERR_NO_NODE_DEVICE,/* node device not found */
 } virErrorNumber;
 
 /**
diff --git a/src/libvirt.c b/src/libvirt.c
index f4dbe68..c0c9da0 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -39,6 +39,7 @@
 #include "uuid.h"
 #include "util.h"
 #include "memory.h"
+#include "node_device.h"
 
 #ifdef WITH_TEST
 #include "test.h"
@@ -77,6 +78,8 @@ static virNetworkDriverPtr virNetworkDriverTab[MAX_DRIVERS];
 static int virNetworkDriverTabCount = 0;
 static virStorageDriverPtr virStorageDriverTab[MAX_DRIVERS];
 static int virStorageDriverTabCount = 0;
+static virDeviceMonitorPtr virDeviceMonitorTab[MAX_DRIVERS];
+static int virDeviceMonitorTabCount = 0;
 #ifdef WITH_LIBVIRTD
 static virStateDriverPtr virStateDriverTab[MAX_DRIVERS];
 static int virStateDriverTabCount = 0;
@@ -301,6 +304,19 @@ virInitialize(void)
 #ifdef WITH_NETWORK
     if (networkRegister() == -1) return -1;
 #endif
+#if defined(HAVE_HAL) && defined(HAVE_DEVKIT)
+    /* Register only one of these two - they conflict */
+    if (halNodeRegister() == -1)
+        if (devkitNodeRegister() == -1)
+            return -1;
+#else
+#ifdef HAVE_HAL
+    if (halNodeRegister() == -1) return -1;
+#endif
+#ifdef HAVE_DEVKIT
+    if (devkitNodeRegister() == -1) return -1;
+#endif
+#endif
 #ifdef WITH_STORAGE_DIR
     if (storageRegister() == -1) return -1;
 #endif
@@ -460,6 +476,32 @@ virLibStorageVolError(virStorageVolPtr vol, virErrorNumber error,
 }
 
 /**
+ * virLibNodeDeviceError:
+ * @dev: the device if available
+ * @error: the error number
+ * @info: extra information string
+ *
+ * Handle an error at the node device level
+ */
+static void
+virLibNodeDeviceError(virNodeDevicePtr dev, virErrorNumber error,
+                      const char *info)
+{
+    virConnectPtr conn = NULL;
+    const char *errmsg;
+
+    if (error == VIR_ERR_OK)
+        return;
+
+    errmsg = virErrorMsg(error, info);
+    if (error != VIR_ERR_INVALID_NODE_DEVICE)
+        conn = dev->conn;
+
+    virRaiseError(conn, NULL, NULL, VIR_FROM_DEVMONITOR, error, VIR_ERR_ERROR,
+                    errmsg, info, NULL, 0, 0, errmsg, info);
+}
+
+/**
  * virRegisterNetworkDriver:
  * @driver: pointer to a network driver block
  *
@@ -516,6 +558,34 @@ virRegisterStorageDriver(virStorageDriverPtr driver)
 }
 
 /**
+ * virRegisterDeviceMonitor:
+ * @driver: pointer to a device monitor block
+ *
+ * Register a device monitor
+ *
+ * Returns the driver priority or -1 in case of error.
+ */
+int
+virRegisterDeviceMonitor(virDeviceMonitorPtr driver)
+{
+    if (virInitialize() < 0)
+      return -1;
+
+    if (driver == NULL) {
+        virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return(-1);
+    }
+
+    if (virDeviceMonitorTabCount >= MAX_DRIVERS) {
+        virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return(-1);
+    }
+
+    virDeviceMonitorTab[virDeviceMonitorTabCount] = driver;
+    return virDeviceMonitorTabCount++;
+}
+
+/**
  * virRegisterDriver:
  * @driver: pointer to a driver block
  *
@@ -839,6 +909,33 @@ do_open (const char *name,
         }
     }
 
+    /* Node driver (optional) */
+    for (i = 0; i < virDeviceMonitorTabCount; i++) {
+        res = virDeviceMonitorTab[i]->open (ret, uri, auth, flags);
+        DEBUG("node driver %d %s returned %s",
+              i, virDeviceMonitorTab[i]->name,
+              res == VIR_DRV_OPEN_SUCCESS ? "SUCCESS" :
+              (res == VIR_DRV_OPEN_DECLINED ? "DECLINED" :
+               (res == VIR_DRV_OPEN_ERROR ? "ERROR" : "unknown status")));
+        if (res == VIR_DRV_OPEN_ERROR) {
+            if (STREQ(virDeviceMonitorTab[i]->name, "remote")) {
+                virLibConnWarning (NULL, VIR_WAR_NO_NODE,
+                                   "Is the libvirtd daemon running ?");
+            } else {
+                char *msg;
+                if (asprintf(&msg, "Is the %s daemon running?",
+                             virDeviceMonitorTab[i]->name) > 0) {
+                    virLibConnWarning (NULL, VIR_WAR_NO_NODE, msg);
+                    VIR_FREE(msg);
+                }
+            }
+            break;
+        } else if (res == VIR_DRV_OPEN_SUCCESS) {
+            ret->deviceMonitor = virDeviceMonitorTab[i];
+            break;
+        }
+    }
+
     xmlFreeURI (uri);
 
     return ret;
@@ -959,6 +1056,8 @@ virConnectClose(virConnectPtr conn)
         conn->networkDriver->close (conn);
     if (conn->storageDriver)
         conn->storageDriver->close (conn);
+    if (conn->deviceMonitor)
+        conn->deviceMonitor->close (conn);
     conn->driver->close (conn);
 
     if (virUnrefConnect(conn) < 0)
@@ -5299,6 +5398,383 @@ virStorageVolGetPath(virStorageVolPtr vol)
 
 
 
+
+/**
+ * virNodeNumOfDevices:
+ * @conn: pointer to the hypervisor connection
+ * @flags: flags (unused, pass 0)
+ *
+ * Provides the number of node devices.
+ *
+ * Returns the number of node devices or -1 in case of error
+ */
+int
+virNodeNumOfDevices(virConnectPtr conn, unsigned int flags)
+{
+    DEBUG("conn=%p, flags=%d", conn, flags);
+
+    if (!VIR_IS_CONNECT(conn)) {
+        virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__);
+        return (-1);
+    }
+    if (flags != 0) {
+        virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return (-1);
+    }
+
+    if (conn->deviceMonitor && conn->deviceMonitor->numOfDevices)
+        return conn->deviceMonitor->numOfDevices (conn, flags);
+
+    virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -1;
+}
+
+
+/**
+ * virNodeListDevices:
+ * @conn: pointer to the hypervisor connection
+ * @names: array to collect the list of node device names
+ * @maxnames: size of @names
+ * @flags: flags (unused, pass 0)
+ *
+ * Collect the list of node devices, and store their names in @names
+ *
+ * Returns the number of node devices found or -1 in case of error
+ */
+int
+virNodeListDevices(virConnectPtr conn, char **const names, int maxnames,
+                   unsigned int flags)
+{
+    DEBUG("conn=%p, names=%p, maxnames=%d, flags=%d",
+          conn, names, maxnames, flags);
+
+    if (!VIR_IS_CONNECT(conn)) {
+        virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__);
+        return (-1);
+    }
+    if ((flags != 0) || (names == NULL) || (maxnames < 0)) {
+        virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return (-1);
+    }
+
+    if (conn->deviceMonitor && conn->deviceMonitor->listDevices)
+        return conn->deviceMonitor->listDevices (conn, names, maxnames, flags);
+
+    virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -1;
+}
+
+/**
+ * virNodeNumOfDevicesByCap:
+ * @conn: pointer to the hypervisor connection
+ * @cap: capability name
+ * @flags: flags (unused, pass 0)
+ *
+ * Provides the number of node devices with the given capability.
+ *
+ * Returns the number of node devices with the capability or -1 in case of error
+ */
+int
+virNodeNumOfDevicesByCap(virConnectPtr conn, const char *cap,
+                         unsigned int flags)
+{
+    DEBUG("conn=%p, cap=%p, flags=%d", conn, cap, flags);
+
+    if (!VIR_IS_CONNECT(conn)) {
+        virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__);
+        return (-1);
+    }
+
+    if ((flags != 0) || (cap == NULL)) {
+        virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return (-1);
+    }
+
+    if (conn->deviceMonitor && conn->deviceMonitor->numOfDevicesByCap)
+        return conn->deviceMonitor->numOfDevicesByCap (conn, cap, flags);
+
+    virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -1;
+}
+
+
+/**
+ * virNodeListDevicesByCap:
+ * @conn: pointer to the hypervisor connection
+ * @cap: capability name
+ * @names: array to collect the list of node device names
+ * @maxnames: size of @names
+ * @flags: flags (unused, pass 0)
+ *
+ * Collect the list of node devices with the given capability, and store
+ * their names in @names
+ *
+ * Returns the number of node devices found with the capability or -1 in case of error
+ */
+int
+virNodeListDevicesByCap(virConnectPtr conn, const char *cap,
+                        char **const names, int maxnames,
+                        unsigned int flags)
+{
+    DEBUG("conn=%p, cap=%p, names=%p, maxnames=%d, flags=%d",
+          conn, cap, names, maxnames, flags);
+
+    if (!VIR_IS_CONNECT(conn)) {
+        virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__);
+        return (-1);
+    }
+
+    if ((flags != 0) || (names == NULL) || (maxnames < 0) || (cap == NULL)) {
+        virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return (-1);
+    }
+
+    if (conn->deviceMonitor && conn->deviceMonitor->listDevicesByCap)
+        return conn->deviceMonitor->listDevicesByCap (conn, cap, names, maxnames, flags);
+
+    virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -1;
+}
+
+
+/**
+ * virNodeDeviceLookupByName:
+ * @conn: pointer to the hypervisor connection
+ * @name: unique device name
+ *
+ * Lookup a node device by its name.
+ *
+ * Returns a virNodeDevicePtr if found, NULL otherwise.
+ */
+virNodeDevicePtr virNodeDeviceLookupByName(virConnectPtr conn, const char *name)
+{
+    DEBUG("conn=%p, name=%p", conn, name);
+
+    if (!VIR_IS_CONNECT(conn)) {
+        virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__);
+        return NULL;
+    }
+
+    if (name == NULL) {
+        virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return NULL;
+    }
+
+    if (conn->deviceMonitor && conn->deviceMonitor->deviceLookupByName)
+        return conn->deviceMonitor->deviceLookupByName (conn, name);
+
+    virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return NULL;
+}
+
+
+/**
+ * virNodeDeviceCreate:
+ * @conn: pointer to the hypervisor connection
+ * @xml: XML description for new device
+ * @flags: flags (unused, pass 0)
+ *
+ * Creates a new node device, as described by the XML.
+ * [Need to describe what kinds of devices can be created this way]
+ *
+ * Returns a pointer to the new device, or NULL for error.
+ */
+virNodeDevicePtr virNodeDeviceCreate(virConnectPtr conn, const char *xml,
+                                     unsigned int flags)
+{
+    DEBUG("conn=%p, xml=%p, flags=%d", conn, xml, flags);
+
+    if (!VIR_IS_CONNECT(conn)) {
+        virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__);
+        return NULL;
+    }
+
+    if ((flags != 0) || (xml == NULL)) {
+        virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return NULL;
+    }
+
+    if (conn->deviceMonitor && conn->deviceMonitor->deviceCreate)
+        return conn->deviceMonitor->deviceCreate (conn, xml, flags);
+
+    virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return NULL;
+}
+
+/**
+ * virNodeDeviceGetXMLDesc:
+ * @dev: pointer to the node device
+ * @flags: flags for XML generation (unused, pass 0)
+ *
+ * Fetch an XML document describing all aspects of
+ * the device.
+ *
+ * Return the XML document, or NULL on error
+ */
+char *virNodeDeviceGetXMLDesc(virNodeDevicePtr dev, unsigned int flags)
+{
+    DEBUG("dev=%p, conn=%p", dev, dev ? dev->conn : NULL);
+
+    if (!VIR_IS_CONNECTED_NODE_DEVICE(dev)) {
+        virLibNodeDeviceError(NULL, VIR_ERR_INVALID_NODE_DEVICE, __FUNCTION__);
+        return NULL;
+    }
+
+    if (dev->conn->deviceMonitor && dev->conn->deviceMonitor->deviceDumpXML)
+        return dev->conn->deviceMonitor->deviceDumpXML (dev, flags);
+
+    virLibConnError (dev->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return NULL;
+}
+
+
+/**
+ * virNodeDeviceDestroy:
+ * @dev: pointer to the created device
+ * @flags: flags (unused, pass 0)
+ *
+ * Removes a device created by virNodeDeviceCreate.
+ *
+ * Returns 0 for success, <0 for failure.
+ */
+int virNodeDeviceDestroy(virNodeDevicePtr dev, unsigned int flags)
+{
+    DEBUG("dev=%p, conn=%p, flags=%d", dev, dev ? dev->conn : NULL, flags);
+
+    if (!VIR_IS_CONNECTED_NODE_DEVICE(dev)) {
+        virLibNodeDeviceError(NULL, VIR_ERR_INVALID_NODE_DEVICE, __FUNCTION__);
+        return (-1);
+    }
+
+    if (flags != 0) {
+        virLibConnError(dev->conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return -1;
+    }
+
+    if (dev->conn->deviceMonitor && dev->conn->deviceMonitor->deviceDestroy)
+        return dev->conn->deviceMonitor->deviceDestroy (dev, flags);
+
+    virLibConnError (dev->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -1;
+}
+
+
+/**
+ * virNodeDeviceGetName:
+ * @dev: the device
+ *
+ * Returns the device name.
+ */
+const char *virNodeDeviceGetName(virNodeDevicePtr dev)
+{
+    DEBUG("dev=%p, conn=%p", dev, dev ? dev->conn : NULL);
+
+    if (!VIR_IS_CONNECTED_NODE_DEVICE(dev)) {
+        virLibNodeDeviceError(NULL, VIR_ERR_INVALID_NODE_DEVICE, __FUNCTION__);
+        return NULL;
+    }
+
+    return dev->name;
+}
+
+/**
+ * virNodeDeviceGetParent:
+ * @dev: the device
+ *
+ * Returns the name of the device's parent, or NULL if the
+ * device has no parent.
+ */
+const char *virNodeDeviceGetParent(virNodeDevicePtr dev)
+{
+    DEBUG("dev=%p, conn=%p", dev, dev ? dev->conn : NULL);
+
+    if (!VIR_IS_CONNECTED_NODE_DEVICE(dev)) {
+        virLibNodeDeviceError(NULL, VIR_ERR_INVALID_NODE_DEVICE, __FUNCTION__);
+        return NULL;
+    }
+
+    if (dev->conn->deviceMonitor && dev->conn->deviceMonitor->deviceGetParent)
+        return dev->conn->deviceMonitor->deviceGetParent (dev);
+
+    virLibConnError (dev->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return NULL;
+}
+
+/**
+ * virNodeDeviceNumOfCaps:
+ * @dev: the device
+ *
+ * Returns the number of capabilities supported by the device.
+ */
+int virNodeDeviceNumOfCaps(virNodeDevicePtr dev)
+{
+    DEBUG("dev=%p, conn=%p", dev, dev ? dev->conn : NULL);
+
+    if (!VIR_IS_CONNECTED_NODE_DEVICE(dev)) {
+        virLibNodeDeviceError(NULL, VIR_ERR_INVALID_NODE_DEVICE, __FUNCTION__);
+        return -1;
+    }
+
+    if (dev->conn->deviceMonitor && dev->conn->deviceMonitor->deviceNumOfCaps)
+        return dev->conn->deviceMonitor->deviceNumOfCaps (dev);
+
+    virLibConnError (dev->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -1;
+}
+
+/**
+ * virNodeDeviceListCaps:
+ * @dev: the device
+ * @names: array to collect the list of capability names
+ * @maxnames: size of @names
+ *
+ * Lists the names of the capabilities supported by the device.
+ *
+ * Returns the number of capability names listed in @names.
+ */
+int virNodeDeviceListCaps(virNodeDevicePtr dev,
+                          char **const names,
+                          int maxnames)
+{
+    DEBUG("dev=%p, conn=%p, names=%p, maxnames=%d",
+          dev, dev ? dev->conn : NULL, names, maxnames);
+
+    if (!VIR_IS_CONNECTED_NODE_DEVICE(dev)) {
+        virLibNodeDeviceError(NULL, VIR_ERR_INVALID_NODE_DEVICE, __FUNCTION__);
+        return -1;
+    }
+
+    if (dev->conn->deviceMonitor && dev->conn->deviceMonitor->deviceListCaps)
+        return dev->conn->deviceMonitor->deviceListCaps (dev, names, maxnames);
+
+    virLibConnError (dev->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -1;
+}
+
+
+/**
+ * virNodeDeviceFree:
+ * @dev: pointer to the node device
+ *
+ * Drops a reference to the node device, freeing it if
+ * this was the last reference.
+ *
+ * Returns the 0 for success, -1 for error.
+ */
+int virNodeDeviceFree(virNodeDevicePtr dev)
+{
+    DEBUG("dev=%p, conn=%p", dev, dev ? dev->conn : NULL);
+
+    if (!VIR_IS_CONNECTED_NODE_DEVICE(dev)) {
+        virLibNodeDeviceError(NULL, VIR_ERR_INVALID_NODE_DEVICE, __FUNCTION__);
+        return (-1);
+    }
+    if (virUnrefNodeDevice(dev) < 0)
+        return (-1);
+    return(0);
+}
+
+
 /*
  * Domain Event Notification
  */
diff --git a/src/libvirt_sym.version b/src/libvirt_sym.version
index 7835837..c4a3d5d 100644
--- a/src/libvirt_sym.version
+++ b/src/libvirt_sym.version
@@ -147,6 +147,20 @@
 	virStorageVolGetXMLDesc;
 	virStorageVolGetPath;
 
+        virNodeNumOfDevices;
+        virNodeListDevices;
+        virNodeNumOfDevicesByCap;
+        virNodeListDevicesByCap;
+        virNodeDeviceLookupByName;
+        virNodeDeviceFree;
+        virNodeDeviceGetXMLDesc;
+        virNodeDeviceCreate;
+        virNodeDeviceDestroy;
+	virNodeDeviceGetName;
+	virNodeDeviceGetParent;
+	virNodeDeviceNumOfCaps;
+	virNodeDeviceListCaps;
+
         virEventRegisterImpl;
         virConnectDomainEventRegister;
         virConnectDomainEventDeregister;
@@ -170,7 +184,7 @@
 	__virGetNetwork;
 	__virGetStoragePool;
 	__virGetStorageVol;
-
+	__virGetNodeDevice;
 	__virStateInitialize;
 	__virStateCleanup;
 	__virStateReload;
diff --git a/src/virterror.c b/src/virterror.c
index 15eb0a1..538841e 100644
--- a/src/virterror.c
+++ b/src/virterror.c
@@ -310,6 +310,9 @@ virDefaultErrorFunc(virErrorPtr err)
         case VIR_FROM_DOMAIN:
             dom = "Domain Config ";
             break;
+        case VIR_FROM_DEVMONITOR:
+            dom = "Device Monitor ";
+            break;
 
     }
     if ((err->dom != NULL) && (err->code != VIR_ERR_INVALID_DOMAIN)) {
@@ -719,6 +722,24 @@ virErrorMsg(virErrorNumber error, const char *info)
                 else
                         errmsg = _("Failed to find a storage driver: %s");
                 break;
+        case VIR_WAR_NO_NODE:
+                if (info == NULL)
+                        errmsg = _("Failed to find a node driver");
+                else
+                        errmsg = _("Failed to find a node driver: %s");
+                break;
+        case VIR_ERR_INVALID_NODE_DEVICE:
+                if (info == NULL)
+                        errmsg = _("invalid node device pointer");
+                else
+                        errmsg = _("invalid node device pointer in %s");
+                break;
+        case VIR_ERR_NO_NODE_DEVICE:
+                if (info == NULL)
+                        errmsg = _("Node device not found");
+                else
+                        errmsg = _("Node device not found: %s");
+                break;
     }
     return (errmsg);
 }
diff --git a/src/datatypes.c b/src/datatypes.c
index d351721..6289fec 100644
--- a/src/datatypes.c
+++ b/src/datatypes.c
@@ -139,6 +139,9 @@ virGetConnect(void) {
     ret->storageVols = virHashCreate(20);
     if (ret->storageVols == NULL)
         goto failed;
+    ret->nodeDevices = virHashCreate(256);
+    if (ret->nodeDevices == NULL)
+        goto failed;
 
     pthread_mutex_init(&ret->lock, NULL);
 
@@ -155,6 +158,8 @@ failed:
             virHashFree(ret->storagePools, (virHashDeallocator) virStoragePoolFreeName);
         if (ret->storageVols != NULL)
             virHashFree(ret->storageVols, (virHashDeallocator) virStorageVolFreeName);
+        if (ret->nodeDevices != NULL)
+            virHashFree(ret->nodeDevices, (virHashDeallocator) virNodeDeviceFree);
 
         pthread_mutex_destroy(&ret->lock);
         VIR_FREE(ret);
@@ -182,6 +187,8 @@ virReleaseConnect(virConnectPtr conn) {
         virHashFree(conn->storagePools, (virHashDeallocator) virStoragePoolFreeName);
     if (conn->storageVols != NULL)
         virHashFree(conn->storageVols, (virHashDeallocator) virStorageVolFreeName);
+    if (conn->nodeDevices != NULL)
+        virHashFree(conn->nodeDevices, (virHashDeallocator) virNodeDeviceFree);
 
     virResetError(&conn->err);
     if (virLastErr.conn == conn)
@@ -770,3 +777,126 @@ virUnrefStorageVol(virStorageVolPtr vol) {
     pthread_mutex_unlock(&vol->conn->lock);
     return (refs);
 }
+
+
+/**
+ * virGetNodeDevice:
+ * @conn: the hypervisor connection
+ * @name: device name (unique on node)
+ *
+ * Lookup if the device is already registered for that connection,
+ * if yes return a new pointer to it, if no allocate a new structure,
+ * and register it in the table. In any case a corresponding call to
+ * virFreeNodeDevice() is needed to not leak data.
+ *
+ * Returns a pointer to the node device, or NULL in case of failure
+ */
+virNodeDevicePtr
+__virGetNodeDevice(virConnectPtr conn, const char *name)
+{
+    virNodeDevicePtr ret = NULL;
+
+    if ((!VIR_IS_CONNECT(conn)) || (name == NULL)) {
+        virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return(NULL);
+    }
+    pthread_mutex_lock(&conn->lock);
+
+    ret = (virNodeDevicePtr) virHashLookup(conn->nodeDevices, name);
+    if (ret == NULL) {
+       if (VIR_ALLOC(ret) < 0) {
+            virLibConnError(conn, VIR_ERR_NO_MEMORY, _("allocating node dev"));
+            goto error;
+        }
+        ret->magic = VIR_NODE_DEVICE_MAGIC;
+        ret->conn = conn;
+        ret->name = strdup(name);
+        if (ret->name == NULL) {
+            virLibConnError(conn, VIR_ERR_NO_MEMORY, _("copying node dev name"));
+            goto error;
+        }
+
+        if (virHashAddEntry(conn->nodeDevices, name, ret) < 0) {
+            virLibConnError(conn, VIR_ERR_INTERNAL_ERROR,
+                            _("failed to add node dev to conn hash table"));
+            goto error;
+        }
+        conn->refs++;
+    }
+    ret->refs++;
+    pthread_mutex_unlock(&conn->lock);
+    return(ret);
+
+error:
+    pthread_mutex_unlock(&conn->lock);
+    if (ret != NULL) {
+        VIR_FREE(ret->name);
+        VIR_FREE(ret);
+    }
+    return(NULL);
+}
+
+
+/**
+ * virReleaseNodeDevice:
+ * @dev: the dev to release
+ *
+ * Unconditionally release all memory associated with a dev.
+ * The conn.lock mutex must be held prior to calling this, and will
+ * be released prior to this returning. The dev obj must not
+ * be used once this method returns.
+ *
+ * It will also unreference the associated connection object,
+ * which may also be released if its ref count hits zero.
+ */
+static void
+virReleaseNodeDevice(virNodeDevicePtr dev) {
+    virConnectPtr conn = dev->conn;
+    DEBUG("release dev %p %s", dev, dev->name);
+
+    if (virHashRemoveEntry(conn->nodeDevices, dev->name, NULL) < 0)
+        virLibConnError(conn, VIR_ERR_INTERNAL_ERROR,
+                        _("dev missing from connection hash table"));
+
+    dev->magic = -1;
+    VIR_FREE(dev->name);
+    VIR_FREE(dev);
+
+    DEBUG("unref connection %p %s %d", conn, conn->name, conn->refs);
+    conn->refs--;
+    if (conn->refs == 0) {
+        virReleaseConnect(conn);
+        /* Already unlocked mutex */
+        return;
+    }
+
+    pthread_mutex_unlock(&conn->lock);
+}
+
+
+/**
+ * virUnrefNodeDevice:
+ * @dev: the dev to unreference
+ *
+ * Unreference the dev. If the use count drops to zero, the structure is
+ * actually freed.
+ *
+ * Returns the reference count or -1 in case of failure.
+ */
+int
+virUnrefNodeDevice(virNodeDevicePtr dev) {
+    int refs;
+
+    pthread_mutex_lock(&dev->conn->lock);
+    DEBUG("unref dev %p %s %d", dev, dev->name, dev->refs);
+    dev->refs--;
+    refs = dev->refs;
+    if (refs == 0) {
+        virReleaseNodeDevice(dev);
+        /* Already unlocked mutex */
+        return (0);
+    }
+
+    pthread_mutex_unlock(&dev->conn->lock);
+    return (refs);
+}
diff --git a/src/datatypes.h b/src/datatypes.h
index 7b5b8cb..268db2a 100644
--- a/src/datatypes.h
+++ b/src/datatypes.h
@@ -78,6 +78,16 @@
 #define VIR_IS_STORAGE_VOL(obj)		((obj) && (obj)->magic==VIR_STORAGE_VOL_MAGIC)
 #define VIR_IS_CONNECTED_STORAGE_VOL(obj)	(VIR_IS_STORAGE_VOL(obj) && VIR_IS_CONNECT((obj)->conn))
 
+/**
+ * VIR_NODE_DEVICE_MAGIC:
+ *
+ * magic value used to protect the API when pointers to storage vol structures
+ * are passed down by the users.
+ */
+#define VIR_NODE_DEVICE_MAGIC                   0xDEAD5679
+#define VIR_IS_NODE_DEVICE(obj)                 ((obj) && (obj)->magic==VIR_NODE_DEVICE_MAGIC)
+#define VIR_IS_CONNECTED_NODE_DEVICE(obj)       (VIR_IS_NODE_DEVICE(obj) && VIR_IS_CONNECT((obj)->conn))
+
 
 /**
  * _virConnect:
@@ -93,6 +103,7 @@ struct _virConnect {
     virDriverPtr      driver;
     virNetworkDriverPtr networkDriver;
     virStorageDriverPtr storageDriver;
+    virDeviceMonitorPtr  deviceMonitor;
 
     /* Private data pointer which can be used by driver and
      * network driver as they wish.
@@ -101,6 +112,7 @@ struct _virConnect {
     void *            privateData;
     void *            networkPrivateData;
     void *            storagePrivateData;
+    void *            devMonPrivateData;
 
     /* Per-connection error. */
     virError err;           /* the last error */
@@ -118,6 +130,7 @@ struct _virConnect {
     virHashTablePtr networks; /* hash table for known domains */
     virHashTablePtr storagePools;/* hash table for known storage pools */
     virHashTablePtr storageVols;/* hash table for known storage vols */
+    virHashTablePtr nodeDevices; /* hash table for known node devices */
     int refs;                 /* reference count */
 };
 
@@ -176,6 +189,19 @@ struct _virStorageVol {
     char key[PATH_MAX];                  /* unique key for storage vol */
 };
 
+/**
+ * _virNodeDevice:
+ *
+ * Internal structure associated with a node device
+ */
+struct _virNodeDevice {
+    unsigned int magic;                 /* specific value to check */
+    int refs;                           /* reference count */
+    virConnectPtr conn;                 /* pointer back to the connection */
+    char *name;                         /* device name (unique on node) */
+};
+
+
 /************************************************************************
  *									*
  *	API for domain/connections (de)allocations and lookups		*
@@ -202,11 +228,15 @@ virStorageVolPtr __virGetStorageVol(virConnectPtr conn,
                                     const char *name,
                                     const char *key);
 int virUnrefStorageVol(virStorageVolPtr vol);
+virNodeDevicePtr  __virGetNodeDevice  (virConnectPtr conn,
+                                       const char *name);
+int               virUnrefNodeDevice  (virNodeDevicePtr dev);
 
 #define virGetDomain(c,n,u) __virGetDomain((c),(n),(u))
 #define virGetNetwork(c,n,u) __virGetNetwork((c),(n),(u))
 #define virGetStoragePool(c,n,u) __virGetStoragePool((c),(n),(u))
 #define virGetStorageVol(c,p,n,u) __virGetStorageVol((c),(p),(n),(u))
+#define virGetNodeDevice(c,n) __virGetNodeDevice((c),(n))
 
 
 #endif
diff --git a/src/node_device_conf.c b/src/node_device_conf.c
new file mode 100644
index 0000000..d47fdfb
--- /dev/null
+++ b/src/node_device_conf.c
@@ -0,0 +1,425 @@
+/*
+ * node_device_conf.c: config handling for node devices
+ *
+ * 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 virtualiron com>
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <errno.h>
+
+#include "virterror_internal.h"
+#include "memory.h"
+
+#include "node_device_conf.h"
+#include "memory.h"
+#include "xml.h"
+#include "util.h"
+#include "buf.h"
+#include "uuid.h"
+
+
+VIR_ENUM_IMPL(virNodeDevCap, VIR_NODE_DEV_CAP_LAST,
+              "system",
+              "pci",
+              "usb_device",
+              "usb",
+              "net",
+              "block",
+              "scsi_host",
+              "scsi",
+              "storage");
+
+VIR_ENUM_IMPL(virNodeDevNetCap, VIR_NODE_DEV_CAP_NET_LAST,
+              "80203",
+              "80211");
+
+
+#define virNodeDeviceLog(msg...) fprintf(stderr, msg)
+
+virNodeDeviceObjPtr virNodeDeviceFindByName(const virNodeDeviceObjListPtr devs,
+                                            const char *name)
+{
+    unsigned int i;
+
+    for (i = 0; i < devs->count; i++)
+        if (STREQ(devs->objs[i]->def->name, name))
+            return devs->objs[i];
+
+    return NULL;
+}
+
+
+void virNodeDeviceDefFree(virNodeDeviceDefPtr def)
+{
+    virNodeDevCapsDefPtr caps;
+
+    if (!def)
+        return;
+
+    VIR_FREE(def->name);
+    VIR_FREE(def->parent);
+
+    caps = def->caps;
+    while (caps) {
+        virNodeDevCapsDefPtr next = caps->next;
+        virNodeDevCapsDefFree(caps);
+        caps = next;
+    }
+
+    VIR_FREE(def);
+}
+
+void virNodeDeviceObjFree(virNodeDeviceObjPtr dev)
+{
+    if (!dev)
+        return;
+
+    virNodeDeviceDefFree(dev->def);
+    if (dev->privateFree)
+        (*dev->privateFree)(dev->privateData);
+
+    VIR_FREE(dev);
+}
+
+void virNodeDeviceObjListFree(virNodeDeviceObjListPtr devs)
+{
+    unsigned int i;
+    for (i = 0 ; i < devs->count ; i++)
+        virNodeDeviceObjFree(devs->objs[i]);
+    VIR_FREE(devs->objs);
+    devs->count = 0;
+}
+
+virNodeDeviceObjPtr virNodeDeviceAssignDef(virConnectPtr conn,
+                                           virNodeDeviceObjListPtr devs,
+                                           const virNodeDeviceDefPtr def)
+{
+    virNodeDeviceObjPtr device;
+
+    if ((device = virNodeDeviceFindByName(devs, def->name))) {
+        virNodeDeviceDefFree(device->def);
+        device->def = def;
+        return device;
+    }
+
+    if (VIR_ALLOC(device) < 0) {
+        virNodeDeviceReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+        return NULL;
+    }
+
+    device->def = def;
+
+    if (VIR_REALLOC_N(devs->objs, devs->count+1) < 0) {
+        device->def = NULL;
+        virNodeDeviceObjFree(device);
+        virNodeDeviceReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+        return NULL;
+    }
+    devs->objs[devs->count++] = device;
+
+    return device;
+
+}
+
+void virNodeDeviceObjRemove(virNodeDeviceObjListPtr devs,
+                            const virNodeDeviceObjPtr dev)
+{
+    unsigned int i;
+
+    for (i = 0; i < devs->count; i++) {
+        if (devs->objs[i] == dev) {
+            virNodeDeviceObjFree(devs->objs[i]);
+
+            if (i < (devs->count - 1))
+                memmove(devs->objs + i, devs->objs + i + 1,
+                        sizeof(*(devs->objs)) * (devs->count - (i + 1)));
+
+            if (VIR_REALLOC_N(devs->objs, devs->count - 1) < 0) {
+                ; /* Failure to reduce memory allocation isn't fatal */
+            }
+            devs->count--;
+
+            break;
+        }
+    }
+}
+
+char *virNodeDeviceDefFormat(virConnectPtr conn,
+                             const virNodeDeviceDefPtr def)
+{
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    virNodeDevCapsDefPtr caps = def->caps;
+    char *tmp;
+
+    virBufferAddLit(&buf, "<device>\n");
+    virBufferEscapeString(&buf, "  <name>%s</name>\n", def->name);
+
+    if (def->parent)
+        virBufferEscapeString(&buf, "  <parent>%s</parent>\n", def->parent);
+
+    for (caps = def->caps; caps; caps = caps->next) {
+        char uuidstr[VIR_UUID_STRING_BUFLEN];
+        union _virNodeDevCapData *data = &caps->data;
+
+        virBufferVSprintf(&buf, "  <capability type='%s'>\n",
+                          virNodeDevCapTypeToString(caps->type));
+        switch (caps->type) {
+        case VIR_NODE_DEV_CAP_SYSTEM:
+            if (data->system.product_name)
+                virBufferEscapeString(&buf, "    <product>%s</product>\n",
+                                      data->system.product_name);
+            virBufferAddLit(&buf, "    <hardware>\n");
+            if (data->system.hardware.vendor_name)
+                virBufferEscapeString(&buf, "      <vendor>%s</vendor>\n",
+                                      data->system.hardware.vendor_name);
+            if (data->system.hardware.version)
+                virBufferEscapeString(&buf, "      <version>%s</version>\n",
+                                      data->system.hardware.version);
+            if (data->system.hardware.serial)
+                virBufferEscapeString(&buf, "      <serial>%s</serial>\n",
+                                      data->system.hardware.serial);
+            virUUIDFormat(data->system.hardware.uuid, uuidstr);
+            virBufferVSprintf(&buf, "      <uuid>%s</uuid>\n", uuidstr);
+            virBufferAddLit(&buf, "    </hardware>\n");
+            virBufferAddLit(&buf, "    <firmware>\n");
+            if (data->system.firmware.vendor_name)
+                virBufferEscapeString(&buf, "      <vendor>%s</vendor>\n",
+                                      data->system.firmware.vendor_name);
+            if (data->system.firmware.version)
+                virBufferEscapeString(&buf, "      <version>%s</version>\n",
+                                      data->system.firmware.version);
+            if (data->system.firmware.release_date)
+                virBufferEscapeString(&buf,
+                                      "      <release_date>%s</release_date>\n",
+                                      data->system.firmware.release_date);
+            virBufferAddLit(&buf, "    </firmware>\n");
+            break;
+        case VIR_NODE_DEV_CAP_PCI_DEV:
+            virBufferVSprintf(&buf, "    <domain>%d</domain>\n",
+                              data->pci_dev.domain);
+            virBufferVSprintf(&buf, "    <bus>%d</bus>\n", data->pci_dev.bus);
+            virBufferVSprintf(&buf, "    <slot>%d</slot>\n",
+                              data->pci_dev.slot);
+            virBufferVSprintf(&buf, "    <function>%d</function>\n",
+                              data->pci_dev.function);
+            virBufferVSprintf(&buf, "    <product id='%d'",
+                                  data->pci_dev.product);
+            if (data->pci_dev.product_name)
+                virBufferEscapeString(&buf, ">%s</product>\n",
+                                      data->pci_dev.product_name);
+            else
+                virBufferAddLit(&buf, " />\n");
+            virBufferVSprintf(&buf, "    <vendor id='%d'",
+                                  data->pci_dev.vendor);
+            if (data->pci_dev.vendor_name)
+                virBufferEscapeString(&buf, ">%s</vendor>\n",
+                                      data->pci_dev.vendor_name);
+            else
+                virBufferAddLit(&buf, " />\n");
+            break;
+        case VIR_NODE_DEV_CAP_USB_DEV:
+            virBufferVSprintf(&buf, "    <bus>%d</bus>\n", data->usb_dev.bus);
+            virBufferVSprintf(&buf, "    <device>%d</device>\n",
+                              data->usb_dev.device);
+            virBufferVSprintf(&buf, "    <product id='%d'",
+                                  data->usb_dev.product);
+            if (data->usb_dev.product_name)
+                virBufferEscapeString(&buf, ">%s</product>\n",
+                                      data->usb_dev.product_name);
+            else
+                virBufferAddLit(&buf, " />\n");
+            virBufferVSprintf(&buf, "    <vendor id='%d'",
+                                  data->usb_dev.vendor);
+            if (data->usb_dev.vendor_name)
+                virBufferEscapeString(&buf, ">%s</vendor>\n",
+                                      data->usb_dev.vendor_name);
+            else
+                virBufferAddLit(&buf, " />\n");
+            break;
+        case VIR_NODE_DEV_CAP_USB_INTERFACE:
+            virBufferVSprintf(&buf, "    <number>%d</number>\n",
+                              data->usb_if.number);
+            virBufferVSprintf(&buf, "    <class>%d</class>\n",
+                              data->usb_if._class);
+            virBufferVSprintf(&buf, "    <subclass>%d</subclass>\n",
+                              data->usb_if.subclass);
+            virBufferVSprintf(&buf, "    <protocol>%d</protocol>\n",
+                              data->usb_if.protocol);
+            if (data->usb_if.description)
+                virBufferVSprintf(&buf, "    <description>%s</description>\n",
+                                  data->usb_if.description);
+            break;
+        case VIR_NODE_DEV_CAP_NET:
+            virBufferVSprintf(&buf, "    <interface>%s</interface>\n",
+                              data->net.interface);
+            if (data->net.address)
+                virBufferVSprintf(&buf, "    <address>%s</address>\n",
+                                  data->net.address);
+            if (data->net.subtype != VIR_NODE_DEV_CAP_NET_LAST) {
+                const char *subtyp =
+                    virNodeDevNetCapTypeToString(data->net.subtype);
+                virBufferVSprintf(&buf, "    <capability type='%s'>\n", subtyp);
+                switch (data->net.subtype) {
+                case VIR_NODE_DEV_CAP_NET_80203:
+                    virBufferVSprintf(&buf,
+                                      "      <mac_address>%012llx</address>\n",
+                                      data->net.data.ieee80203.mac_address);
+                    break;
+                case VIR_NODE_DEV_CAP_NET_80211:
+                    virBufferVSprintf(&buf,
+                                      "      <mac_address>%012llx</address>\n",
+                                      data->net.data.ieee80211.mac_address);
+                    break;
+                case VIR_NODE_DEV_CAP_NET_LAST:
+                    /* Keep dumb compiler happy */
+                    break;
+                }
+                virBufferAddLit(&buf, "    </capability>\n");
+            }
+            break;
+        case VIR_NODE_DEV_CAP_BLOCK:
+            virBufferVSprintf(&buf, "    <device>%s</device>\n",
+                              data->block.device);
+            break;
+        case VIR_NODE_DEV_CAP_SCSI_HOST:
+            virBufferVSprintf(&buf, "    <host>%d</host>\n",
+                              data->scsi_host.host);
+            break;
+        case VIR_NODE_DEV_CAP_SCSI:
+            virBufferVSprintf(&buf, "    <host>%d</host>\n", data->scsi.host);
+            virBufferVSprintf(&buf, "    <bus>%d</bus>\n", data->scsi.bus);
+            virBufferVSprintf(&buf, "    <target>%d</target>\n",
+                              data->scsi.target);
+            virBufferVSprintf(&buf, "    <lun>%d</lun>\n", data->scsi.lun);
+            if (data->scsi.type)
+                virBufferVSprintf(&buf, "    <type>%s</type>\n",
+                                  data->scsi.type);
+            break;
+        case VIR_NODE_DEV_CAP_STORAGE:
+            if (data->storage.bus)
+                virBufferVSprintf(&buf, "    <bus>%s</bus>\n",
+                                  data->storage.bus);
+            if (data->storage.drive_type)
+                virBufferVSprintf(&buf, "    <drive_type>%s</drive_type>\n",
+                                  data->storage.drive_type);
+            if (data->storage.originating_device)
+                virBufferVSprintf(&buf,
+                                  "    <originating_device>%s"
+                                  "</originating_device>\n",
+                                  data->storage.originating_device);
+            if (data->storage.model)
+                virBufferVSprintf(&buf, "    <model>%s</model>\n",
+                                  data->storage.model);
+            if (data->storage.vendor)
+                virBufferVSprintf(&buf, "    <vendor>%s</vendor>\n",
+                                  data->storage.vendor);
+            if (data->storage.flags & VIR_NODE_DEV_CAP_STORAGE_REMOVABLE) {
+                int avl = data->storage.flags &
+                    VIR_NODE_DEV_CAP_STORAGE_REMOVABLE_MEDIA_AVAILABLE;
+                virBufferAddLit(&buf, "    <capability type='removable'>\n");
+                virBufferVSprintf(&buf,
+                                  "      <media_available>%d"
+                                  "</media_available>\n", avl ? 1 : 0);
+                virBufferVSprintf(&buf, "      <media_size>%llu</media_size>\n",
+                                  data->storage.removable_media_size);
+                virBufferAddLit(&buf, "    </capability>\n");
+            } else {
+                virBufferVSprintf(&buf, "    <size>%llu</size>\n",
+                                  data->storage.size);
+            }
+            if (data->storage.flags & VIR_NODE_DEV_CAP_STORAGE_HOTPLUGGABLE)
+                virBufferAddLit(&buf,
+                                "    <capability type='hotpluggable' />\n");
+            break;
+        case VIR_NODE_DEV_CAP_LAST:
+            /* ignore special LAST value */
+            break;
+        }
+
+        virBufferAddLit(&buf, "  </capability>\n");
+    }
+
+    virBufferAddLit(&buf, "</device>\n");
+
+    if (virBufferError(&buf))
+        goto no_memory;
+
+    return virBufferContentAndReset(&buf);
+
+ no_memory:
+    virNodeDeviceReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+    tmp = virBufferContentAndReset(&buf);
+    VIR_FREE(tmp);
+    return NULL;
+}
+
+void virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps)
+{
+    union _virNodeDevCapData *data = &caps->data;
+
+    switch (caps->type) {
+    case VIR_NODE_DEV_CAP_SYSTEM:
+        VIR_FREE(data->system.product_name);
+        VIR_FREE(data->system.hardware.vendor_name);
+        VIR_FREE(data->system.hardware.version);
+        VIR_FREE(data->system.hardware.serial);
+        VIR_FREE(data->system.firmware.vendor_name);
+        VIR_FREE(data->system.firmware.version);
+        VIR_FREE(data->system.firmware.release_date);
+        break;
+    case VIR_NODE_DEV_CAP_PCI_DEV:
+        VIR_FREE(data->pci_dev.product_name);
+        VIR_FREE(data->pci_dev.vendor_name);
+        break;
+    case VIR_NODE_DEV_CAP_USB_DEV:
+        VIR_FREE(data->usb_dev.product_name);
+        VIR_FREE(data->usb_dev.vendor_name);
+        break;
+    case VIR_NODE_DEV_CAP_USB_INTERFACE:
+        VIR_FREE(data->usb_if.description);
+        break;
+    case VIR_NODE_DEV_CAP_NET:
+        VIR_FREE(data->net.interface);
+        VIR_FREE(data->net.address);
+        break;
+    case VIR_NODE_DEV_CAP_BLOCK:
+        VIR_FREE(data->block.device);
+        break;
+    case VIR_NODE_DEV_CAP_SCSI_HOST:
+        break;
+    case VIR_NODE_DEV_CAP_SCSI:
+        VIR_FREE(data->scsi.type);
+        break;
+    case VIR_NODE_DEV_CAP_STORAGE:
+        VIR_FREE(data->storage.bus);
+        VIR_FREE(data->storage.drive_type);
+        VIR_FREE(data->storage.model);
+        VIR_FREE(data->storage.vendor);
+        break;
+    case VIR_NODE_DEV_CAP_LAST:
+        /* This case is here to shutup the compiler */
+        break;
+    }
+
+    VIR_FREE(caps);
+}
+
diff --git a/src/node_device_conf.h b/src/node_device_conf.h
new file mode 100644
index 0000000..664a510
--- /dev/null
+++ b/src/node_device_conf.h
@@ -0,0 +1,204 @@
+/*
+ * node_device_conf.h: config handling for node devices
+ *
+ * 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 virtualiron com>
+ */
+
+#ifndef __VIR_NODE_DEVICE_CONF_H__
+#define __VIR_NODE_DEVICE_CONF_H__
+
+#include "internal.h"
+#include "util.h"
+
+enum virNodeDevCapType {
+    /* Keep in sync with VIR_ENUM_IMPL in node_device_conf.c */
+    VIR_NODE_DEV_CAP_SYSTEM,		/* System capability */
+    VIR_NODE_DEV_CAP_PCI_DEV,		/* PCI device */
+    VIR_NODE_DEV_CAP_USB_DEV,		/* USB device */
+    VIR_NODE_DEV_CAP_USB_INTERFACE,	/* USB interface */
+    VIR_NODE_DEV_CAP_NET,		/* Network device */
+    VIR_NODE_DEV_CAP_BLOCK,		/* Block device */
+    VIR_NODE_DEV_CAP_SCSI_HOST,		/* SCSI Host Bus Adapter */
+    VIR_NODE_DEV_CAP_SCSI,		/* SCSI device */
+    VIR_NODE_DEV_CAP_STORAGE,		/* Storage device */
+    VIR_NODE_DEV_CAP_LAST
+};
+
+enum virNodeDevNetCapType {
+    /* Keep in sync with VIR_ENUM_IMPL in node_device_conf.c */
+    VIR_NODE_DEV_CAP_NET_80203,		/* 802.03 network device */
+    VIR_NODE_DEV_CAP_NET_80211,		/* 802.11 network device */
+    VIR_NODE_DEV_CAP_NET_LAST
+};
+
+VIR_ENUM_DECL(virNodeDevCap);
+VIR_ENUM_DECL(virNodeDevNetCap);
+
+enum virNodeDevStorageCapFlags {
+    VIR_NODE_DEV_CAP_STORAGE_REMOVABLE			= (1 << 0),
+    VIR_NODE_DEV_CAP_STORAGE_REMOVABLE_MEDIA_AVAILABLE	= (1 << 1),
+    VIR_NODE_DEV_CAP_STORAGE_HOTPLUGGABLE		= (1 << 2),
+};
+
+typedef struct _virNodeDevCapsDef virNodeDevCapsDef;
+typedef virNodeDevCapsDef *virNodeDevCapsDefPtr;
+struct _virNodeDevCapsDef {
+    enum virNodeDevCapType type;
+    union _virNodeDevCapData {
+        struct {
+            char *product_name;
+            struct {
+                char *vendor_name;
+                char *version;
+                char *serial;
+                unsigned char uuid[VIR_UUID_BUFLEN];
+            } hardware;
+            struct {
+                char *vendor_name;
+                char *version;
+                char *release_date;
+            } firmware;
+        } system;
+        struct {
+            unsigned domain;
+            unsigned bus;
+            unsigned slot;
+            unsigned function;
+            unsigned product;
+            unsigned vendor;
+            char *product_name;
+            char *vendor_name;
+        } pci_dev;
+        struct {
+            unsigned bus;
+            unsigned device;
+            unsigned product;
+            unsigned vendor;
+            char *product_name;
+            char *vendor_name;
+        } usb_dev;
+        struct {
+            unsigned number;
+            unsigned _class;		/* "class" is reserved in C */
+            unsigned subclass;
+            unsigned protocol;
+            char *description;
+        } usb_if;
+        struct {
+            char *address;
+            char *interface;
+            enum virNodeDevNetCapType subtype;  /* LAST -> no subtype */
+            union {
+                struct {
+                    unsigned long long mac_address;
+                } ieee80203;
+                struct {
+                    unsigned long long mac_address;
+                } ieee80211;
+            } data;
+        } net;
+        struct {
+            char *device;
+        } block;
+        struct {
+            unsigned host;
+        } scsi_host;
+        struct {
+            unsigned host;
+            unsigned bus;
+            unsigned target;
+            unsigned lun;
+            char *type;
+        } scsi;
+        struct {
+            unsigned long long size;
+            unsigned long long removable_media_size;
+            char *bus;
+            char *drive_type;
+            char *originating_device;
+            char *model;
+            char *vendor;
+            unsigned flags;	/* virNodeDevStorageCapFlags bits */
+        } storage;
+    } data;
+    virNodeDevCapsDefPtr next;          /* next capability */
+};
+
+
+typedef struct _virNodeDeviceDef virNodeDeviceDef;
+typedef virNodeDeviceDef *virNodeDeviceDefPtr;
+struct _virNodeDeviceDef {
+    char *name;                         /* device name (unique on node) */
+    char *parent;			/* optional parent device name */
+    virNodeDevCapsDefPtr caps;		/* optional device capabilities */
+};
+
+
+typedef struct _virNodeDeviceObj virNodeDeviceObj;
+typedef virNodeDeviceObj *virNodeDeviceObjPtr;
+struct _virNodeDeviceObj {
+    virNodeDeviceDefPtr def;		/* device definition */
+    void *privateData;			/* driver-specific private data */
+    void (*privateFree)(void *data);	/* destructor for private data */
+
+};
+
+typedef struct _virNodeDeviceObjList virNodeDeviceObjList;
+typedef virNodeDeviceObjList *virNodeDeviceObjListPtr;
+struct _virNodeDeviceObjList {
+    unsigned int count;
+    virNodeDeviceObjPtr *objs;
+};
+
+typedef struct _virDeviceMonitorState virDeviceMonitorState;
+typedef virDeviceMonitorState *virDeviceMonitorStatePtr;
+struct _virDeviceMonitorState {
+    virNodeDeviceObjList devs;		/* currently-known devices */
+    void *privateData;			/* driver-specific private data */
+};
+
+#define virNodeDeviceReportError(conn, code, fmt...)			\
+        virReportErrorHelper(conn, VIR_FROM_DEVMONITOR, code, __FILE__,	\
+                               __FUNCTION__, __LINE__, fmt)
+
+virNodeDeviceObjPtr virNodeDeviceFindByName(const virNodeDeviceObjListPtr devs,
+                                            const char *name);
+
+virNodeDeviceObjPtr virNodeDeviceAssignDef(virConnectPtr conn,
+                                           virNodeDeviceObjListPtr devs,
+                                           const virNodeDeviceDefPtr def);
+
+void virNodeDeviceObjRemove(virNodeDeviceObjListPtr devs,
+                            const virNodeDeviceObjPtr dev);
+
+char *virNodeDeviceDefFormat(virConnectPtr conn,
+                             const virNodeDeviceDefPtr def);
+
+// TODO: virNodeDeviceDefParseString/File/Node for virNodeDeviceCreate
+
+void virNodeDeviceDefFree(virNodeDeviceDefPtr def);
+
+void virNodeDeviceObjFree(virNodeDeviceObjPtr dev);
+
+void virNodeDeviceObjListFree(virNodeDeviceObjListPtr devs);
+
+void virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps);
+
+#endif /* __VIR_NODE_DEVICE_CONF_H__ */
diff --git a/configure.in b/configure.in
index ef69730..b5ad9e5 100644
--- a/configure.in
+++ b/configure.in
@@ -1066,6 +1066,100 @@ test "$enable_shared" = no && lt_cv_objdir=.
 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=check])
+
+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])
+
+
 # very annoying
 rm -f COPYING
 cp COPYING.LIB COPYING
@@ -1147,6 +1241,16 @@ AC_MSG_NOTICE([  xen: $XEN_CFLAGS $XEN_LIBS])
 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 --git a/po/POTFILES.in b/po/POTFILES.in
index fdb34b5..a72d653 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -12,6 +12,7 @@ src/lxc_controller.c
 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 --git a/src/Makefile.am b/src/Makefile.am
index 4779a22..c965900 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,22 @@
 ## Process this file with automake to produce Makefile.in
 
+if WITH_LIBVIRTD
+NODE_DEVICE_SOURCES = node_device.c node_device.h \
+		node_device_conf.c node_device_conf.h
+NODE_DEVICE_CFLAGS =
+NODE_DEVICE_LIBS =
+if HAVE_HAL
+NODE_DEVICE_CFLAGS += $(HAL_CFLAGS)
+NODE_DEVICE_LIBS += $(HAL_LIBS)
+NODE_DEVICE_SOURCES += node_device_hal.c
+endif
+if HAVE_DEVKIT
+NODE_DEVICE_CFLAGS += $(DEVKIT_CFLAGS)
+NODE_DEVICE_LIBS += $(DEVKIT_LIBS)
+NODE_DEVICE_SOURCES += node_device_devkit.c
+endif
+endif
+
 INCLUDES = \
 	   -I$(top_srcdir)/gnulib/lib -I../gnulib/lib \
 	   -I../include \
@@ -11,6 +28,7 @@ INCLUDES = \
 	   $(SELINUX_CFLAGS) \
 	   $(NUMACTL_CFLAGS) \
 	   $(XEN_CFLAGS) \
+	   $(NODE_DEVICE_CFLAGS) \
 	   -DBINDIR=\""$(libexecdir)"\" \
 	   -DSBINDIR=\""$(sbindir)"\" \
 	   -DSYSCONF_DIR="\"$(sysconfdir)\"" \
@@ -155,6 +173,7 @@ libvirt_la_SOURCES =						\
 		libvirt.c libvirt_internal.h			\
 		$(GENERIC_LIB_SOURCES)				\
 		$(DOMAIN_CONF_SOURCES)				\
+		$(NODE_DEVICE_SOURCES)				\
 		$(NETWORK_CONF_SOURCES)				\
 		$(STORAGE_CONF_SOURCES)
 
@@ -221,7 +240,7 @@ EXTRA_DIST +=							\
 
 
 libvirt_la_LIBADD = $(LIBXML_LIBS) $(GNUTLS_LIBS) $(SASL_LIBS) $(SELINUX_LIBS) \
-                    $(NUMACTL_LIBS) $(XEN_LIBS) \
+                    $(NUMACTL_LIBS) $(XEN_LIBS) $(NODE_DEVICE_LIBS) \
 		    @CYGWIN_EXTRA_LIBADD@ ../gnulib/lib/libgnu.la
 libvirt_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libvirt_sym.version \
                      -version-info @LIBVIRT_VERSION_INFO@ \
@@ -343,7 +362,7 @@ libvirt_lxc_SOURCES =						\
 		$(GENERIC_LIB_SOURCES) 				\
 		$(DOMAIN_CONF_SOURCES)
 libvirt_lxc_LDFLAGS = $(WARN_CFLAGS) $(COVERAGE_LDCFLAGS)
-libvirt_lxc_LDADD = $(LIBXML_LIBS) ../gnulib/lib/libgnu.la
+libvirt_lxc_LDADD = $(LIBXML_LIBS) ../gnulib/lib/libgnu.la $(NODE_DEVICE_LIBS)
 libvirt_lxc_CFLAGS =  $(LIBPARTED_CFLAGS)
 endif
 endif
diff --git a/src/driver.h b/src/driver.h
index 766c83e..d882543 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -607,6 +607,73 @@ struct _virStateDriver {
 };
 #endif
 
+
+typedef struct _virDeviceMonitor virDeviceMonitor;
+typedef virDeviceMonitor *virDeviceMonitorPtr;
+
+typedef int (*virDevMonNumOfDevices)(virConnectPtr conn,
+                                     unsigned int flags);
+
+typedef int (*virDevMonListDevices)(virConnectPtr conn,
+                                    char **const names,
+                                    int maxnames,
+                                    unsigned int flags);
+
+typedef int (*virDevMonNumOfDevicesByCap)(virConnectPtr conn,
+                                          const char *cap,
+                                          unsigned int flags);
+
+typedef int (*virDevMonListDevicesByCap)(virConnectPtr conn,
+                                         const char *cap,
+                                         char **const names,
+                                         int maxnames,
+                                         unsigned int flags);
+
+typedef virNodeDevicePtr (*virDevMonDeviceLookupByName)(virConnectPtr conn,
+                                                        const char *name);
+
+typedef char * (*virDevMonDeviceDumpXML)(virNodeDevicePtr dev,
+                                         unsigned int flags);
+
+typedef char * (*virDevMonDeviceGetParent)(virNodeDevicePtr dev);
+
+typedef int (*virDevMonDeviceNumOfCaps)(virNodeDevicePtr dev);
+
+typedef int (*virDevMonDeviceListCaps)(virNodeDevicePtr dev,
+                                       char **const names,
+                                       int maxnames);
+
+typedef virNodeDevicePtr (*virDevMonDeviceCreate)(virConnectPtr conn,
+                                                  const char *xml,
+                                                  unsigned int flags);
+
+typedef int (*virDevMonDeviceDestroy)(virNodeDevicePtr dev,
+                                      unsigned int flags);
+
+/**
+ * _virDeviceMonitor:
+ *
+ * Structure associated with monitoring the devices
+ * on a virtualized node.
+ *
+ */
+struct _virDeviceMonitor {
+    const char * name;    /* the name of the driver */
+    virDrvOpen open;
+    virDrvClose close;
+    virDevMonNumOfDevices numOfDevices;
+    virDevMonListDevices listDevices;
+    virDevMonNumOfDevicesByCap numOfDevicesByCap;
+    virDevMonListDevicesByCap listDevicesByCap;
+    virDevMonDeviceLookupByName deviceLookupByName;
+    virDevMonDeviceDumpXML deviceDumpXML;
+    virDevMonDeviceGetParent deviceGetParent;
+    virDevMonDeviceNumOfCaps deviceNumOfCaps;
+    virDevMonDeviceListCaps deviceListCaps;
+    virDevMonDeviceCreate deviceCreate;
+    virDevMonDeviceDestroy deviceDestroy;
+};
+
 /*
  * Registration
  * TODO: also need ways to (des)activate a given driver
@@ -615,6 +682,7 @@ struct _virStateDriver {
 int virRegisterDriver(virDriverPtr);
 int virRegisterNetworkDriver(virNetworkDriverPtr);
 int virRegisterStorageDriver(virStorageDriverPtr);
+int virRegisterDeviceMonitor(virDeviceMonitorPtr);
 #ifdef WITH_LIBVIRTD
 int virRegisterStateDriver(virStateDriverPtr);
 #endif
diff --git a/src/node_device.c b/src/node_device.c
new file mode 100644
index 0000000..fb60cdf
--- /dev/null
+++ b/src/node_device.c
@@ -0,0 +1,224 @@
+/*
+ * 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 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,
+                            unsigned int flags ATTRIBUTE_UNUSED)
+{
+    virDeviceMonitorStatePtr driver = conn->devMonPrivateData;
+    return driver->devs.count;
+}
+
+static int
+nodeListDevices(virConnectPtr conn, 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 ((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 int nodeNumOfDevicesByCap(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 (dev_has_cap(driver->devs.objs[i], cap))
+            ++ndevs;
+
+    return ndevs;
+}
+
+static int nodeListDevicesByCap(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 (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->numOfDevicesByCap = nodeNumOfDevicesByCap;
+    driver->listDevicesByCap = nodeListDevicesByCap;
+    driver->deviceLookupByName = nodeDeviceLookupByName;
+    driver->deviceDumpXML = nodeDeviceDumpXML;
+    driver->deviceGetParent = nodeDeviceGetParent;
+    driver->deviceNumOfCaps = nodeDeviceNumOfCaps;
+    driver->deviceListCaps = nodeDeviceListCaps;
+}
diff --git a/src/node_device.h b/src/node_device.h
new file mode 100644
index 0000000..3919e78
--- /dev/null
+++ b/src/node_device.h
@@ -0,0 +1,38 @@
+/*
+ * 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 virtualiron com>
+ */
+
+#ifndef __VIR_NODE_DEVICE_H__
+#define __VIR_NODE_DEVICE_H__
+
+#include "libvirt/libvirt.h"
+
+#ifdef HAVE_HAL
+int halNodeRegister(void);
+#endif
+#ifdef HAVE_DEVKIT
+int devkitNodeRegister(void);
+#endif
+
+void registerCommonNodeFuncs(virDeviceMonitorPtr mon);
+
+#endif /* __VIR_NODE_DEVICE_H__ */
diff --git a/src/node_device_devkit.c b/src/node_device_devkit.c
new file mode 100644
index 0000000..1737a54
--- /dev/null
+++ b/src/node_device_devkit.c
@@ -0,0 +1,446 @@
+/*
+ * 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 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,
+                  xmlURIPtr uri ATTRIBUTE_UNUSED,
+                  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 virNodeDevicePtr
+devkitNodeDeviceCreate(virConnectPtr conn ATTRIBUTE_UNUSED,
+                       const char *xml ATTRIBUTE_UNUSED,
+                       unsigned int flags ATTRIBUTE_UNUSED)
+{
+    return NULL;
+}
+
+static int devkitNodeDeviceDestroy(virNodeDevicePtr dev ATTRIBUTE_UNUSED,
+                                   unsigned int flags ATTRIBUTE_UNUSED)
+{
+    return -1;
+}
+
+static virDeviceMonitor devkitDeviceMonitor = {
+    .name = "devkitDeviceMonitor",
+    .open = devkitNodeDrvOpen,
+    .close = devkitNodeDrvClose,
+    .deviceCreate = devkitNodeDeviceCreate,
+    .deviceDestroy = devkitNodeDeviceDestroy,
+};
+
+
+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 --git a/src/node_device_hal.c b/src/node_device_hal.c
new file mode 100644
index 0000000..db6213b
--- /dev/null
+++ b/src/node_device_hal.c
@@ -0,0 +1,776 @@
+/*
+ * 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 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 "node_device.h"
+
+/*
+ * Host device enumeration (HAL 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_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 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 ATTRIBUTE_UNUSED)
+{
+    int flags = 0;
+
+    if (dbus_watch_get_enabled(watch))
+        flags = xlate_dbus_watch_flags(dbus_watch_get_flags(watch));
+
+    return virEventAddHandle(dbus_watch_get_unix_fd(watch), flags,
+                             dbus_watch_callback, watch) == 0;
+}
+
+
+static void remove_dbus_watch(DBusWatch *watch,
+                              void *data ATTRIBUTE_UNUSED)
+{
+    (void)virEventRemoveHandle(dbus_watch_get_unix_fd(watch));
+}
+
+
+static void toggle_dbus_watch(DBusWatch *watch,
+                              void *data ATTRIBUTE_UNUSED)
+{
+    int flags = 0;
+
+    if (dbus_watch_get_enabled(watch))
+        flags = xlate_dbus_watch_flags(dbus_watch_get_flags(watch));
+
+    (void)virEventUpdateHandle(dbus_watch_get_unix_fd(watch), 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,
+                                             NULL, 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,
+                                       xmlURIPtr uri ATTRIBUTE_UNUSED,
+                                       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 virNodeDevicePtr halNodeDeviceCreate(virConnectPtr conn ATTRIBUTE_UNUSED,
+                                            const char *xml ATTRIBUTE_UNUSED,
+                                            unsigned int flags ATTRIBUTE_UNUSED)
+{
+    return NULL;
+}
+
+static int halNodeDeviceDestroy(virNodeDevicePtr dev ATTRIBUTE_UNUSED,
+                                unsigned int flags ATTRIBUTE_UNUSED)
+{
+    return -1;
+}
+
+static virDeviceMonitor halDeviceMonitor = {
+    .name = "halDeviceMonitor",
+    .open = halNodeDrvOpen,
+    .close = halNodeDrvClose,
+    .deviceCreate = halNodeDeviceCreate,
+    .deviceDestroy = halNodeDeviceDestroy,
+};
+
+
+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);
+}
diff --git a/qemud/remote.c b/qemud/remote.c
index 068f97f..239abbb 100644
--- a/qemud/remote.c
+++ b/qemud/remote.c
@@ -67,6 +67,7 @@ static void make_nonnull_domain (remote_nonnull_domain *dom_dst, virDomainPtr do
 static void make_nonnull_network (remote_nonnull_network *net_dst, virNetworkPtr net_src);
 static void make_nonnull_storage_pool (remote_nonnull_storage_pool *pool_dst, virStoragePoolPtr pool_src);
 static void make_nonnull_storage_vol (remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src);
+static void make_nonnull_node_device (remote_nonnull_node_device *dev_dst, virNodeDevicePtr dev_src);
 
 #include "remote_dispatch_prototypes.h"
 
@@ -3641,6 +3642,303 @@ remoteDispatchStorageVolLookupByPath (struct qemud_server *server ATTRIBUTE_UNUS
 }
 
 
+/***************************************************************
+ *     NODE INFO APIS
+ **************************************************************/
+
+static int
+remoteDispatchNodeNumOfDevices (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                struct qemud_client *client,
+                                remote_message_header *req,
+                                remote_node_num_of_devices_args *args,
+                                remote_node_num_of_devices_ret *ret)
+{
+    CHECK_CONN(client);
+
+    ret->num = virNodeNumOfDevices (client->conn, args->flags);
+    if (ret->num == -1) return -1;
+
+    return 0;
+}
+
+
+static int
+remoteDispatchNodeListDevices (struct qemud_server *server ATTRIBUTE_UNUSED,
+                               struct qemud_client *client,
+                               remote_message_header *req,
+                               remote_node_list_devices_args *args,
+                               remote_node_list_devices_ret *ret)
+{
+    CHECK_CONN(client);
+
+    if (args->maxnames > REMOTE_NODE_DEVICE_NAME_LIST_MAX) {
+        remoteDispatchError (client, req,
+                             "%s", _("maxnames > REMOTE_NODE_DEVICE_NAME_LIST_MAX"));
+        return -2;
+    }
+
+    /* Allocate return buffer. */
+    if (VIR_ALLOC_N(ret->names.names_val, args->maxnames) < 0) {
+        remoteDispatchSendError(client, req, VIR_ERR_NO_MEMORY, NULL);
+        return -2;
+    }
+
+    ret->names.names_len =
+        virNodeListDevices (client->conn,
+                            ret->names.names_val, args->maxnames, args->flags);
+    if (ret->names.names_len == -1) {
+        VIR_FREE(ret->names.names_val);
+        return -1;
+    }
+
+    return 0;
+}
+
+
+static int
+remoteDispatchNodeNumOfDevicesByCap (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                     struct qemud_client *client,
+                                     remote_message_header *req,
+                                     remote_node_num_of_devices_by_cap_args *args,
+                                     remote_node_num_of_devices_by_cap_ret *ret)
+{
+    CHECK_CONN(client);
+
+    ret->num = virNodeNumOfDevicesByCap (client->conn, args->cap, args->flags);
+    if (ret->num == -1) return -1;
+
+    return 0;
+}
+
+
+static int
+remoteDispatchNodeListDevicesByCap (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                    struct qemud_client *client,
+                                    remote_message_header *req,
+                                    remote_node_list_devices_by_cap_args *args,
+                                    remote_node_list_devices_by_cap_ret *ret)
+{
+    CHECK_CONN(client);
+
+    if (args->maxnames > REMOTE_NODE_DEVICE_NAME_LIST_MAX) {
+        remoteDispatchError (client, req,
+                             "%s", _("maxnames > REMOTE_NODE_DEVICE_NAME_LIST_MAX"));
+        return -2;
+    }
+
+    /* Allocate return buffer. */
+    if (VIR_ALLOC_N(ret->names.names_val, args->maxnames) < 0) {
+        remoteDispatchSendError(client, req, VIR_ERR_NO_MEMORY, NULL);
+        return -2;
+    }
+
+    ret->names.names_len =
+        virNodeListDevicesByCap (client->conn, args->cap,
+                                 ret->names.names_val, args->maxnames,
+                                 args->flags);
+    if (ret->names.names_len == -1) {
+        VIR_FREE(ret->names.names_val);
+        return -1;
+    }
+
+    return 0;
+}
+
+
+static int
+remoteDispatchNodeDeviceLookupByName (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                      struct qemud_client *client,
+                                      remote_message_header *req,
+                                      remote_node_device_lookup_by_name_args *args,
+                                      remote_node_device_lookup_by_name_ret *ret)
+{
+    virNodeDevicePtr dev;
+
+    CHECK_CONN(client);
+
+    dev = virNodeDeviceLookupByName (client->conn, args->name);
+    if (dev == NULL) return -1;
+
+    make_nonnull_node_device (&ret->dev, dev);
+    virNodeDeviceFree(dev);
+    return 0;
+}
+
+
+static int
+remoteDispatchNodeDeviceDumpXml (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                 struct qemud_client *client,
+                                 remote_message_header *req,
+                                 remote_node_device_dump_xml_args *args,
+                                 remote_node_device_dump_xml_ret *ret)
+{
+    virNodeDevicePtr dev;
+    CHECK_CONN(client);
+
+    dev = virNodeDeviceLookupByName(client->conn, args->name);
+    if (dev == NULL) {
+        remoteDispatchError (client, req, "%s", _("node_device not found"));
+        return -2;
+    }
+
+    /* remoteDispatchClientRequest will free this. */
+    ret->xml = virNodeDeviceGetXMLDesc (dev, args->flags);
+    if (!ret->xml) {
+        virNodeDeviceFree(dev);
+        return -1;
+    }
+    virNodeDeviceFree(dev);
+    return 0;
+}
+
+
+static int
+remoteDispatchNodeDeviceGetParent (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                   struct qemud_client *client,
+                                   remote_message_header *req,
+                                   remote_node_device_get_parent_args *args,
+                                   remote_node_device_get_parent_ret *ret)
+{
+    virNodeDevicePtr dev;
+    const char *parent;
+    CHECK_CONN(client);
+
+    dev = virNodeDeviceLookupByName(client->conn, args->name);
+    if (dev == NULL) {
+        remoteDispatchError (client, req, "%s", _("node_device not found"));
+        return -2;
+    }
+
+    parent = virNodeDeviceGetParent(dev);
+
+    if (parent == NULL) {
+        ret->parent = NULL;
+    } else {
+        /* remoteDispatchClientRequest will free this. */
+        char **parent_p;
+        if (VIR_ALLOC(parent_p) < 0) {
+            remoteDispatchSendError(client, req, VIR_ERR_NO_MEMORY, NULL);
+            return -2;
+        }
+        *parent_p = strdup(parent);
+        if (*parent_p == NULL) {
+            remoteDispatchSendError(client, req, VIR_ERR_NO_MEMORY, NULL);
+            return -2;
+        }
+        ret->parent = parent_p;
+    }
+
+    virNodeDeviceFree(dev);
+    return 0;
+}
+
+
+static int
+remoteDispatchNodeDeviceNumOfCaps (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                   struct qemud_client *client,
+                                   remote_message_header *req,
+                                   remote_node_device_num_of_caps_args *args,
+                                   remote_node_device_num_of_caps_ret *ret)
+{
+    virNodeDevicePtr dev;
+    CHECK_CONN(client);
+
+    dev = virNodeDeviceLookupByName(client->conn, args->name);
+    if (dev == NULL) {
+        remoteDispatchError (client, req, "%s", _("node_device not found"));
+        return -2;
+    }
+
+    ret->num = virNodeDeviceNumOfCaps(dev);
+
+    virNodeDeviceFree(dev);
+    return 0;
+}
+
+
+static int
+remoteDispatchNodeDeviceListCaps (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                  struct qemud_client *client,
+                                  remote_message_header *req,
+                                  remote_node_device_list_caps_args *args,
+                                  remote_node_device_list_caps_ret *ret)
+{
+    virNodeDevicePtr dev;
+    CHECK_CONN(client);
+
+    dev = virNodeDeviceLookupByName(client->conn, args->name);
+    if (dev == NULL) {
+        remoteDispatchError (client, req, "%s", _("node_device not found"));
+        return -2;
+    }
+
+    if (args->maxnames > REMOTE_NODE_DEVICE_NAME_LIST_MAX) {
+        remoteDispatchError (client, req,
+                             "%s", _("maxnames > REMOTE_NODE_DEVICE_NAME_LIST_MAX"));
+        return -2;
+    }
+
+    /* Allocate return buffer. */
+    if (VIR_ALLOC_N(ret->names.names_val, args->maxnames) < 0) {
+        remoteDispatchSendError(client, req, VIR_ERR_NO_MEMORY, NULL);
+        return -2;
+    }
+
+    ret->names.names_len =
+        virNodeDeviceListCaps (dev, ret->names.names_val,
+                               args->maxnames);
+    if (ret->names.names_len == -1) {
+        VIR_FREE(ret->names.names_val);
+        return -1;
+    }
+
+    return 0;
+}
+
+
+static int
+remoteDispatchNodeDeviceCreate (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                struct qemud_client *client,
+                                remote_message_header *req,
+                                remote_node_device_create_args *args,
+                                remote_node_device_create_ret *ret)
+{
+    virNodeDevicePtr dev;
+
+    CHECK_CONN(client);
+
+    dev = virNodeDeviceCreate (client->conn, args->xml, args->flags);
+    if (dev == NULL) return -1;
+
+    make_nonnull_node_device (&ret->dev, dev);
+    virNodeDeviceFree(dev);
+    return 0;
+}
+
+
+static int
+remoteDispatchNodeDeviceDestroy (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                struct qemud_client *client,
+                                remote_message_header *req,
+                                remote_node_device_destroy_args *args,
+                                void *ret ATTRIBUTE_UNUSED)
+{
+    virNodeDevicePtr dev;
+    CHECK_CONN(client);
+
+    dev = virNodeDeviceLookupByName(client->conn, args->name);
+    if (dev == NULL) {
+        remoteDispatchError (client, req, "%s", _("node_device not found"));
+        return -2;
+    }
+
+    if (virNodeDeviceDestroy (dev, args->flags) < 0) {
+        virNodeDeviceFree(dev);
+        return -1;
+    }
+    virNodeDeviceFree(dev);
+    return 0;
+}
 /**************************
  * Async Events
  **************************/
@@ -3764,6 +4062,7 @@ remoteDispatchDomainEventSend (struct qemud_client *client,
     client->bufferLength = len;
     client->bufferOffset = 0;
 }
+
 /*----- Helpers. -----*/
 
 /* get_nonnull_domain and get_nonnull_network turn an on-wire
@@ -3834,3 +4133,9 @@ make_nonnull_storage_vol (remote_nonnull_storage_vol *vol_dst, virStorageVolPtr
     vol_dst->name = strdup (vol_src->name);
     vol_dst->key = strdup (vol_src->key);
 }
+
+static void
+make_nonnull_node_device (remote_nonnull_node_device *dev_dst, virNodeDevicePtr dev_src)
+{
+    dev_dst->name = strdup(dev_src->name);
+}
diff --git a/qemud/remote_protocol.x b/qemud/remote_protocol.x
index bef9711..d6ed85c 100644
--- a/qemud/remote_protocol.x
+++ b/qemud/remote_protocol.x
@@ -86,6 +86,12 @@ const REMOTE_STORAGE_POOL_NAME_LIST_MAX = 256;
 /* Upper limit on lists of storage vol names. */
 const REMOTE_STORAGE_VOL_NAME_LIST_MAX = 1024;
 
+/* Upper limit on lists of node device names. */
+const REMOTE_NODE_DEVICE_NAME_LIST_MAX = 16384;
+
+/* Upper limit on lists of node device capabilities. */
+const REMOTE_NODE_DEVICE_CAPS_LIST_MAX = 16384;
+
 /* Upper limit on list of scheduler parameters. */
 const REMOTE_DOMAIN_SCHEDULER_PARAMETERS_MAX = 16;
 
@@ -139,11 +145,17 @@ struct remote_nonnull_storage_vol {
     remote_nonnull_string key;
 };
 
+/* A node device which may not be NULL. */
+struct remote_nonnull_node_device {
+    remote_nonnull_string name;
+};
+
 /* A domain or network which may be NULL. */
 typedef remote_nonnull_domain *remote_domain;
 typedef remote_nonnull_network *remote_network;
 typedef remote_nonnull_storage_pool *remote_storage_pool;
 typedef remote_nonnull_storage_vol *remote_storage_vol;
+typedef remote_nonnull_node_device *remote_node_device;
 
 /* Error message. See <virterror.h> for explanation of fields. */
 
@@ -965,6 +977,100 @@ struct remote_storage_vol_get_path_ret {
     remote_nonnull_string name;
 };
 
+/* Node driver calls: */
+
+struct remote_node_num_of_devices_args {
+    unsigned flags;
+};
+
+struct remote_node_num_of_devices_ret {
+    int num;
+};
+
+struct remote_node_list_devices_args {
+    int maxnames;
+    unsigned flags;
+};
+
+struct remote_node_list_devices_ret {
+    remote_nonnull_string names<REMOTE_NODE_DEVICE_NAME_LIST_MAX>;
+};
+
+struct remote_node_num_of_devices_by_cap_args {
+    remote_nonnull_string cap;
+    unsigned flags;
+};
+
+struct remote_node_num_of_devices_by_cap_ret {
+    int num;
+};
+
+struct remote_node_list_devices_by_cap_args {
+    int maxnames;
+    remote_nonnull_string cap;
+    unsigned flags;
+};
+
+struct remote_node_list_devices_by_cap_ret {
+    remote_nonnull_string names<REMOTE_NODE_DEVICE_NAME_LIST_MAX>;
+};
+
+struct remote_node_device_lookup_by_name_args {
+    remote_nonnull_string name;
+};
+
+struct remote_node_device_lookup_by_name_ret {
+    remote_nonnull_node_device dev;
+};
+
+struct remote_node_device_dump_xml_args {
+    remote_nonnull_string name;
+    unsigned flags;
+};
+
+struct remote_node_device_dump_xml_ret {
+    remote_nonnull_string xml;
+};
+
+struct remote_node_device_get_parent_args {
+    remote_nonnull_string name;
+};
+
+struct remote_node_device_get_parent_ret {
+    remote_string parent;
+};
+
+struct remote_node_device_num_of_caps_args {
+    remote_nonnull_string name;
+};
+
+struct remote_node_device_num_of_caps_ret {
+    int num;
+};
+
+struct remote_node_device_list_caps_args {
+    remote_nonnull_string name;
+    int maxnames;
+};
+
+struct remote_node_device_list_caps_ret {
+    remote_nonnull_string names<REMOTE_NODE_DEVICE_CAPS_LIST_MAX>;
+};
+
+struct remote_node_device_create_args {
+    remote_nonnull_string xml;
+    unsigned flags;
+};
+
+struct remote_node_device_create_ret {
+    remote_nonnull_node_device dev;
+};
+
+struct remote_node_device_destroy_args {
+    remote_nonnull_string name;
+    unsigned flags;
+};
+
 /**
  * Events Register/Deregister:
  * It would seem rpcgen does not like both args, and ret
@@ -1109,7 +1215,19 @@ enum remote_procedure {
 
     REMOTE_PROC_DOMAIN_EVENTS_REGISTER = 105,
     REMOTE_PROC_DOMAIN_EVENTS_DEREGISTER = 106,
-    REMOTE_PROC_DOMAIN_EVENT = 107
+    REMOTE_PROC_DOMAIN_EVENT = 107,
+
+    REMOTE_PROC_NODE_NUM_OF_DEVICES = 108,
+    REMOTE_PROC_NODE_LIST_DEVICES = 109,
+    REMOTE_PROC_NODE_NUM_OF_DEVICES_BY_CAP = 110,
+    REMOTE_PROC_NODE_LIST_DEVICES_BY_CAP = 111,
+    REMOTE_PROC_NODE_DEVICE_LOOKUP_BY_NAME = 112,
+    REMOTE_PROC_NODE_DEVICE_DUMP_XML = 113,
+    REMOTE_PROC_NODE_DEVICE_CREATE = 114,
+    REMOTE_PROC_NODE_DEVICE_DESTROY = 115,
+    REMOTE_PROC_NODE_DEVICE_GET_PARENT = 116,
+    REMOTE_PROC_NODE_DEVICE_NUM_OF_CAPS = 117,
+    REMOTE_PROC_NODE_DEVICE_LIST_CAPS = 118
 };
 
 /* Custom RPC structure. */
diff --git a/src/remote_internal.c b/src/remote_internal.c
index 03462b4..8511cdd 100644
--- a/src/remote_internal.c
+++ b/src/remote_internal.c
@@ -141,6 +141,14 @@ struct private_data {
         return (retcode);                                               \
     }
 
+#define GET_DEVMON_PRIVATE(conn,retcode)                               \
+    struct private_data *priv = (struct private_data *) (conn)->devMonPrivateData; \
+    if (!priv || priv->magic != MAGIC) {                                \
+        error (conn, VIR_ERR_INVALID_ARG,                               \
+               _("tried to use a closed or uninitialised handle"));     \
+        return (retcode);                                               \
+    }
+
 
 enum {
     REMOTE_CALL_IN_OPEN = 1,
@@ -170,6 +178,7 @@ static virDomainPtr get_nonnull_domain (virConnectPtr conn, remote_nonnull_domai
 static virNetworkPtr get_nonnull_network (virConnectPtr conn, remote_nonnull_network network);
 static virStoragePoolPtr get_nonnull_storage_pool (virConnectPtr conn, remote_nonnull_storage_pool pool);
 static virStorageVolPtr get_nonnull_storage_vol (virConnectPtr conn, remote_nonnull_storage_vol vol);
+static virNodeDevicePtr get_nonnull_node_device (virConnectPtr conn, remote_nonnull_node_device dev);
 static void make_nonnull_domain (remote_nonnull_domain *dom_dst, virDomainPtr dom_src);
 static void make_nonnull_network (remote_nonnull_network *net_dst, virNetworkPtr net_src);
 static void make_nonnull_storage_pool (remote_nonnull_storage_pool *pool_dst, virStoragePoolPtr vol_src);
@@ -3642,6 +3651,332 @@ remoteStorageVolGetPath (virStorageVolPtr vol)
 
 /*----------------------------------------------------------------------*/
 
+static virDrvOpenStatus
+remoteDevMonOpen(virConnectPtr conn,
+                 xmlURIPtr uri ATTRIBUTE_UNUSED,
+                 virConnectAuthPtr auth ATTRIBUTE_UNUSED,
+                 int flags ATTRIBUTE_UNUSED)
+{
+    if (conn &&
+        conn->driver &&
+        STREQ (conn->driver->name, "remote")) {
+        /* If we're here, the remote driver is already
+         * in use due to a) a QEMU uri, or b) a remote
+         * URI. So we can re-use existing connection
+         */
+        conn->devMonPrivateData = conn->privateData;
+        return VIR_DRV_OPEN_SUCCESS;
+    }
+
+    /* Decline open.  Will fallback to appropriate local node driver. */
+    return VIR_DRV_OPEN_DECLINED;
+}
+
+static int remoteDevMonClose(virConnectPtr conn)
+{
+    int ret = 0;
+    GET_DEVMON_PRIVATE (conn, -1);
+    if (priv->localUses) {
+        priv->localUses--;
+        if (!priv->localUses) {
+            ret = doRemoteClose(conn, priv);
+            VIR_FREE(priv);
+            conn->devMonPrivateData = NULL;
+        }
+    }
+    return ret;
+}
+
+static int remoteNodeNumOfDevices(virConnectPtr conn,
+                                  unsigned int flags)
+{
+    remote_node_num_of_devices_args args;
+    remote_node_num_of_devices_ret ret;
+    GET_STORAGE_PRIVATE (conn, -1);
+
+    args.flags = flags;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_NODE_NUM_OF_DEVICES,
+              (xdrproc_t) xdr_remote_node_num_of_devices_args, (char *) &args,
+              (xdrproc_t) xdr_remote_node_num_of_devices_ret, (char *) &ret) == -1)
+        return -1;
+
+    return ret.num;
+}
+
+
+static int remoteNodeListDevices(virConnectPtr conn,
+                                 char **const names,
+                                 int maxnames,
+                                 unsigned int flags)
+{
+    int i;
+    remote_node_list_devices_args args;
+    remote_node_list_devices_ret ret;
+    GET_STORAGE_PRIVATE (conn, -1);
+
+    if (maxnames > REMOTE_NODE_DEVICE_NAME_LIST_MAX) {
+        error (conn, VIR_ERR_RPC, _("too many device names requested"));
+        return -1;
+    }
+    args.maxnames = maxnames;
+    args.flags = flags;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_NODE_LIST_DEVICES,
+              (xdrproc_t) xdr_remote_node_list_devices_args, (char *) &args,
+              (xdrproc_t) xdr_remote_node_list_devices_ret, (char *) &ret) == -1)
+        return -1;
+
+    if (ret.names.names_len > maxnames) {
+        error (conn, VIR_ERR_RPC, _("too many device names received"));
+        xdr_free ((xdrproc_t) xdr_remote_node_list_devices_ret, (char *) &ret);
+        return -1;
+    }
+
+    /* This call is caller-frees (although that isn't clear from
+     * the documentation).  However xdr_free will free up both the
+     * names and the list of pointers, so we have to strdup the
+     * names here.
+     */
+    for (i = 0; i < ret.names.names_len; ++i)
+        names[i] = strdup (ret.names.names_val[i]);
+
+    xdr_free ((xdrproc_t) xdr_remote_node_list_devices_ret, (char *) &ret);
+
+    return ret.names.names_len;
+}
+
+
+static int remoteNodeNumOfDevicesByCap(virConnectPtr conn,
+                                       const char *cap,
+                                       unsigned int flags)
+{
+    remote_node_num_of_devices_by_cap_args args;
+    remote_node_num_of_devices_by_cap_ret ret;
+    GET_STORAGE_PRIVATE (conn, -1);
+
+    args.cap = (char *)cap;
+    args.flags = flags;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_NODE_NUM_OF_DEVICES_BY_CAP,
+              (xdrproc_t) xdr_remote_node_num_of_devices_by_cap_args, (char *) &args,
+              (xdrproc_t) xdr_remote_node_num_of_devices_by_cap_ret, (char *) &ret) == -1)
+        return -1;
+
+    return ret.num;
+}
+
+static int remoteNodeListDevicesByCap(virConnectPtr conn,
+                                      const char *cap,
+                                      char **const names,
+                                      int maxnames,
+                                      unsigned int flags)
+{
+    int i;
+    remote_node_list_devices_by_cap_args args;
+    remote_node_list_devices_by_cap_ret ret;
+    GET_STORAGE_PRIVATE (conn, -1);
+
+    if (maxnames > REMOTE_NODE_DEVICE_NAME_LIST_MAX) {
+        error (conn, VIR_ERR_RPC, _("too many device names requested"));
+        return -1;
+    }
+    args.maxnames = maxnames;
+    args.cap = (char *)cap;
+    args.flags = flags;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_NODE_LIST_DEVICES_BY_CAP,
+              (xdrproc_t) xdr_remote_node_list_devices_by_cap_args, (char *) &args,
+              (xdrproc_t) xdr_remote_node_list_devices_by_cap_ret, (char *) &ret) == -1)
+        return -1;
+
+    if (ret.names.names_len > maxnames) {
+        error (conn, VIR_ERR_RPC, _("too many device names received"));
+        xdr_free ((xdrproc_t) xdr_remote_node_list_devices_ret, (char *) &ret);
+        return -1;
+    }
+
+    /* This call is caller-frees (although that isn't clear from
+     * the documentation).  However xdr_free will free up both the
+     * names and the list of pointers, so we have to strdup the
+     * names here.
+     */
+    for (i = 0; i < ret.names.names_len; ++i)
+        names[i] = strdup (ret.names.names_val[i]);
+
+    xdr_free ((xdrproc_t) xdr_remote_node_list_devices_by_cap_ret, (char *) &ret);
+
+    return ret.names.names_len;
+}
+
+static virNodeDevicePtr remoteNodeDeviceLookupByName(virConnectPtr conn,
+                                                     const char *name)
+{
+    remote_node_device_lookup_by_name_args args;
+    remote_node_device_lookup_by_name_ret ret;
+    virNodeDevicePtr dev;
+    GET_STORAGE_PRIVATE (conn, NULL);
+
+    args.name = (char *)name;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_NODE_DEVICE_LOOKUP_BY_NAME,
+              (xdrproc_t) xdr_remote_node_device_lookup_by_name_args, (char *) &args,
+              (xdrproc_t) xdr_remote_node_device_lookup_by_name_ret, (char *) &ret) == -1)
+        return NULL;
+
+    dev = get_nonnull_node_device(conn, ret.dev);
+
+    xdr_free ((xdrproc_t) xdr_remote_node_device_lookup_by_name_ret, (char *) &ret);
+
+    return dev;
+}
+
+static char *remoteNodeDeviceDumpXML(virNodeDevicePtr dev,
+                                     unsigned int flags)
+{
+    remote_node_device_dump_xml_args args;
+    remote_node_device_dump_xml_ret ret;
+    GET_STORAGE_PRIVATE (dev->conn, NULL);
+
+    args.name = dev->name;
+    args.flags = flags;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (dev->conn, priv, 0, REMOTE_PROC_NODE_DEVICE_DUMP_XML,
+              (xdrproc_t) xdr_remote_node_device_dump_xml_args, (char *) &args,
+              (xdrproc_t) xdr_remote_node_device_dump_xml_ret, (char *) &ret) == -1)
+        return NULL;
+
+    /* Caller frees. */
+    return ret.xml;
+}
+
+static char *remoteNodeDeviceGetParent(virNodeDevicePtr dev)
+{
+    remote_node_device_get_parent_args args;
+    remote_node_device_get_parent_ret ret;
+    GET_STORAGE_PRIVATE (dev->conn, NULL);
+
+    args.name = dev->name;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (dev->conn, priv, 0, REMOTE_PROC_NODE_DEVICE_GET_PARENT,
+              (xdrproc_t) xdr_remote_node_device_get_parent_args, (char *) &args,
+              (xdrproc_t) xdr_remote_node_device_get_parent_ret, (char *) &ret) == -1)
+        return NULL;
+
+    /* Caller frees. */
+    return ret.parent ? *ret.parent : NULL;
+}
+
+static int remoteNodeDeviceNumOfCaps(virNodeDevicePtr dev)
+{
+    remote_node_device_num_of_caps_args args;
+    remote_node_device_num_of_caps_ret ret;
+    GET_STORAGE_PRIVATE (dev->conn, -1);
+
+    args.name = dev->name;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (dev->conn, priv, 0, REMOTE_PROC_NODE_DEVICE_NUM_OF_CAPS,
+              (xdrproc_t) xdr_remote_node_device_num_of_caps_args, (char *) &args,
+              (xdrproc_t) xdr_remote_node_device_num_of_caps_ret, (char *) &ret) == -1)
+        return -1;
+
+    return ret.num;
+}
+
+static int remoteNodeDeviceListCaps(virNodeDevicePtr dev,
+                                    char **const names,
+                                    int maxnames)
+{
+    int i;
+    remote_node_device_list_caps_args args;
+    remote_node_device_list_caps_ret ret;
+    GET_STORAGE_PRIVATE (dev->conn, -1);
+
+    if (maxnames > REMOTE_NODE_DEVICE_CAPS_LIST_MAX) {
+        error (dev->conn, VIR_ERR_RPC, _("too many capability names requested"));
+        return -1;
+    }
+    args.maxnames = maxnames;
+    args.name = dev->name;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (dev->conn, priv, 0, REMOTE_PROC_NODE_DEVICE_LIST_CAPS,
+              (xdrproc_t) xdr_remote_node_device_list_caps_args, (char *) &args,
+              (xdrproc_t) xdr_remote_node_device_list_caps_ret, (char *) &ret) == -1)
+        return -1;
+
+    if (ret.names.names_len > maxnames) {
+        error (dev->conn, VIR_ERR_RPC, _("too many capability names received"));
+        xdr_free ((xdrproc_t) xdr_remote_node_device_list_caps_ret, (char *) &ret);
+        return -1;
+    }
+
+    /* This call is caller-frees (although that isn't clear from
+     * the documentation).  However xdr_free will free up both the
+     * names and the list of pointers, so we have to strdup the
+     * names here.
+     */
+    for (i = 0; i < ret.names.names_len; ++i)
+        names[i] = strdup (ret.names.names_val[i]);
+
+    xdr_free ((xdrproc_t) xdr_remote_node_device_list_caps_ret, (char *) &ret);
+
+    return ret.names.names_len;
+}
+
+static virNodeDevicePtr remoteNodeDeviceCreate(virConnectPtr conn,
+                                               const char *xml,
+                                               unsigned int flags)
+{
+    remote_node_device_create_args args;
+    remote_node_device_create_ret ret;
+    virNodeDevicePtr dev;
+    GET_STORAGE_PRIVATE (conn, NULL);
+
+    args.xml = (char *)xml;
+    args.flags = flags;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (conn, priv, 0, REMOTE_PROC_NODE_DEVICE_CREATE,
+              (xdrproc_t) xdr_remote_node_device_create_args, (char *) &args,
+              (xdrproc_t) xdr_remote_node_device_create_ret, (char *) &ret) == -1)
+        return NULL;
+
+    dev = get_nonnull_node_device(conn, ret.dev);
+
+    xdr_free ((xdrproc_t) xdr_remote_node_device_create_ret, (char *) &ret);
+
+    return dev;
+}
+
+static int remoteNodeDeviceDestroy(virNodeDevicePtr dev,
+                                   unsigned int flags)
+{
+    remote_node_device_destroy_args args;
+    GET_STORAGE_PRIVATE (dev->conn, -1);
+
+    args.name = dev->name;
+    args.flags = flags;
+
+    if (call (dev->conn, priv, 0, REMOTE_PROC_NODE_DEVICE_DESTROY,
+              (xdrproc_t) xdr_remote_node_device_destroy_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
+
+    return 0;
+}
+
+
+/*----------------------------------------------------------------------*/
+
 static int
 remoteAuthenticate (virConnectPtr conn, struct private_data *priv, int in_open,
                     virConnectAuthPtr auth
@@ -4897,6 +5232,12 @@ get_nonnull_storage_vol (virConnectPtr conn, remote_nonnull_storage_vol vol)
     return virGetStorageVol (conn, vol.pool, vol.name, vol.key);
 }
 
+static virNodeDevicePtr
+get_nonnull_node_device (virConnectPtr conn, remote_nonnull_node_device dev)
+{
+    return virGetNodeDevice(conn, dev.name);
+}
+
 /* Make remote_nonnull_domain and remote_nonnull_network. */
 static void
 make_nonnull_domain (remote_nonnull_domain *dom_dst, virDomainPtr dom_src)
@@ -5051,6 +5392,24 @@ static virStorageDriver storage_driver = {
     .volGetPath = remoteStorageVolGetPath,
 };
 
+static virDeviceMonitor dev_monitor = {
+    .name = "remote",
+    .open = remoteDevMonOpen,
+    .close = remoteDevMonClose,
+    .numOfDevices = remoteNodeNumOfDevices,
+    .listDevices = remoteNodeListDevices,
+    .numOfDevicesByCap = remoteNodeNumOfDevicesByCap,
+    .listDevicesByCap = remoteNodeListDevicesByCap,
+    .deviceLookupByName = remoteNodeDeviceLookupByName,
+    .deviceDumpXML = remoteNodeDeviceDumpXML,
+    .deviceCreate = remoteNodeDeviceCreate,
+    .deviceDestroy = remoteNodeDeviceDestroy,
+    .deviceGetParent = remoteNodeDeviceGetParent,
+    .deviceNumOfCaps = remoteNodeDeviceNumOfCaps,
+    .deviceListCaps = remoteNodeDeviceListCaps,
+};
+
+
 #ifdef WITH_LIBVIRTD
 static virStateDriver state_driver = {
     .initialize = remoteStartup,
@@ -5070,6 +5429,7 @@ remoteRegister (void)
     if (virRegisterDriver (&driver) == -1) return -1;
     if (virRegisterNetworkDriver (&network_driver) == -1) return -1;
     if (virRegisterStorageDriver (&storage_driver) == -1) return -1;
+    if (virRegisterDeviceMonitor (&dev_monitor) == -1) return -1;
 #ifdef WITH_LIBVIRTD
     if (virRegisterStateDriver (&state_driver) == -1) return -1;
 #endif
diff --git a/src/virsh.c b/src/virsh.c
index 89aa4fa..7ccab31 100644
--- a/src/virsh.c
+++ b/src/virsh.c
@@ -4415,6 +4415,96 @@ cmdVersion(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
 }
 
 /*
+ * "node-list-devices" command
+ */
+static const vshCmdInfo info_node_list_devices[] = {
+    {"syntax", "node-list-devices [--cap <capability>]"},
+    {"help", gettext_noop("enumerate devices on this host")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_node_list_devices[] = {
+    {"cap", VSH_OT_STRING, VSH_OFLAG_NONE, gettext_noop("capability name")},
+    {NULL, 0, 0, NULL}
+};
+
+static int
+cmdNodeListDevices (vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+{
+    char *cap;
+    char **devices;
+    int found, num_devices, i;
+
+    if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
+        return FALSE;
+
+    cap = vshCommandOptString(cmd, "cap", &found);
+    if (!found)
+        cap = NULL;
+
+    num_devices = cap ? virNodeNumOfDevicesByCap(ctl->conn, cap, 0) :
+        virNodeNumOfDevices(ctl->conn, 0);
+    if (num_devices < 0) {
+        vshError(ctl, FALSE, "%s", _("Failed to count node devices"));
+        return FALSE;
+    } else if (num_devices == 0) {
+        return TRUE;
+    }
+
+    devices = vshMalloc(ctl, sizeof(char *) * num_devices);
+    num_devices = cap ?
+        virNodeListDevicesByCap(ctl->conn, cap, devices, num_devices, 0) :
+        virNodeListDevices(ctl->conn, devices, num_devices, 0);
+    if (num_devices < 0) {
+        vshError(ctl, FALSE, "%s", _("Failed to list node devices"));
+        free(devices);
+        return FALSE;
+    }
+    for (i = 0; i < num_devices; i++) {
+        vshPrint(ctl, "%s\n", devices[i]);
+        free(devices[i]);
+    }
+    free(devices);
+    return TRUE;
+}
+
+/*
+ * "node-device-dumpxml" command
+ */
+static const vshCmdInfo info_node_device_dumpxml[] = {
+    {"syntax", "node-device-dumpxml <device>"},
+    {"help", gettext_noop("node device details in XML")},
+    {"desc", gettext_noop("Output the node device details as an XML dump to stdout.")},
+    {NULL, NULL}
+};
+
+
+static const vshCmdOptDef opts_node_device_dumpxml[] = {
+    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("device key")},
+    {NULL, 0, 0, NULL}
+};
+
+static int
+cmdNodeDeviceDumpXML (vshControl *ctl, const vshCmd *cmd)
+{
+    const char *name;
+    virNodeDevicePtr device;
+
+    if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
+        return FALSE;
+    if (!(name = vshCommandOptString(cmd, "device", NULL)))
+        return FALSE;
+    if (!(device = virNodeDeviceLookupByName(ctl->conn, name))) {
+        vshError(ctl, FALSE, "%s '%s'", _("Could not find matching device"), name);
+        return FALSE;
+    }
+
+    vshPrint(ctl, "%s\n", virNodeDeviceGetXMLDesc(device, 0));
+    virNodeDeviceFree(device);
+    return TRUE;
+}
+
+/*
  * "hostkey" command
  */
 static const vshCmdInfo info_hostname[] = {
@@ -5566,6 +5656,9 @@ static const vshCmdDef commands[] = {
     {"net-uuid", cmdNetworkUuid, opts_network_uuid, info_network_uuid},
     {"nodeinfo", cmdNodeinfo, NULL, info_nodeinfo},
 
+    {"node-list-devices", cmdNodeListDevices, opts_node_list_devices, info_node_list_devices},
+    {"node-device-dumpxml", cmdNodeDeviceDumpXML, opts_node_device_dumpxml, info_node_device_dumpxml},
+
     {"pool-autostart", cmdPoolAutostart, opts_pool_autostart, info_pool_autostart},
     {"pool-build", cmdPoolBuild, opts_pool_build, info_pool_build},
     {"pool-create", cmdPoolCreate, opts_pool_create, info_pool_create},
diff --git a/python/generator.py b/python/generator.py
index 7b153b9..3589fb6 100755
--- a/python/generator.py
+++ b/python/generator.py
@@ -260,6 +260,11 @@ py_types = {
     'const virConnectPtr':  ('O', "virConnect", "virConnectPtr", "virConnectPtr"),
     'virConnect *':  ('O', "virConnect", "virConnectPtr", "virConnectPtr"),
     'const virConnect *':  ('O', "virConnect", "virConnectPtr", "virConnectPtr"),
+
+    'virNodeDevicePtr':  ('O', "virNodeDevice", "virNodeDevicePtr", "virNodeDevicePtr"),
+    'const virNodeDevicePtr':  ('O', "virNodeDevice", "virNodeDevicePtr", "virNodeDevicePtr"),
+    'virNodeDevice *':  ('O', "virNodeDevice", "virNodeDevicePtr", "virNodeDevicePtr"),
+    'const virNodeDevice *':  ('O', "virNodeDevice", "virNodeDevicePtr", "virNodeDevicePtr"),
 }
 
 py_return_types = {
@@ -318,6 +323,9 @@ skip_impl = (
     'virDomainBlockPeek',
     'virDomainMemoryPeek',
     'virEventRegisterImpl',
+    'virNodeListDevicesByCap',
+    'virNodeListDevices',
+    'virNodeDeviceListCaps',
 )
 
 
@@ -601,6 +609,8 @@ classes_type = {
     "virStoragePool *": ("._o", "virStoragePool(self, _obj=%s)", "virStoragePool"),
     "virStorageVolPtr": ("._o", "virStorageVol(self, _obj=%s)", "virStorageVol"),
     "virStorageVol *": ("._o", "virStorageVol(self, _obj=%s)", "virStorageVol"),
+    "virNodeDevicePtr": ("._o", "virNodeDevice(self, _obj=%s)", "virNodeDevice"),
+    "virNodeDevice *": ("._o", "virNodeDevice(self, _obj=%s)", "virNodeDevice"),
     "virConnectPtr": ("._o", "virConnect(_obj=%s)", "virConnect"),
     "virConnect *": ("._o", "virConnect(_obj=%s)", "virConnect"),
 }
@@ -608,7 +618,8 @@ classes_type = {
 converter_type = {
 }
 
-primary_classes = ["virDomain", "virNetwork", "virStoragePool", "virStorageVol", "virConnect"]
+primary_classes = ["virDomain", "virNetwork", "virStoragePool", "virStorageVol",
+                   "virConnect", "virNodeDevice" ]
 
 classes_ancestor = {
 }
@@ -617,6 +628,7 @@ classes_destructors = {
     "virNetwork": "virNetworkFree",
     "virStoragePool": "virStoragePoolFree",
     "virStorageVol": "virStorageVolFree",
+    "virNodeDevice" : "virNodeDeviceFree"
 }
 
 functions_noexcept = {
@@ -626,6 +638,8 @@ functions_noexcept = {
     'virStoragePoolGetName': True,
     'virStorageVolGetName': True,
     'virStorageVolGetkey': True,
+    'virNodeDeviceGetName': True,
+    'virNodeDeviceGetParent': True,
 }
 
 reference_keepers = {
@@ -706,6 +720,13 @@ def nameFixup(name, classe, type, file):
     elif name[0:13] == "virStorageVol":
         func = name[13:]
         func = string.lower(func[0:1]) + func[1:]
+    elif name[0:13] == "virNodeDevice":
+        if name[13:16] == "Get":
+            func = string.lower(name[16]) + name[17:]
+        elif name[13:19] == "Lookup" or name[13:] == "Create":
+            func = string.lower(name[3]) + name[4:]
+        else:
+            func = string.lower(name[13]) + name[14:]
     elif name[0:7] == "virNode":
         func = name[7:]
         func = string.lower(func[0:1]) + func[1:]
@@ -958,7 +979,7 @@ def buildWrappers():
 	    else:
 		txt.write("Class %s()\n" % (classname))
 		classes.write("class %s:\n" % (classname))
-                if classname in [ "virDomain", "virNetwork", "virStoragePool", "virStorageVol" ]:
+                if classname in [ "virDomain", "virNetwork", "virStoragePool", "virStorageVol", "virNodeDevice" ]:
                     classes.write("    def __init__(self, conn, _obj=None):\n")
                 else:
                     classes.write("    def __init__(self, _obj=None):\n")
@@ -966,7 +987,7 @@ def buildWrappers():
 		    list = reference_keepers[classname]
 		    for ref in list:
 		        classes.write("        self.%s = None\n" % ref[1])
-                if classname in [ "virDomain", "virNetwork" ]:
+                if classname in [ "virDomain", "virNetwork", "virNodeDevice" ]:
                     classes.write("        self._conn = conn\n")
                 elif classname in [ "virStorageVol", "virStoragePool" ]:
                     classes.write("        self._conn = conn\n" + \
diff --git a/python/libvir.c b/python/libvir.c
index a2e4c79..b73eae8 100644
--- a/python/libvir.c
+++ b/python/libvir.c
@@ -1466,6 +1466,132 @@ libvirt_virStoragePoolLookupByUUID(PyObject *self ATTRIBUTE_UNUSED, PyObject *ar
     return(py_retval);
 }
 
+static PyObject *
+libvirt_virNodeListDevices(PyObject *self ATTRIBUTE_UNUSED,
+                           PyObject *args) {
+    PyObject *py_retval;
+    char **names = NULL;
+    int c_retval, i;
+    virConnectPtr conn;
+    PyObject *pyobj_conn;
+    unsigned int flags;
+
+
+    if (!PyArg_ParseTuple(args, (char *)"Oi:virNodeListDevices", &pyobj_conn, &flags))
+        return(NULL);
+    conn = (virConnectPtr) PyvirConnect_Get(pyobj_conn);
+
+    c_retval = virNodeNumOfDevices(conn, flags);
+    if (c_retval < 0)
+        return VIR_PY_NONE;
+
+    if (c_retval) {
+        names = malloc(sizeof(*names) * c_retval);
+        if (!names)
+            return VIR_PY_NONE;
+        c_retval = virNodeListDevices(conn, names, c_retval, flags);
+        if (c_retval < 0) {
+            free(names);
+            return VIR_PY_NONE;
+        }
+    }
+    py_retval = PyList_New(c_retval);
+
+    if (names) {
+        for (i = 0;i < c_retval;i++) {
+            PyList_SetItem(py_retval, i, libvirt_constcharPtrWrap(names[i]));
+            free(names[i]);
+        }
+        free(names);
+    }
+
+    return(py_retval);
+}
+
+static PyObject *
+libvirt_virNodeListDevicesByCap(PyObject *self ATTRIBUTE_UNUSED,
+                                PyObject *args) {
+    PyObject *py_retval;
+    char **names = NULL;
+    int c_retval, i;
+    virConnectPtr conn;
+    PyObject *pyobj_conn;
+    char *cap;
+    unsigned int flags;
+
+    if (!PyArg_ParseTuple(args, (char *)"Ozi:virNodeListDevicesByCap",
+                          &pyobj_conn, &cap, &flags))
+        return(NULL);
+    conn = (virConnectPtr) PyvirConnect_Get(pyobj_conn);
+
+    c_retval = virNodeNumOfDevicesByCap(conn, cap, flags);
+    if (c_retval < 0)
+        return VIR_PY_NONE;
+
+    if (c_retval) {
+        names = malloc(sizeof(*names) * c_retval);
+        if (!names)
+            return VIR_PY_NONE;
+        c_retval = virNodeListDevicesByCap(conn, cap, names, c_retval, flags);
+        if (c_retval < 0) {
+            free(names);
+            return VIR_PY_NONE;
+        }
+    }
+    py_retval = PyList_New(c_retval);
+
+    if (names) {
+        for (i = 0;i < c_retval;i++) {
+            PyList_SetItem(py_retval, i, libvirt_constcharPtrWrap(names[i]));
+            free(names[i]);
+        }
+        free(names);
+    }
+
+    return(py_retval);
+}
+
+static PyObject *
+libvirt_virNodeDeviceListCaps(PyObject *self ATTRIBUTE_UNUSED,
+                              PyObject *args) {
+    PyObject *py_retval;
+    char **names = NULL;
+    int c_retval, i;
+    virNodeDevicePtr dev;
+    PyObject *pyobj_dev;
+
+    if (!PyArg_ParseTuple(args, (char *)"O:virNodeDeviceListCaps", &pyobj_dev))
+        return(NULL);
+    dev = (virNodeDevicePtr) PyvirNodeDevice_Get(pyobj_dev);
+
+    c_retval = virNodeDeviceNumOfCaps(dev);
+    if (c_retval < 0)
+        return VIR_PY_NONE;
+
+    if (c_retval) {
+        names = malloc(sizeof(*names) * c_retval);
+        if (!names)
+            return VIR_PY_NONE;
+        c_retval = virNodeDeviceListCaps(dev, names, c_retval);
+        if (c_retval < 0) {
+            free(names);
+            return VIR_PY_NONE;
+        }
+    }
+    py_retval = PyList_New(c_retval);
+
+    if (names) {
+        for (i = 0;i < c_retval;i++) {
+            PyList_SetItem(py_retval, i, libvirt_constcharPtrWrap(names[i]));
+            free(names[i]);
+        }
+        free(names);
+    }
+
+    return(py_retval);
+}
+
+
 /*******************************************
  * Helper functions to avoid importing modules
  * for every callback
@@ -1952,6 +2078,9 @@ static PyMethodDef libvirtMethods[] = {
     {(char *) "virEventRegisterImpl", libvirt_virEventRegisterImpl, METH_VARARGS, NULL},
     {(char *) "virEventInvokeHandleCallback", libvirt_virEventInvokeHandleCallback, METH_VARARGS, NULL},
     {(char *) "virEventInvokeTimeoutCallback", libvirt_virEventInvokeTimeoutCallback, METH_VARARGS, NULL},
+    {(char *) "virNodeListDevices", libvirt_virNodeListDevices, METH_VARARGS, NULL},
+    {(char *) "virNodeListDevicesByCap", libvirt_virNodeListDevicesByCap, METH_VARARGS, NULL},
+    {(char *) "virNodeDeviceListCaps", libvirt_virNodeDeviceListCaps, METH_VARARGS, NULL},
     {NULL, NULL, 0, NULL}
 };
 
diff --git a/python/libvirt-python-api.xml b/python/libvirt-python-api.xml
index f3b82fc..d74806f 100644
--- a/python/libvirt-python-api.xml
+++ b/python/libvirt-python-api.xml
@@ -160,5 +160,23 @@
       <return type='int *' info='the list of information or None in case of error'/>
       <arg name='vol' type='virStorageVolPtr' info='a storage vol object'/>
     </function>
+    <function name='virNodeListDevices' file='python'>
+      <info>list the node devices</info>
+      <arg name='conn' type='virConnectPtr' info='pointer to the hypervisor connection'/>
+      <arg name='flags' type='unsigned int' info='flags (unused; pass 0)'/>
+      <return type='str *' info='the list of Names or None in case of error'/>
+    </function>
+    <function name='virNodeListDevicesByCap' file='python'>
+      <info>list the node devices with a given capability</info>
+      <arg name='conn' type='virConnectPtr' info='pointer to the hypervisor connection'/>
+      <arg name='cap' type='const unsigned char *' info='capability name'/>
+      <arg name='flags' type='unsigned int' info='flags (unused; pass 0)'/>
+      <return type='str *' info='the list of Names or None in case of error'/>
+    </function>
+    <function name='virNodeDeviceListCaps' file='python'>
+      <info>list the node device's capabilities</info>
+      <arg name='dev' type='virNodeDevicePtr' info='pointer to the node device'/>
+      <return type='str *' info='the list of Names or None in case of error'/>
+    </function>
   </symbols>
 </api>
diff --git a/python/libvirt_wrap.h b/python/libvirt_wrap.h
index b46deaf..070284d 100644
--- a/python/libvirt_wrap.h
+++ b/python/libvirt_wrap.h
@@ -65,6 +65,16 @@ typedef struct {
     virStorageVolPtr obj;
 } PyvirStorageVol_Object;
 
+
+#define PyvirNodeDevice_Get(v) (((v) == Py_None) ? NULL : \
+        (((PyvirNodeDevice_Object *)(v))->obj))
+
+typedef struct {
+    PyObject_HEAD
+    virNodeDevicePtr obj;
+} PyvirNodeDevice_Object;
+
+
 #define PyvirEventHandleCallback_Get(v) (((v) == Py_None) ? NULL : \
         (((PyvirEventHandleCallback_Object *)(v))->obj))
 
@@ -89,6 +99,7 @@ typedef struct {
     void* obj;
 } PyvirVoidPtr_Object;
 
+
 PyObject * libvirt_intWrap(int val);
 PyObject * libvirt_longWrap(long val);
 PyObject * libvirt_ulongWrap(unsigned long val);
@@ -104,6 +115,8 @@ PyObject * libvirt_virStorageVolPtrWrap(virStorageVolPtr node);
 PyObject * libvirt_virEventHandleCallbackWrap(virEventHandleCallback node);
 PyObject * libvirt_virEventTimeoutCallbackWrap(virEventTimeoutCallback node);
 PyObject * libvirt_virVoidPtrWrap(void* node);
+PyObject * libvirt_virNodeDevicePtrWrap(virNodeDevicePtr node);
+
 
 /* Provide simple macro statement wrappers (adapted from GLib, in turn from Perl):
  *  LIBVIRT_STMT_START { statements; } LIBVIRT_STMT_END;
diff --git a/python/types.c b/python/types.c
index 1c1db89..bd7fcc2 100644
--- a/python/types.c
+++ b/python/types.c
@@ -164,6 +164,21 @@ libvirt_virConnectPtrWrap(virConnectPtr node)
 }
 
 PyObject *
+libvirt_virNodeDevicePtrWrap(virNodeDevicePtr node)
+{
+    PyObject *ret;
+
+    if (node == NULL) {
+        Py_INCREF(Py_None);
+        return (Py_None);
+    }
+    ret =
+        PyCObject_FromVoidPtrAndDesc((void *) node, (char *) "virNodeDevicePtr",
+                                     NULL);
+    return (ret);
+}
+
+PyObject *
 libvirt_virEventHandleCallbackWrap(virEventHandleCallback node)
 {
     PyObject *ret;

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]