[libvirt] [PATCHv3 2/2] interface: add udev based backend for virInterface

Doug Goldstein cardoe at cardoe.com
Tue Sep 18 01:27:07 UTC 2012


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 at 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




More information about the libvir-list mailing list