mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-30 17:45:23 +00:00
5165ff0971
All of the ones being removed are pulled in by internal.h. The only exception is sanlock which expects the application to include <stdint.h> before sanlock's headers, because sanlock prototypes use fixed width int, but they don't include stdint.h themselves, so we have to leave that one in place. Signed-off-by: Erik Skultety <eskultet@redhat.com> Acked-by: Michal Privoznik <mprivozn@redhat.com>
1311 lines
38 KiB
C
1311 lines
38 KiB
C
/*
|
|
* interface_backend_udev.c: udev backend for virInterface
|
|
*
|
|
* Copyright (C) 2014 Red Hat, Inc.
|
|
* 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 <dirent.h>
|
|
#include <libudev.h>
|
|
|
|
#include "virerror.h"
|
|
#include "virfile.h"
|
|
#include "c-ctype.h"
|
|
#include "datatypes.h"
|
|
#include "domain_conf.h"
|
|
#include "interface_driver.h"
|
|
#include "interface_conf.h"
|
|
#include "viralloc.h"
|
|
#include "virstring.h"
|
|
#include "viraccessapicheck.h"
|
|
#include "virinterfaceobj.h"
|
|
#include "virnetdev.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_INTERFACE
|
|
|
|
struct udev_iface_driver {
|
|
struct udev *udev;
|
|
bool privileged;
|
|
};
|
|
|
|
typedef enum {
|
|
VIR_UDEV_IFACE_ACTIVE,
|
|
VIR_UDEV_IFACE_INACTIVE,
|
|
VIR_UDEV_IFACE_ALL
|
|
} virUdevStatus;
|
|
|
|
static struct udev_iface_driver *driver;
|
|
|
|
static virInterfaceDef *udevGetIfaceDef(struct udev *udev, const char *name);
|
|
|
|
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 "";
|
|
}
|
|
|
|
/*
|
|
* Get a minimal virInterfaceDef containing enough metadata
|
|
* for access control checks to be performed. Currently
|
|
* this implies existence of name and mac address attributes
|
|
*/
|
|
static virInterfaceDef * ATTRIBUTE_NONNULL(1)
|
|
udevGetMinimalDefForDevice(struct udev_device *dev)
|
|
{
|
|
virInterfaceDef *def;
|
|
|
|
/* Allocate our interface definition structure */
|
|
if (VIR_ALLOC(def) < 0)
|
|
return NULL;
|
|
|
|
if (VIR_STRDUP(def->name, udev_device_get_sysname(dev)) < 0)
|
|
goto cleanup;
|
|
|
|
if (VIR_STRDUP(def->mac, udev_device_get_sysattr_value(dev, "address")) < 0)
|
|
goto cleanup;
|
|
|
|
return def;
|
|
|
|
cleanup:
|
|
virInterfaceDefFree(def);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static struct udev_enumerate * ATTRIBUTE_NONNULL(1)
|
|
udevGetDevices(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 int
|
|
udevNumOfInterfacesByStatus(virConnectPtr conn, virUdevStatus status,
|
|
virInterfaceObjListFilter filter)
|
|
{
|
|
struct udev *udev = udev_ref(driver->udev);
|
|
struct udev_enumerate *enumerate = NULL;
|
|
struct udev_list_entry *devices;
|
|
struct udev_list_entry *dev_entry;
|
|
int count = 0;
|
|
|
|
enumerate = udevGetDevices(udev, status);
|
|
|
|
if (!enumerate) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("failed to get number of %s interfaces on host"),
|
|
virUdevStatusString(status));
|
|
count = -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) {
|
|
struct udev_device *dev;
|
|
const char *path;
|
|
virInterfaceDefPtr def;
|
|
|
|
path = udev_list_entry_get_name(dev_entry);
|
|
dev = udev_device_new_from_syspath(udev, path);
|
|
|
|
def = udevGetMinimalDefForDevice(dev);
|
|
if (filter(conn, def))
|
|
count++;
|
|
udev_device_unref(dev);
|
|
virInterfaceDefFree(def);
|
|
}
|
|
|
|
cleanup:
|
|
if (enumerate)
|
|
udev_enumerate_unref(enumerate);
|
|
udev_unref(udev);
|
|
|
|
return count;
|
|
}
|
|
|
|
static int
|
|
udevListInterfacesByStatus(virConnectPtr conn,
|
|
char **const names,
|
|
int names_len,
|
|
virUdevStatus status,
|
|
virInterfaceObjListFilter filter)
|
|
{
|
|
struct udev *udev = udev_ref(driver->udev);
|
|
struct udev_enumerate *enumerate = NULL;
|
|
struct udev_list_entry *devices;
|
|
struct udev_list_entry *dev_entry;
|
|
int count = 0;
|
|
|
|
enumerate = udevGetDevices(udev, status);
|
|
|
|
if (!enumerate) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("failed to get list of %s interfaces on host"),
|
|
virUdevStatusString(status));
|
|
goto error;
|
|
}
|
|
|
|
/* 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;
|
|
virInterfaceDefPtr def;
|
|
|
|
/* 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);
|
|
|
|
def = udevGetMinimalDefForDevice(dev);
|
|
if (filter(conn, def)) {
|
|
if (VIR_STRDUP(names[count], udev_device_get_sysname(dev)) < 0) {
|
|
udev_device_unref(dev);
|
|
virInterfaceDefFree(def);
|
|
goto error;
|
|
}
|
|
count++;
|
|
}
|
|
udev_device_unref(dev);
|
|
virInterfaceDefFree(def);
|
|
}
|
|
|
|
udev_enumerate_unref(enumerate);
|
|
udev_unref(udev);
|
|
|
|
return count;
|
|
|
|
error:
|
|
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
|
|
udevConnectNumOfInterfaces(virConnectPtr conn)
|
|
{
|
|
if (virConnectNumOfInterfacesEnsureACL(conn) < 0)
|
|
return -1;
|
|
|
|
return udevNumOfInterfacesByStatus(conn, VIR_UDEV_IFACE_ACTIVE,
|
|
virConnectNumOfInterfacesCheckACL);
|
|
}
|
|
|
|
static int
|
|
udevConnectListInterfaces(virConnectPtr conn,
|
|
char **const names,
|
|
int names_len)
|
|
{
|
|
if (virConnectListInterfacesEnsureACL(conn) < 0)
|
|
return -1;
|
|
|
|
return udevListInterfacesByStatus(conn, names, names_len,
|
|
VIR_UDEV_IFACE_ACTIVE,
|
|
virConnectListInterfacesCheckACL);
|
|
}
|
|
|
|
static int
|
|
udevConnectNumOfDefinedInterfaces(virConnectPtr conn)
|
|
{
|
|
if (virConnectNumOfDefinedInterfacesEnsureACL(conn) < 0)
|
|
return -1;
|
|
|
|
return udevNumOfInterfacesByStatus(conn, VIR_UDEV_IFACE_INACTIVE,
|
|
virConnectNumOfDefinedInterfacesCheckACL);
|
|
}
|
|
|
|
static int
|
|
udevConnectListDefinedInterfaces(virConnectPtr conn,
|
|
char **const names,
|
|
int names_len)
|
|
{
|
|
if (virConnectListDefinedInterfacesEnsureACL(conn) < 0)
|
|
return -1;
|
|
|
|
return udevListInterfacesByStatus(conn, names, names_len,
|
|
VIR_UDEV_IFACE_INACTIVE,
|
|
virConnectListDefinedInterfacesCheckACL);
|
|
}
|
|
|
|
#define MATCH(FLAG) (flags & (FLAG))
|
|
static int
|
|
udevConnectListAllInterfaces(virConnectPtr conn,
|
|
virInterfacePtr **ifaces,
|
|
unsigned int flags)
|
|
{
|
|
struct udev *udev;
|
|
struct udev_enumerate *enumerate = NULL;
|
|
struct udev_list_entry *devices;
|
|
struct udev_list_entry *dev_entry;
|
|
virInterfacePtr *ifaces_list = NULL;
|
|
virInterfacePtr iface_obj;
|
|
int tmp_count;
|
|
int count = 0;
|
|
int status = 0;
|
|
int ret;
|
|
|
|
virCheckFlags(VIR_CONNECT_LIST_INTERFACES_FILTERS_ACTIVE, -1);
|
|
|
|
if (virConnectListAllInterfacesEnsureACL(conn) < 0)
|
|
return -1;
|
|
|
|
/* Grab a udev reference */
|
|
udev = udev_ref(driver->udev);
|
|
|
|
/* List all interfaces in case we support more filter flags in the future */
|
|
enumerate = udevGetDevices(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 && VIR_ALLOC_N(ifaces_list, count + 1) < 0) {
|
|
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;
|
|
virInterfaceDefPtr def;
|
|
|
|
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");
|
|
|
|
def = udevGetMinimalDefForDevice(dev);
|
|
if (!virConnectListAllInterfacesCheckACL(conn, def)) {
|
|
udev_device_unref(dev);
|
|
virInterfaceDefFree(def);
|
|
continue;
|
|
}
|
|
virInterfaceDefFree(def);
|
|
|
|
/* Filter the results */
|
|
if (MATCH(VIR_CONNECT_LIST_INTERFACES_FILTERS_ACTIVE) &&
|
|
!((MATCH(VIR_CONNECT_LIST_INTERFACES_ACTIVE) && status) ||
|
|
(MATCH(VIR_CONNECT_LIST_INTERFACES_INACTIVE) && !status))) {
|
|
udev_device_unref(dev);
|
|
continue;
|
|
}
|
|
|
|
/* If we matched a filter, then add it */
|
|
if (ifaces) {
|
|
iface_obj = virGetInterface(conn, name, macaddr);
|
|
ifaces_list[count++] = iface_obj;
|
|
}
|
|
udev_device_unref(dev);
|
|
}
|
|
|
|
/* 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;
|
|
ifaces_list = NULL;
|
|
}
|
|
|
|
return count;
|
|
|
|
cleanup:
|
|
if (enumerate)
|
|
udev_enumerate_unref(enumerate);
|
|
udev_unref(udev);
|
|
|
|
if (ifaces) {
|
|
for (tmp_count = 0; tmp_count < count; tmp_count++)
|
|
virObjectUnref(ifaces_list[tmp_count]);
|
|
}
|
|
|
|
VIR_FREE(ifaces_list);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
static virInterfacePtr
|
|
udevInterfaceLookupByName(virConnectPtr conn, const char *name)
|
|
{
|
|
struct udev *udev = udev_ref(driver->udev);
|
|
struct udev_device *dev;
|
|
virInterfacePtr ret = NULL;
|
|
virInterfaceDefPtr def = 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 cleanup;
|
|
}
|
|
|
|
if (!(def = udevGetMinimalDefForDevice(dev)))
|
|
goto cleanup;
|
|
|
|
if (virInterfaceLookupByNameEnsureACL(conn, def) < 0)
|
|
goto cleanup;
|
|
|
|
ret = virGetInterface(conn, def->name, def->mac);
|
|
udev_device_unref(dev);
|
|
|
|
cleanup:
|
|
udev_unref(udev);
|
|
virInterfaceDefFree(def);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static virInterfacePtr
|
|
udevInterfaceLookupByMACString(virConnectPtr conn, const char *macstr)
|
|
{
|
|
struct udev *udev = udev_ref(driver->udev);
|
|
struct udev_enumerate *enumerate = NULL;
|
|
struct udev_list_entry *dev_entry;
|
|
struct udev_device *dev;
|
|
virInterfaceDefPtr def = NULL;
|
|
virInterfacePtr ret = NULL;
|
|
|
|
enumerate = udevGetDevices(udev, VIR_UDEV_IFACE_ALL);
|
|
|
|
if (!enumerate) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("failed to lookup interface with MAC address '%s'"),
|
|
macstr);
|
|
goto cleanup;
|
|
}
|
|
|
|
/* 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 cleanup;
|
|
}
|
|
|
|
/* 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 cleanup;
|
|
}
|
|
|
|
dev = udev_device_new_from_syspath(udev, udev_list_entry_get_name(dev_entry));
|
|
|
|
if (!(def = udevGetMinimalDefForDevice(dev)))
|
|
goto cleanup;
|
|
|
|
if (virInterfaceLookupByMACStringEnsureACL(conn, def) < 0)
|
|
goto cleanup;
|
|
|
|
ret = virGetInterface(conn, def->name, def->mac);
|
|
udev_device_unref(dev);
|
|
|
|
cleanup:
|
|
if (enumerate)
|
|
udev_enumerate_unref(enumerate);
|
|
udev_unref(udev);
|
|
virInterfaceDefFree(def);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Helper function for finding bond slaves using scandir()
|
|
*
|
|
* @param entry - directory entry passed by scandir()
|
|
*
|
|
* @return 1 if we want to add it to scandir's list, 0 if not.
|
|
*/
|
|
static int
|
|
udevBondScanDirFilter(const struct dirent *entry)
|
|
{
|
|
/* This is ugly so if anyone has a better suggestion, please improve
|
|
* this. Unfortunately the kernel stores everything in the top level
|
|
* interface sysfs entry and references the slaves as slave_eth0 for
|
|
* example.
|
|
*/
|
|
if (STRPREFIX(entry->d_name, "slave_"))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Helper function for finding bridge members using scandir()
|
|
*
|
|
* @param entry - directory entry passed by scandir()
|
|
*
|
|
* @return 1 if we want to add it to scandir's list, 0 if not.
|
|
*/
|
|
static int
|
|
udevBridgeScanDirFilter(const struct dirent *entry)
|
|
{
|
|
if (STREQ(entry->d_name, ".") || STREQ(entry->d_name, ".."))
|
|
return 0;
|
|
|
|
/* Omit the domain interfaces from the list of bridge attached
|
|
* devices. All we can do is check for the device name matching
|
|
* vnet%d. Improvements to this check are welcome.
|
|
*/
|
|
if (strlen(entry->d_name) >= 5) {
|
|
if (STRPREFIX(entry->d_name, VIR_NET_GENERATED_TAP_PREFIX) &&
|
|
c_isdigit(entry->d_name[4]))
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int
|
|
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
|
|
ATTRIBUTE_NONNULL(4) ATTRIBUTE_RETURN_CHECK
|
|
udevGetIfaceDefBond(struct udev *udev,
|
|
struct udev_device *dev,
|
|
const char *name,
|
|
virInterfaceDef *ifacedef)
|
|
{
|
|
struct dirent **slave_list = NULL;
|
|
int slave_count = 0;
|
|
size_t i;
|
|
const char *tmp_str;
|
|
int tmp_int;
|
|
|
|
/* Initial defaults */
|
|
ifacedef->data.bond.target = NULL;
|
|
ifacedef->data.bond.nbItf = 0;
|
|
ifacedef->data.bond.itf = NULL;
|
|
|
|
/* Set the bond specifics */
|
|
tmp_str = udev_device_get_sysattr_value(dev, "bonding/downdelay");
|
|
if (!tmp_str) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not retrieve 'bonding/downdelay' for '%s'"), name);
|
|
goto error;
|
|
}
|
|
if (virStrToLong_i(tmp_str, NULL, 10, &tmp_int) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not parse 'bonding/downdelay' '%s' for '%s'"),
|
|
tmp_str, name);
|
|
goto error;
|
|
}
|
|
ifacedef->data.bond.downdelay = tmp_int;
|
|
|
|
tmp_str = udev_device_get_sysattr_value(dev, "bonding/updelay");
|
|
if (!tmp_str) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not retrieve 'bonding/updelay' for '%s'"), name);
|
|
goto error;
|
|
}
|
|
if (virStrToLong_i(tmp_str, NULL, 10, &tmp_int) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not parse 'bonding/updelay' '%s' for '%s'"),
|
|
tmp_str, name);
|
|
goto error;
|
|
}
|
|
ifacedef->data.bond.updelay = tmp_int;
|
|
|
|
tmp_str = udev_device_get_sysattr_value(dev, "bonding/miimon");
|
|
if (!tmp_str) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not retrieve 'bonding/miimon' for '%s'"), name);
|
|
goto error;
|
|
}
|
|
if (virStrToLong_i(tmp_str, NULL, 10, &tmp_int) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not parse 'bonding/miimon' '%s' for '%s'"),
|
|
tmp_str, name);
|
|
goto error;
|
|
}
|
|
ifacedef->data.bond.frequency = tmp_int;
|
|
|
|
tmp_str = udev_device_get_sysattr_value(dev, "bonding/arp_interval");
|
|
if (!tmp_str) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not retrieve 'bonding/arp_interval' for '%s'"), name);
|
|
goto error;
|
|
}
|
|
if (virStrToLong_i(tmp_str, NULL, 10, &tmp_int) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not parse 'bonding/arp_interval' '%s' for '%s'"),
|
|
tmp_str, name);
|
|
goto error;
|
|
}
|
|
ifacedef->data.bond.interval = tmp_int;
|
|
|
|
/* bonding/mode is in the format: "balance-rr 0" so we find the
|
|
* space and increment the pointer to get the number and convert
|
|
* it to an interger. libvirt uses 1 through 7 while the raw
|
|
* number is 0 through 6 so increment it by 1.
|
|
*/
|
|
tmp_str = udev_device_get_sysattr_value(dev, "bonding/mode");
|
|
if (!tmp_str) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not retrieve 'bonding/mode' for '%s'"), name);
|
|
goto error;
|
|
}
|
|
tmp_str = strchr(tmp_str, ' ');
|
|
if (!tmp_str) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Invalid format for 'bonding/mode' for '%s'"), name);
|
|
goto error;
|
|
}
|
|
if (strlen(tmp_str) < 2) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Unable to find correct value in 'bonding/mode' for '%s'"),
|
|
name);
|
|
goto error;
|
|
}
|
|
if (virStrToLong_i(tmp_str + 1, NULL, 10, &tmp_int) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not parse 'bonding/mode' '%s' for '%s'"),
|
|
tmp_str, name);
|
|
goto error;
|
|
}
|
|
ifacedef->data.bond.mode = tmp_int + 1;
|
|
|
|
/* bonding/arp_validate is in the format: "none 0" so we find the
|
|
* space and increment the pointer to get the number and convert
|
|
* it to an interger.
|
|
*/
|
|
tmp_str = udev_device_get_sysattr_value(dev, "bonding/arp_validate");
|
|
if (!tmp_str) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not retrieve 'bonding/arp_validate' for '%s'"), name);
|
|
goto error;
|
|
}
|
|
tmp_str = strchr(tmp_str, ' ');
|
|
if (!tmp_str) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Invalid format for 'bonding/arp_validate' for '%s'"), name);
|
|
goto error;
|
|
}
|
|
if (strlen(tmp_str) < 2) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Unable to find correct value in 'bonding/arp_validate' "
|
|
"for '%s'"), name);
|
|
goto error;
|
|
}
|
|
if (virStrToLong_i(tmp_str + 1, NULL, 10, &tmp_int) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not parse 'bonding/arp_validate' '%s' for '%s'"),
|
|
tmp_str, name);
|
|
goto error;
|
|
}
|
|
ifacedef->data.bond.validate = tmp_int;
|
|
|
|
/* bonding/use_carrier is 0 or 1 and libvirt stores it as 1 or 2. */
|
|
tmp_str = udev_device_get_sysattr_value(dev, "bonding/use_carrier");
|
|
if (!tmp_str) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not retrieve 'bonding/use_carrier' for '%s'"), name);
|
|
goto error;
|
|
}
|
|
if (virStrToLong_i(tmp_str, NULL, 10, &tmp_int) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not parse 'bonding/use_carrier' '%s' for '%s'"),
|
|
tmp_str, name);
|
|
goto error;
|
|
}
|
|
ifacedef->data.bond.carrier = tmp_int + 1;
|
|
|
|
/* MII or ARP Monitoring is based on arp_interval and miimon.
|
|
* if arp_interval > 0 then ARP monitoring is in play, if
|
|
* miimon > 0 then MII monitoring is in play.
|
|
*/
|
|
if (ifacedef->data.bond.interval > 0)
|
|
ifacedef->data.bond.monit = VIR_INTERFACE_BOND_MONIT_ARP;
|
|
else if (ifacedef->data.bond.frequency > 0)
|
|
ifacedef->data.bond.monit = VIR_INTERFACE_BOND_MONIT_MII;
|
|
else
|
|
ifacedef->data.bond.monit = VIR_INTERFACE_BOND_MONIT_NONE;
|
|
|
|
tmp_str = udev_device_get_sysattr_value(dev, "bonding/arp_ip_target");
|
|
if (!tmp_str) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not retrieve 'bonding/arp_ip_target' for '%s'"), name);
|
|
goto error;
|
|
}
|
|
if (VIR_STRDUP(ifacedef->data.bond.target, tmp_str) < 0)
|
|
goto error;
|
|
|
|
/* Slaves of the bond */
|
|
/* Get each slave in the bond */
|
|
slave_count = scandir(udev_device_get_syspath(dev), &slave_list,
|
|
udevBondScanDirFilter, alphasort);
|
|
|
|
if (slave_count < 0) {
|
|
virReportSystemError(errno,
|
|
_("Could not get slaves of bond '%s'"), name);
|
|
goto error;
|
|
}
|
|
|
|
/* Allocate our list of slave devices */
|
|
if (VIR_ALLOC_N(ifacedef->data.bond.itf, slave_count) < 0)
|
|
goto error;
|
|
ifacedef->data.bond.nbItf = slave_count;
|
|
|
|
for (i = 0; i < slave_count; i++) {
|
|
/* Names are slave_interface. e.g. slave_eth0
|
|
* so we use the part after the _
|
|
*/
|
|
tmp_str = strchr(slave_list[i]->d_name, '_');
|
|
if (!tmp_str || strlen(tmp_str) < 2) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Invalid enslaved interface name '%s' seen for "
|
|
"bond '%s'"), slave_list[i]->d_name, name);
|
|
goto error;
|
|
}
|
|
/* go past the _ */
|
|
tmp_str++;
|
|
|
|
ifacedef->data.bond.itf[i] =
|
|
udevGetIfaceDef(udev, tmp_str);
|
|
if (!ifacedef->data.bond.itf[i]) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not get interface information for '%s', which is "
|
|
"a enslaved in bond '%s'"), slave_list[i]->d_name, name);
|
|
goto error;
|
|
}
|
|
VIR_FREE(slave_list[i]);
|
|
}
|
|
|
|
VIR_FREE(slave_list);
|
|
|
|
return 0;
|
|
|
|
error:
|
|
for (i = 0; slave_count != -1 && i < slave_count; i++)
|
|
VIR_FREE(slave_list[i]);
|
|
VIR_FREE(slave_list);
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
|
|
ATTRIBUTE_NONNULL(4) ATTRIBUTE_RETURN_CHECK
|
|
udevGetIfaceDefBridge(struct udev *udev,
|
|
struct udev_device *dev,
|
|
const char *name,
|
|
virInterfaceDef *ifacedef)
|
|
{
|
|
struct dirent **member_list = NULL;
|
|
int member_count = 0;
|
|
char *member_path;
|
|
const char *tmp_str;
|
|
int stp;
|
|
size_t i;
|
|
|
|
/* Set our type to Bridge */
|
|
ifacedef->type = VIR_INTERFACE_TYPE_BRIDGE;
|
|
|
|
/* Retrieve the forward delay */
|
|
tmp_str = udev_device_get_sysattr_value(dev, "bridge/forward_delay");
|
|
if (!tmp_str) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not retrieve 'bridge/forward_delay' for '%s'"), name);
|
|
goto error;
|
|
}
|
|
|
|
if (VIR_STRDUP(ifacedef->data.bridge.delay, tmp_str) < 0)
|
|
goto error;
|
|
|
|
/* Retrieve Spanning Tree State. Valid values = -1, 0, 1 */
|
|
tmp_str = udev_device_get_sysattr_value(dev, "bridge/stp_state");
|
|
if (!tmp_str) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not retrieve 'bridge/stp_state' for '%s'"), name);
|
|
goto error;
|
|
}
|
|
|
|
if (virStrToLong_i(tmp_str, NULL, 10, &stp) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not parse 'bridge/stp_state' '%s' for '%s'"),
|
|
tmp_str, name);
|
|
goto error;
|
|
}
|
|
|
|
switch (stp) {
|
|
case -1:
|
|
case 0:
|
|
case 1:
|
|
ifacedef->data.bridge.stp = stp;
|
|
break;
|
|
default:
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Invalid STP state value %d received for '%s'. Must be "
|
|
"-1, 0, or 1."), stp, name);
|
|
goto error;
|
|
}
|
|
|
|
/* Members of the bridge */
|
|
if (virAsprintf(&member_path, "%s/%s",
|
|
udev_device_get_syspath(dev), "brif") < 0)
|
|
goto error;
|
|
|
|
/* Get each member of the bridge */
|
|
member_count = scandir(member_path, &member_list,
|
|
udevBridgeScanDirFilter, alphasort);
|
|
|
|
/* Don't need the path anymore */
|
|
VIR_FREE(member_path);
|
|
|
|
if (member_count < 0) {
|
|
virReportSystemError(errno,
|
|
_("Could not get members of bridge '%s'"),
|
|
name);
|
|
goto error;
|
|
}
|
|
|
|
/* Allocate our list of member devices */
|
|
if (VIR_ALLOC_N(ifacedef->data.bridge.itf, member_count) < 0)
|
|
goto error;
|
|
ifacedef->data.bridge.nbItf = member_count;
|
|
|
|
/* Get the interface definitions for each member of the bridge */
|
|
for (i = 0; i < member_count; i++) {
|
|
ifacedef->data.bridge.itf[i] =
|
|
udevGetIfaceDef(udev, member_list[i]->d_name);
|
|
if (!ifacedef->data.bridge.itf[i]) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not get interface information for '%s', which is "
|
|
"a member of bridge '%s'"), member_list[i]->d_name, name);
|
|
goto error;
|
|
}
|
|
VIR_FREE(member_list[i]);
|
|
}
|
|
|
|
VIR_FREE(member_list);
|
|
|
|
return 0;
|
|
|
|
error:
|
|
for (i = 0; member_count != -1 && i < member_count; i++)
|
|
VIR_FREE(member_list[i]);
|
|
VIR_FREE(member_list);
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
|
|
ATTRIBUTE_NONNULL(4) ATTRIBUTE_RETURN_CHECK
|
|
udevGetIfaceDefVlan(struct udev *udev ATTRIBUTE_UNUSED,
|
|
struct udev_device *dev ATTRIBUTE_UNUSED,
|
|
const char *name,
|
|
virInterfaceDef *ifacedef)
|
|
{
|
|
char *procpath = NULL;
|
|
char *buf = NULL;
|
|
char *vid_pos, *dev_pos;
|
|
size_t vid_len, dev_len;
|
|
const char *vid_prefix = "VID: ";
|
|
const char *dev_prefix = "\nDevice: ";
|
|
int ret = -1;
|
|
|
|
if (virAsprintf(&procpath, "/proc/net/vlan/%s", name) < 0)
|
|
goto cleanup;
|
|
|
|
if (virFileReadAll(procpath, BUFSIZ, &buf) < 0)
|
|
goto cleanup;
|
|
|
|
if ((vid_pos = strstr(buf, vid_prefix)) == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("failed to find the VID for the VLAN device '%s'"),
|
|
name);
|
|
goto cleanup;
|
|
}
|
|
vid_pos += strlen(vid_prefix);
|
|
|
|
if ((vid_len = strspn(vid_pos, "0123456789")) == 0 ||
|
|
!c_isspace(vid_pos[vid_len])) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("failed to find the VID for the VLAN device '%s'"),
|
|
name);
|
|
goto cleanup;
|
|
}
|
|
|
|
if ((dev_pos = strstr(vid_pos + vid_len, dev_prefix)) == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("failed to find the real device for the VLAN device '%s'"),
|
|
name);
|
|
goto cleanup;
|
|
}
|
|
dev_pos += strlen(dev_prefix);
|
|
|
|
if ((dev_len = strcspn(dev_pos, "\n")) == 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("failed to find the real device for the VLAN device '%s'"),
|
|
name);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (VIR_STRNDUP(ifacedef->data.vlan.tag, vid_pos, vid_len) < 0)
|
|
goto cleanup;
|
|
if (VIR_STRNDUP(ifacedef->data.vlan.dev_name, dev_pos, dev_len) < 0) {
|
|
VIR_FREE(ifacedef->data.vlan.tag);
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
VIR_FREE(procpath);
|
|
VIR_FREE(buf);
|
|
return ret;
|
|
}
|
|
|
|
static virInterfaceDef * ATTRIBUTE_NONNULL(1)
|
|
udevGetIfaceDef(struct udev *udev, const char *name)
|
|
{
|
|
struct udev_device *dev = NULL;
|
|
virInterfaceDef *ifacedef;
|
|
unsigned int mtu;
|
|
const char *mtu_str;
|
|
char *vlan_parent_dev = NULL;
|
|
const char *devtype;
|
|
|
|
/* Allocate our interface definition structure */
|
|
if (VIR_ALLOC(ifacedef) < 0)
|
|
return NULL;
|
|
|
|
/* Clear our structure and set safe defaults */
|
|
ifacedef->startmode = VIR_INTERFACE_START_UNSPECIFIED;
|
|
if (VIR_STRDUP(ifacedef->name, name) < 0)
|
|
goto error;
|
|
|
|
/* Lookup the device we've been asked about */
|
|
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 error;
|
|
}
|
|
|
|
/* MAC address */
|
|
if (VIR_STRDUP(ifacedef->mac,
|
|
udev_device_get_sysattr_value(dev, "address")) < 0)
|
|
goto error;
|
|
|
|
/* Link state and speed */
|
|
if (virNetDevGetLinkInfo(ifacedef->name, &ifacedef->lnk) < 0)
|
|
goto error;
|
|
|
|
/* MTU */
|
|
mtu_str = udev_device_get_sysattr_value(dev, "mtu");
|
|
if (virStrToLong_ui(mtu_str, NULL, 10, &mtu) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not parse MTU value '%s'"), mtu_str);
|
|
goto error;
|
|
}
|
|
ifacedef->mtu = mtu;
|
|
|
|
/* Number of IP protocols this interface has assigned */
|
|
/* XXX: Do we want a netlink query or a call out to ip or leave it? */
|
|
ifacedef->nprotos = 0;
|
|
ifacedef->protos = NULL;
|
|
|
|
/* Check the type of device we are working with based on the devtype */
|
|
devtype = udev_device_get_devtype(dev);
|
|
|
|
/* Set our type to ethernet as the default case */
|
|
ifacedef->type = VIR_INTERFACE_TYPE_ETHERNET;
|
|
|
|
if (STREQ_NULLABLE(devtype, "vlan")) {
|
|
/* This only works on modern kernels (3.7 and newer)
|
|
* e949b09b71d975a82f13ac88ce4ad338fed213da
|
|
*/
|
|
ifacedef->type = VIR_INTERFACE_TYPE_VLAN;
|
|
} else if (STREQ_NULLABLE(devtype, "bridge")) {
|
|
ifacedef->type = VIR_INTERFACE_TYPE_BRIDGE;
|
|
} else if (STREQ_NULLABLE(devtype, "bond")) {
|
|
/* This only works on modern kernels (3.9 and newer) */
|
|
ifacedef->type = VIR_INTERFACE_TYPE_BOND;
|
|
}
|
|
|
|
/* Fallback checks if the devtype check didn't work. */
|
|
if (ifacedef->type == VIR_INTERFACE_TYPE_ETHERNET) {
|
|
/* First check if its a VLAN based on the name containing a dot,
|
|
* to prevent false positives
|
|
*/
|
|
vlan_parent_dev = strrchr(name, '.');
|
|
if (vlan_parent_dev)
|
|
ifacedef->type = VIR_INTERFACE_TYPE_VLAN;
|
|
|
|
/* Fallback check to see if this is a bond device */
|
|
if (udev_device_get_sysattr_value(dev, "bonding/mode"))
|
|
ifacedef->type = VIR_INTERFACE_TYPE_BOND;
|
|
}
|
|
|
|
switch (ifacedef->type) {
|
|
case VIR_INTERFACE_TYPE_VLAN:
|
|
if (udevGetIfaceDefVlan(udev, dev, name, ifacedef) < 0)
|
|
goto error;
|
|
break;
|
|
case VIR_INTERFACE_TYPE_BRIDGE:
|
|
if (udevGetIfaceDefBridge(udev, dev, name, ifacedef) < 0)
|
|
goto error;
|
|
break;
|
|
case VIR_INTERFACE_TYPE_BOND:
|
|
if (udevGetIfaceDefBond(udev, dev, name, ifacedef) < 0)
|
|
goto error;
|
|
break;
|
|
case VIR_INTERFACE_TYPE_ETHERNET:
|
|
break;
|
|
}
|
|
|
|
udev_device_unref(dev);
|
|
|
|
return ifacedef;
|
|
|
|
error:
|
|
udev_device_unref(dev);
|
|
|
|
virInterfaceDefFree(ifacedef);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static char *
|
|
udevInterfaceGetXMLDesc(virInterfacePtr ifinfo,
|
|
unsigned int flags)
|
|
{
|
|
struct udev *udev = udev_ref(driver->udev);
|
|
virInterfaceDef *ifacedef;
|
|
char *xmlstr = NULL;
|
|
|
|
virCheckFlags(VIR_INTERFACE_XML_INACTIVE, NULL);
|
|
|
|
/* Recursively build up the interface XML based on the requested
|
|
* interface name
|
|
*/
|
|
ifacedef = udevGetIfaceDef(udev, ifinfo->name);
|
|
|
|
if (!ifacedef)
|
|
goto cleanup;
|
|
|
|
if (virInterfaceGetXMLDescEnsureACL(ifinfo->conn, ifacedef) < 0)
|
|
goto cleanup;
|
|
|
|
xmlstr = virInterfaceDefFormat(ifacedef);
|
|
|
|
virInterfaceDefFree(ifacedef);
|
|
|
|
cleanup:
|
|
/* decrement our udev ptr */
|
|
udev_unref(udev);
|
|
|
|
return xmlstr;
|
|
}
|
|
|
|
static int
|
|
udevInterfaceIsActive(virInterfacePtr ifinfo)
|
|
{
|
|
struct udev *udev = udev_ref(driver->udev);
|
|
struct udev_device *dev;
|
|
virInterfaceDefPtr def = NULL;
|
|
int status = -1;
|
|
|
|
dev = udev_device_new_from_subsystem_sysname(udev, "net",
|
|
ifinfo->name);
|
|
if (!dev) {
|
|
virReportError(VIR_ERR_NO_INTERFACE,
|
|
_("couldn't find interface named '%s'"),
|
|
ifinfo->name);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(def = udevGetMinimalDefForDevice(dev)))
|
|
goto cleanup;
|
|
|
|
if (virInterfaceIsActiveEnsureACL(ifinfo->conn, def) < 0)
|
|
goto cleanup;
|
|
|
|
/* Check if it's active or not */
|
|
status = STREQ(udev_device_get_sysattr_value(dev, "operstate"), "up");
|
|
|
|
udev_device_unref(dev);
|
|
|
|
cleanup:
|
|
udev_unref(udev);
|
|
virInterfaceDefFree(def);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
static int
|
|
udevStateInitialize(bool privileged,
|
|
virStateInhibitCallback callback ATTRIBUTE_UNUSED,
|
|
void *opaque ATTRIBUTE_UNUSED)
|
|
{
|
|
int ret = -1;
|
|
|
|
if (VIR_ALLOC(driver) < 0)
|
|
goto cleanup;
|
|
|
|
driver->udev = udev_new();
|
|
if (!driver->udev) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("failed to create udev context"));
|
|
goto cleanup;
|
|
}
|
|
driver->privileged = privileged;
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
udevStateCleanup(void)
|
|
{
|
|
if (!driver)
|
|
return -1;
|
|
|
|
udev_unref(driver->udev);
|
|
|
|
VIR_FREE(driver);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static virDrvOpenStatus
|
|
udevConnectOpen(virConnectPtr conn,
|
|
virConnectAuthPtr auth ATTRIBUTE_UNUSED,
|
|
virConfPtr conf ATTRIBUTE_UNUSED,
|
|
unsigned int flags)
|
|
{
|
|
virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);
|
|
|
|
if (driver == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("interface state driver is not active"));
|
|
return VIR_DRV_OPEN_ERROR;
|
|
}
|
|
|
|
if (driver->privileged) {
|
|
if (STRNEQ(conn->uri->path, "/system")) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("unexpected interface URI path '%s', try interface:///system"),
|
|
conn->uri->path);
|
|
return VIR_DRV_OPEN_ERROR;
|
|
}
|
|
} else {
|
|
if (STRNEQ(conn->uri->path, "/session")) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("unexpected interface URI path '%s', try interface:///session"),
|
|
conn->uri->path);
|
|
return VIR_DRV_OPEN_ERROR;
|
|
}
|
|
}
|
|
|
|
if (virConnectOpenEnsureACL(conn) < 0)
|
|
return VIR_DRV_OPEN_ERROR;
|
|
|
|
return VIR_DRV_OPEN_SUCCESS;
|
|
}
|
|
|
|
static int udevConnectClose(virConnectPtr conn ATTRIBUTE_UNUSED)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int udevConnectIsSecure(virConnectPtr conn ATTRIBUTE_UNUSED)
|
|
{
|
|
/* Trivially secure, since always inside the daemon */
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int udevConnectIsEncrypted(virConnectPtr conn ATTRIBUTE_UNUSED)
|
|
{
|
|
/* Not encrypted, but remote driver takes care of that */
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int udevConnectIsAlive(virConnectPtr conn ATTRIBUTE_UNUSED)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
|
|
static virInterfaceDriver udevIfaceDriver = {
|
|
.name = "udev",
|
|
.connectNumOfInterfaces = udevConnectNumOfInterfaces, /* 1.0.0 */
|
|
.connectListInterfaces = udevConnectListInterfaces, /* 1.0.0 */
|
|
.connectNumOfDefinedInterfaces = udevConnectNumOfDefinedInterfaces, /* 1.0.0 */
|
|
.connectListDefinedInterfaces = udevConnectListDefinedInterfaces, /* 1.0.0 */
|
|
.connectListAllInterfaces = udevConnectListAllInterfaces, /* 1.0.0 */
|
|
.interfaceLookupByName = udevInterfaceLookupByName, /* 1.0.0 */
|
|
.interfaceLookupByMACString = udevInterfaceLookupByMACString, /* 1.0.0 */
|
|
.interfaceIsActive = udevInterfaceIsActive, /* 1.0.0 */
|
|
.interfaceGetXMLDesc = udevInterfaceGetXMLDesc, /* 1.0.0 */
|
|
};
|
|
|
|
|
|
static virHypervisorDriver udevHypervisorDriver = {
|
|
.name = "interface",
|
|
.connectOpen = udevConnectOpen, /* 4.1.0 */
|
|
.connectClose = udevConnectClose, /* 4.1.0 */
|
|
.connectIsEncrypted = udevConnectIsEncrypted, /* 4.1.0 */
|
|
.connectIsSecure = udevConnectIsSecure, /* 4.1.0 */
|
|
.connectIsAlive = udevConnectIsAlive, /* 4.1.0 */
|
|
};
|
|
|
|
|
|
static virConnectDriver udevConnectDriver = {
|
|
.localOnly = true,
|
|
.uriSchemes = (const char *[]){ "interface", NULL },
|
|
.hypervisorDriver = &udevHypervisorDriver,
|
|
.interfaceDriver = &udevIfaceDriver,
|
|
};
|
|
|
|
|
|
static virStateDriver interfaceStateDriver = {
|
|
.name = "udev",
|
|
.stateInitialize = udevStateInitialize,
|
|
.stateCleanup = udevStateCleanup,
|
|
};
|
|
|
|
int
|
|
udevIfaceRegister(void)
|
|
{
|
|
if (virRegisterConnectDriver(&udevConnectDriver, false) < 0)
|
|
return -1;
|
|
if (virSetSharedInterfaceDriver(&udevIfaceDriver) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("failed to register udev interface driver"));
|
|
return -1;
|
|
}
|
|
if (virRegisterStateDriver(&interfaceStateDriver) < 0)
|
|
return -1;
|
|
return 0;
|
|
}
|