[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[libvirt] [PATCHv4] interface: add udev based backend for virInterface



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 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 |  430 ++++++++++++++++++++++++++++++++
 tools/virsh.c                          |    2 +
 5 files changed, 450 insertions(+), 13 deletions(-)
 create mode 100644 src/interface/interface_backend_udev.c

diff --git a/configure.ac b/configure.ac
index 3e9073d..a3d9ab1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2798,18 +2798,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 12a2b25..0999260 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 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..a823d2d
--- /dev/null
+++ b/src/interface/interface_backend_udev.c
@@ -0,0 +1,430 @@
+/*
+ * interface_backend_udev.c: udev backend for virInterface
+ *
+ * Copyright (C) 2012 Doug Goldstein <cardoe 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)
+{
+    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
+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;
+
+        /* 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:
+    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
+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;
+
+        /* 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:
+    udev_enumerate_unref(enumerate);
+    udev_unref(udev);
+
+    for (names_len = 0; names_len < count; names_len++)
+        VIR_FREE(names[names_len]);
+
+    return -1;
+}
+
+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.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 */
+    .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


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]