Daniel P. Berrange wrote:
On Wed, Nov 11, 2009 at 05:06:18PM -0500, Dave Allan wrote:
> >From 94d99c19668d3c804c84ff878023b0f93560dc81 Mon Sep 17 00:00:00 2001
> From: David Allan <dallan(a)redhat.com>
> Date: Fri, 16 Oct 2009 16:52:40 -0400
> Subject: [PATCH 1/6] Add several fields to node device capabilities
>
> ---
> src/conf/node_device_conf.c | 22 ++++++++++++++++++++++
> src/conf/node_device_conf.h | 7 +++++++
> 2 files changed, 29 insertions(+), 0 deletions(-)
>
> diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c
> index c5083cc..ece339f 100644
> --- a/src/conf/node_device_conf.c
> +++ b/src/conf/node_device_conf.c
> @@ -248,6 +248,12 @@ 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);
I don't think we should be exposing this in the XML. It is a linux
specific concepts. We expose some relevant bits of DMI data in the
XML elsewhere, so would rather we added more data, than point clients
to sysfs.
Removed entirely.
> + if (data->system.description)
> + virBufferEscapeString(&buf, "
<description>%s</description>\n",
> + data->system.description);
I'm also not sure what this is useful for ? All it seems to output is
<description>fictional device to root the node device tree</description>
which is really just documentation about the schema, not something
that needs to be included in actual document output.
I thought it might be used to describe the actual system at some point,
but it's not important. Removed.
> virBufferAddLit(&buf, "
<hardware>\n");
> if (data->system.hardware.vendor_name)
> virBufferEscapeString(&buf, "
<vendor>%s</vendor>\n",
> @@ -325,6 +331,9 @@ 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);
What are the semantics of this element ?
On my system it comes out as
<interface_name>9/0/0</interface_name>
which is pretty much duplicating info already available in a structured
format
<class>9</class>
<subclass>0</subclass>
<protocol>0</protocol>
So do we actually need to add this new element ?
Removed.
> >From ecb4c2c2a0e42ed5f7578441b5290980663c549c Mon Sep 17
00:00:00 2001
> From: David Allan <dallan(a)redhat.com>
> Date: Tue, 3 Nov 2009 21:16:51 -0500
> Subject: [PATCH 2/6] Implement a node device backend using libudev.
>
> Monitoring for addition and removal of devices is implemented.
>
> There is a lot of detail work in this code, so we should try to get people running it
on a wide variety of hardware so we can shake out the differences in implementation
between the HAL and libudev backends.
>
> I have moved the new fields in the node device capabilities to a separate patch.
>
> This version contains changes per all the feedback I've received on earlier
versions.
> ---
> diff --git a/configure.in b/configure.in
> index 7ad1a90..4e5afef 100644
> --- a/configure.in
> +++ b/configure.in
> @@ -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=143
Already agreed on IRC that we should increase this to 145
Done.
> +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])
Typo here - libudev-devel
Fixed.
> diff --git a/src/conf/node_device_conf.c
b/src/conf/node_device_conf.c
> index ece339f..4d1bfda 100644
> --- a/src/conf/node_device_conf.c
> +++ b/src/conf/node_device_conf.c
> @@ -91,6 +91,26 @@ int virNodeDeviceHasCap(const virNodeDeviceObjPtr dev, const char
*cap)
> return 0;
> }
>
> +
> +virNodeDeviceObjPtr
> +virNodeDeviceFindByUdevName(const virNodeDeviceObjListPtr devs,
> + const char *udev_name)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < devs->count; i++) {
> + virNodeDeviceObjLock(devs->objs[i]);
> + if ((devs->objs[i]->def->udev_name != NULL) &&
> + (STREQ(devs->objs[i]->def->udev_name, udev_name))) {
> + 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->udev_name);
> + VIR_FREE(def->parent_udev_name);
>
> 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->udev_name != NULL) {
> + virBufferEscapeString(&buf, "
<udev_name>%s</udev_name>\n",
> + def->udev_name);
> + }
> + if (def->parent) {
> virBufferEscapeString(&buf, "
<parent>%s</parent>\n", def->parent);
> + }
> + if (def->parent_udev_name != NULL) {
> + virBufferEscapeString(&buf, "
<parent_udev_name>%s</parent_udev_name>\n",
> + def->parent_udev_name);
> + }
> if (def->driver) {
> virBufferAddLit(&buf, " <driver>\n");
> virBufferEscapeString(&buf, "
<name>%s</name>\n", def->driver);
> diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h
> index f70184d..91ef94e 100644
> --- a/src/conf/node_device_conf.h
> +++ b/src/conf/node_device_conf.h
> @@ -164,7 +164,9 @@ typedef struct _virNodeDeviceDef virNodeDeviceDef;
> typedef virNodeDeviceDef *virNodeDeviceDefPtr;
> struct _virNodeDeviceDef {
> char *name; /* device name (unique on node) */
> + char *udev_name; /* udev name/sysfs path */
> char *parent; /* optional parent device name */
> + char *parent_udev_name; /* udev parent name/sysfs path */
> char *driver; /* optional driver name */
> virNodeDevCapsDefPtr caps; /* optional device capabilities */
> };
> +
> +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;
> + }
> + }
> +
> + /* NB: drive_type has changed from HAL; now it's "cd" instead of
"cdrom" */
> + if (STREQ(def->caps->data.storage.drive_type, "cd")) {
> + ret = udevProcessCDROM(device, def);
Is this comment still accurate ? It appears to show 'cdrom' for me
when using udev
Fixed in the later patch reflecting Cole's feedback; I've rolled those
changes into the main patch.
<device>
<name>block_sr0</name>
<sysfs_path>/sys/devices/pci0000:00/0000:00:01.1/host1/target1:0:0/1:0:0:0/block/sr0</sysfs_path>
<parent>scsi_1:0:0:0</parent>
<parent_sysfs_path>/sys/devices/pci0000:00/0000:00:01.1/host1/target1:0:0/1:0:0:0</parent_sysfs_path>
<capability type='storage'>
<block>/dev/sr0</block>
<bus>scsi</bus>
<drive_type>cdrom</drive_type>
<model>QEMU DVD-ROM</model>
<vendor>QEMU</vendor>
<capability type='removable'>
<media_available>0</media_available>
<media_size>0</media_size>
<logical_block_size>0</logical_block_size>
<num_blocks>0</num_blocks>
</capability>
</capability>
</device>
> +
> +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) {
> + goto out;
> + }
> +
> + def->name = strdup("computer");
> + if (def->name == NULL) {
> + goto out;
> + }
> +
> + if (VIR_ALLOC(def->caps) != 0) {
> + goto out;
> + }
> +
> + udev = udev_monitor_get_udev(DRV_STATE_UDEV_MONITOR(driverState));
> + device = udev_device_new_from_syspath(udev, DMI_DEVPATH);
> + if (device == NULL) {
> + goto out;
> + }
> +
> + data = &def->caps->data;
> +
> + data->system.dmi_devpath = strdup(DMI_DEVPATH);
> + data->system.description = strdup(SYSTEM_DESCRIPTION);
> +
> + 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;
> + }
The udevGetStringSysfsAttr() method is returning empty string
for many of these, while HAL fills the fields with NULL. This
causes the udev generated XML to included many emptry elements.
<hardware>
<vendor></vendor>
<version></version>
<serial></serial>
<uuid>a6f7e16a-6e5e-a930-bca7-cc597167fab4</uuid>
</hardware>
So I think udevGetStringSysfsAttr() should convert "" into NULL
Good point--done.
> +#define virBuildPath(path, ...) virBuildPathInternal(path,
__VA_ARGS__, NULL)
> +int virBuildPathInternal(char **path, ...) __attribute__ ((sentinel));
This should use ATTRIBUTE_SENTINAL, otherwise it won't compile with
non-gcc, or older gcc.
Fixed.
> diff --git a/tools/virsh.c b/tools/virsh.c
> index 0d0ebca..16b3f1c 100644
> --- a/tools/virsh.c
> +++ b/tools/virsh.c
> @@ -5795,9 +5795,17 @@ cmdNodeListDevices (vshControl *ctl, const vshCmd *cmd
ATTRIBUTE_UNUSED)
> char **parents = vshMalloc(ctl, sizeof(char *) * num_devices);
> for (i = 0; i < num_devices; i++) {
> virNodeDevicePtr dev = virNodeDeviceLookupByName(ctl->conn,
devices[i]);
> + virNodeDevicePtr parent_dev = NULL;
> +
> if (dev && STRNEQ(devices[i], "computer")) {
> const char *parent = virNodeDeviceGetParent(dev);
> - parents[i] = parent ? strdup(parent) : NULL;
> + parent_dev = virNodeDeviceLookupByName(ctl->conn, parent);
> + if (parent_dev) {
> + parents[i] = strdup(parent);
> + } else {
> + parents[i] = strdup("computer");
> + }
> + virNodeDeviceFree(parent_dev);
Why do we need lookup the actual device object here ? Is there
really a time when we would report a parent which doesn't
actually exist ?
I'm not sure what's up with this--I'm fairly certain that I was losing
devices from the tree view of virsh nodedev-list without it, but I'm
currently seeing everything I should be, so I'm clearly losing my mind.
:) In any case, I've reverted this change and everything seems to be
working ok.
> @@ -251,16 +251,16 @@ char *virNodeDeviceDefFormat(virConnectPtr
conn,
>
> virBufferAddLit(&buf, "<device>\n");
> virBufferEscapeString(&buf, " <name>%s</name>\n",
def->name);
> - if (def->udev_name != NULL) {
> - virBufferEscapeString(&buf, "
<udev_name>%s</udev_name>\n",
> - def->udev_name);
> + 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_udev_name != NULL) {
> - virBufferEscapeString(&buf, "
<parent_udev_name>%s</parent_udev_name>\n",
> - def->parent_udev_name);
> + if (def->parent_sysfs_path != NULL) {
> + virBufferEscapeString(&buf, "
<parent_sysfs_path>%s</parent_sysfs_path>\n",
> + def->parent_sysfs_path);
> }
I'm not all that keen on us exposing an XML element named sysfs_path here,
since again that's Linux specific concept, and if an app needed more metadata
about a device then we ought to provide it directly, since most apps using
libvirt run remotely & so can't access /sysfs aanyway.
Removed from the XML.
One final thought which doesn't really fit elsehwere. In the
device
names
# virsh nodedev-list
block_QEMU_HARDDISK_QM00001
block_sr0
computer
net_54:52:00:39:ee:20
pci_0000:00:00.0
pci_0000:00:01.0
pci_0000:00:01.1
pci_0000:00:01.2
pci_0000:00:01.3
pci_0000:00:02.0
pci_0000:00:03.0
pci_0000:00:04.0
scsi_0:0:0:0
scsi_1:0:0:0
scsi_host0
scsi_host1
scsi_target0:0:0
scsi_target1:0:0
usb_1-0:1.0
usb_1-2
usb_1-2:1.0
usb_usb1
I think it would be worth getting rid of the punctuation characters, just
doing a straight search & replace with '_', for anything which isn't
in the set 0-9, a-Z, _
Good idea. Done, and added the additional bit of disambiguation of the
udev device name so devices have the form:
net_eth0_54_52_00_02_c4_46
Updated patch set attached.
Dave
From 4e5bbd37d51fb82f3be70ae248403d10c7b373cd Mon Sep 17 00:00:00 2001
From: David Allan <dallan(a)redhat.com>
Date: Fri, 16 Oct 2009 16:52:40 -0400
Subject: [PATCH 1/5] Add several fields to node device capabilities
---
src/conf/node_device_conf.c | 22 ++++++++++++++++++++++
src/conf/node_device_conf.h | 7 +++++++
2 files changed, 29 insertions(+), 0 deletions(-)
diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c
index c5083cc..ece339f 100644
--- a/src/conf/node_device_conf.c
+++ b/src/conf/node_device_conf.c
@@ -248,6 +248,12 @@ 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",
@@ -325,6 +331,9 @@ 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);
@@ -394,10 +403,20 @@ char *virNodeDeviceDefFormat(virConnectPtr conn,
"</media_available>\n", avl ? 1 : 0);
virBufferVSprintf(&buf, "
<media_size>%llu</media_size>\n",
data->storage.removable_media_size);
+ virBufferVSprintf(&buf, "
<logical_block_size>%llu"
+ "</logical_block_size>\n",
+ data->storage.logical_block_size);
+ virBufferVSprintf(&buf, "
<num_blocks>%llu</num_blocks>\n",
+ data->storage.num_blocks);
virBufferAddLit(&buf, " </capability>\n");
} else {
virBufferVSprintf(&buf, "
<size>%llu</size>\n",
data->storage.size);
+ virBufferVSprintf(&buf, "
<logical_block_size>%llu"
+ "</logical_block_size>\n",
+ data->storage.logical_block_size);
+ virBufferVSprintf(&buf, "
<num_blocks>%llu</num_blocks>\n",
+ data->storage.num_blocks);
}
if (data->storage.flags & VIR_NODE_DEV_CAP_STORAGE_HOTPLUGGABLE)
virBufferAddLit(&buf,
@@ -1315,6 +1334,8 @@ 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);
@@ -1331,6 +1352,7 @@ 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:
diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h
index a7bb6c6..f70184d 100644
--- a/src/conf/node_device_conf.h
+++ b/src/conf/node_device_conf.h
@@ -82,6 +82,8 @@ struct _virNodeDevCapsDef {
union _virNodeDevCapData {
struct {
char *product_name;
+ char *description;
+ char *dmi_devpath;
struct {
char *vendor_name;
char *version;
@@ -101,6 +103,7 @@ struct _virNodeDevCapsDef {
unsigned function;
unsigned product;
unsigned vendor;
+ unsigned class;
char *product_name;
char *vendor_name;
} pci_dev;
@@ -117,10 +120,12 @@ struct _virNodeDevCapsDef {
unsigned _class; /* "class" is reserved in C */
unsigned subclass;
unsigned protocol;
+ char *interface_name;
char *description;
} usb_if;
struct {
char *address;
+ unsigned address_len;
char *ifname;
enum virNodeDevNetCapType subtype; /* LAST -> no subtype */
} net;
@@ -139,6 +144,8 @@ struct _virNodeDevCapsDef {
} scsi;
struct {
unsigned long long size;
+ unsigned long long num_blocks;
+ unsigned long long logical_block_size;
unsigned long long removable_media_size;
char *block;
char *bus;
--
1.6.5.1
From 86231984a2d4001c9bed8e69cf1d300d8628d3ef Mon Sep 17 00:00:00 2001
From: David Allan <dallan(a)redhat.com>
Date: Thu, 12 Nov 2009 15:21:38 -0500
Subject: [PATCH 2/5] Implement a node device backend using libudev.
Monitoring for addition and removal of devices is implemented.
There is a lot of detail work in this code, so we should try to get people running it on a
wide variety of hardware so we can shake out the differences in implementation between the
HAL and libudev backends.
I have moved the new fields in the node device capabilities to a separate patch.
This version contains changes per all the feedback I've received on earlier versions.
Fixes per feedback from Cole
* Fixed crash on daemon shutdown due to incorrect free
* Fixed uint64_t warnings
* Fixed log level and added significant additional logging
* Fixed a bug that caused some PCI devices not to be created
* Changed USB human readable vendor & product strings to sysfs source
* Changed struct member from udev_name and parent_udev_name to sysfs_path and
parent_sysfs_path to better interoperate with HAL
Fixes per feedback from danpb
* Removed dmi_devpath attribute from system device type
* Removed description attribute from system device type
* Removed interface_name attribute from usb interface device type
* Increased required libudev version to 145
* Fixed typo in error output to direct user to install libudev-devel >= 145
* Made empty strings from Sysfs convert to NULL
* Changed __attribute__ ((sentinel)) to ATTRIBUTE_SENTINEL
* Removed sysfs_path and parent_sysfs_path from XML
* Changed device naming to add device name for disambiguation and to convert
non-alphanumeric characters to underscores
* Reverted changes to virsh.c
---
configure.in | 47 +-
daemon/libvirtd.c | 3 +-
po/POTFILES.in | 3 +-
src/Makefile.am | 16 +-
src/conf/node_device_conf.c | 46 +-
src/conf/node_device_conf.h | 8 +-
src/libvirt_private.syms | 1 +
src/node_device/node_device_driver.c | 12 +-
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 | 1519 ++++++++++++++++++++
src/node_device/node_device_udev.h | 31 +
src/util/util.c | 28 +
src/util/util.h | 3 +
15 files changed, 1711 insertions(+), 47 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 7ad1a90..dc1e43e 100644
--- a/configure.in
+++ b/configure.in
@@ -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
diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c
index 01c9bbc..ef07460 100644
--- a/daemon/libvirtd.c
+++ b/daemon/libvirtd.c
@@ -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();
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 000be09..266f70c 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -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
diff --git a/src/Makefile.am b/src/Makefile.am
index 92dbae4..afe7eac 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -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) \
diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c
index ece339f..f3aca76 100644
--- a/src/conf/node_device_conf.c
+++ b/src/conf/node_device_conf.c
@@ -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);
@@ -1334,8 +1355,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);
@@ -1352,7 +1371,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:
diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h
index f70184d..e97242a 100644
--- a/src/conf/node_device_conf.h
+++ b/src/conf/node_device_conf.h
@@ -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,
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 0506867..c473d49 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -341,6 +341,7 @@ virNodeDeviceHasCap;
virNodeDeviceObjRemove;
virNodeDevCapTypeToString;
virNodeDeviceFindByName;
+virNodeDeviceFindBySysfsPath;
virNodeDeviceObjListFree;
virNodeDeviceDefFree;
virNodeDevCapsDefFree;
diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_device_driver.c
index f33ff48..c139907 100644
--- a/src/node_device/node_device_driver.c
+++ b/src/node_device/node_device_driver.c
@@ -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
}
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..d608b76
--- /dev/null
+++ b/src/node_device/node_device_udev.c
@@ -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(a)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...
+ *
+ * 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
index 0000000..0fd39ae
--- /dev/null
+++ b/src/node_device/node_device_udev.h
@@ -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(a)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
diff --git a/src/util/util.c b/src/util/util.c
index 853d3a0..e472e0c 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -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;
+}
diff --git a/src/util/util.h b/src/util/util.h
index f4e395e..8c9d401 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -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__ */
--
1.6.5.1
From fb2686fa7f9c3e553bdd5c3d08757dae3fef9c21 Mon Sep 17 00:00:00 2001
From: David Allan <dallan(a)redhat.com>
Date: Thu, 12 Nov 2009 12:12:09 -0500
Subject: [PATCH 3/5] Add scsi_target device type
Having libvirt understand scsi targets makes the node device tree look a bit better; there
isn't a lot of use for them otherwise. The device tree is still not perfect, but one
step at a time.
---
src/conf/node_device_conf.c | 43 ++++++++++++++++++++++++++++++++++++
src/conf/node_device_conf.h | 4 +++
src/node_device/node_device_udev.c | 34 ++++++++++++++++++++++++++++
3 files changed, 81 insertions(+), 0 deletions(-)
diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c
index f3aca76..f0bdff0 100644
--- a/src/conf/node_device_conf.c
+++ b/src/conf/node_device_conf.c
@@ -46,6 +46,7 @@ VIR_ENUM_IMPL(virNodeDevCap, VIR_NODE_DEV_CAP_LAST,
"usb",
"net",
"scsi_host",
+ "scsi_target",
"scsi",
"storage")
@@ -387,6 +388,12 @@ char *virNodeDeviceDefFormat(virConnectPtr conn,
}
break;
+
+ case VIR_NODE_DEV_CAP_SCSI_TARGET:
+ virBufferVSprintf(&buf, "
<target>%s</target>\n",
+ data->scsi_target.name);
+ break;
+
case VIR_NODE_DEV_CAP_SCSI:
virBufferVSprintf(&buf, " <host>%d</host>\n",
data->scsi.host);
virBufferVSprintf(&buf, " <bus>%d</bus>\n",
data->scsi.bus);
@@ -654,6 +661,36 @@ out:
return ret;
}
+
+static int
+virNodeDevCapScsiTargetParseXML(virConnectPtr conn,
+ xmlXPathContextPtr ctxt,
+ virNodeDeviceDefPtr def,
+ xmlNodePtr node,
+ union _virNodeDevCapData *data)
+{
+ xmlNodePtr orignode;
+ int ret = -1;
+
+ orignode = ctxt->node;
+ ctxt->node = node;
+
+ data->scsi_target.name = virXPathString(conn, "string(./name[1])",
ctxt);
+ if (!data->scsi_target.name) {
+ virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("no target name supplied for
'%s'"),
+ def->name);
+ goto out;
+ }
+
+ ret = 0;
+
+out:
+ ctxt->node = orignode;
+ return ret;
+}
+
+
static int
virNodeDevCapScsiHostParseXML(virConnectPtr conn,
xmlXPathContextPtr ctxt,
@@ -1058,6 +1095,9 @@ virNodeDevCapsDefParseXML(virConnectPtr conn,
case VIR_NODE_DEV_CAP_SCSI_HOST:
ret = virNodeDevCapScsiHostParseXML(conn, ctxt, def, node, &caps->data,
create);
break;
+ case VIR_NODE_DEV_CAP_SCSI_TARGET:
+ ret = virNodeDevCapScsiTargetParseXML(conn, ctxt, def, node,
&caps->data);
+ break;
case VIR_NODE_DEV_CAP_SCSI:
ret = virNodeDevCapScsiParseXML(conn, ctxt, def, node, &caps->data);
break;
@@ -1381,6 +1421,9 @@ void virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps)
VIR_FREE(data->scsi_host.wwnn);
VIR_FREE(data->scsi_host.wwpn);
break;
+ case VIR_NODE_DEV_CAP_SCSI_TARGET:
+ VIR_FREE(data->scsi_target.name);
+ break;
case VIR_NODE_DEV_CAP_SCSI:
VIR_FREE(data->scsi.type);
break;
diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h
index e97242a..639a7e7 100644
--- a/src/conf/node_device_conf.h
+++ b/src/conf/node_device_conf.h
@@ -41,6 +41,7 @@ enum virNodeDevCapType {
VIR_NODE_DEV_CAP_USB_INTERFACE, /* USB interface */
VIR_NODE_DEV_CAP_NET, /* Network device */
VIR_NODE_DEV_CAP_SCSI_HOST, /* SCSI Host Bus Adapter */
+ VIR_NODE_DEV_CAP_SCSI_TARGET, /* SCSI Target */
VIR_NODE_DEV_CAP_SCSI, /* SCSI device */
VIR_NODE_DEV_CAP_STORAGE, /* Storage device */
VIR_NODE_DEV_CAP_LAST
@@ -133,6 +134,9 @@ struct _virNodeDevCapsDef {
unsigned flags;
} scsi_host;
struct {
+ char *name;
+ } scsi_target;
+ struct {
unsigned host;
unsigned bus;
unsigned target;
diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c
index d608b76..010ff75 100644
--- a/src/node_device/node_device_udev.c
+++ b/src/node_device/node_device_udev.c
@@ -601,6 +601,32 @@ out:
}
+static int udevProcessSCSITarget(struct udev_device *device ATTRIBUTE_UNUSED,
+ virNodeDeviceDefPtr def)
+{
+ int ret = -1;
+ const char *sysname = NULL;
+ union _virNodeDevCapData *data = &def->caps->data;
+
+ sysname = udev_device_get_sysname(device);
+
+ data->scsi_target.name = strdup(sysname);
+ if (data->scsi_target.name == NULL) {
+ virReportOOMError(NULL);
+ goto out;
+ }
+
+ if (udevGenerateDeviceName(device, def, NULL) != 0) {
+ goto out;
+ }
+
+ ret = 0;
+
+out:
+ return ret;
+}
+
+
static int udevGetSCSIType(unsigned int type, char **typestring)
{
int ret = 0;
@@ -948,6 +974,11 @@ static int udevGetDeviceType(struct udev_device *device,
goto out;
}
+ if (devtype != NULL && STREQ(devtype, "scsi_target")) {
+ *type = VIR_NODE_DEV_CAP_SCSI_TARGET;
+ goto out;
+ }
+
if (devtype != NULL && STREQ(devtype, "scsi_device")) {
*type = VIR_NODE_DEV_CAP_SCSI;
goto out;
@@ -1008,6 +1039,9 @@ static int udevGetDeviceDetails(struct udev_device *device,
case VIR_NODE_DEV_CAP_SCSI_HOST:
ret = udevProcessSCSIHost(device, def);
break;
+ case VIR_NODE_DEV_CAP_SCSI_TARGET:
+ ret = udevProcessSCSITarget(device, def);
+ break;
case VIR_NODE_DEV_CAP_SCSI:
ret = udevProcessSCSIDevice(device, def);
break;
--
1.6.5.1
From 39c6c695eec6a9e99a70e51824da3e86fafbfa23 Mon Sep 17 00:00:00 2001
From: David Allan <dallan(a)redhat.com>
Date: Thu, 12 Nov 2009 15:47:59 -0500
Subject: [PATCH 4/5] Remove DevKit node device backend
---
configure.in | 7 +-
src/Makefile.am | 9 -
src/node_device/node_device_devkit.c | 447 ----------------------------------
src/node_device/node_device_driver.c | 3 -
src/node_device/node_device_driver.h | 3 -
5 files changed, 1 insertions(+), 468 deletions(-)
delete mode 100644 src/node_device/node_device_devkit.c
diff --git a/configure.in b/configure.in
index dc1e43e..c167381 100644
--- a/configure.in
+++ b/configure.in
@@ -1787,7 +1787,7 @@ AC_SUBST([UDEV_CFLAGS])
AC_SUBST([UDEV_LIBS])
with_nodedev=no;
-if test "$with_devkit" = "yes" -o "$with_hal" =
"yes" -o "$with_udev" = "yes";
+if test "$with_hal" = "yes" -o "$with_udev" =
"yes";
then
with_nodedev=yes
AC_DEFINE_UNQUOTED([WITH_NODE_DEVICES], 1, [with node device driver])
@@ -1947,11 +1947,6 @@ AC_MSG_NOTICE([ hal: $HAL_CFLAGS $HAL_LIBS])
else
AC_MSG_NOTICE([ hal: no])
fi
-if test "$with_devkit" = "yes" ; then
-AC_MSG_NOTICE([ devkit: $DEVKIT_CFLAGS $DEVKIT_LIBS])
-else
-AC_MSG_NOTICE([ devkit: no])
-fi
if test "$with_udev" = "yes" ; then
AC_MSG_NOTICE([ udev: $UDEV_CFLAGS $UDEV_LIBS])
else
diff --git a/src/Makefile.am b/src/Makefile.am
index afe7eac..4aaad6b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -267,9 +267,6 @@ NODE_DEVICE_DRIVER_HAL_SOURCES = \
node_device/node_device_hal.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
@@ -646,11 +643,6 @@ libvirt_driver_nodedev_la_SOURCES +=
$(NODE_DEVICE_DRIVER_HAL_SOURCES)
libvirt_driver_nodedev_la_CFLAGS += $(HAL_CFLAGS)
libvirt_driver_nodedev_la_LDFLAGS += $(HAL_LIBS)
endif
-if HAVE_DEVKIT
-libvirt_driver_nodedev_la_SOURCES += $(NODE_DEVICE_DRIVER_DEVKIT_SOURCES)
-libvirt_driver_nodedev_la_CFLAGS += $(DEVKIT_CFLAGS)
-libvirt_driver_nodedev_la_LDFLAGS += $(DEVKIT_LIBS)
-endif
if HAVE_UDEV
libvirt_driver_nodedev_la_SOURCES += $(NODE_DEVICE_DRIVER_UDEV_SOURCES)
libvirt_driver_nodedev_la_CFLAGS += $(UDEV_CFLAGS)
@@ -704,7 +696,6 @@ EXTRA_DIST += \
$(STORAGE_DRIVER_DISK_SOURCES) \
$(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) \
diff --git a/src/node_device/node_device_devkit.c b/src/node_device/node_device_devkit.c
deleted file mode 100644
index d2ffa1d..0000000
--- a/src/node_device/node_device_devkit.c
+++ /dev/null
@@ -1,447 +0,0 @@
-/*
- * node_device_devkit.c: node device enumeration - DeviceKit-based implementation
- *
- * Copyright (C) 2008 Virtual Iron Software, Inc.
- * Copyright (C) 2008 David F. Lively
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Author: David F. Lively <dlively(a)virtualiron.com>
- */
-
-#include <config.h>
-#include <devkit-gobject.h>
-
-#include "node_device_conf.h"
-#include "virterror_internal.h"
-#include "driver.h"
-#include "datatypes.h"
-#include "event.h"
-#include "memory.h"
-#include "uuid.h"
-#include "logging.h"
-
-#include "node_device.h"
-
-/*
- * Host device enumeration (DeviceKit implementation)
- */
-
-static virDeviceMonitorStatePtr driverState;
-
-#define CONN_DRV_STATE(conn) \
- ((virDeviceMonitorStatePtr)((conn)->devMonPrivateData))
-#define DRV_STATE_DKCLIENT(ds) ((DevkitClient *)((ds)->privateData))
-#define CONN_DKCLIENT(conn) DRV_STATE_DKCLIENT(CONN_DRV_STATE(conn))
-
-#define NODE_DEV_DKDEV(obj) ((DevkitDevice *)((obj)->privateData)
-
-static int get_str_prop(DevkitDevice *dkdev, const char *prop, char **val_p)
-{
- char *val = devkit_device_dup_property_as_str(dkdev, prop);
-
- if (val) {
- if (*val) {
- *val_p = val;
- return 0;
- } else {
- /* Treat empty strings as NULL values */
- VIR_FREE(val);
- }
- }
-
- return -1;
-}
-
-#if 0
-static int get_int_prop(DevkitDevice *dkdev, const char *prop, int *val_p)
-{
- if (! devkit_device_has_property(dkdev, prop))
- return -1;
- *val_p = devkit_device_get_property_as_int(dkdev, prop);
- return 0;
-}
-
-static int get_uint64_prop(DevkitDevice *dkdev, const char *prop,
- unsigned long long *val_p)
-{
- if (! devkit_device_has_property(dkdev, prop))
- return -1;
- *val_p = devkit_device_get_property_as_uint64(dkdev, prop);
- return 0;
-}
-#endif
-
-static int gather_pci_cap(DevkitDevice *dkdev,
- union _virNodeDevCapData *d)
-{
- const char *sysfs_path = devkit_device_get_native_path(dkdev);
-
- if (sysfs_path != NULL) {
- char *p = strrchr(sysfs_path, '/');
- if (p) {
- (void)virStrToLong_ui(p+1, &p, 16, &d->pci_dev.domain);
- (void)virStrToLong_ui(p+1, &p, 16, &d->pci_dev.bus);
- (void)virStrToLong_ui(p+1, &p, 16, &d->pci_dev.slot);
- (void)virStrToLong_ui(p+1, &p, 16, &d->pci_dev.function);
- }
- }
- return 0;
-}
-
-
-static int gather_usb_cap(DevkitDevice *dkdev,
- union _virNodeDevCapData *d)
-{
- (void)get_str_prop(dkdev, "ID_VENDOR", &d->usb_dev.vendor_name);
- (void)get_str_prop(dkdev, "ID_MODEL", &d->usb_dev.product_name);
- return 0;
-}
-
-
-static int gather_net_cap(DevkitDevice *dkdev,
- union _virNodeDevCapData *d)
-{
- const char *sysfs_path = devkit_device_get_native_path(dkdev);
- const char *ifname;
-
- if (sysfs_path == NULL)
- return -1;
- ifname = strrchr(sysfs_path, '/');
- if (!ifname || !*ifname || !*(++ifname))
- return -1;
- if ((d->net.ifname = strdup(ifname)) == NULL)
- return -1;
-
- d->net.subtype = VIR_NODE_DEV_CAP_NET_LAST;
-
- return 0;
-}
-
-
-static int gather_storage_cap(DevkitDevice *dkdev,
- union _virNodeDevCapData *d)
-{
- const char *device = devkit_device_get_device_file(dkdev);
-
- if (device && ((d->storage.block = strdup(device)) == NULL))
- return -1;
-
- return 0;
-}
-
-
-struct _caps_tbl_entry {
- const char *cap_name;
- enum virNodeDevCapType type;
- int (*gather_fn)(DevkitDevice *dkdev,
- union _virNodeDevCapData *data);
-};
-
-typedef struct _caps_tbl_entry caps_tbl_entry;
-
-static caps_tbl_entry caps_tbl[] = {
- { "pci", VIR_NODE_DEV_CAP_PCI_DEV, gather_pci_cap },
- { "usb", VIR_NODE_DEV_CAP_USB_DEV, gather_usb_cap },
- { "net", VIR_NODE_DEV_CAP_NET, gather_net_cap },
- { "block", VIR_NODE_DEV_CAP_STORAGE, gather_storage_cap },
- // TODO: more caps!
-};
-
-
-/* qsort/bsearch string comparator */
-static int cmpstringp(const void *p1, const void *p2)
-{
- /* from man 3 qsort */
- return strcmp(* (char * const *) p1, * (char * const *) p2);
-}
-
-
-static int gather_capability(DevkitDevice *dkdev,
- const char *cap_name,
- virNodeDevCapsDefPtr *caps_p)
-{
- size_t caps_tbl_len = sizeof(caps_tbl) / sizeof(caps_tbl[0]);
- caps_tbl_entry *entry;
-
- entry = bsearch(&cap_name, caps_tbl, caps_tbl_len,
- sizeof(caps_tbl[0]), cmpstringp);
-
- if (entry) {
- virNodeDevCapsDefPtr caps;
- if (VIR_ALLOC(caps) < 0)
- return ENOMEM;
- caps->type = entry->type;
- if (entry->gather_fn) {
- int rv = (*entry->gather_fn)(dkdev, &caps->data);
- if (rv != 0) {
- virNodeDevCapsDefFree(caps);
- return rv;
- }
- }
- caps->next = *caps_p;
- *caps_p = caps;
- }
-
- return 0;
-}
-
-
-static int gather_capabilities(DevkitDevice *dkdev,
- virNodeDevCapsDefPtr *caps_p)
-{
- const char *subsys = devkit_device_get_subsystem(dkdev);
- const char *bus_name = devkit_device_get_property(dkdev, "ID_BUS");
- virNodeDevCapsDefPtr caps = NULL;
- int rv;
-
- if (subsys) {
- rv = gather_capability(dkdev, subsys, &caps);
- if (rv != 0) goto failure;
- }
-
- if (bus_name && (subsys == NULL || !STREQ(bus_name, subsys))) {
- rv = gather_capability(dkdev, bus_name, &caps);
- if (rv != 0) goto failure;
- }
-
- *caps_p = caps;
- return 0;
-
- failure:
- while (caps) {
- virNodeDevCapsDefPtr next = caps->next;
- virNodeDevCapsDefFree(caps);
- caps = next;
- }
- return rv;
-}
-
-static void dev_create(void *_dkdev, void *_dkclient ATTRIBUTE_UNUSED)
-{
- DevkitDevice *dkdev = _dkdev;
- const char *sysfs_path = devkit_device_get_native_path(dkdev);
- virNodeDeviceObjPtr dev = NULL;
- virNodeDeviceDefPtr def = NULL;
- const char *name;
- int rv;
-
- if (sysfs_path == NULL)
- /* Currently using basename(sysfs_path) as device name (key) */
- return;
-
- name = strrchr(sysfs_path, '/');
- if (name == NULL)
- name = sysfs_path;
- else
- ++name;
-
- if (VIR_ALLOC(def) < 0)
- goto failure;
-
- if ((def->name = strdup(name)) == NULL)
- goto failure;
-
- // TODO: Find device parent, if any
-
- rv = gather_capabilities(dkdev, &def->caps);
- if (rv != 0) goto failure;
-
- nodeDeviceLock(driverState);
- dev = virNodeDeviceAssignDef(NULL,
- &driverState->devs,
- def);
-
- if (!dev) {
- nodeDeviceUnlock(driverState);
- goto failure;
- }
-
- dev->privateData = dkdev;
- dev->privateFree = NULL; /* XXX some free func needed ? */
- virNodeDeviceObjUnlock(dev);
-
- nodeDeviceUnlock(driverState);
-
- return;
-
- failure:
- DEBUG("FAILED TO ADD dev %s", name);
- if (def)
- virNodeDeviceDefFree(def);
-}
-
-
-static int devkitDeviceMonitorStartup(int privileged ATTRIBUTE_UNUSED)
-{
- size_t caps_tbl_len = sizeof(caps_tbl) / sizeof(caps_tbl[0]);
- DevkitClient *devkit_client = NULL;
- GError *err = NULL;
- GList *devs;
- int i;
-
- /* Ensure caps_tbl is sorted by capability name */
- qsort(caps_tbl, caps_tbl_len, sizeof(caps_tbl[0]), cmpstringp);
-
- if (VIR_ALLOC(driverState) < 0)
- return -1;
-
- if (virMutexInit(&driverState->lock) < 0) {
- VIR_FREE(driverState);
- return -1;
- }
-
- g_type_init();
-
- /* Get new devkit_client and connect to daemon */
- devkit_client = devkit_client_new(NULL);
- if (devkit_client == NULL) {
- DEBUG0("devkit_client_new returned NULL");
- goto failure;
- }
- if (!devkit_client_connect(devkit_client, &err)) {
- DEBUG0("devkit_client_connect failed");
- goto failure;
- }
-
- /* Populate with known devices.
- *
- * This really should be:
- devs = devkit_client_enumerate_by_subsystem(devkit_client, NULL, &err);
- if (err) {
- DEBUG0("devkit_client_enumerate_by_subsystem failed");
- devs = NULL;
- goto failure;
- }
- g_list_foreach(devs, dev_create, devkit_client);
- * but devkit_client_enumerate_by_subsystem currently fails when the second
- * arg is null (contrary to the API documentation). So the following code
- * (from Dan B) works around this by listing devices per handled subsystem.
- */
-
- for (i = 0 ; i < ARRAY_CARDINALITY(caps_tbl) ; i++) {
- const char *caps[] = { caps_tbl[i].cap_name, NULL };
- devs = devkit_client_enumerate_by_subsystem(devkit_client,
- caps,
- &err);
- if (err) {
- DEBUG0("devkit_client_enumerate_by_subsystem failed");
- devs = NULL;
- goto failure;
- }
- g_list_foreach(devs, dev_create, devkit_client);
- }
-
- driverState->privateData = devkit_client;
-
- // TODO: Register to get DeviceKit events on device changes and
- // coordinate updates with queries and other operations.
-
- return 0;
-
- failure:
- if (err) {
- DEBUG("\terror[%d]: %s", err->code, err->message);
- g_error_free(err);
- }
- if (devs) {
- g_list_foreach(devs, (GFunc)g_object_unref, NULL);
- g_list_free(devs);
- }
- if (devkit_client)
- g_object_unref(devkit_client);
- VIR_FREE(driverState);
-
- return -1;
-}
-
-
-static int devkitDeviceMonitorShutdown(void)
-{
- if (driverState) {
- DevkitClient *devkit_client;
-
- nodeDeviceLock(driverState);
- devkit_client = DRV_STATE_DKCLIENT(driverState);
- virNodeDeviceObjListFree(&driverState->devs);
- if (devkit_client)
- g_object_unref(devkit_client);
- nodeDeviceUnlock(driverState);
- virMutexDestroy(&driveState->lock);
- VIR_FREE(driverState);
- return 0;
- }
- return -1;
-}
-
-
-static int devkitDeviceMonitorReload(void)
-{
- /* XXX This isn't thread safe because its free'ing the thing
- * we're locking */
- (void)devkitDeviceMonitorShutdown();
- return devkitDeviceMonitorStartup();
-}
-
-
-static int devkitDeviceMonitorActive(void)
-{
- /* Always ready to deal with a shutdown */
- return 0;
-}
-
-
-static virDrvOpenStatus
-devkitNodeDrvOpen(virConnectPtr conn,
- virConnectAuthPtr auth ATTRIBUTE_UNUSED,
- int flags ATTRIBUTE_UNUSED)
-{
- if (driverState == NULL)
- return VIR_DRV_OPEN_DECLINED;
-
- conn->devMonPrivateData = driverState;
-
- return VIR_DRV_OPEN_SUCCESS;
-}
-
-static int devkitNodeDrvClose(virConnectPtr conn ATTRIBUTE_UNUSED)
-{
- conn->devMonPrivateData = NULL;
- return 0;
-}
-
-
-static virDeviceMonitor devkitDeviceMonitor = {
- .name = "devkitDeviceMonitor",
- .open = devkitNodeDrvOpen,
- .close = devkitNodeDrvClose,
-};
-
-
-static virStateDriver devkitStateDriver = {
- .name = "DeviceKit",
- .initialize = devkitDeviceMonitorStartup,
- .cleanup = devkitDeviceMonitorShutdown,
- .reload = devkitDeviceMonitorReload,
- .active = devkitDeviceMonitorActive,
-};
-
-int devkitNodeRegister(void)
-{
- registerCommonNodeFuncs(&devkitDeviceMonitor);
- if (virRegisterDeviceMonitor(&devkitDeviceMonitor) < 0)
- return -1;
- return virRegisterStateDriver(&devkitStateDriver);
-}
diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_device_driver.c
index c139907..cddd994 100644
--- a/src/node_device/node_device_driver.c
+++ b/src/node_device/node_device_driver.c
@@ -670,9 +670,6 @@ int nodedevRegister(void) {
#ifdef HAVE_HAL
return halNodeRegister();
#endif
-#ifdef HAVE_DEVKIT
- return devkitNodeRegister();
-#endif
#ifdef HAVE_UDEV
return udevNodeRegister();
#endif
diff --git a/src/node_device/node_device_driver.h b/src/node_device/node_device_driver.h
index 5be0781..4f0822c 100644
--- a/src/node_device/node_device_driver.h
+++ b/src/node_device/node_device_driver.h
@@ -42,9 +42,6 @@
#ifdef HAVE_HAL
int halNodeRegister(void);
#endif
-#ifdef HAVE_DEVKIT
-int devkitNodeRegister(void);
-#endif
#ifdef HAVE_UDEV
int udevNodeRegister(void);
#endif
--
1.6.5.1
From ff9a4c442c24bde4c429afd17609932688abcce8 Mon Sep 17 00:00:00 2001
From: David Allan <dallan(a)redhat.com>
Date: Thu, 12 Nov 2009 12:15:28 -0500
Subject: [PATCH 5/5] Add translation of PCI vendor and product IDs
This patch uses libpciaccess to provide human readable names for PCI vendor and device
IDs.
---
configure.in | 9 +++++
src/Makefile.am | 4 +-
src/node_device/node_device_udev.c | 64 ++++++++++++++++++++++++++++++++++-
3 files changed, 73 insertions(+), 4 deletions(-)
diff --git a/configure.in b/configure.in
index c167381..8e7c059 100644
--- a/configure.in
+++ b/configure.in
@@ -1781,10 +1781,19 @@ if test "x$with_udev" = "xyes" -o
"x$with_udev" = "xcheck"; then
CFLAGS="$old_CFLAGS"
LDFLAGS="$old_LDFLAGS"
fi
+ PCIACCESS_REQUIRED=0.10.0
+ PCIACCESS_CFLAGS=
+ PCIACCESS_LIBS=
+ PKG_CHECK_MODULES([PCIACCESS], [pciaccess >= $PCIACCESS_REQUIRED], [],
[PCIACCESS_FOUND=no])
+ if test "$PCIACCESS_FOUND" = "no" ; then
+ AC_MSG_ERROR([You must install libpciaccess/libpciaccess-devel >=
$PCIACCESS_REQUIRED to compile libvirt])
+ fi
fi
AM_CONDITIONAL([HAVE_UDEV], [test "x$with_udev" = "xyes"])
AC_SUBST([UDEV_CFLAGS])
AC_SUBST([UDEV_LIBS])
+AC_SUBST([PCIACCESS_CFLAGS])
+AC_SUBST([PCIACCESS_LIBS])
with_nodedev=no;
if test "$with_hal" = "yes" -o "$with_udev" =
"yes";
diff --git a/src/Makefile.am b/src/Makefile.am
index 4aaad6b..d22a103 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -645,8 +645,8 @@ libvirt_driver_nodedev_la_LDFLAGS += $(HAL_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)
+libvirt_driver_nodedev_la_CFLAGS += $(UDEV_CFLAGS) $(PCIACCESS_CFLAGS)
+libvirt_driver_nodedev_la_LDFLAGS += $(UDEV_LIBS) $(PCIACCESS_LIBS)
endif
if WITH_DRIVER_MODULES
diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c
index 010ff75..4ddf360 100644
--- a/src/node_device/node_device_udev.c
+++ b/src/node_device/node_device_udev.c
@@ -22,6 +22,7 @@
#include <config.h>
#include <libudev.h>
+#include <pciaccess.h>
#include <scsi/scsi.h>
#include <c-ctype.h>
@@ -351,6 +352,61 @@ static void udevLogFunction(struct udev *udev ATTRIBUTE_UNUSED,
}
+static int udevTranslatePCIIds(unsigned int vendor,
+ unsigned int product,
+ char **vendor_string,
+ char **product_string)
+{
+ int ret = -1;
+ struct pci_id_match m;
+ const char *vendor_name = NULL, *device_name = NULL;
+
+ if (pci_system_init() != 0) {
+ VIR_ERROR0("Failed to initialize libpciaccess\n");
+ goto out;
+ }
+
+ m.vendor_id = vendor;
+ m.device_id = product;
+ m.subvendor_id = PCI_MATCH_ANY;
+ m.subdevice_id = PCI_MATCH_ANY;
+ m.device_class = 0;
+ m.device_class_mask = 0;
+ m.match_data = 0;
+
+ /* pci_get_strings returns void */
+ pci_get_strings(&m,
+ &vendor_name,
+ &device_name,
+ NULL,
+ NULL);
+
+ if (vendor_name != NULL) {
+ *vendor_string = strdup(vendor_name);
+ if (*vendor_string == NULL) {
+ virReportOOMError(NULL);
+ goto out;
+ }
+ }
+
+ if (device_name != NULL) {
+ *product_string = strdup(device_name);
+ if (*product_string == NULL) {
+ virReportOOMError(NULL);
+ goto out;
+ }
+ }
+
+ /* pci_system_cleanup returns void */
+ pci_system_cleanup();
+
+ ret = 0;
+
+out:
+ return ret;
+}
+
+
static int udevProcessPCI(struct udev_device *device,
virNodeDeviceDefPtr def)
{
@@ -411,8 +467,12 @@ static int udevProcessPCI(struct udev_device *device,
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 (udevTranslatePCIIds(data->pci_dev.vendor,
+ data->pci_dev.product,
+ &data->pci_dev.vendor_name,
+ &data->pci_dev.product_name) != 0) {
+ goto out;
+ }
if (udevGenerateDeviceName(device, def, NULL) != 0) {
goto out;
--
1.6.5.1