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()
* virConnectListAllInterfaces()
* virConnectInterfaceLookupByName()
* virConnectInterfaceLookupByMACString()
---
Change from v5:
* squash the virConnectListAllInterfaces() patch in
* rename status enum to virUdevStatus
* remove checks that don't print errors as the value is checked elsewhere
* provide helpers for NumOfInterfaces/NumOfDefinedInterfaces to reduce code
* provide helpers for ListInterfaces/ListDefinedInterfaces to reduce code
Change from v4:
* Fix up udev reference counting
Change from v3:
* fix make syntax-check issues
* check strdup() and handle that case
* simplify configure check
Change from v2:
* rebase from master
Change from v1:
* rebase from master
configure.ac | 18 +-
po/POTFILES.in | 1 +
src/Makefile.am | 12 +-
src/interface/interface_backend_udev.c | 511 ++++++++++++++++++++++++++++++++
tools/virsh.c | 2 +
5 files changed, 531 insertions(+), 13 deletions(-)
create mode 100644 src/interface/interface_backend_udev.c
diff --git a/configure.ac b/configure.ac
index 6d50985..92f85df 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2801,18 +2801,12 @@ if test "$with_libvirtd" = "no" ; then
with_interface=no
fi
-dnl The interface driver depends on the netcf library
-if test "$with_interface:$with_netcf" = "check:yes" ; then
- with_interface=yes
-fi
-
-if test "$with_interface:$with_netcf" = "check:no" ; then
- with_interface=no
-fi
-
-if test "$with_interface:$with_netcf" = "yes:no" ; then
- AC_MSG_ERROR([Requested the Interface driver without netcf support])
-fi
+dnl The interface driver depends on the netcf library or udev library
+case $with_interface:$with_netcf:$with_udev in
+ check:*yes*) with_interface=yes ;;
+ check:no:no) with_interface=no ;;
+ yes:no:no) AC_MSG_ERROR([Requested the Interface driver without netcf or udev
support]) ;;
+esac
if test "$with_interface" = "yes" ; then
AC_DEFINE_UNQUOTED([WITH_INTERFACE], [1],
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 74d8dcc..2538225 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -41,6 +41,7 @@ src/hyperv/hyperv_driver.c
src/hyperv/hyperv_util.c
src/hyperv/hyperv_wmi.c
src/interface/interface_backend_netcf.c
+src/interface/interface_backend_udev.c
src/internal.h
src/libvirt.c
src/libvirt-qemu.c
diff --git a/src/Makefile.am b/src/Makefile.am
index ae3d491..98448c1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -558,11 +558,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 = \
@@ -1045,6 +1050,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..bb161ab
--- /dev/null
+++ b/src/interface/interface_backend_udev.c
@@ -0,0 +1,511 @@
+/*
+ * 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;
+};
+
+typedef enum {
+ VIR_UDEV_IFACE_ACTIVE,
+ VIR_UDEV_IFACE_INACTIVE,
+ VIR_UDEV_IFACE_ALL
+} virUdevStatus ;
+
+static const char *
+virUdevStatusString(virUdevStatus status)
+{
+ switch (status) {
+ case VIR_UDEV_IFACE_ACTIVE:
+ return "active";
+ case VIR_UDEV_IFACE_INACTIVE:
+ return "inactive";
+ case VIR_UDEV_IFACE_ALL:
+ return "all";
+ }
+
+ return "";
+}
+
+static struct udev_enumerate * ATTRIBUTE_NONNULL(1)
+udevIfaceGetDevices(struct udev *udev, virUdevStatus status)
+{
+ struct udev_enumerate *enumerate;
+
+ /* 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 VIR_UDEV_IFACE_ACTIVE:
+ udev_enumerate_add_match_sysattr(enumerate, "operstate",
"up");
+ break;
+
+ case VIR_UDEV_IFACE_INACTIVE:
+ udev_enumerate_add_match_sysattr(enumerate, "operstate",
"down");
+ break;
+
+ case VIR_UDEV_IFACE_ALL:
+ break;
+ }
+
+ /* We don't want to see the TUN devices that QEMU creates for other guests
+ * 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)
+{
+ struct udev_iface_driver *driverState = NULL;
+
+ virCheckFlags(0, VIR_DRV_OPEN_ERROR);
+
+ 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
+udevIfaceNumOfInterfacesByStatus(virConnectPtr conn, virUdevStatus status)
+{
+ 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, status);
+
+ if (!enumerate) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to get number of %s interfaces on host"),
+ virUdevStatusString(status));
+ count = -1;
+ goto err;
+ }
+
+ /* 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++;
+ }
+
+err:
+ if (enumerate)
+ udev_enumerate_unref(enumerate);
+ udev_unref(udev);
+
+ return count;
+}
+
+static int
+udevIfaceListInterfacesByStatus(virConnectPtr conn,
+ char **const names,
+ int names_len,
+ virUdevStatus status)
+{
+ 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, status);
+
+ if (!enumerate) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to get list of %s interfaces on host"),
+ virUdevStatusString(status));
+ goto err;
+ }
+
+ /* 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;
+
+ /* Ensure we won't exceed the size of our array */
+ if (count > names_len)
+ break;
+
+ 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);
+
+ /* If strdup() failed, we are out of memory */
+ if (!names[count]) {
+ virReportOOMError();
+ goto err;
+ }
+
+ count++;
+ }
+
+ udev_enumerate_unref(enumerate);
+ udev_unref(udev);
+
+ return count;
+
+err:
+ if (enumerate)
+ udev_enumerate_unref(enumerate);
+ udev_unref(udev);
+
+ for (names_len = 0; names_len < count; names_len++)
+ VIR_FREE(names[names_len]);
+
+ return -1;
+}
+
+static int
+udevIfaceNumOfInterfaces(virConnectPtr conn)
+{
+ return udevIfaceNumOfInterfacesByStatus(conn, VIR_UDEV_IFACE_ACTIVE);
+}
+
+static int
+udevIfaceListInterfaces(virConnectPtr conn,
+ char **const names,
+ int names_len)
+{
+ return udevIfaceListInterfacesByStatus(conn, names, names_len,
+ VIR_UDEV_IFACE_ACTIVE);
+}
+
+static int
+udevIfaceNumOfDefinedInterfaces(virConnectPtr conn)
+{
+ return udevIfaceNumOfInterfacesByStatus(conn, VIR_UDEV_IFACE_INACTIVE);
+}
+
+static int
+udevIfaceListDefinedInterfaces(virConnectPtr conn,
+ char **const names,
+ int names_len)
+{
+ return udevIfaceListInterfacesByStatus(conn, names, names_len,
+ VIR_UDEV_IFACE_INACTIVE);
+}
+
+static int
+udevIfaceListAllInterfaces(virConnectPtr conn,
+ virInterfacePtr **ifaces,
+ unsigned int flags)
+{
+ struct udev_iface_driver *driverState = conn->interfacePrivateData;
+ struct udev *udev;
+ struct udev_enumerate *enumerate = NULL;
+ struct udev_list_entry *devices;
+ struct udev_list_entry *dev_entry;
+ virInterfacePtr *ifaces_list;
+ virInterfacePtr iface_obj;
+ int tmp_count;
+ int count = 0;
+ int status = 0;
+ int ret;
+
+ virCheckFlags(VIR_CONNECT_LIST_INTERFACES_ACTIVE |
+ VIR_CONNECT_LIST_INTERFACES_INACTIVE, -1);
+
+ /* Grab a udev reference */
+ udev = udev_ref(driverState->udev);
+
+ /* List all interfaces incase we support more filter flags in the future */
+ enumerate = udevIfaceGetDevices(udev, VIR_UDEV_IFACE_ALL);
+
+ if (!enumerate) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to get list of %s interfaces on host"),
+ virUdevStatusString(status));
+ ret = -1;
+ goto cleanup;
+ }
+
+ /* 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++;
+ }
+
+ /* If we've got nothing, exit out */
+ if (count == 0) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ /* If we're asked for the ifaces then alloc up memory */
+ if (ifaces) {
+ if (VIR_ALLOC_N(ifaces_list, count + 1) < 0) {
+ virReportOOMError();
+ ret = -1;
+ goto cleanup;
+ }
+ }
+
+ /* Get a list we can walk */
+ devices = udev_enumerate_get_list_entry(enumerate);
+
+ /* reset our iterator */
+ count = 0;
+
+ /* Walk through each device */
+ udev_list_entry_foreach(dev_entry, devices) {
+ struct udev_device *dev;
+ const char *path;
+ const char *name;
+ const char *macaddr;
+ int add_to_list;
+
+ path = udev_list_entry_get_name(dev_entry);
+ dev = udev_device_new_from_syspath(udev, path);
+ name = udev_device_get_sysname(dev);
+ macaddr = udev_device_get_sysattr_value(dev, "address");
+ status = STREQ(udev_device_get_sysattr_value(dev, "operstate"),
"up");
+ udev_device_unref(dev);
+
+ /* Filter the results */
+ if (status && (flags & VIR_CONNECT_LIST_INTERFACES_ACTIVE))
+ add_to_list = 1;
+ else if (!status && (flags & VIR_CONNECT_LIST_INTERFACES_INACTIVE))
+ add_to_list = 1;
+
+ /* If we matched a filter, then add it */
+ if (add_to_list) {
+ if (ifaces) {
+ iface_obj = virGetInterface(conn, name, macaddr);
+ ifaces_list[count] = iface_obj;
+ }
+ count++;
+ }
+ }
+
+ /* Drop our refcounts */
+ udev_enumerate_unref(enumerate);
+ udev_unref(udev);
+
+ /* Trim the array to its final size */
+ if (ifaces) {
+ ignore_value(VIR_REALLOC_N(ifaces_list, count + 1));
+ *ifaces = ifaces_list;
+ }
+
+ return count;
+
+cleanup:
+ if (enumerate)
+ udev_enumerate_unref(enumerate);
+ udev_unref(udev);
+
+ if (ifaces) {
+ for (tmp_count = 0; tmp_count < count; tmp_count++)
+ virInterfaceFree(ifaces_list[tmp_count]);
+ }
+
+ VIR_FREE(ifaces_list);
+
+ return ret;
+
+}
+
+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 = NULL;
+
+ /* 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);
+ goto err;
+ }
+
+ macaddr = udev_device_get_sysattr_value(dev, "address");
+ ret = virGetInterface(conn, name, macaddr);
+ udev_device_unref(dev);
+
+err:
+ 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 = NULL;
+
+ enumerate = udevIfaceGetDevices(udev, VIR_UDEV_IFACE_ALL);
+
+ if (!enumerate) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to lookup interface with MAC address
'%s'"),
+ macstr);
+ goto err;
+ }
+
+ /* 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);
+ goto err;
+ }
+
+ /* 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);
+ goto err;
+ }
+
+ 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);
+
+err:
+ if (enumerate)
+ udev_enumerate_unref(enumerate);
+ udev_unref(udev);
+
+ return ret;
+}
+
+static virInterfaceDriver udevIfaceDriver = {
+ "udev",
+ .open = udevIfaceOpenInterface, /* 0.10.3 */
+ .close = udevIfaceCloseInterface, /* 0.10.3 */
+ .numOfInterfaces = udevIfaceNumOfInterfaces, /* 0.10.3 */
+ .listInterfaces = udevIfaceListInterfaces, /* 0.10.3 */
+ .numOfDefinedInterfaces = udevIfaceNumOfDefinedInterfaces, /* 0.10.3 */
+ .listDefinedInterfaces = udevIfaceListDefinedInterfaces, /* 0.10.3 */
+ .listAllInterfaces = udevIfaceListAllInterfaces, /* 0.10.3 */
+ .interfaceLookupByName = udevIfaceLookupByName, /* 0.10.3 */
+ .interfaceLookupByMACString = udevIfaceLookupByMACString, /* 0.10.3 */
+};
+
+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 b32ee8b..f0ec625 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -2708,6 +2708,8 @@ vshShowVersion(vshControl *ctl ATTRIBUTE_UNUSED)
vshPrint(ctl, " Interface");
# if defined(WITH_NETCF)
vshPrint(ctl, " netcf");
+# elif defined(HAVE_UDEV)
+ vshPrint(ctl, " udev");
# endif
#endif
#ifdef WITH_NWFILTER
--
1.7.8.6