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