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

[libvirt] A first look at the node device libudev backend



Hi all,

Here is a first cut at the node device backend. The only devices implemented are part of storage and PCI devices, but it's conceptually finished. The remaining work is implementing the other device properties and types. Before I go do that, I'd like to get feedback on my approach. Since libudev doesn't have the concept of capabilities the way HAL does, Chris Lalancette and I thought it made more sense and rendered much more readable code to implement each device type in a single function and avoid the indirection created by the caps_tbl structure containing a function pointer.

Monitoring of libudev for new devices is not implemented, but I'm not too concerned about that at this point. As Dan Berrange suggested on IRC, putting a call in the main loop to poll the monitor file descriptor will do the job.

Dave
>From 34fa8bf6ce1b72276dc73f292ae12a9dd1ab2433 Mon Sep 17 00:00:00 2001
From: David Allan <dallan redhat com>
Date: Fri, 25 Sep 2009 10:59:28 -0400
Subject: [PATCH 1/1] A first look at the libudev node device backend.

Thanks to Jiri for the build system changes and to Chris Lalancette for his code and suggestions on how this backend should be implemented.  Storage device properties are partly populated and PCI devices are discovered, but all other devices are as yet unimplemented.
---
 configure.in                                       |   47 ++-
 daemon/libvirtd.c                                  |    3 +-
 src/Makefile.am                                    |   16 +-
 src/node_device/node_device_driver.c               |    3 +
 src/node_device/node_device_driver.h               |   22 +
 src/node_device/node_device_hal.h                  |   19 -
 ...evice_hal_linux.c => node_device_linux_sysfs.c} |    0
 src/node_device/node_device_udev.c                 |  627 ++++++++++++++++++++
 src/node_device/node_device_udev.h                 |   27 +
 src/util/util.c                                    |   46 ++
 src/util/util.h                                    |    4 +
 11 files changed, 788 insertions(+), 26 deletions(-)
 rename src/node_device/{node_device_hal_linux.c => node_device_linux_sysfs.c} (100%)
 create mode 100644 src/node_device/node_device_udev.c
 create mode 100644 src/node_device/node_device_udev.h

diff --git a/configure.in b/configure.in
index cb5ce55..356abb7 100644
--- a/configure.in
+++ b/configure.in
@@ -1560,7 +1560,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=
@@ -1654,8 +1654,46 @@ AM_CONDITIONAL([HAVE_DEVKIT], [test "x$with_devkit" = "xyes"])
 AC_SUBST([DEVKIT_CFLAGS])
 AC_SUBST([DEVKIT_LIBS])

+UDEV_REQUIRED=143
+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 udev-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])
@@ -1815,6 +1853,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
diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c
index 2bae782..c60a09f 100644
--- a/daemon/libvirtd.c
+++ b/daemon/libvirtd.c
@@ -832,8 +832,7 @@ static struct qemud_server *qemudInitialize(int sigread) {
 #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();
diff --git a/src/Makefile.am b/src/Makefile.am
index 9cbec47..b4328d6 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -237,16 +237,20 @@ SECURITY_DRIVER_SELINUX_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
+

 #########################
 #
@@ -626,6 +630,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
@@ -667,6 +676,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)		\
 		$(SECRET_DRIVER_SOURCES)			\
 		$(VBOX_DRIVER_EXTRA_DIST)
diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_device_driver.c
index 93ca28c..3d06ed1 100644
--- a/src/node_device/node_device_driver.c
+++ b/src/node_device/node_device_driver.c
@@ -760,5 +760,8 @@ int nodedevRegister(void) {
 #ifdef HAVE_DEVKIT
     return devkitNodeRegister();
 #endif
+#ifdef HAVE_UDEV
+    return udevNodeRegister();
+#endif
 #endif
 }
diff --git a/src/node_device/node_device_driver.h b/src/node_device/node_device_driver.h
index db01624..5be0781 100644
--- a/src/node_device/node_device_driver.h
+++ b/src/node_device/node_device_driver.h
@@ -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__ */
diff --git a/src/node_device/node_device_hal.h b/src/node_device/node_device_hal.h
index c859fe3..8ac8a35 100644
--- a/src/node_device/node_device_hal.h
+++ b/src/node_device/node_device_hal.h
@@ -22,23 +22,4 @@
 #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_linux_sysfs.c
similarity index 100%
rename from src/node_device/node_device_hal_linux.c
rename to src/node_device/node_device_linux_sysfs.c
diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c
new file mode 100644
index 0000000..22669ff
--- /dev/null
+++ b/src/node_device/node_device_udev.c
@@ -0,0 +1,627 @@
+/*
+ * 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 <regex.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 "uuid.h"
+#include "logging.h"
+#include "memory.h"
+#include "util.h"
+
+#define VIR_FROM_THIS VIR_FROM_NODEDEV
+
+static virDeviceMonitorStatePtr driverState = NULL;
+
+static struct udev *udevGetUdev(void)
+{
+    return udev_monitor_get_udev(DRV_STATE_UDEV_MONITOR(driverState));
+}
+
+
+static int udevGetSysfsData(char **data, const char *path)
+{
+    char *tmp = NULL;
+    FILE *fp = NULL;
+    int ret = -1;
+
+    if (VIR_ALLOC_N(tmp, SYSFS_DATA_SIZE) != 0) {
+        goto out;
+    }
+
+    memset(tmp, 0, SYSFS_DATA_SIZE);
+
+    fp = fopen(path, "r");
+    if (fp == NULL) {
+        goto out;
+    }
+
+    if (fread(tmp, SYSFS_DATA_SIZE, 1, fp) == SYSFS_DATA_SIZE) {
+        goto out;
+    }
+
+    /* strip off the trailing \n */
+    tmp[strlen(tmp) - 1] = '\0';
+
+    /* return a block of memory containing only what was read;
+     * otherwise we will consume vast amounts of largely unused memory
+     * on systems with large numbers of devices. */
+    *data = strdup(tmp);
+    if (*data == NULL) {
+        goto out;
+    }
+
+    ret = 0;
+
+out:
+    VIR_FREE(tmp);
+    fclose(fp);
+
+    return ret;
+}
+
+
+static int udevGetPCI(unsigned int *data,
+                      const char *devpath,
+                      const char *field)
+{
+    const char *syspath = NULL;
+    char *path = NULL, *tmp = NULL;
+    char *dummy;
+    int ret = 0;
+
+    syspath = udev_get_sys_path(udevGetUdev());
+
+    if (virBuildPath(&path, syspath, devpath, field) != 0) {
+        goto out;
+    }
+
+    if (udevGetSysfsData(&tmp, path) == 0) {
+        ret = virStrToLong_ui(tmp, &dummy, 16, data);
+    }
+
+out:
+    VIR_FREE(path);
+    return ret;
+}
+
+
+static int udevGetDMIData(char **data,
+                          const char *field)
+{
+    const char *syspath = NULL;
+    char *path = NULL;
+    int ret = -1;
+
+    syspath = udev_get_sys_path(udevGetUdev());
+
+    if (virBuildPath(&path, syspath, "class/dmi/id", field) == 0) {
+        ret = udevGetSysfsData(data, path);
+        VIR_FREE(path);
+    }
+
+    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 = -1;
+
+    udev_value = udev_device_get_property_value(udev_device, property_key);
+    if (udev_value == NULL) {
+        VIR_ERROR(_("udev reports property '%s' does not exist"),
+                  property_key);
+        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 '%s' on device '%s'"),
+                  property_key, udev_device_get_sysname(udev_device));
+        goto out;
+    }
+
+    ret = 0;
+
+out:
+    if (ret != 0) {
+        VIR_ERROR(_("Failed to get udev property '%s' for device '%s'"),
+                  property_key, udev_device_get_sysname(udev_device));
+    }
+
+    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)
+{
+    char *udev_value = NULL;
+    int ret = 0;
+
+    ret = udevGetDeviceProperty(udev_device, property_key, &udev_value);
+
+    if (ret == 0) {
+        ret = virStrToLong_i(udev_value, NULL, 0, value);
+    }
+
+    return ret;
+}
+
+
+static int udevGetUint64Property(struct udev_device *udev_device,
+                                 const char *property_key,
+                                 uint64_t *value)
+{
+    char *udev_value = NULL;
+    int ret = 0;
+
+    ret = udevGetDeviceProperty(udev_device, property_key, &udev_value);
+
+    if (ret == 0) {
+        ret = virStrToLong_ull(udev_value, NULL, 0, value);
+    }
+
+    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 void virUdevGetDeviceProperties(struct udev *udev, const char *name)
+{
+    struct udev_device *device = NULL;
+    struct udev_list_entry *list_entry = NULL;
+
+    device = udev_device_new_from_syspath(udev, name);
+
+    udev_list_entry_foreach(list_entry,
+                            udev_device_get_properties_list_entry(device)) {
+
+        VIR_ERROR("  name: '%s' value: '%s'\n",
+                  udev_list_entry_get_name(list_entry),
+                  udev_list_entry_get_value(list_entry));
+    }
+
+    udev_device_unref(device);
+
+    return;
+}
+
+
+static int udevProcessPCI(struct udev_device *device,
+                          virNodeDeviceDefPtr def)
+{
+    const char *devpath = NULL, *syspath = NULL;
+    int ret = -1;
+
+    devpath = udev_device_get_devpath(device);
+    syspath = udev_device_get_syspath(device);
+
+    if (VIR_ALLOC(def->caps) != 0) {
+        goto out;
+    }
+
+    def->caps->type = VIR_NODE_DEV_CAP_PCI_DEV;
+
+    char *p = strrchr(devpath, '/');
+    if (p) {
+        virStrToLong_ui(p+1, &p, 16, &def->caps->data.pci_dev.domain);
+        virStrToLong_ui(p+1, &p, 16, &def->caps->data.pci_dev.bus);
+        virStrToLong_ui(p+1, &p, 16, &def->caps->data.pci_dev.slot);
+        virStrToLong_ui(p+1, &p, 16, &def->caps->data.pci_dev.function);
+    }
+
+    if (udevGetPCI(&def->caps->data.pci_dev.vendor, devpath, "vendor") ||
+        udevGetPCI(&def->caps->data.pci_dev.product, devpath, "device")) {
+        goto out;
+    }
+
+    /* FIXME: to do the vendor name and product name, we have to
+     * parse /usr/share/hwdata/pci.ids.  Look in hal/hald/ids.c
+     */
+
+    ret = 0;
+
+out:
+    return ret;
+}
+
+
+static int udevProcessStorage(struct udev_device *device,
+                              virNodeDeviceDefPtr def)
+{
+    const char *devpath = NULL;
+    struct udev_list_entry *list_entry = NULL;
+    int ret = -1;
+
+    devpath = udev_device_get_devpath(device);
+
+    /* Can we get libudev to add device subtypes? */
+    if (STRPREFIX(devpath, "/devices/virtual/block")) {
+        goto out;
+    }
+
+    if (VIR_ALLOC(def->caps) != 0) {
+        goto out;
+    }
+
+    def->caps->type = VIR_NODE_DEV_CAP_STORAGE;
+
+    def->caps->data.storage.block = strdup(udev_device_get_devnode(device));
+    /* Can we get libudev to add subtypes? */
+    def->caps->data.storage.drive_type = strdup("disk");
+
+    udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device)) {
+        if (STREQ(udev_list_entry_get_name(list_entry), "/dev/cdrom")) {
+            VIR_FREE(def->caps->data.storage.drive_type);
+            def->caps->data.storage.drive_type = strdup("cdrom");
+        }
+    }
+
+    /* These lines are somewhat more than 80 col, but I think the code
+     * is more readable if they aren't broken up. */
+    if (udevGetStringProperty(device, "DEVNAME", &def->caps->data.storage.block)) {
+        /* None of these properties are available through libudev... 
+        udevGetStringProperty(device, "ID_MODEL", &def->caps->data.storage.model) ||
+        udevGetStringProperty(device, "ID_VENDOR", &def->caps->data.storage.vendor) ||
+        udevGetStringProperty(device, "ID_SERIAL", &def->caps->data.storage.serial)) {
+        */
+        goto out;
+    }
+    ret = 0;
+
+out:
+    return ret;
+}
+
+
+static int udevProcessOneDevice(struct udev_device *device,
+                                virNodeDeviceDefPtr def)
+{
+    virNodeDeviceObjPtr dev = NULL;
+    const char *devtype = NULL, *devpath = NULL;
+    regex_t pci_rec;
+    int ret = -1;
+
+    /* How to handle parent devices?  Shall we change the meaning of
+     * the parent pointer for the udev backend?  What does client code
+     * use the parent relationship for?  We really want to preserve
+     * the behavior of the HAL backend, but that may be difficult in
+     * practice.  */
+    //parent = udev_device_get_parent(device);
+
+    devtype = udev_device_get_devtype(device);
+    devpath = udev_device_get_devpath(device);
+
+    regcomp(&pci_rec,
+            "^/devices/pci[0-9]{4}:[0-9]{2}/[0-9]{4}:[0-9]{2}:[0-9]{2}.[0-9]$",
+            REG_EXTENDED | REG_NOSUB);
+
+    if (devtype != NULL && STREQ(devtype, "disk")) {
+        ret = udevProcessStorage(device, def);
+    } else if (regexec(&pci_rec, devpath, 0, NULL, 0) == 0) {
+        /* This is puzzling...why isn't there a devtype for pci in libudev? */
+        ret = udevProcessPCI(device, def);
+    }
+
+    if (ret == 0) {
+        dev = virNodeDeviceAssignDef(NULL, &driverState->devs, def);
+        virNodeDeviceObjUnlock(dev);
+    }
+
+    return ret;
+}
+
+
+static int udevProcessDeviceListEntry(struct udev *udev,
+                                      struct udev_list_entry *list_entry)
+{
+    struct udev_device *device;
+    virNodeDeviceDefPtr def = NULL;
+    const char *name = NULL;
+    int ret = -1;
+
+    name = udev_list_entry_get_name(list_entry);
+
+    if (VIR_ALLOC(def) != 0) {
+        goto out;
+    }
+
+    def->name = strdup(name);
+    if (def->name == NULL) {
+        goto out;
+    }
+
+    device = udev_device_new_from_syspath(udev, name);
+    if (device != NULL) {
+        udevProcessOneDevice(device, def);
+        udev_device_unref(device);
+        ret = 0;
+    }
+
+out:
+    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);
+        virUdevGetDeviceProperties(udev, name);
+    }
+
+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 int udevSetupSystemDev(void)
+{
+    virNodeDeviceDefPtr def = NULL;
+    virNodeDeviceObjPtr dev = NULL;
+    char *tmp = NULL;
+    int ret = -1;
+
+    if (VIR_ALLOC(def) != 0) {
+        goto out;
+    }
+
+    def->name = strdup("system");
+    if (def->name == NULL) {
+        goto out;
+    }
+
+    if (VIR_ALLOC(def->caps) != 0) {
+        goto out;
+    }
+
+    /* These lines are somewhat more than 80 col, but I think the code
+     * is more readable if they aren't broken up. */
+    if (udevGetDMIData(&def->caps->data.system.product_name, "product_name") ||
+        udevGetDMIData(&def->caps->data.system.hardware.vendor_name, "sys_vendor") ||
+        udevGetDMIData(&def->caps->data.system.hardware.version, "product_version") ||
+        udevGetDMIData(&def->caps->data.system.hardware.serial, "product_serial")) {
+        goto out;
+    }
+
+    udevGetDMIData(&tmp, "product_uuid");
+    virUUIDParse(tmp, def->caps->data.system.hardware.uuid);
+    VIR_FREE(tmp);
+
+    if (udevGetDMIData(&def->caps->data.system.firmware.vendor_name, "bios_vendor") ||
+        udevGetDMIData(&def->caps->data.system.firmware.version, "bios_version") ||
+        udevGetDMIData(&def->caps->data.system.firmware.release_date, "bios_date")) {
+        goto out;
+    }
+
+    dev = virNodeDeviceAssignDef(NULL, &driverState->devs, def);
+    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) {
+        ret = -1;
+        goto out;
+    }
+
+    if (virMutexInit(&driverState->lock) < 0) {
+        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) {
+        ret = -1;
+        goto out;
+    }
+
+    /* udev can be retrieved from udev_monitor */
+    driverState->privateData = udev_monitor;
+    nodeDeviceUnlock(driverState);
+
+    /* Populate with known devices */
+
+    if (udevSetupSystemDev() != 0) {
+        ret = -1;
+        goto out;
+    }
+
+    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_ERROR0("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
index 0000000..bac628d
--- /dev/null
+++ b/src/node_device/node_device_udev.h
@@ -0,0 +1,27 @@
+/*
+ * 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))
diff --git a/src/util/util.c b/src/util/util.c
index ef07a6a..a1fe0d5 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -2088,3 +2088,49 @@ void virFileWaitForDevices(virConnectPtr conn)
 void virFileWaitForDevices(virConnectPtr conn ATTRIBUTE_UNUSED) {}
 #endif
 #endif
+
+/* Don't call this function directly, call virBuildPath instead. */
+int virBuildPathInternal(char **path, ...)
+{
+    char *path_component = NULL;
+    va_list ap;
+    size_t used = 0;
+    int ret = 0;
+
+    if (VIR_ALLOC_N(*path, PATH_MAX) != 0) {
+        ret = -1;
+        goto out;
+    }
+
+    va_start(ap, *path);
+
+    while ((path_component = va_arg(ap, char *)) != NULL)
+    {
+        used += snprintf(*path + used, PATH_MAX - used, "%s/", path_component);
+
+        /* From the snprintf man page:
+
+           The functions snprintf() and vsnprintf() do not write more
+           than size bytes (including the trailing '\0').  If the
+           output was truncated due to this limit then the return
+           value is the number of characters (not including the
+           trailing '\0') which would have been written to the final
+           string if enough space had been available.  Thus, a return
+           value of size or more means that the output was truncated.
+
+           We therefore set used to the size of the buffer to avoid
+           the size being passed to snprintf from becoming negative.
+        */
+
+        if (used > PATH_MAX) {
+            used = PATH_MAX;
+        }
+    }
+
+    va_end(ap);
+
+    *(*path + used - 1) = '\0'; /* kill trailing slash */
+
+out:
+    return ret;
+}
diff --git a/src/util/util.h b/src/util/util.h
index dbfdc45..aa466af 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -244,4 +244,8 @@ char *virFileFindMountPoint(const char *type);

 void virFileWaitForDevices(virConnectPtr conn);

+#define virBuildPath(path, ...) virBuildPathInternal(path, __VA_ARGS__, NULL)
+/* Don't call virBuildPathInternal directly, use virBuildPath */
+int virBuildPathInternal(char **path, ...);
+
 #endif /* __VIR_UTIL_H__ */
-- 
1.6.4.4


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