Add a read-only udev based backend for virInterface. Useful for distros
that do not have netcf support yet. Multiple libvirt based utilities use
a HAL based fallback when virInterface is not available which is less
than ideal. This implements:
* virConnectNumOfInterfaces()
* virConnectListInterfaces()
* virConnectNumOfDefinedInterfaces()
* virConnectListDefinedInterfaces()
* virConnectInterfaceLookupByName()
* virConnectInterfaceLookupByMACString()
Signed-off-by: Doug Goldstein <cardoe(a)cardoe.com>
---
configure.ac | 10 +-
src/Makefile.am | 12 +-
src/interface/interface_backend_udev.c | 377 ++++++++++++++++++++++++++++++++
3 files changed, 395 insertions(+), 4 deletions(-)
create mode 100644 src/interface/interface_backend_udev.c
diff --git a/configure.ac b/configure.ac
index fd546fc..c2ad8e2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2822,11 +2822,15 @@ if test "$with_interface:$with_netcf" =
"check:yes" ; then
fi
if test "$with_interface:$with_netcf" = "check:no" ; then
- with_interface=no
+ if test "$with_udev" = "yes" ; then
+ with_interface=yes
+ else
+ with_interface=no
+ fi
fi
-if test "$with_interface:$with_netcf" = "yes:no" ; then
- AC_MSG_ERROR([Requested the Interface driver without netcf support])
+if test "$with_interface:$with_netcf:$with_udev" = "yes:no:no" ;
then
+ AC_MSG_ERROR([Requested the Interface driver without netcf or udev support])
fi
if test "$with_interface" = "yes" ; then
diff --git a/src/Makefile.am b/src/Makefile.am
index 72c4d6c..7143ae0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -553,11 +553,16 @@ INTERFACE_DRIVER_SOURCES =
if WITH_INTERFACE
INTERFACE_DRIVER_SOURCES += \
interface/interface_driver.h
-endif
if WITH_NETCF
INTERFACE_DRIVER_SOURCES += \
interface/interface_backend_netcf.c
+else
+if HAVE_UDEV
+INTERFACE_DRIVER_SOURCES += \
+ interface/interface_backend_udev.c
+endif
+endif
endif
SECRET_DRIVER_SOURCES = \
@@ -1040,6 +1045,11 @@ libvirt_driver_interface_la_LIBADD =
if WITH_NETCF
libvirt_driver_interface_la_CFLAGS += $(NETCF_CFLAGS)
libvirt_driver_interface_la_LIBADD += $(NETCF_LIBS)
+else
+if HAVE_UDEV
+libvirt_driver_interface_la_CFLAGS += $(UDEV_CFLAGS)
+libvirt_driver_interface_la_LIBADD += $(UDEV_LIBS)
+endif
endif
if WITH_DRIVER_MODULES
libvirt_driver_interface_la_LIBADD += ../gnulib/lib/libgnu.la
diff --git a/src/interface/interface_backend_udev.c
b/src/interface/interface_backend_udev.c
new file mode 100644
index 0000000..b1a4be3
--- /dev/null
+++ b/src/interface/interface_backend_udev.c
@@ -0,0 +1,377 @@
+
+#include <config.h>
+
+#include <libudev.h>
+
+#include "virterror_internal.h"
+#include "datatypes.h"
+#include "interface_driver.h"
+#include "interface_conf.h"
+#include "memory.h"
+
+#define VIR_FROM_THIS VIR_FROM_INTERFACE
+
+struct udev_iface_driver {
+ struct udev *udev;
+};
+
+enum udev_status { UDEV_IFACE_ACTIVE, UDEV_IFACE_INACTIVE, UDEV_IFACE_ALL };
+
+static struct udev_enumerate *
+udevIfaceGetDevices(struct udev *udev, enum udev_status status)
+{
+ struct udev_enumerate *enumerate;
+
+ /* Check input params for sanity */
+ if (!udev)
+ return NULL;
+
+ /* Create a new enumeration to create a list */
+ enumerate = udev_enumerate_new(udev);
+
+ if (!enumerate)
+ return NULL;
+
+ /* Enumerate all network subsystem devices */
+ udev_enumerate_add_match_subsystem(enumerate, "net");
+
+ /* Ignore devices that are part of a bridge */
+ udev_enumerate_add_nomatch_sysattr(enumerate, "brport/state", NULL);
+
+ /* State of the device */
+ switch (status) {
+ case UDEV_IFACE_ACTIVE:
+ udev_enumerate_add_match_sysattr(enumerate, "operstate",
"up");
+ break;
+
+ case UDEV_IFACE_INACTIVE:
+ udev_enumerate_add_match_sysattr(enumerate, "operstate",
"down");
+ break;
+
+ case UDEV_IFACE_ALL:
+ break;
+ }
+
+ /* We don't want to see the TUN devices that QEMU creates for other gets
+ * running on this machine. By saying nomatch NULL, we just are getting
+ * devices without the tun_flags sysattr.
+ */
+ udev_enumerate_add_nomatch_sysattr(enumerate, "tun_flags", NULL);
+
+ return enumerate;
+}
+
+static virDrvOpenStatus
+udevIfaceOpenInterface(virConnectPtr conn,
+ virConnectAuthPtr auth ATTRIBUTE_UNUSED,
+ unsigned int flags ATTRIBUTE_UNUSED)
+{
+ struct udev_iface_driver *driverState = NULL;
+
+ if (VIR_ALLOC(driverState) < 0) {
+ virReportOOMError();
+ goto err;
+ }
+
+ driverState->udev = udev_new();
+ if (!driverState->udev) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("failed to create udev context"));
+ goto err;
+ }
+
+ conn->interfacePrivateData = driverState;
+
+ return VIR_DRV_OPEN_SUCCESS;
+
+err:
+ VIR_FREE(driverState);
+
+ return VIR_DRV_OPEN_ERROR;
+}
+
+static int
+udevIfaceCloseInterface(virConnectPtr conn)
+{
+ struct udev_iface_driver *driverState;
+
+ if (conn->interfacePrivateData != NULL) {
+ driverState = conn->interfacePrivateData;
+
+ udev_unref(driverState->udev);
+
+ VIR_FREE(driverState);
+ }
+
+ conn->interfacePrivateData = NULL;
+ return 0;
+}
+
+static int
+udevIfaceNumOfInterfaces(virConnectPtr conn)
+{
+ struct udev_iface_driver *driverState = conn->interfacePrivateData;
+ struct udev *udev = udev_ref(driverState->udev);
+ struct udev_enumerate *enumerate = NULL;
+ struct udev_list_entry *devices;
+ struct udev_list_entry *dev_entry;
+ int count = 0;
+
+ enumerate = udevIfaceGetDevices(udev, UDEV_IFACE_ACTIVE);
+
+ if (!enumerate) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("failed to get number of active interfaces on
host"));
+ return -1;
+ }
+
+ /* Do the scan to load up the enumeration */
+ udev_enumerate_scan_devices(enumerate);
+
+ /* Get a list we can walk */
+ devices = udev_enumerate_get_list_entry(enumerate);
+
+ /* For each item so we can count */
+ udev_list_entry_foreach(dev_entry, devices) {
+ count++;
+ }
+
+ udev_enumerate_unref(enumerate);
+
+ udev_unref(udev);
+
+ return count;
+}
+
+static int
+udevIfaceListInterfaces(virConnectPtr conn, char **const names, int names_len)
+{
+ struct udev_iface_driver *driverState = conn->interfacePrivateData;
+ struct udev *udev = udev_ref(driverState->udev);
+ struct udev_enumerate *enumerate = NULL;
+ struct udev_list_entry *devices;
+ struct udev_list_entry *dev_entry;
+ int count = 0;
+
+ enumerate = udevIfaceGetDevices(udev, UDEV_IFACE_ACTIVE);
+
+ if (!enumerate) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("failed to get list of active interfaces on host"));
+ return -1;
+ }
+
+ /* Do the scan to load up the enumeration */
+ udev_enumerate_scan_devices(enumerate);
+
+ /* Get a list we can walk */
+ devices = udev_enumerate_get_list_entry(enumerate);
+
+ /* For each item so we can count */
+ udev_list_entry_foreach(dev_entry, devices) {
+ struct udev_device *dev;
+ const char *path;
+
+ path = udev_list_entry_get_name(dev_entry);
+ dev = udev_device_new_from_syspath(udev, path);
+ names[count] = strdup(udev_device_get_sysname(dev));
+ udev_device_unref(dev);
+
+ count++;
+ if (count > names_len)
+ break;
+ }
+
+ udev_enumerate_unref(enumerate);
+
+ udev_unref(udev);
+
+ return count;
+}
+
+static int
+udevIfaceNumOfDefinedInterfaces(virConnectPtr conn)
+{
+ struct udev_iface_driver *driverState = conn->interfacePrivateData;
+ struct udev *udev = udev_ref(driverState->udev);
+ struct udev_enumerate *enumerate = NULL;
+ struct udev_list_entry *devices;
+ struct udev_list_entry *dev_entry;
+ int count = 0;
+
+ enumerate = udevIfaceGetDevices(udev, UDEV_IFACE_INACTIVE);
+
+ if (!enumerate) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("failed to get number of defined interfaces on
host"));
+ return -1;
+ }
+
+ /* Do the scan to load up the enumeration */
+ udev_enumerate_scan_devices(enumerate);
+
+ /* Get a list we can walk */
+ devices = udev_enumerate_get_list_entry(enumerate);
+
+ /* For each item so we can count */
+ udev_list_entry_foreach(dev_entry, devices) {
+ count++;
+ }
+
+ udev_enumerate_unref(enumerate);
+
+ udev_unref(udev);
+
+ return count;
+}
+
+static int
+udevIfaceListDefinedInterfaces(virConnectPtr conn,
+ char **const names,
+ int names_len)
+{
+ struct udev_iface_driver *driverState = conn->interfacePrivateData;
+ struct udev *udev = udev_ref(driverState->udev);
+ struct udev_enumerate *enumerate = NULL;
+ struct udev_list_entry *devices;
+ struct udev_list_entry *dev_entry;
+ int count = 0;
+
+ enumerate = udevIfaceGetDevices(udev, UDEV_IFACE_INACTIVE);
+
+ if (!enumerate) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("failed to get list of defined interfaces on host"));
+ return -1;
+ }
+
+ /* Do the scan to load up the enumeration */
+ udev_enumerate_scan_devices(enumerate);
+
+ /* Get a list we can walk */
+ devices = udev_enumerate_get_list_entry(enumerate);
+
+ /* For each item so we can count */
+ udev_list_entry_foreach(dev_entry, devices) {
+ struct udev_device *dev;
+ const char *path;
+
+ path = udev_list_entry_get_name(dev_entry);
+ dev = udev_device_new_from_syspath(udev, path);
+ names[count] = strdup(udev_device_get_sysname(dev));
+ udev_device_unref(dev);
+
+ count++;
+ if (count > names_len)
+ break;
+ }
+
+ udev_enumerate_unref(enumerate);
+
+ udev_unref(udev);
+
+ return count;
+}
+
+static virInterfacePtr
+udevIfaceLookupByName(virConnectPtr conn, const char *name)
+{
+ struct udev_iface_driver *driverState = conn->interfacePrivateData;
+ struct udev *udev = udev_ref(driverState->udev);
+ struct udev_device *dev;
+ const char *macaddr;
+ virInterfacePtr ret;
+
+ /* get a device reference based on the device name */
+ dev = udev_device_new_from_subsystem_sysname(udev, "net", name);
+ if (!dev) {
+ virReportError(VIR_ERR_NO_INTERFACE,
+ _("couldn't find interface named '%s'"),
+ name);
+ return NULL;
+ }
+
+ macaddr = udev_device_get_sysattr_value(dev, "address");
+ ret = virGetInterface(conn, name, macaddr);
+ udev_device_unref(dev);
+
+ udev_unref(udev);
+
+ return ret;
+}
+
+static virInterfacePtr
+udevIfaceLookupByMACString(virConnectPtr conn, const char *macstr)
+{
+ struct udev_iface_driver *driverState = conn->interfacePrivateData;
+ struct udev *udev = udev_ref(driverState->udev);
+ struct udev_enumerate *enumerate = NULL;
+ struct udev_list_entry *dev_entry;
+ struct udev_device *dev;
+ const char *name;
+ virInterfacePtr ret;
+
+ enumerate = udevIfaceGetDevices(udev, UDEV_IFACE_ALL);
+
+ if (!enumerate) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to lookup interface with MAC address
'%s'"),
+ macstr);
+ return NULL;
+ }
+
+ /* Match on MAC */
+ udev_enumerate_add_match_sysattr(enumerate, "address", macstr);
+
+ /* Do the scan to load up the enumeration */
+ udev_enumerate_scan_devices(enumerate);
+
+ /* Get a list we can walk */
+ dev_entry = udev_enumerate_get_list_entry(enumerate);
+
+ /* Check that we got something back */
+ if (!dev_entry) {
+ virReportError(VIR_ERR_NO_INTERFACE,
+ _("couldn't find interface with MAC address
'%s'"),
+ macstr);
+ return NULL;
+ }
+
+ /* Check that we didn't get multiple items back */
+ if (udev_list_entry_get_next(dev_entry)) {
+ virReportError(VIR_ERR_MULTIPLE_INTERFACES,
+ _("the MAC address '%s' matches multiple
interfaces"),
+ macstr);
+ return NULL;
+ }
+
+ dev = udev_device_new_from_syspath(udev, udev_list_entry_get_name(dev_entry));
+ name = udev_device_get_sysname(dev);
+ ret = virGetInterface(conn, name, macstr);
+ udev_device_unref(dev);
+
+ udev_enumerate_unref(enumerate);
+
+ udev_unref(udev);
+
+ return ret;
+}
+static virInterfaceDriver udevIfaceDriver = {
+ "Interface",
+ .open = udevIfaceOpenInterface,
+ .close = udevIfaceCloseInterface,
+ .numOfInterfaces = udevIfaceNumOfInterfaces,
+ .listInterfaces = udevIfaceListInterfaces,
+ .numOfDefinedInterfaces = udevIfaceNumOfDefinedInterfaces,
+ .listDefinedInterfaces = udevIfaceListDefinedInterfaces,
+ .interfaceLookupByName = udevIfaceLookupByName,
+ .interfaceLookupByMACString = udevIfaceLookupByMACString,
+};
+
+int
+interfaceRegister(void) {
+ if (virRegisterInterfaceDriver(&udevIfaceDriver) < 0)
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("failed to register udev interface driver"));
+ return 0;
+}
--
1.7.8.6