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()
---
Change from v2:
* rebase from master
Change from v1:
* rebase from master
.gnulib | 2 +-
configure.ac | 10 +-
src/Makefile.am | 12 +-
src/interface/interface_backend_udev.c | 395 ++++++++++++++++++++++++++++++++
tools/virsh.c | 3 +
5 files changed, 417 insertions(+), 5 deletions(-)
create mode 100644 src/interface/interface_backend_udev.c
diff --git a/.gnulib b/.gnulib
index 440a1db..271dd74 160000
--- a/.gnulib
+++ b/.gnulib
@@ -1 +1 @@
-Subproject commit 440a1dbe523e37f206252cb034c3a62f26867e42
+Subproject commit 271dd74fdf54ec2a03e73a5173b0b5697f6088f1
diff --git a/configure.ac b/configure.ac
index 1a44d21..e7757dd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2803,11 +2803,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 4ae741b..53f1fbf 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -557,11 +557,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 = \
@@ -1044,6 +1049,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..30b71fd
--- /dev/null
+++ b/src/interface/interface_backend_udev.c
@@ -0,0 +1,395 @@
+/*
+ * interface_backend_udev.c: udev backend for virInterface
+ *
+ * Copyright (C) 2012 Doug Goldstein <cardoe(a)cardoe.com>
+ *
+ * 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, see
+ * <
http://www.gnu.org/licenses/>.
+ */
+#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, /* 0.7.0 */
+ .close = udevIfaceCloseInterface, /* 0.7.0 */
+ .numOfInterfaces = udevIfaceNumOfInterfaces, /* 0.7.0 */
+ .listInterfaces = udevIfaceListInterfaces, /* 0.7.0 */
+ .numOfDefinedInterfaces = udevIfaceNumOfDefinedInterfaces, /* 0.7.0 */
+ .listDefinedInterfaces = udevIfaceListDefinedInterfaces, /* 0.7.0 */
+ .interfaceLookupByName = udevIfaceLookupByName, /* 0.7.0 */
+ .interfaceLookupByMACString = udevIfaceLookupByMACString, /* 0.7.0 */
+};
+
+int
+interfaceRegister(void) {
+ if (virRegisterInterfaceDriver(&udevIfaceDriver) < 0)
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("failed to register udev interface driver"));
+ return 0;
+}
diff --git a/tools/virsh.c b/tools/virsh.c
index 6a7b89d..7d682f2 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -2712,6 +2712,9 @@ vshShowVersion(vshControl *ctl ATTRIBUTE_UNUSED)
#if defined(WITH_NETCF)
vshPrint(ctl, " netcf");
#endif
+#if !defined(WITH_NETCF) && defined(HAVE_UDEV)
+ vshPrint(ctl, " udev");
+#endif
#endif
#ifdef WITH_NWFILTER
vshPrint(ctl, " Nwfilter");
--
1.7.8.6