]> xenbits.xensource.com Git - libvirt.git/commitdiff
Implement a node device backend using libudev
authorDavid Allan <dallan@redhat.com>
Thu, 12 Nov 2009 21:48:24 +0000 (22:48 +0100)
committerDaniel Veillard <veillard@redhat.com>
Thu, 12 Nov 2009 21:48:24 +0000 (22:48 +0100)
* configure.in: add new --with-udev, disabled by default, and requiring
  libudev > 145
* src/node_device/node_device_udev.c src/node_device/node_device_udev.h:
  the new node device backend
* src/node_device/node_device_linux_sysfs.c: moved node_device_hal_linux.c
  to a better file name
* src/conf/node_device_conf.c src/conf/node_device_conf.h: add a couple
  of fields in node device definitions, and an API to look them up,
  remove a couple of unused fields from previous patch.
* src/node_device/node_device_driver.c src/node_device/node_device_driver.h:
  plug the new driver
* po/POTFILES.in src/Makefile.am src/libvirt_private.syms: add the new
  files and symbols
* src/util/util.h src/util/util.c: add a new convenience macro
  virBuildPath and virBuildPathInternal() function

16 files changed:
configure.in
daemon/libvirtd.c
po/POTFILES.in
src/Makefile.am
src/conf/node_device_conf.c
src/conf/node_device_conf.h
src/libvirt_private.syms
src/node_device/node_device_driver.c
src/node_device/node_device_driver.h
src/node_device/node_device_hal.h
src/node_device/node_device_hal_linux.c [deleted file]
src/node_device/node_device_linux_sysfs.c [new file with mode: 0644]
src/node_device/node_device_udev.c [new file with mode: 0644]
src/node_device/node_device_udev.h [new file with mode: 0644]
src/util/util.c
src/util/util.h

index 7ad1a9018877e55850dd48b3e400147fa000f50a..dc1e43e5e5b431e80a349eb6719023e389a52329 100644 (file)
@@ -1654,7 +1654,7 @@ 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
+dnl HAL, DeviceKit, or libudev library for host device enumeration
 HAL_REQUIRED=0.0
 HAL_CFLAGS=
 HAL_LIBS=
@@ -1748,8 +1748,46 @@ AM_CONDITIONAL([HAVE_DEVKIT], [test "x$with_devkit" = "xyes"])
 AC_SUBST([DEVKIT_CFLAGS])
 AC_SUBST([DEVKIT_LIBS])
 
+UDEV_REQUIRED=145
+UDEV_CFLAGS=
+UDEV_LIBS=
+AC_ARG_WITH([udev],
+  [  --with-udev        use libudev for host device enumeration],
+  [],
+  [with_udev=check])
+
+if test "$with_libvirtd" = "no" ; then
+  with_udev=no
+fi
+if test "x$with_udev" = "xyes" -o "x$with_udev" = "xcheck"; then
+  PKG_CHECK_MODULES(UDEV, libudev >= $UDEV_REQUIRED,
+    [with_udev=yes], [
+    if test "x$with_udev" = "xcheck" ; then
+       with_udev=no
+    else
+       AC_MSG_ERROR(
+         [You must install libudev-devel >= $UDEV_REQUIRED to compile libvirt])
+    fi
+  ])
+  if test "x$with_udev" = "xyes" ; then
+    AC_DEFINE_UNQUOTED([HAVE_UDEV], 1,
+      [use UDEV for host device enumeration])
+
+    old_CFLAGS=$CFLAGS
+    old_LDFLAGS=$LDFLAGS
+    CFLAGS="$CFLAGS $UDEV_CFLAGS"
+    LDFLAGS="$LDFLAGS $UDEV_LIBS"
+    AC_CHECK_FUNCS([udev_new],,[with_udev=no])
+    CFLAGS="$old_CFLAGS"
+    LDFLAGS="$old_LDFLAGS"
+  fi
+fi
+AM_CONDITIONAL([HAVE_UDEV], [test "x$with_udev" = "xyes"])
+AC_SUBST([UDEV_CFLAGS])
+AC_SUBST([UDEV_LIBS])
+
 with_nodedev=no;
-if test "$with_devkit" = "yes" -o "$with_hal" = "yes";
+if test "$with_devkit" = "yes" -o "$with_hal" = "yes" -o "$with_udev" = "yes";
 then
   with_nodedev=yes
   AC_DEFINE_UNQUOTED([WITH_NODE_DEVICES], 1, [with node device driver])
@@ -1914,6 +1952,11 @@ AC_MSG_NOTICE([  devkit: $DEVKIT_CFLAGS $DEVKIT_LIBS])
 else
 AC_MSG_NOTICE([  devkit: no])
 fi
+if test "$with_udev" = "yes" ; then
+AC_MSG_NOTICE([    udev: $UDEV_CFLAGS $UDEV_LIBS])
+else
+AC_MSG_NOTICE([    udev: no])
+fi
 if test "$with_netcf" = "yes" ; then
 AC_MSG_NOTICE([   netcf: $NETCF_CFLAGS $NETCF_LIBS])
 else
index 01c9bbc6774e903653243b68c250d27f59a1beb3..ef07460fdc104361891cd0bf515d81394974b013 100644 (file)
@@ -877,8 +877,7 @@ static struct qemud_server *qemudInitialize(void) {
 #ifdef WITH_STORAGE_DIR
     storageRegister();
 #endif
-#if defined(WITH_NODE_DEVICES) && \
-    (defined(HAVE_HAL) || defined(HAVE_DEVKIT))
+#if defined(WITH_NODE_DEVICES)
     nodedevRegister();
 #endif
     secretRegister();
index 000be092176579106fdfe2ca7125ba2b0856368c..266f70c7350115747fbbaf08148306332a859b34 100644 (file)
@@ -17,7 +17,8 @@ src/lxc/lxc_controller.c
 src/lxc/lxc_driver.c
 src/network/bridge_driver.c
 src/node_device/node_device_driver.c
-src/node_device/node_device_hal_linux.c
+src/node_device/node_device_linux_sysfs.c
+src/node_device/node_device_udev.c
 src/nodeinfo.c
 src/opennebula/one_conf.c
 src/opennebula/one_driver.c
index 92dbae4c70d3e51711ed3cdb0fdfa2bd7cba6aae..afe7eac0ea7eb68529b7f42df09631fc3f3572ea 100644 (file)
@@ -259,16 +259,20 @@ SECURITY_DRIVER_APPARMOR_SOURCES =                                \
 
 
 NODE_DEVICE_DRIVER_SOURCES =                                   \
-               node_device/node_device_driver.c node_device/node_device_driver.h
+               node_device/node_device_driver.c \
+               node_device/node_device_driver.h \
+               node_device/node_device_linux_sysfs.c
 
 NODE_DEVICE_DRIVER_HAL_SOURCES =                               \
                node_device/node_device_hal.c                   \
-               node_device/node_device_hal.h                   \
-               node_device/node_device_hal_linux.c
+               node_device/node_device_hal.h
 
 NODE_DEVICE_DRIVER_DEVKIT_SOURCES =                            \
                node_device/node_device_devkit.c
 
+NODE_DEVICE_DRIVER_UDEV_SOURCES =                              \
+               node_device/node_device_udev.c
+
 
 #########################
 #
@@ -647,6 +651,11 @@ libvirt_driver_nodedev_la_SOURCES += $(NODE_DEVICE_DRIVER_DEVKIT_SOURCES)
 libvirt_driver_nodedev_la_CFLAGS += $(DEVKIT_CFLAGS)
 libvirt_driver_nodedev_la_LDFLAGS += $(DEVKIT_LIBS)
 endif
+if HAVE_UDEV
+libvirt_driver_nodedev_la_SOURCES += $(NODE_DEVICE_DRIVER_UDEV_SOURCES)
+libvirt_driver_nodedev_la_CFLAGS += $(UDEV_CFLAGS)
+libvirt_driver_nodedev_la_LDFLAGS += $(UDEV_LIBS)
+endif
 
 if WITH_DRIVER_MODULES
 libvirt_driver_nodedev_la_LDFLAGS += -module -avoid-version
@@ -696,6 +705,7 @@ EXTRA_DIST +=                                                       \
                $(NODE_DEVICE_DRIVER_SOURCES)                   \
                $(NODE_DEVICE_DRIVER_HAL_SOURCES)               \
                $(NODE_DEVICE_DRIVER_DEVKIT_SOURCES)            \
+               $(NODE_DEVICE_DRIVER_UDEV_SOURCES)              \
                $(SECURITY_DRIVER_SELINUX_SOURCES)              \
                $(SECURITY_DRIVER_APPARMOR_SOURCES)             \
                $(SECRET_DRIVER_SOURCES)                        \
index 626c8aae05db9aa7e6ef34f9fc03e561f8dd135e..379787dbb1449774d1350dc9a63e7d3df07d0168 100644 (file)
@@ -91,6 +91,26 @@ int virNodeDeviceHasCap(const virNodeDeviceObjPtr dev, const char *cap)
     return 0;
 }
 
+
+virNodeDeviceObjPtr
+virNodeDeviceFindBySysfsPath(const virNodeDeviceObjListPtr devs,
+                             const char *sysfs_path)
+{
+    unsigned int i;
+
+    for (i = 0; i < devs->count; i++) {
+        virNodeDeviceObjLock(devs->objs[i]);
+        if ((devs->objs[i]->def->sysfs_path != NULL) &&
+            (STREQ(devs->objs[i]->def->sysfs_path, sysfs_path))) {
+            return devs->objs[i];
+        }
+        virNodeDeviceObjUnlock(devs->objs[i]);
+    }
+
+    return NULL;
+}
+
+
 virNodeDeviceObjPtr virNodeDeviceFindByName(const virNodeDeviceObjListPtr devs,
                                             const char *name)
 {
@@ -117,6 +137,8 @@ void virNodeDeviceDefFree(virNodeDeviceDefPtr def)
     VIR_FREE(def->name);
     VIR_FREE(def->parent);
     VIR_FREE(def->driver);
+    VIR_FREE(def->sysfs_path);
+    VIR_FREE(def->parent_sysfs_path);
 
     caps = def->caps;
     while (caps) {
@@ -228,9 +250,17 @@ char *virNodeDeviceDefFormat(virConnectPtr conn,
 
     virBufferAddLit(&buf, "<device>\n");
     virBufferEscapeString(&buf, "  <name>%s</name>\n", def->name);
-
-    if (def->parent)
+    if (def->sysfs_path != NULL) {
+        virBufferEscapeString(&buf, "  <sysfs_path>%s</sysfs_path>\n",
+                              def->sysfs_path);
+    }
+    if (def->parent) {
         virBufferEscapeString(&buf, "  <parent>%s</parent>\n", def->parent);
+    }
+    if (def->parent_sysfs_path != NULL) {
+        virBufferEscapeString(&buf, "  <parent_sysfs_path>%s</parent_sysfs_path>\n",
+                              def->parent_sysfs_path);
+    }
     if (def->driver) {
         virBufferAddLit(&buf, "  <driver>\n");
         virBufferEscapeString(&buf, "    <name>%s</name>\n", def->driver);
@@ -248,12 +278,6 @@ char *virNodeDeviceDefFormat(virConnectPtr conn,
             if (data->system.product_name)
                 virBufferEscapeString(&buf, "    <product>%s</product>\n",
                                       data->system.product_name);
-            if (data->system.dmi_devpath)
-                virBufferEscapeString(&buf, "    <dmi_devpath>%s</dmi_devpath>\n",
-                                      data->system.dmi_devpath);
-            if (data->system.description)
-                virBufferEscapeString(&buf, "    <description>%s</description>\n",
-                                      data->system.description);
             virBufferAddLit(&buf, "    <hardware>\n");
             if (data->system.hardware.vendor_name)
                 virBufferEscapeString(&buf, "      <vendor>%s</vendor>\n",
@@ -331,9 +355,6 @@ char *virNodeDeviceDefFormat(virConnectPtr conn,
                               data->usb_if.subclass);
             virBufferVSprintf(&buf, "    <protocol>%d</protocol>\n",
                               data->usb_if.protocol);
-            if (data->usb_if.interface_name)
-                virBufferVSprintf(&buf, "    <interface_name>%s</interface_name>\n",
-                                  data->usb_if.interface_name);
             if (data->usb_if.description)
                 virBufferVSprintf(&buf, "    <description>%s</description>\n",
                                   data->usb_if.description);
@@ -1340,8 +1361,6 @@ void virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps)
     switch (caps->type) {
     case VIR_NODE_DEV_CAP_SYSTEM:
         VIR_FREE(data->system.product_name);
-        VIR_FREE(data->system.dmi_devpath);
-        VIR_FREE(data->system.description);
         VIR_FREE(data->system.hardware.vendor_name);
         VIR_FREE(data->system.hardware.version);
         VIR_FREE(data->system.hardware.serial);
@@ -1358,7 +1377,6 @@ void virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps)
         VIR_FREE(data->usb_dev.vendor_name);
         break;
     case VIR_NODE_DEV_CAP_USB_INTERFACE:
-        VIR_FREE(data->usb_if.interface_name);
         VIR_FREE(data->usb_if.description);
         break;
     case VIR_NODE_DEV_CAP_NET:
index f70184d3d5bac456f71aa65885c119d5edbcd596..e97242ada98a24035bc7d545c25a6bc0c68c4b8c 100644 (file)
@@ -82,8 +82,6 @@ struct _virNodeDevCapsDef {
     union _virNodeDevCapData {
         struct {
             char *product_name;
-            char *description;
-            char *dmi_devpath;
             struct {
                 char *vendor_name;
                 char *version;
@@ -120,7 +118,6 @@ struct _virNodeDevCapsDef {
             unsigned _class;           /* "class" is reserved in C */
             unsigned subclass;
             unsigned protocol;
-            char *interface_name;
             char *description;
         } usb_if;
         struct {
@@ -164,7 +161,9 @@ typedef struct _virNodeDeviceDef virNodeDeviceDef;
 typedef virNodeDeviceDef *virNodeDeviceDefPtr;
 struct _virNodeDeviceDef {
     char *name;                         /* device name (unique on node) */
+    char *sysfs_path;                   /* udev name/sysfs path */
     char *parent;                      /* optional parent device name */
+    char *parent_sysfs_path;            /* udev parent name/sysfs path */
     char *driver;                       /* optional driver name */
     virNodeDevCapsDefPtr caps;         /* optional device capabilities */
 };
@@ -206,6 +205,9 @@ int virNodeDeviceHasCap(const virNodeDeviceObjPtr dev, const char *cap);
 
 virNodeDeviceObjPtr virNodeDeviceFindByName(const virNodeDeviceObjListPtr devs,
                                             const char *name);
+virNodeDeviceObjPtr
+virNodeDeviceFindBySysfsPath(const virNodeDeviceObjListPtr devs,
+                             const char *sysfs_path);
 
 virNodeDeviceObjPtr virNodeDeviceAssignDef(virConnectPtr conn,
                                            virNodeDeviceObjListPtr devs,
index 05068672cb09bfa6c7f27b92945bb0c052b4a0ee..c473d499c50b7dad220efa9bd3239cd8160d44f0 100644 (file)
@@ -341,6 +341,7 @@ virNodeDeviceHasCap;
 virNodeDeviceObjRemove;
 virNodeDevCapTypeToString;
 virNodeDeviceFindByName;
+virNodeDeviceFindBySysfsPath;
 virNodeDeviceObjListFree;
 virNodeDeviceDefFree;
 virNodeDevCapsDefFree;
index f33ff48639cc1077ff49943ca521d5cd5bb9fde5..c13990737c686b32725b9d50a08542aa62dff84b 100644 (file)
@@ -70,7 +70,10 @@ static int update_caps(virNodeDeviceObjPtr dev)
 }
 
 
-#ifdef __linux__
+#if defined (__linux__) && defined (HAVE_HAL)
+/* Under libudev changes to the driver name should be picked up as
+ * "change" events, so we don't call update driver name unless we're
+ * using the HAL backend. */
 static int update_driver_name(virConnectPtr conn,
                               virNodeDeviceObjPtr dev)
 {
@@ -658,10 +661,10 @@ void registerCommonNodeFuncs(virDeviceMonitorPtr driver)
 
 
 int nodedevRegister(void) {
-#if defined(HAVE_HAL) && defined(HAVE_DEVKIT)
+#if defined(HAVE_HAL) && defined(HAVE_UDEV)
     /* Register only one of these two - they conflict */
     if (halNodeRegister() == -1)
-        return devkitNodeRegister();
+        return udevNodeRegister();
     return 0;
 #else
 #ifdef HAVE_HAL
@@ -670,5 +673,8 @@ int nodedevRegister(void) {
 #ifdef HAVE_DEVKIT
     return devkitNodeRegister();
 #endif
+#ifdef HAVE_UDEV
+    return udevNodeRegister();
+#endif
 #endif
 }
index db016244efc8be536ebc6c6adf45d2d5176fa143..5be07812e57f56703c5a1437e30b45510faec02d 100644 (file)
@@ -45,6 +45,9 @@ int halNodeRegister(void);
 #ifdef HAVE_DEVKIT
 int devkitNodeRegister(void);
 #endif
+#ifdef HAVE_UDEV
+int udevNodeRegister(void);
+#endif
 
 void nodeDeviceLock(virDeviceMonitorStatePtr driver);
 void nodeDeviceUnlock(virDeviceMonitorStatePtr driver);
@@ -53,4 +56,23 @@ void registerCommonNodeFuncs(virDeviceMonitorPtr mon);
 
 int nodedevRegister(void);
 
+#ifdef __linux__
+
+#define check_fc_host(d) check_fc_host_linux(d)
+int check_fc_host_linux(union _virNodeDevCapData *d);
+
+#define check_vport_capable(d) check_vport_capable_linux(d)
+int check_vport_capable_linux(union _virNodeDevCapData *d);
+
+#define read_wwn(host, file, wwn) read_wwn_linux(host, file, wwn)
+int read_wwn_linux(int host, const char *file, char **wwn);
+
+#else  /* __linux__ */
+
+#define check_fc_host(d)
+#define check_vport_capable(d)
+#define read_wwn(host, file, wwn)
+
+#endif /* __linux__ */
+
 #endif /* __VIR_NODE_DEVICE_H__ */
index c859fe382e40e96f686b38032a8ce56936cf92c8..8ac8a3539f04b9eeae087edb3d9ffab58ea78b6c 100644 (file)
 #ifndef __VIR_NODE_DEVICE_HAL_H__
 #define __VIR_NODE_DEVICE_HAL_H__
 
-#ifdef __linux__
-
-#define check_fc_host(d) check_fc_host_linux(d)
-int check_fc_host_linux(union _virNodeDevCapData *d);
-
-#define check_vport_capable(d) check_vport_capable_linux(d)
-int check_vport_capable_linux(union _virNodeDevCapData *d);
-
-#define read_wwn(host, file, wwn) read_wwn_linux(host, file, wwn)
-int read_wwn_linux(int host, const char *file, char **wwn);
-
-#else  /* __linux__ */
-
-#define check_fc_host(d)
-#define check_vport_capable(d)
-#define read_wwn(host, file, wwn)
-
-#endif /* __linux__ */
-
 #endif /* __VIR_NODE_DEVICE_HAL_H__ */
diff --git a/src/node_device/node_device_hal_linux.c b/src/node_device/node_device_hal_linux.c
deleted file mode 100644 (file)
index b7cf782..0000000
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * node_device_hal_linuc.c: Linux specific code to gather device data
- * not available through HAL.
- *
- * Copyright (C) 2009 Red Hat, Inc.
- *
- * 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
- *
- */
-
-#include <config.h>
-
-#include <fcntl.h>
-
-#include "node_device_driver.h"
-#include "node_device_hal.h"
-#include "virterror_internal.h"
-#include "memory.h"
-#include "logging.h"
-
-#define VIR_FROM_THIS VIR_FROM_NODEDEV
-
-#ifdef __linux__
-
-static int open_wwn_file(const char *prefix,
-                         int host,
-                         const char *file,
-                         int *fd)
-{
-    int retval = 0;
-    char *wwn_path = NULL;
-
-    if (virAsprintf(&wwn_path, "%s/host%d/%s", prefix, host, file) < 0) {
-        virReportOOMError(NULL);
-        retval = -1;
-        goto out;
-    }
-
-    /* fd will be closed by caller */
-    if ((*fd = open(wwn_path, O_RDONLY)) != -1) {
-        VIR_ERROR(_("Opened WWN path '%s' for reading"),
-                  wwn_path);
-    } else {
-        VIR_ERROR(_("Failed to open WWN path '%s' for reading"),
-                  wwn_path);
-    }
-
-out:
-    VIR_FREE(wwn_path);
-    return retval;
-}
-
-
-int read_wwn_linux(int host, const char *file, char **wwn)
-{
-    char *p = NULL;
-    int fd = -1, retval = 0;
-    char buf[64];
-
-    if (open_wwn_file(LINUX_SYSFS_FC_HOST_PREFIX, host, file, &fd) < 0) {
-            goto out;
-    }
-
-    memset(buf, 0, sizeof(buf));
-    if (saferead(fd, buf, sizeof(buf)) < 0) {
-        retval = -1;
-        VIR_DEBUG(_("Failed to read WWN for host%d '%s'"),
-                  host, file);
-        goto out;
-    }
-
-    p = strstr(buf, "0x");
-    if (p != NULL) {
-        p += strlen("0x");
-    } else {
-        p = buf;
-    }
-
-    *wwn = strndup(p, sizeof(buf));
-    if (*wwn == NULL) {
-        virReportOOMError(NULL);
-        retval = -1;
-        goto out;
-    }
-
-    p = strchr(*wwn, '\n');
-    if (p != NULL) {
-        *p = '\0';
-    }
-
-out:
-    if (fd != -1) {
-        close(fd);
-    }
-    return retval;
-}
-
-
-int check_fc_host_linux(union _virNodeDevCapData *d)
-{
-    char *sysfs_path = NULL;
-    int retval = 0;
-    struct stat st;
-
-    VIR_DEBUG(_("Checking if host%d is an FC HBA"), d->scsi_host.host);
-
-    if (virAsprintf(&sysfs_path, "%s/host%d",
-                    LINUX_SYSFS_FC_HOST_PREFIX,
-                    d->scsi_host.host) < 0) {
-        virReportOOMError(NULL);
-        retval = -1;
-        goto out;
-    }
-
-    if (stat(sysfs_path, &st) != 0) {
-        /* Not an FC HBA; not an error, either. */
-        goto out;
-    }
-
-    d->scsi_host.flags |= VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST;
-
-    if (read_wwn(d->scsi_host.host,
-                 "port_name",
-                 &d->scsi_host.wwpn) == -1) {
-        VIR_ERROR(_("Failed to read WWPN for host%d"),
-                  d->scsi_host.host);
-        retval = -1;
-        goto out;
-    }
-
-    if (read_wwn(d->scsi_host.host,
-                 "node_name",
-                 &d->scsi_host.wwnn) == -1) {
-        VIR_ERROR(_("Failed to read WWNN for host%d"),
-                  d->scsi_host.host);
-        retval = -1;
-    }
-
-out:
-    if (retval == -1) {
-        VIR_FREE(d->scsi_host.wwnn);
-        VIR_FREE(d->scsi_host.wwpn);
-    }
-    VIR_FREE(sysfs_path);
-    return 0;
-}
-
-
-int check_vport_capable_linux(union _virNodeDevCapData *d)
-{
-    char *sysfs_path = NULL;
-    struct stat st;
-    int retval = 0;
-
-    if (virAsprintf(&sysfs_path, "%s/host%d/vport_create",
-                    LINUX_SYSFS_FC_HOST_PREFIX,
-                    d->scsi_host.host) < 0) {
-        virReportOOMError(NULL);
-        retval = -1;
-        goto out;
-    }
-
-    if (stat(sysfs_path, &st) != 0) {
-        /* Not a vport capable HBA; not an error, either. */
-        goto out;
-    }
-
-    d->scsi_host.flags |= VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS;
-
-out:
-    VIR_FREE(sysfs_path);
-    return retval;
-}
-
-#endif /* __linux__ */
diff --git a/src/node_device/node_device_linux_sysfs.c b/src/node_device/node_device_linux_sysfs.c
new file mode 100644 (file)
index 0000000..b7cf782
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * node_device_hal_linuc.c: Linux specific code to gather device data
+ * not available through HAL.
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * 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
+ *
+ */
+
+#include <config.h>
+
+#include <fcntl.h>
+
+#include "node_device_driver.h"
+#include "node_device_hal.h"
+#include "virterror_internal.h"
+#include "memory.h"
+#include "logging.h"
+
+#define VIR_FROM_THIS VIR_FROM_NODEDEV
+
+#ifdef __linux__
+
+static int open_wwn_file(const char *prefix,
+                         int host,
+                         const char *file,
+                         int *fd)
+{
+    int retval = 0;
+    char *wwn_path = NULL;
+
+    if (virAsprintf(&wwn_path, "%s/host%d/%s", prefix, host, file) < 0) {
+        virReportOOMError(NULL);
+        retval = -1;
+        goto out;
+    }
+
+    /* fd will be closed by caller */
+    if ((*fd = open(wwn_path, O_RDONLY)) != -1) {
+        VIR_ERROR(_("Opened WWN path '%s' for reading"),
+                  wwn_path);
+    } else {
+        VIR_ERROR(_("Failed to open WWN path '%s' for reading"),
+                  wwn_path);
+    }
+
+out:
+    VIR_FREE(wwn_path);
+    return retval;
+}
+
+
+int read_wwn_linux(int host, const char *file, char **wwn)
+{
+    char *p = NULL;
+    int fd = -1, retval = 0;
+    char buf[64];
+
+    if (open_wwn_file(LINUX_SYSFS_FC_HOST_PREFIX, host, file, &fd) < 0) {
+            goto out;
+    }
+
+    memset(buf, 0, sizeof(buf));
+    if (saferead(fd, buf, sizeof(buf)) < 0) {
+        retval = -1;
+        VIR_DEBUG(_("Failed to read WWN for host%d '%s'"),
+                  host, file);
+        goto out;
+    }
+
+    p = strstr(buf, "0x");
+    if (p != NULL) {
+        p += strlen("0x");
+    } else {
+        p = buf;
+    }
+
+    *wwn = strndup(p, sizeof(buf));
+    if (*wwn == NULL) {
+        virReportOOMError(NULL);
+        retval = -1;
+        goto out;
+    }
+
+    p = strchr(*wwn, '\n');
+    if (p != NULL) {
+        *p = '\0';
+    }
+
+out:
+    if (fd != -1) {
+        close(fd);
+    }
+    return retval;
+}
+
+
+int check_fc_host_linux(union _virNodeDevCapData *d)
+{
+    char *sysfs_path = NULL;
+    int retval = 0;
+    struct stat st;
+
+    VIR_DEBUG(_("Checking if host%d is an FC HBA"), d->scsi_host.host);
+
+    if (virAsprintf(&sysfs_path, "%s/host%d",
+                    LINUX_SYSFS_FC_HOST_PREFIX,
+                    d->scsi_host.host) < 0) {
+        virReportOOMError(NULL);
+        retval = -1;
+        goto out;
+    }
+
+    if (stat(sysfs_path, &st) != 0) {
+        /* Not an FC HBA; not an error, either. */
+        goto out;
+    }
+
+    d->scsi_host.flags |= VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST;
+
+    if (read_wwn(d->scsi_host.host,
+                 "port_name",
+                 &d->scsi_host.wwpn) == -1) {
+        VIR_ERROR(_("Failed to read WWPN for host%d"),
+                  d->scsi_host.host);
+        retval = -1;
+        goto out;
+    }
+
+    if (read_wwn(d->scsi_host.host,
+                 "node_name",
+                 &d->scsi_host.wwnn) == -1) {
+        VIR_ERROR(_("Failed to read WWNN for host%d"),
+                  d->scsi_host.host);
+        retval = -1;
+    }
+
+out:
+    if (retval == -1) {
+        VIR_FREE(d->scsi_host.wwnn);
+        VIR_FREE(d->scsi_host.wwpn);
+    }
+    VIR_FREE(sysfs_path);
+    return 0;
+}
+
+
+int check_vport_capable_linux(union _virNodeDevCapData *d)
+{
+    char *sysfs_path = NULL;
+    struct stat st;
+    int retval = 0;
+
+    if (virAsprintf(&sysfs_path, "%s/host%d/vport_create",
+                    LINUX_SYSFS_FC_HOST_PREFIX,
+                    d->scsi_host.host) < 0) {
+        virReportOOMError(NULL);
+        retval = -1;
+        goto out;
+    }
+
+    if (stat(sysfs_path, &st) != 0) {
+        /* Not a vport capable HBA; not an error, either. */
+        goto out;
+    }
+
+    d->scsi_host.flags |= VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS;
+
+out:
+    VIR_FREE(sysfs_path);
+    return retval;
+}
+
+#endif /* __linux__ */
diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c
new file mode 100644 (file)
index 0000000..d608b76
--- /dev/null
@@ -0,0 +1,1519 @@
+/*
+ * node_device_udev.c: node device enumeration - libudev implementation
+ *
+ * Copyright (C) 2009 Red Hat
+ *
+ * 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: Dave Allan <dallan@redhat.com>
+ */
+
+#include <config.h>
+#include <libudev.h>
+#include <scsi/scsi.h>
+#include <c-ctype.h>
+
+#include "node_device_udev.h"
+#include "virterror_internal.h"
+#include "node_device_conf.h"
+#include "node_device_driver.h"
+#include "driver.h"
+#include "datatypes.h"
+#include "logging.h"
+#include "memory.h"
+#include "uuid.h"
+#include "util.h"
+#include "buf.h"
+#include "daemon/event.h"
+
+#define VIR_FROM_THIS VIR_FROM_NODEDEV
+
+static virDeviceMonitorStatePtr driverState = NULL;
+
+static int udevStrToLong_ull(char const *s,
+                             char **end_ptr,
+                             int base,
+                             unsigned long long *result)
+{
+    int ret = 0;
+
+    ret = virStrToLong_ull(s, end_ptr, base, result);
+    if (ret != 0) {
+        VIR_ERROR("Failed to convert '%s' to unsigned long long\n", s);
+    } else {
+        VIR_DEBUG("Converted '%s' to unsigned long %llu\n", s, *result);
+    }
+
+    return ret;
+}
+
+
+static int udevStrToLong_ui(char const *s,
+                            char **end_ptr,
+                            int base,
+                            unsigned int *result)
+{
+    int ret = 0;
+
+    ret = virStrToLong_ui(s, end_ptr, base, result);
+    if (ret != 0) {
+        VIR_ERROR("Failed to convert '%s' to unsigned int\n", s);
+    } else {
+        VIR_DEBUG("Converted '%s' to unsigned int %u\n", s, *result);
+    }
+
+    return ret;
+}
+
+static int udevStrToLong_i(char const *s,
+                           char **end_ptr,
+                           int base,
+                           int *result)
+{
+    int ret = 0;
+
+    ret = virStrToLong_i(s, end_ptr, base, result);
+    if (ret != 0) {
+        VIR_ERROR("Failed to convert '%s' to int\n", s);
+    } else {
+        VIR_DEBUG("Converted '%s' to int %u\n", s, *result);
+    }
+
+    return ret;
+}
+
+
+/* This function allocates memory from the heap for the property
+ * value.  That memory must be later freed by some other code. */
+static int udevGetDeviceProperty(struct udev_device *udev_device,
+                                 const char *property_key,
+                                 char **property_value)
+{
+    const char *udev_value = NULL;
+    int ret = PROPERTY_FOUND;
+
+    udev_value = udev_device_get_property_value(udev_device, property_key);
+    if (udev_value == NULL) {
+        VIR_INFO(_("udev reports device '%s' does not have property '%s'"),
+                 udev_device_get_sysname(udev_device), property_key);
+        ret = PROPERTY_MISSING;
+        goto out;
+    }
+
+    /* If this allocation is changed, the comment at the beginning
+     * of the function must also be changed. */
+    *property_value = strdup(udev_value);
+    if (*property_value == NULL) {
+        VIR_ERROR("Failed to allocate memory for property value for "
+                  "property key '%s' on device with sysname '%s'",
+                  property_key, udev_device_get_sysname(udev_device));
+        virReportOOMError(NULL);
+        ret = PROPERTY_ERROR;
+        goto out;
+    }
+
+    VIR_DEBUG("Found property key '%s' value '%s' "
+              "for device with sysname '%s'\n",
+              property_key, *property_value,
+              udev_device_get_sysname(udev_device));
+
+out:
+    return ret;
+}
+
+
+static int udevGetStringProperty(struct udev_device *udev_device,
+                                 const char *property_key,
+                                 char **value)
+{
+    return udevGetDeviceProperty(udev_device, property_key, value);
+}
+
+
+static int udevGetIntProperty(struct udev_device *udev_device,
+                              const char *property_key,
+                              int *value,
+                              int base)
+{
+    char *udev_value = NULL;
+    int ret = PROPERTY_FOUND;
+
+    ret = udevGetDeviceProperty(udev_device, property_key, &udev_value);
+
+    if (ret == PROPERTY_FOUND) {
+        if (udevStrToLong_i(udev_value, NULL, base, value) != 0) {
+            ret = PROPERTY_ERROR;
+        }
+    }
+
+    VIR_FREE(udev_value);
+    return ret;
+}
+
+
+static int udevGetUintProperty(struct udev_device *udev_device,
+                               const char *property_key,
+                               unsigned int *value,
+                               int base)
+{
+    char *udev_value = NULL;
+    int ret = PROPERTY_FOUND;
+
+    ret = udevGetDeviceProperty(udev_device, property_key, &udev_value);
+
+    if (ret == PROPERTY_FOUND) {
+        if (udevStrToLong_ui(udev_value, NULL, base, value) != 0) {
+            ret = PROPERTY_ERROR;
+        }
+    }
+
+    VIR_FREE(udev_value);
+    return ret;
+}
+
+
+/* This function allocates memory from the heap for the property
+ * value.  That memory must be later freed by some other code. */
+static int udevGetDeviceSysfsAttr(struct udev_device *udev_device,
+                                  const char *attr_name,
+                                  char **attr_value)
+{
+    const char *udev_value = NULL;
+    int ret = PROPERTY_FOUND;
+
+    udev_value = udev_device_get_sysattr_value(udev_device, attr_name);
+    if (udev_value == NULL) {
+        VIR_INFO(_("udev reports device '%s' does not have sysfs attr '%s'"),
+                 udev_device_get_sysname(udev_device), attr_name);
+        ret = PROPERTY_MISSING;
+        goto out;
+    }
+
+    /* If this allocation is changed, the comment at the beginning
+     * of the function must also be changed. */
+    *attr_value = strdup(udev_value);
+    if (*attr_value == NULL) {
+        VIR_ERROR("Failed to allocate memory for sysfs attribute value for "
+                  "sysfs attribute '%s' on device with sysname '%s'",
+                  attr_name, udev_device_get_sysname(udev_device));
+        virReportOOMError(NULL);
+        ret = PROPERTY_ERROR;
+        goto out;
+    }
+
+    VIR_DEBUG("Found sysfs attribute '%s' value '%s' "
+              "for device with sysname '%s'\n",
+              attr_name, *attr_value,
+              udev_device_get_sysname(udev_device));
+
+out:
+    return ret;
+}
+
+
+static int udevGetStringSysfsAttr(struct udev_device *udev_device,
+                                  const char *attr_name,
+                                  char **value)
+{
+    char *tmp = NULL;
+    int ret = PROPERTY_MISSING;
+
+    ret = udevGetDeviceSysfsAttr(udev_device, attr_name, &tmp);
+
+    if (tmp != NULL && (STREQ(tmp, ""))) {
+        VIR_FREE(tmp);
+        tmp = NULL;
+        ret = PROPERTY_MISSING;
+    }
+
+    *value = tmp;
+
+    return ret;
+}
+
+
+static int udevGetIntSysfsAttr(struct udev_device *udev_device,
+                               const char *attr_name,
+                               int *value,
+                               int base)
+{
+    char *udev_value = NULL;
+    int ret = PROPERTY_FOUND;
+
+    ret = udevGetDeviceSysfsAttr(udev_device, attr_name, &udev_value);
+
+    if (ret == PROPERTY_FOUND) {
+        if (udevStrToLong_i(udev_value, NULL, base, value) != 0) {
+            ret = PROPERTY_ERROR;
+        }
+    }
+
+    VIR_FREE(udev_value);
+    return ret;
+}
+
+
+static int udevGetUintSysfsAttr(struct udev_device *udev_device,
+                                const char *attr_name,
+                                unsigned int *value,
+                                int base)
+{
+    char *udev_value = NULL;
+    int ret = PROPERTY_FOUND;
+
+    ret = udevGetDeviceSysfsAttr(udev_device, attr_name, &udev_value);
+
+    if (ret == PROPERTY_FOUND) {
+        if (udevStrToLong_ui(udev_value, NULL, base, value) != 0) {
+            ret = PROPERTY_ERROR;
+        }
+    }
+
+    VIR_FREE(udev_value);
+    return ret;
+}
+
+
+static int udevGetUint64SysfsAttr(struct udev_device *udev_device,
+                                  const char *attr_name,
+                                  unsigned long long *value)
+{
+    char *udev_value = NULL;
+    int ret = PROPERTY_FOUND;
+
+    ret = udevGetDeviceSysfsAttr(udev_device, attr_name, &udev_value);
+
+    if (ret == PROPERTY_FOUND) {
+        if (udevStrToLong_ull(udev_value, NULL, 0, value) != 0) {
+            ret = PROPERTY_ERROR;
+        }
+    }
+
+    VIR_FREE(udev_value);
+    return ret;
+}
+
+
+static int udevGenerateDeviceName(struct udev_device *device,
+                                  virNodeDeviceDefPtr def,
+                                  const char *s)
+{
+    int ret = 0, i = 0;
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+    virBufferVSprintf(&buf, "%s_%s",
+                      udev_device_get_subsystem(device),
+                      udev_device_get_sysname(device));
+
+    if (s != NULL) {
+        virBufferVSprintf(&buf, "_%s", s);
+    }
+
+    if (virBufferError(&buf)) {
+        VIR_ERROR("Buffer error when generating device name for device "
+                  "with sysname '%s'\n", udev_device_get_sysname(device));
+        ret = -1;
+    }
+
+    def->name = virBufferContentAndReset(&buf);
+
+    for (i = 0; i < strlen(def->name) ; i++) {
+        if (!(c_isalnum(*(def->name + i)))) {
+            *(def->name + i) = '_';
+        }
+    }
+
+    return ret;
+}
+
+
+static void udevLogFunction(struct udev *udev ATTRIBUTE_UNUSED,
+                            int priority ATTRIBUTE_UNUSED,
+                            const char *file,
+                            int line,
+                            const char *fn,
+                            const char *fmt,
+                            va_list args)
+{
+    VIR_ERROR_INT(file, fn, line, fmt, args);
+}
+
+
+static int udevProcessPCI(struct udev_device *device,
+                          virNodeDeviceDefPtr def)
+{
+    const char *devpath = NULL;
+    union _virNodeDevCapData *data = &def->caps->data;
+    int ret = -1;
+
+    devpath = udev_device_get_devpath(device);
+
+    if (udevGetUintProperty(device,
+                            "PCI_CLASS",
+                            &data->pci_dev.class,
+                            16) == PROPERTY_ERROR) {
+        goto out;
+    }
+
+    char *p = strrchr(devpath, '/');
+
+    if ((p == NULL) || (udevStrToLong_ui(p+1,
+                                         &p,
+                                         16,
+                                         &data->pci_dev.domain) == -1)) {
+        goto out;
+    }
+
+    if ((p == NULL) || (udevStrToLong_ui(p+1,
+                                         &p,
+                                         16,
+                                         &data->pci_dev.bus) == -1)) {
+        goto out;
+    }
+
+    if ((p == NULL) || (udevStrToLong_ui(p+1,
+                                         &p,
+                                         16,
+                                         &data->pci_dev.slot) == -1)) {
+        goto out;
+    }
+
+    if ((p == NULL) || (udevStrToLong_ui(p+1,
+                                         &p,
+                                         16,
+                                         &data->pci_dev.function) == -1)) {
+        goto out;
+    }
+
+    if (udevGetUintSysfsAttr(device,
+                             "vendor",
+                             &data->pci_dev.vendor,
+                             0) == PROPERTY_ERROR) {
+        goto out;
+    }
+
+    if (udevGetUintSysfsAttr(device,
+                             "device",
+                             &data->pci_dev.product,
+                             0) == PROPERTY_ERROR) {
+        goto out;
+    }
+
+    /* XXX FIXME: to do the vendor name and product name, we have to
+     * parse /usr/share/hwdata/pci.ids.  Use libpciaccess perhaps? */
+
+    if (udevGenerateDeviceName(device, def, NULL) != 0) {
+        goto out;
+    }
+
+    ret = 0;
+
+out:
+    return ret;
+}
+
+
+static int udevProcessUSBDevice(struct udev_device *device,
+                                virNodeDeviceDefPtr def)
+{
+    union _virNodeDevCapData *data = &def->caps->data;
+    int ret = -1;
+
+    if (udevGetUintProperty(device,
+                            "BUSNUM",
+                            &data->usb_dev.bus,
+                            0) == PROPERTY_ERROR) {
+        goto out;
+    }
+
+    if (udevGetUintProperty(device,
+                            "DEVNUM",
+                            &data->usb_dev.device,
+                            0) == PROPERTY_ERROR) {
+        goto out;
+    }
+
+    if (udevGetUintProperty(device,
+                            "ID_VENDOR_ID",
+                            &data->usb_dev.vendor,
+                            16) == PROPERTY_ERROR) {
+        goto out;
+    }
+
+    if (udevGetStringSysfsAttr(device,
+                              "manufacturer",
+                              &data->usb_dev.vendor_name) == PROPERTY_ERROR) {
+        goto out;
+    }
+
+    if (udevGetUintProperty(device,
+                            "ID_MODEL_ID",
+                            &data->usb_dev.product,
+                            0) == PROPERTY_ERROR) {
+        goto out;
+    }
+
+    if (udevGetStringSysfsAttr(device,
+                              "product",
+                              &data->usb_dev.product_name) == PROPERTY_ERROR) {
+        goto out;
+    }
+
+    if (udevGenerateDeviceName(device, def, NULL) != 0) {
+        goto out;
+    }
+
+    ret = 0;
+
+out:
+    return ret;
+}
+
+
+/* XXX Is 10 the correct base for the Number/Class/SubClass/Protocol
+ * conversions?  */
+static int udevProcessUSBInterface(struct udev_device *device,
+                                   virNodeDeviceDefPtr def)
+{
+    int ret = -1;
+    union _virNodeDevCapData *data = &def->caps->data;
+
+    if (udevGetUintSysfsAttr(device,
+                             "bInterfaceNumber",
+                             &data->usb_if.number,
+                             10) == PROPERTY_ERROR) {
+        goto out;
+    }
+
+    if (udevGetUintSysfsAttr(device,
+                             "bInterfaceClass",
+                             &data->usb_if._class,
+                             10) == PROPERTY_ERROR) {
+        goto out;
+    }
+
+    if (udevGetUintSysfsAttr(device,
+                             "bInterfaceSubClass",
+                             &data->usb_if.subclass,
+                             10) == PROPERTY_ERROR) {
+        goto out;
+    }
+
+    if (udevGetUintSysfsAttr(device,
+                             "bInterfaceProtocol",
+                             &data->usb_if.protocol,
+                             10) == PROPERTY_ERROR) {
+        goto out;
+    }
+
+    if (udevGenerateDeviceName(device, def, NULL) != 0) {
+        goto out;
+    }
+
+    ret = 0;
+
+out:
+    return ret;
+}
+
+
+static int udevProcessNetworkInterface(struct udev_device *device,
+                                       virNodeDeviceDefPtr def)
+{
+    int ret = -1;
+    union _virNodeDevCapData *data = &def->caps->data;
+
+    if (udevGetStringProperty(device,
+                              "INTERFACE",
+                              &data->net.ifname) == PROPERTY_ERROR) {
+        goto out;
+    }
+
+    if (udevGetStringSysfsAttr(device,
+                               "address",
+                               &data->net.address) == PROPERTY_ERROR) {
+        goto out;
+    }
+
+    if (udevGetUintSysfsAttr(device,
+                             "addr_len",
+                             &data->net.address_len,
+                             0) == PROPERTY_ERROR) {
+        goto out;
+    }
+
+    if (udevGenerateDeviceName(device, def, data->net.address) != 0) {
+        goto out;
+    }
+
+    ret = 0;
+
+out:
+    return ret;
+}
+
+
+static int udevProcessSCSIHost(struct udev_device *device ATTRIBUTE_UNUSED,
+                               virNodeDeviceDefPtr def)
+{
+    int ret = -1;
+    union _virNodeDevCapData *data = &def->caps->data;
+    char *filename = NULL;
+
+    filename = basename(def->sysfs_path);
+
+    if (!STRPREFIX(filename, "host")) {
+        VIR_ERROR("SCSI host found, but its udev name '%s' does "
+                  "not begin with 'host'\n", filename);
+        goto out;
+    }
+
+    if (udevStrToLong_ui(filename + strlen("host"),
+                         NULL,
+                         0,
+                         &data->scsi_host.host) == -1) {
+        goto out;
+    }
+
+    check_fc_host(&def->caps->data);
+    check_vport_capable(&def->caps->data);
+
+    if (udevGenerateDeviceName(device, def, NULL) != 0) {
+        goto out;
+    }
+
+    ret = 0;
+
+out:
+    return ret;
+}
+
+
+static int udevGetSCSIType(unsigned int type, char **typestring)
+{
+    int ret = 0;
+    int foundtype = 1;
+
+    *typestring = NULL;
+
+    switch (type) {
+    case TYPE_DISK:
+        *typestring = strdup("disk");
+        break;
+    case TYPE_TAPE:
+        *typestring = strdup("tape");
+        break;
+    case TYPE_PROCESSOR:
+        *typestring = strdup("processor");
+        break;
+    case TYPE_WORM:
+        *typestring = strdup("worm");
+        break;
+    case TYPE_ROM:
+        *typestring = strdup("cdrom");
+        break;
+    case TYPE_SCANNER:
+        *typestring = strdup("scanner");
+        break;
+    case TYPE_MOD:
+        *typestring = strdup("mod");
+        break;
+    case TYPE_MEDIUM_CHANGER:
+        *typestring = strdup("changer");
+        break;
+    case TYPE_ENCLOSURE:
+        *typestring = strdup("enclosure");
+        break;
+    case TYPE_NO_LUN:
+    default:
+        foundtype = 0;
+        break;
+    }
+
+    if (*typestring == NULL) {
+        if (foundtype == 1) {
+            ret = -1;
+            virReportOOMError(NULL);
+        } else {
+            VIR_ERROR("Failed to find SCSI device type %d\n", type);
+        }
+    }
+
+    return ret;
+}
+
+
+static int udevProcessSCSIDevice(struct udev_device *device ATTRIBUTE_UNUSED,
+                                 virNodeDeviceDefPtr def)
+{
+    int ret = -1;
+    unsigned int tmp = 0;
+    union _virNodeDevCapData *data = &def->caps->data;
+    char *filename = NULL, *p = NULL;
+
+    filename = basename(def->sysfs_path);
+
+    if (udevStrToLong_ui(filename, &p, 10, &data->scsi.host) == -1) {
+        goto out;
+    }
+
+    if ((p == NULL) || (udevStrToLong_ui(p+1,
+                                         &p,
+                                         10,
+                                         &data->scsi.bus) == -1)) {
+        goto out;
+    }
+
+    if ((p == NULL) || (udevStrToLong_ui(p+1,
+                                         &p,
+                                         10,
+                                         &data->scsi.target) == -1)) {
+        goto out;
+    }
+
+    if ((p == NULL) || (udevStrToLong_ui(p+1,
+                                         &p,
+                                         10,
+                                         &data->scsi.lun) == -1)) {
+        goto out;
+    }
+
+    switch (udevGetUintSysfsAttr(device, "type", &tmp, 0)) {
+    case PROPERTY_FOUND:
+        if (udevGetSCSIType(tmp, &data->scsi.type) == -1) {
+            goto out;
+        }
+        break;
+    case PROPERTY_MISSING:
+        break; /* No type is not an error */
+    case PROPERTY_ERROR:
+    default:
+        goto out;
+        break;
+    }
+
+    if (udevGenerateDeviceName(device, def, NULL) != 0) {
+        goto out;
+    }
+
+    ret = 0;
+
+out:
+    if (ret != 0) {
+        VIR_ERROR("Failed to process SCSI device with sysfs path '%s'\n",
+                  def->sysfs_path);
+    }
+    return ret;
+}
+
+
+static int udevProcessDisk(struct udev_device *device,
+                           virNodeDeviceDefPtr def)
+{
+    union _virNodeDevCapData *data = &def->caps->data;
+    int ret = 0;
+
+    data->storage.drive_type = strdup("disk");
+    if (data->storage.drive_type == NULL) {
+        virReportOOMError(NULL);
+        ret = -1;
+        goto out;
+    }
+
+    if (udevGetUint64SysfsAttr(device,
+                               "size",
+                               &data->storage.num_blocks) == PROPERTY_ERROR) {
+        goto out;
+    }
+
+    if (udevGetUint64SysfsAttr(device,
+                               "queue/logical_block_size",
+                               &data->storage.logical_block_size)
+        == PROPERTY_ERROR) {
+        goto out;
+    }
+
+    data->storage.size = data->storage.num_blocks *
+        data->storage.logical_block_size;
+
+out:
+    return ret;
+}
+
+
+static int udevProcessCDROM(struct udev_device *device,
+                            virNodeDeviceDefPtr def)
+{
+    union _virNodeDevCapData *data = &def->caps->data;
+    int tmp_int = 0, ret = 0;
+
+    /* NB: the drive_type string provided by udev is different from
+     * that provided by HAL; now it's "cd" instead of "cdrom" We
+     * change it to cdrom to preserve compatibility with earlier
+     * versions of libvirt.  */
+    VIR_FREE(def->caps->data.storage.drive_type);
+    def->caps->data.storage.drive_type = strdup("cdrom");
+    if (def->caps->data.storage.drive_type == NULL) {
+        virReportOOMError(NULL);
+        goto out;
+    }
+
+    if ((udevGetIntSysfsAttr(device, "removable", &tmp_int, 0) == PROPERTY_FOUND) &&
+        (tmp_int == 1)) {
+        def->caps->data.storage.flags |= VIR_NODE_DEV_CAP_STORAGE_REMOVABLE;
+    }
+
+    if ((udevGetIntProperty(device, "ID_CDROM_MEDIA", &tmp_int, 0)
+         == PROPERTY_FOUND) && (tmp_int == 1)) {
+
+        def->caps->data.storage.flags |=
+            VIR_NODE_DEV_CAP_STORAGE_REMOVABLE_MEDIA_AVAILABLE;
+
+        if (udevGetUint64SysfsAttr(device,
+                                   "size",
+                                   &data->storage.num_blocks) == PROPERTY_ERROR) {
+            goto out;
+        }
+
+        if (udevGetUint64SysfsAttr(device,
+                                   "queue/logical_block_size",
+                                   &data->storage.logical_block_size) == PROPERTY_ERROR) {
+            goto out;
+        }
+
+        /* XXX This calculation is wrong for the qemu virtual cdrom
+         * which reports the size in 512 byte blocks, but the logical
+         * block size as 2048.  I don't have a physical cdrom on a
+         * devel system to see how they behave. */
+        def->caps->data.storage.removable_media_size =
+            def->caps->data.storage.num_blocks *
+            def->caps->data.storage.logical_block_size;
+    }
+
+out:
+    return ret;
+}
+
+
+/* This function exists to deal with the case in which a driver does
+ * not provide a device type in the usual place, but udev told us it's
+ * a storage device, and we can make a good guess at what kind of
+ * storage device it is from other information that is provided. */
+static int udevKludgeStorageType(virNodeDeviceDefPtr def)
+{
+    int ret = -1;
+
+    VIR_INFO("Could not find definitive storage type for device "
+             "with sysfs path '%s', trying to guess it\n",
+             def->sysfs_path);
+
+    if (STRPREFIX(def->caps->data.storage.block, "/dev/vd")) {
+        /* virtio disk */
+        def->caps->data.storage.drive_type = strdup("disk");
+        if (def->caps->data.storage.drive_type != NULL) {
+            ret = 0;
+        }
+    }
+
+    if (ret != 0) {
+        VIR_INFO("Could not determine storage type for device "
+                 "with sysfs path '%s'\n", def->sysfs_path);
+    } else {
+        VIR_DEBUG("Found storage type '%s' for device "
+                  "with sysfs path '%s'\n",
+                  def->caps->data.storage.drive_type,
+                  def->sysfs_path);
+    }
+
+    return ret;
+}
+
+
+static void udevStripSpaces(char *s)
+{
+    if (s == NULL) {
+        return;
+    }
+
+    while (virFileStripSuffix(s, " ")) {
+        /* do nothing */
+        ;
+    }
+
+    return;
+}
+
+
+static int udevProcessStorage(struct udev_device *device,
+                              virNodeDeviceDefPtr def)
+{
+    union _virNodeDevCapData *data = &def->caps->data;
+    int ret = -1;
+
+    data->storage.block = strdup(udev_device_get_devnode(device));
+    if (udevGetStringProperty(device,
+                              "DEVNAME",
+                              &data->storage.block) == PROPERTY_ERROR) {
+        goto out;
+    }
+    if (udevGetStringProperty(device,
+                              "ID_BUS",
+                              &data->storage.bus) == PROPERTY_ERROR) {
+        goto out;
+    }
+    if (udevGetStringProperty(device,
+                              "ID_SERIAL",
+                              &data->storage.serial) == PROPERTY_ERROR) {
+        goto out;
+    }
+    if (udevGetStringSysfsAttr(device,
+                               "device/vendor",
+                               &data->storage.vendor) == PROPERTY_ERROR) {
+        goto out;
+    }
+    udevStripSpaces(def->caps->data.storage.vendor);
+    if (udevGetStringSysfsAttr(device,
+                               "device/model",
+                               &data->storage.model) == PROPERTY_ERROR) {
+        goto out;
+    }
+    udevStripSpaces(def->caps->data.storage.model);
+    /* There is no equivalent of the hotpluggable property in libudev,
+     * but storage is going toward a world in which hotpluggable is
+     * expected, so I don't see a problem with not having a property
+     * for it. */
+
+    if (udevGetStringProperty(device,
+                              "ID_TYPE",
+                              &data->storage.drive_type) != PROPERTY_FOUND) {
+        /* If udev doesn't have it, perhaps we can guess it. */
+        if (udevKludgeStorageType(def) != 0) {
+            goto out;
+        }
+    }
+
+    if (STREQ(def->caps->data.storage.drive_type, "cd")) {
+        ret = udevProcessCDROM(device, def);
+    } else if (STREQ(def->caps->data.storage.drive_type, "disk")) {
+        ret = udevProcessDisk(device, def);
+    } else {
+        VIR_INFO("Unsupported storage type '%s'\n",
+                 def->caps->data.storage.drive_type);
+        goto out;
+    }
+
+    if (udevGenerateDeviceName(device, def, data->storage.serial) != 0) {
+        goto out;
+    }
+
+out:
+    return ret;
+}
+
+
+static int udevGetDeviceType(struct udev_device *device,
+                             enum virNodeDevCapType *type)
+{
+    const char *devtype = NULL;
+    char *tmp_string = NULL;
+    unsigned int tmp = 0;
+    int ret = 0;
+
+    devtype = udev_device_get_devtype(device);
+
+    if (devtype != NULL && STREQ(devtype, "usb_device")) {
+        *type = VIR_NODE_DEV_CAP_USB_DEV;
+        goto out;
+    }
+
+    if (devtype != NULL && STREQ(devtype, "usb_interface")) {
+        *type = VIR_NODE_DEV_CAP_USB_INTERFACE;
+        goto out;
+    }
+
+    if (devtype != NULL && STREQ(devtype, "scsi_host")) {
+        *type = VIR_NODE_DEV_CAP_SCSI_HOST;
+        goto out;
+    }
+
+    if (devtype != NULL && STREQ(devtype, "scsi_device")) {
+        *type = VIR_NODE_DEV_CAP_SCSI;
+        goto out;
+    }
+
+    if (devtype != NULL && STREQ(devtype, "disk")) {
+        *type = VIR_NODE_DEV_CAP_STORAGE;
+        goto out;
+    }
+
+    if (udevGetUintProperty(device, "PCI_CLASS", &tmp, 16) == PROPERTY_FOUND) {
+        *type = VIR_NODE_DEV_CAP_PCI_DEV;
+        goto out;
+    }
+
+    /* It does not appear that network interfaces set the device type
+     * property. */
+    if (devtype == NULL &&
+        udevGetStringProperty(device,
+                              "INTERFACE",
+                              &tmp_string) == PROPERTY_FOUND) {
+        VIR_FREE(tmp_string);
+        *type = VIR_NODE_DEV_CAP_NET;
+        goto out;
+    }
+
+    VIR_INFO("Could not determine device type for device "
+             "with sysfs path '%s'\n",
+             udev_device_get_sysname(device));
+    ret = -1;
+
+out:
+    return ret;
+}
+
+
+static int udevGetDeviceDetails(struct udev_device *device,
+                                virNodeDeviceDefPtr def)
+{
+    int ret = 0;
+
+    switch (def->caps->type) {
+    case VIR_NODE_DEV_CAP_SYSTEM:
+        /* There's no libudev equivalent of system, so ignore it. */
+        break;
+    case VIR_NODE_DEV_CAP_PCI_DEV:
+        ret = udevProcessPCI(device, def);
+        break;
+    case VIR_NODE_DEV_CAP_USB_DEV:
+        ret = udevProcessUSBDevice(device, def);
+        break;
+    case VIR_NODE_DEV_CAP_USB_INTERFACE:
+        ret = udevProcessUSBInterface(device, def);
+        break;
+    case VIR_NODE_DEV_CAP_NET:
+        ret = udevProcessNetworkInterface(device, def);
+        break;
+    case VIR_NODE_DEV_CAP_SCSI_HOST:
+        ret = udevProcessSCSIHost(device, def);
+        break;
+    case VIR_NODE_DEV_CAP_SCSI:
+        ret = udevProcessSCSIDevice(device, def);
+        break;
+    case VIR_NODE_DEV_CAP_STORAGE:
+        ret = udevProcessStorage(device, def);
+        break;
+    default:
+        VIR_ERROR("Unknown device type %d\n", def->caps->type);
+        ret = -1;
+        break;
+    }
+
+    return ret;
+}
+
+
+static int udevRemoveOneDevice(struct udev_device *device)
+{
+    virNodeDeviceObjPtr dev = NULL;
+    const char *name = NULL;
+    int ret = 0;
+
+    name = udev_device_get_syspath(device);
+    dev = virNodeDeviceFindBySysfsPath(&driverState->devs, name);
+    if (dev != NULL) {
+        VIR_DEBUG("Removing device '%s' with sysfs path '%s'\n",
+                  dev->def->name, name);
+        virNodeDeviceObjRemove(&driverState->devs, dev);
+    } else {
+        VIR_INFO("Failed to find device to remove that has udev name '%s'\n",
+                 name);
+        ret = -1;
+    }
+
+    return ret;
+}
+
+
+static int udevSetParent(struct udev_device *device,
+                         virNodeDeviceDefPtr def)
+{
+    struct udev_device *parent_device = NULL;
+    const char *parent_sysfs_path = NULL;
+    virNodeDeviceObjPtr dev = NULL;
+    int ret = -1;
+
+    parent_device = udev_device_get_parent(device);
+    if (parent_device == NULL) {
+        VIR_INFO("Could not find udev parent for device with sysfs path '%s'\n",
+                 udev_device_get_syspath(device));
+        goto out;
+    }
+
+    parent_sysfs_path = udev_device_get_syspath(parent_device);
+    if (parent_sysfs_path == NULL) {
+        VIR_INFO("Could not get syspath for parent of '%s'\n",
+                 udev_device_get_syspath(device));
+        goto out;
+    }
+
+    def->parent_sysfs_path = strdup(parent_sysfs_path);
+    if (def->parent_sysfs_path == NULL) {
+        virReportOOMError(NULL);
+        goto out;
+    }
+
+    dev = virNodeDeviceFindBySysfsPath(&driverState->devs, parent_sysfs_path);
+    if (dev == NULL) {
+        def->parent = strdup("computer");
+    } else {
+        def->parent = strdup(dev->def->name);
+        virNodeDeviceObjUnlock(dev);
+    }
+
+    if (def->parent == NULL) {
+        virReportOOMError(NULL);
+        goto out;
+    }
+
+    ret = 0;
+
+out:
+    return ret;
+}
+
+
+static int udevAddOneDevice(struct udev_device *device)
+{
+    virNodeDeviceDefPtr def = NULL;
+    virNodeDeviceObjPtr dev = NULL;
+    int ret = -1;
+
+    if (VIR_ALLOC(def) != 0) {
+        virReportOOMError(NULL);
+        goto out;
+    }
+
+    def->sysfs_path = strdup(udev_device_get_syspath(device));
+    if (udevGetStringProperty(device,
+                              "DRIVER",
+                              &def->driver) == PROPERTY_ERROR) {
+        goto out;
+    }
+
+    if (VIR_ALLOC(def->caps) != 0) {
+        virReportOOMError(NULL);
+        goto out;
+    }
+
+    if (udevGetDeviceType(device, &def->caps->type) != 0) {
+        goto out;
+    }
+
+    if (udevGetDeviceDetails(device, def) != 0) {
+        goto out;
+    }
+
+    if (udevSetParent(device, def) != 0) {
+        goto out;
+    }
+
+    dev = virNodeDeviceAssignDef(NULL, &driverState->devs, def);
+    if (dev == NULL) {
+        VIR_ERROR("Failed to create device for '%s'\n", def->name);
+        virNodeDeviceDefFree(def);
+        goto out;
+    }
+
+    dev->devicePath = strdup(udev_device_get_devpath(device));
+    if (dev->devicePath == NULL) {
+        virReportOOMError(NULL);
+        virNodeDeviceObjRemove(&driverState->devs, dev);
+        goto out;
+    }
+
+    virNodeDeviceObjUnlock(dev);
+
+    ret = 0;
+
+out:
+    return ret;
+}
+
+
+static int udevProcessDeviceListEntry(struct udev *udev,
+                                      struct udev_list_entry *list_entry)
+{
+    struct udev_device *device;
+    const char *name = NULL;
+    int ret = -1;
+
+    name = udev_list_entry_get_name(list_entry);
+
+    device = udev_device_new_from_syspath(udev, name);
+    if (device != NULL) {
+        if (udevAddOneDevice(device) != 0) {
+            VIR_INFO("Failed to create node device for udev device '%s'\n",
+                     name);
+        }
+        udev_device_unref(device);
+        ret = 0;
+    }
+
+    return ret;
+}
+
+
+static int udevEnumerateDevices(struct udev *udev)
+{
+    struct udev_enumerate *udev_enumerate = NULL;
+    struct udev_list_entry *list_entry = NULL;
+    const char *name = NULL;
+    int ret = 0;
+
+    udev_enumerate = udev_enumerate_new(udev);
+
+    ret = udev_enumerate_scan_devices(udev_enumerate);
+    if (0 != ret) {
+        VIR_ERROR("udev scan devices returned %d\n", ret);
+        goto out;
+    }
+
+    udev_list_entry_foreach(list_entry,
+                            udev_enumerate_get_list_entry(udev_enumerate)) {
+
+        udevProcessDeviceListEntry(udev, list_entry);
+        name = udev_list_entry_get_name(list_entry);
+    }
+
+out:
+    udev_enumerate_unref(udev_enumerate);
+    return ret;
+}
+
+
+static int udevDeviceMonitorShutdown(void)
+{
+    int ret = 0;
+
+    struct udev_monitor *udev_monitor = NULL;
+    struct udev *udev = NULL;
+
+    if (driverState) {
+
+        nodeDeviceLock(driverState);
+        udev_monitor = DRV_STATE_UDEV_MONITOR(driverState);
+
+        if (udev_monitor != NULL) {
+            udev = udev_monitor_get_udev(udev_monitor);
+            udev_monitor_unref(udev_monitor);
+        }
+
+        if (udev != NULL) {
+            udev_unref(udev);
+        }
+
+        virNodeDeviceObjListFree(&driverState->devs);
+        nodeDeviceUnlock(driverState);
+        virMutexDestroy(&driverState->lock);
+        VIR_FREE(driverState);
+
+    } else {
+        ret = -1;
+    }
+
+    return ret;
+}
+
+
+static void udevEventHandleCallback(int watch ATTRIBUTE_UNUSED,
+                                    int fd,
+                                    int events ATTRIBUTE_UNUSED,
+                                    void *data ATTRIBUTE_UNUSED)
+{
+    struct udev_device *device = NULL;
+    struct udev_monitor *udev_monitor = DRV_STATE_UDEV_MONITOR(driverState);
+    const char *action = NULL;
+    int udev_fd = -1;
+
+    udev_fd = udev_monitor_get_fd(udev_monitor);
+    if (fd != udev_fd) {
+        VIR_ERROR("File descriptor returned by udev %d does not "
+                  "match node device file descriptor %d", fd, udev_fd);
+        goto out;
+    }
+
+    device = udev_monitor_receive_device(udev_monitor);
+    if (device == NULL) {
+        VIR_ERROR0("udev_monitor_receive_device returned NULL\n");
+        goto out;
+    }
+
+    action = udev_device_get_action(device);
+    VIR_DEBUG("udev action: '%s'\n", action);
+
+    if (STREQ(action, "add") || STREQ(action, "change")) {
+        udevAddOneDevice(device);
+        goto out;
+    }
+
+    if (STREQ(action, "remove")) {
+        udevRemoveOneDevice(device);
+        goto out;
+    }
+
+out:
+    return;
+}
+
+
+static int udevSetupSystemDev(void)
+{
+    virNodeDeviceDefPtr def = NULL;
+    virNodeDeviceObjPtr dev = NULL;
+    struct udev *udev = NULL;
+    struct udev_device *device = NULL;
+    union _virNodeDevCapData *data = NULL;
+    char *tmp = NULL;
+    int ret = -1;
+
+    if (VIR_ALLOC(def) != 0) {
+        virReportOOMError(NULL);
+        goto out;
+    }
+
+    def->name = strdup("computer");
+    if (def->name == NULL) {
+        virReportOOMError(NULL);
+        goto out;
+    }
+
+    if (VIR_ALLOC(def->caps) != 0) {
+        virReportOOMError(NULL);
+        goto out;
+    }
+
+    udev = udev_monitor_get_udev(DRV_STATE_UDEV_MONITOR(driverState));
+    device = udev_device_new_from_syspath(udev, DMI_DEVPATH);
+    if (device == NULL) {
+        VIR_ERROR("Failed to get udev device for syspath '%s'\n", DMI_DEVPATH);
+        goto out;
+    }
+
+    data = &def->caps->data;
+
+    if (udevGetStringSysfsAttr(device,
+                               "product_name",
+                               &data->system.product_name) == PROPERTY_ERROR) {
+        goto out;
+    }
+    if (udevGetStringSysfsAttr(device,
+                               "sys_vendor",
+                               &data->system.hardware.vendor_name)
+        == PROPERTY_ERROR) {
+        goto out;
+    }
+    if (udevGetStringSysfsAttr(device,
+                               "product_version",
+                               &data->system.hardware.version)
+        == PROPERTY_ERROR) {
+        goto out;
+    }
+    if (udevGetStringSysfsAttr(device,
+                               "product_serial",
+                               &data->system.hardware.serial)
+        == PROPERTY_ERROR) {
+        goto out;
+    }
+
+    if (udevGetStringSysfsAttr(device,
+                               "product_uuid",
+                               &tmp) == PROPERTY_ERROR) {
+        goto out;
+    }
+    virUUIDParse(tmp, def->caps->data.system.hardware.uuid);
+    VIR_FREE(tmp);
+
+    if (udevGetStringSysfsAttr(device,
+                               "bios_vendor",
+                               &data->system.firmware.vendor_name)
+        == PROPERTY_ERROR) {
+        goto out;
+    }
+    if (udevGetStringSysfsAttr(device,
+                               "bios_version",
+                               &data->system.firmware.version)
+        == PROPERTY_ERROR) {
+        goto out;
+    }
+    if (udevGetStringSysfsAttr(device,
+                               "bios_date",
+                               &data->system.firmware.release_date)
+        == PROPERTY_ERROR) {
+        goto out;
+    }
+
+    udev_device_unref(device);
+
+    dev = virNodeDeviceAssignDef(NULL, &driverState->devs, def);
+    if (dev == NULL) {
+        VIR_ERROR("Failed to create device for '%s'\n", def->name);
+        virNodeDeviceDefFree(def);
+        goto out;
+    }
+
+    virNodeDeviceObjUnlock(dev);
+
+    ret = 0;
+
+out:
+    return ret;
+}
+
+static int udevDeviceMonitorStartup(int privileged ATTRIBUTE_UNUSED)
+{
+    struct udev *udev = NULL;
+    struct udev_monitor *udev_monitor = NULL;
+    int ret = 0;
+
+    if (VIR_ALLOC(driverState) < 0) {
+        virReportOOMError(NULL);
+        ret = -1;
+        goto out;
+    }
+
+    if (virMutexInit(&driverState->lock) < 0) {
+        VIR_ERROR0("Failed to initialize mutex for driverState\n");
+        VIR_FREE(driverState);
+        ret = -1;
+        goto out;
+    }
+
+    nodeDeviceLock(driverState);
+
+    /*
+     * http://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/libudev-udev.html#udev-new
+     *
+     * indicates no return value other than success, so we don't check
+     * its return value.
+     */
+    udev = udev_new();
+    udev_set_log_fn(udev, udevLogFunction);
+
+    udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
+    if (udev_monitor == NULL) {
+        VIR_ERROR0("udev_monitor_new_from_netlink returned NULL\n");
+        ret = -1;
+        goto out;
+    }
+
+    udev_monitor_enable_receiving(udev_monitor);
+
+    /* udev can be retrieved from udev_monitor */
+    driverState->privateData = udev_monitor;
+    nodeDeviceUnlock(driverState);
+
+    /* We register the monitor with the event callback so we are
+     * notified by udev of device changes before we enumerate existing
+     * devices because libvirt will simply recreate the device if we
+     * try to register it twice, i.e., if the device appears between
+     * the time we register the callback and the time we begin
+     * enumeration.  The alternative is to register the callback after
+     * we enumerate, in which case we will fail to create any devices
+     * that appear while the enumeration is taking place.  */
+    if (virEventAddHandleImpl(udev_monitor_get_fd(udev_monitor),
+                              VIR_EVENT_HANDLE_READABLE,
+                              udevEventHandleCallback,
+                              NULL, NULL) == -1) {
+        ret = -1;
+        goto out;
+    }
+
+    /* Create a fictional 'computer' device to root the device tree. */
+    if (udevSetupSystemDev() != 0) {
+        ret = -1;
+        goto out;
+    }
+
+    /* Populate with known devices */
+
+    if (udevEnumerateDevices(udev) != 0) {
+        ret = -1;
+        goto out;
+    }
+
+out:
+    if (ret == -1) {
+        udevDeviceMonitorShutdown();
+    }
+    return ret;
+}
+
+
+static int udevDeviceMonitorReload(void)
+{
+    return 0;
+}
+
+
+static int udevDeviceMonitorActive(void)
+{
+    /* Always ready to deal with a shutdown */
+    return 0;
+}
+
+
+static virDrvOpenStatus udevNodeDrvOpen(virConnectPtr conn,
+                                        virConnectAuthPtr auth ATTRIBUTE_UNUSED,
+                                        int flags ATTRIBUTE_UNUSED)
+{
+    if (driverState == NULL) {
+        return VIR_DRV_OPEN_DECLINED;
+    }
+
+    conn->devMonPrivateData = driverState;
+
+    return VIR_DRV_OPEN_SUCCESS;
+}
+
+static int udevNodeDrvClose(virConnectPtr conn)
+{
+    conn->devMonPrivateData = NULL;
+    return 0;
+}
+
+static virDeviceMonitor udevDeviceMonitor = {
+    .name = "udevDeviceMonitor",
+    .open = udevNodeDrvOpen,
+    .close = udevNodeDrvClose,
+};
+
+static virStateDriver udevStateDriver = {
+    .initialize = udevDeviceMonitorStartup,
+    .cleanup = udevDeviceMonitorShutdown,
+    .reload = udevDeviceMonitorReload,
+    .active = udevDeviceMonitorActive,
+};
+
+int udevNodeRegister(void)
+{
+    VIR_DEBUG0("Registering udev node device backend\n");
+
+    registerCommonNodeFuncs(&udevDeviceMonitor);
+    if (virRegisterDeviceMonitor(&udevDeviceMonitor) < 0) {
+        return -1;
+    }
+
+    return virRegisterStateDriver(&udevStateDriver);
+}
diff --git a/src/node_device/node_device_udev.h b/src/node_device/node_device_udev.h
new file mode 100644 (file)
index 0000000..0fd39ae
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * node_device_udev.h: node device enumeration - libudev implementation
+ *
+ * Copyright (C) 2009 Red Hat
+ *
+ * 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: Dave Allan <dallan@redhat.com>
+ */
+
+#include <libudev.h>
+#include <stdint.h>
+
+#define SYSFS_DATA_SIZE 4096
+#define DRV_STATE_UDEV_MONITOR(ds) ((struct udev_monitor *)((ds)->privateData))
+#define DMI_DEVPATH "/sys/devices/virtual/dmi/id"
+#define PROPERTY_FOUND 0
+#define PROPERTY_MISSING 1
+#define PROPERTY_ERROR -1
index 853d3a0a942cd85ddca1c959831c87e4f704d762..e472e0c2cfc0b3ed16a371c1546d6008e2d00415 100644 (file)
@@ -2128,3 +2128,31 @@ void virFileWaitForDevices(virConnectPtr conn)
 void virFileWaitForDevices(virConnectPtr conn ATTRIBUTE_UNUSED) {}
 #endif
 #endif
+
+int virBuildPathInternal(char **path, ...)
+{
+    char *path_component = NULL;
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    va_list ap;
+    int ret = 0;
+
+    va_start(ap, *path);
+
+    path_component = va_arg(ap, char *);
+    virBufferAdd(&buf, path_component, -1);
+
+    while ((path_component = va_arg(ap, char *)) != NULL)
+    {
+        virBufferAddChar(&buf, '/');
+        virBufferAdd(&buf, path_component, -1);
+    }
+
+    va_end(ap);
+
+    *path = virBufferContentAndReset(&buf);
+    if (*path == NULL) {
+        ret = -1;
+    }
+
+    return ret;
+}
index f4e395ec4e7b438d900960b522aa1bdb7cc1e143..8c9d401187079693963bc9c27ab8f3462acd49ad 100644 (file)
@@ -248,4 +248,7 @@ char *virFileFindMountPoint(const char *type);
 
 void virFileWaitForDevices(virConnectPtr conn);
 
+#define virBuildPath(path, ...) virBuildPathInternal(path, __VA_ARGS__, NULL)
+int virBuildPathInternal(char **path, ...) ATTRIBUTE_SENTINEL;
+
 #endif /* __VIR_UTIL_H__ */