2009-11-12 22:48:24 +01:00
|
|
|
/*
|
|
|
|
* node_device_udev.c: node device enumeration - libudev implementation
|
|
|
|
*
|
2015-05-06 16:40:39 -04:00
|
|
|
* Copyright (C) 2009-2015 Red Hat, Inc.
|
2009-11-12 22:48:24 +01:00
|
|
|
*
|
|
|
|
* 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
|
2012-09-20 16:30:55 -06:00
|
|
|
* License along with this library. If not, see
|
2012-07-21 18:06:23 +08:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
2009-11-12 22:48:24 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
#include <libudev.h>
|
2009-11-12 23:22:00 +01:00
|
|
|
#include <pciaccess.h>
|
2009-11-12 22:48:24 +01:00
|
|
|
#include <scsi/scsi.h>
|
|
|
|
#include <c-ctype.h>
|
|
|
|
|
2013-04-25 14:24:42 -06:00
|
|
|
#include "dirname.h"
|
2009-11-12 22:48:24 +01:00
|
|
|
#include "node_device_conf.h"
|
2016-07-28 14:02:55 +02:00
|
|
|
#include "node_device_event.h"
|
2009-11-12 22:48:24 +01:00
|
|
|
#include "node_device_driver.h"
|
2015-05-06 16:40:39 -04:00
|
|
|
#include "node_device_udev.h"
|
|
|
|
#include "virerror.h"
|
2009-11-12 22:48:24 +01:00
|
|
|
#include "driver.h"
|
|
|
|
#include "datatypes.h"
|
2012-12-12 17:59:27 +00:00
|
|
|
#include "virlog.h"
|
2012-12-12 18:06:53 +00:00
|
|
|
#include "viralloc.h"
|
2012-12-13 18:01:25 +00:00
|
|
|
#include "viruuid.h"
|
2012-12-04 12:04:07 +00:00
|
|
|
#include "virbuffer.h"
|
2013-05-09 14:59:04 -04:00
|
|
|
#include "virfile.h"
|
2012-12-13 14:52:25 +00:00
|
|
|
#include "virpci.h"
|
2019-05-23 11:34:08 +01:00
|
|
|
#include "virpidfile.h"
|
2013-04-03 12:36:23 +02:00
|
|
|
#include "virstring.h"
|
2014-06-05 17:36:31 +02:00
|
|
|
#include "virnetdev.h"
|
2017-03-06 17:20:00 +01:00
|
|
|
#include "virmdev.h"
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2019-05-23 11:34:08 +01:00
|
|
|
#include "configmake.h"
|
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
#define VIR_FROM_THIS VIR_FROM_NODEDEV
|
|
|
|
|
2014-02-28 12:16:17 +00:00
|
|
|
VIR_LOG_INIT("node_device.node_device_udev");
|
|
|
|
|
2011-02-07 17:25:06 +00:00
|
|
|
#ifndef TYPE_RAID
|
|
|
|
# define TYPE_RAID 12
|
|
|
|
#endif
|
|
|
|
|
2017-10-06 15:21:55 +02:00
|
|
|
typedef struct _udevEventData udevEventData;
|
|
|
|
typedef udevEventData *udevEventDataPtr;
|
|
|
|
|
|
|
|
struct _udevEventData {
|
|
|
|
virObjectLockable parent;
|
|
|
|
|
2010-01-26 02:58:37 +01:00
|
|
|
struct udev_monitor *udev_monitor;
|
|
|
|
int watch;
|
2017-06-28 14:34:41 +02:00
|
|
|
|
|
|
|
/* Thread data */
|
|
|
|
virThread th;
|
|
|
|
virCond threadCond;
|
|
|
|
bool threadQuit;
|
|
|
|
bool dataReady;
|
2010-01-26 02:58:37 +01:00
|
|
|
};
|
|
|
|
|
2017-10-06 15:21:55 +02:00
|
|
|
static virClassPtr udevEventDataClass;
|
|
|
|
|
|
|
|
static void
|
|
|
|
udevEventDataDispose(void *obj)
|
|
|
|
{
|
|
|
|
struct udev *udev = NULL;
|
|
|
|
udevEventDataPtr priv = obj;
|
|
|
|
|
|
|
|
if (priv->watch != -1)
|
|
|
|
virEventRemoveHandle(priv->watch);
|
|
|
|
|
|
|
|
if (!priv->udev_monitor)
|
|
|
|
return;
|
|
|
|
|
|
|
|
udev = udev_monitor_get_udev(priv->udev_monitor);
|
|
|
|
udev_monitor_unref(priv->udev_monitor);
|
|
|
|
udev_unref(udev);
|
2017-06-28 14:34:41 +02:00
|
|
|
|
|
|
|
virCondDestroy(&priv->threadCond);
|
2017-10-06 15:21:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
udevEventDataOnceInit(void)
|
|
|
|
{
|
2018-04-17 17:42:33 +02:00
|
|
|
if (!VIR_CLASS_NEW(udevEventData, virClassForObjectLockable()))
|
2017-10-06 15:21:55 +02:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-01-20 12:23:29 -05:00
|
|
|
VIR_ONCE_GLOBAL_INIT(udevEventData);
|
2017-10-06 15:21:55 +02:00
|
|
|
|
|
|
|
static udevEventDataPtr
|
|
|
|
udevEventDataNew(void)
|
|
|
|
{
|
|
|
|
udevEventDataPtr ret = NULL;
|
|
|
|
|
|
|
|
if (udevEventDataInitialize() < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!(ret = virObjectLockableNew(udevEventDataClass)))
|
|
|
|
return NULL;
|
|
|
|
|
2017-06-28 14:34:41 +02:00
|
|
|
if (virCondInit(&ret->threadCond) < 0) {
|
|
|
|
virObjectUnref(ret);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-10-06 15:21:55 +02:00
|
|
|
ret->watch = -1;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 16:36:35 +02:00
|
|
|
static bool
|
|
|
|
udevHasDeviceProperty(struct udev_device *dev,
|
|
|
|
const char *key)
|
|
|
|
{
|
|
|
|
if (udev_device_get_property_value(dev, key))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static const char *
|
|
|
|
udevGetDeviceProperty(struct udev_device *udev_device,
|
|
|
|
const char *property_key)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2016-06-03 15:25:22 +02:00
|
|
|
const char *ret = NULL;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 15:25:22 +02:00
|
|
|
ret = udev_device_get_property_value(udev_device, property_key);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 15:25:22 +02:00
|
|
|
VIR_DEBUG("Found property key '%s' value '%s' for device with sysname '%s'",
|
|
|
|
property_key, NULLSTR(ret), udev_device_get_sysname(udev_device));
|
2009-11-12 22:48:24 +01:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
udevGetStringProperty(struct udev_device *udev_device,
|
|
|
|
const char *property_key,
|
|
|
|
char **value)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2016-06-03 15:25:22 +02:00
|
|
|
if (VIR_STRDUP(*value,
|
|
|
|
udevGetDeviceProperty(udev_device, property_key)) < 0)
|
2016-06-03 15:54:19 +02:00
|
|
|
return -1;
|
2016-06-03 15:25:22 +02:00
|
|
|
|
2016-06-03 15:54:19 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
udevGetIntProperty(struct udev_device *udev_device,
|
|
|
|
const char *property_key,
|
|
|
|
int *value,
|
|
|
|
int base)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2016-06-03 15:25:22 +02:00
|
|
|
const char *str = NULL;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 15:25:22 +02:00
|
|
|
str = udevGetDeviceProperty(udev_device, property_key);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 15:25:22 +02:00
|
|
|
if (str && virStrToLong_i(str, NULL, base, value) < 0) {
|
2016-06-03 19:03:38 +02:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Failed to convert '%s' to int"), str);
|
2016-06-03 16:10:21 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
2016-06-03 16:10:21 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
udevGetUintProperty(struct udev_device *udev_device,
|
|
|
|
const char *property_key,
|
|
|
|
unsigned int *value,
|
|
|
|
int base)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2016-06-03 15:25:22 +02:00
|
|
|
const char *str = NULL;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 15:25:22 +02:00
|
|
|
str = udevGetDeviceProperty(udev_device, property_key);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 15:25:22 +02:00
|
|
|
if (str && virStrToLong_ui(str, NULL, base, value) < 0) {
|
2016-06-03 19:03:38 +02:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Failed to convert '%s' to int"), str);
|
2016-06-03 16:10:21 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
2016-06-03 16:10:21 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static const char *
|
|
|
|
udevGetDeviceSysfsAttr(struct udev_device *udev_device,
|
|
|
|
const char *attr_name)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2016-06-03 17:13:39 +02:00
|
|
|
const char *ret = NULL;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:13:39 +02:00
|
|
|
ret = udev_device_get_sysattr_value(udev_device, attr_name);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
|
|
|
VIR_DEBUG("Found sysfs attribute '%s' value '%s' "
|
2010-04-02 21:44:04 +02:00
|
|
|
"for device with sysname '%s'",
|
2016-06-03 17:13:39 +02:00
|
|
|
attr_name, NULLSTR(ret),
|
2009-11-12 22:48:24 +01:00
|
|
|
udev_device_get_sysname(udev_device));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
udevGetStringSysfsAttr(struct udev_device *udev_device,
|
|
|
|
const char *attr_name,
|
|
|
|
char **value)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2016-06-03 17:13:39 +02:00
|
|
|
if (VIR_STRDUP(*value, udevGetDeviceSysfsAttr(udev_device, attr_name)) < 0)
|
2016-06-03 17:27:48 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:13:39 +02:00
|
|
|
virStringStripControlChars(*value);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:13:39 +02:00
|
|
|
if (*value != NULL && (STREQ(*value, "")))
|
|
|
|
VIR_FREE(*value);
|
2015-04-14 12:30:34 +02:00
|
|
|
|
2016-06-03 17:27:48 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
udevGetIntSysfsAttr(struct udev_device *udev_device,
|
|
|
|
const char *attr_name,
|
|
|
|
int *value,
|
|
|
|
int base)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2016-06-03 17:13:39 +02:00
|
|
|
const char *str = NULL;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:13:39 +02:00
|
|
|
str = udevGetDeviceSysfsAttr(udev_device, attr_name);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:13:39 +02:00
|
|
|
if (str && virStrToLong_i(str, NULL, base, value) < 0) {
|
2016-06-03 19:03:38 +02:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Failed to convert '%s' to int"), str);
|
2016-06-03 17:39:39 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
2016-06-03 17:39:39 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
udevGetUintSysfsAttr(struct udev_device *udev_device,
|
|
|
|
const char *attr_name,
|
|
|
|
unsigned int *value,
|
|
|
|
int base)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2016-06-03 17:13:39 +02:00
|
|
|
const char *str = NULL;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:13:39 +02:00
|
|
|
str = udevGetDeviceSysfsAttr(udev_device, attr_name);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:13:39 +02:00
|
|
|
if (str && virStrToLong_ui(str, NULL, base, value) < 0) {
|
2016-06-03 19:03:38 +02:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Failed to convert '%s' to unsigned int"), str);
|
2016-06-03 18:49:40 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
2016-06-03 18:49:40 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
udevGetUint64SysfsAttr(struct udev_device *udev_device,
|
|
|
|
const char *attr_name,
|
|
|
|
unsigned long long *value)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2016-06-03 17:13:39 +02:00
|
|
|
const char *str = NULL;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:13:39 +02:00
|
|
|
str = udevGetDeviceSysfsAttr(udev_device, attr_name);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:13:39 +02:00
|
|
|
if (str && virStrToLong_ull(str, NULL, 0, value) < 0) {
|
2016-06-03 19:03:38 +02:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Failed to convert '%s' to unsigned long long"), str);
|
2016-06-03 18:54:43 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
2016-06-03 18:54:43 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
udevGenerateDeviceName(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def,
|
|
|
|
const char *s)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/node_device/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 15:09:33 +01:00
|
|
|
size_t i;
|
2009-11-12 22:48:24 +01:00
|
|
|
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
|
|
|
|
2011-04-30 10:34:49 -06:00
|
|
|
virBufferAsprintf(&buf, "%s_%s",
|
2009-11-12 22:48:24 +01:00
|
|
|
udev_device_get_subsystem(device),
|
|
|
|
udev_device_get_sysname(device));
|
|
|
|
|
2014-11-13 15:24:17 +01:00
|
|
|
if (s != NULL)
|
2011-04-30 10:34:49 -06:00
|
|
|
virBufferAsprintf(&buf, "_%s", s);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2014-06-27 10:40:15 +02:00
|
|
|
if (virBufferCheckError(&buf) < 0)
|
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
|
|
|
def->name = virBufferContentAndReset(&buf);
|
|
|
|
|
2013-05-21 15:21:19 +08:00
|
|
|
for (i = 0; i < strlen(def->name); i++) {
|
2014-11-13 15:24:17 +01:00
|
|
|
if (!(c_isalnum(*(def->name + i))))
|
2009-11-12 22:48:24 +01:00
|
|
|
*(def->name + i) = '_';
|
|
|
|
}
|
|
|
|
|
2016-06-03 19:37:06 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
|
|
|
|
static int
|
|
|
|
udevTranslatePCIIds(unsigned int vendor,
|
|
|
|
unsigned int product,
|
|
|
|
char **vendor_string,
|
|
|
|
char **product_string)
|
2009-11-12 23:22:00 +01:00
|
|
|
{
|
|
|
|
struct pci_id_match m;
|
|
|
|
const char *vendor_name = NULL, *device_name = NULL;
|
|
|
|
|
|
|
|
m.vendor_id = vendor;
|
|
|
|
m.device_id = product;
|
|
|
|
m.subvendor_id = PCI_MATCH_ANY;
|
|
|
|
m.subdevice_id = PCI_MATCH_ANY;
|
|
|
|
m.device_class = 0;
|
|
|
|
m.device_class_mask = 0;
|
|
|
|
m.match_data = 0;
|
|
|
|
|
|
|
|
/* pci_get_strings returns void */
|
|
|
|
pci_get_strings(&m,
|
|
|
|
&device_name,
|
2010-05-11 14:44:34 -04:00
|
|
|
&vendor_name,
|
2009-11-12 23:22:00 +01:00
|
|
|
NULL,
|
|
|
|
NULL);
|
|
|
|
|
2016-06-03 19:37:06 +02:00
|
|
|
if (VIR_STRDUP(*vendor_string, vendor_name) < 0 ||
|
2013-05-03 14:44:20 +02:00
|
|
|
VIR_STRDUP(*product_string, device_name) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 23:22:00 +01:00
|
|
|
|
2016-06-03 19:37:06 +02:00
|
|
|
return 0;
|
2009-11-12 23:22:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
udevProcessPCI(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2017-03-02 10:59:25 -05:00
|
|
|
virNodeDevCapPCIDevPtr pci_dev = &def->caps->data.pci_dev;
|
2014-05-15 10:13:45 +02:00
|
|
|
virPCIEDeviceInfoPtr pci_express = NULL;
|
|
|
|
virPCIDevicePtr pciDev = NULL;
|
2015-05-11 13:50:37 -04:00
|
|
|
int ret = -1;
|
build: detect potentential uninitialized variables
Even with -Wuninitialized (which is part of autobuild.sh
--enable-compile-warnings=error), gcc does NOT catch this
use of an uninitialized variable:
{
if (cond)
goto error;
int a = 1;
error:
printf("%d", a);
}
which prints 0 (supposing the stack started life wiped) if
cond was true. Clang will catch it, but we don't use clang
as often. Using gcc -Wjump-misses-init catches it, but also
gives false positives:
{
if (cond)
goto error;
int a = 1;
return a;
error:
return 0;
}
Here, a was never used in the scope of the error block, so
declaring it after goto is technically fine (and clang agrees).
However, given that our HACKING already documents a preference
to C89 decl-before-statement, the false positive warning is
enough of a prod to comply with HACKING.
[Personally, I'd _really_ rather use C99 decl-after-statement
to minimize scope, but until gcc can efficiently and reliably
catch scoping and uninitialized usage bugs, I'll settle with
the compromise of enforcing a coding standard that happens to
reject false positives if it can also detect real bugs.]
* acinclude.m4 (LIBVIRT_COMPILE_WARNINGS): Add -Wjump-misses-init.
* src/util/util.c (__virExec): Adjust offenders.
* src/conf/domain_conf.c (virDomainTimerDefParseXML): Likewise.
* src/remote/remote_driver.c (doRemoteOpen): Likewise.
* src/phyp/phyp_driver.c (phypGetLparNAME, phypGetLparProfile)
(phypGetVIOSFreeSCSIAdapter, phypVolumeGetKey)
(phypGetStoragePoolDevice)
(phypVolumeGetPhysicalVolumeByStoragePool)
(phypVolumeGetPath): Likewise.
* src/vbox/vbox_tmpl.c (vboxNetworkUndefineDestroy)
(vboxNetworkCreate, vboxNetworkDumpXML)
(vboxNetworkDefineCreateXML): Likewise.
* src/xenapi/xenapi_driver.c (getCapsObject)
(xenapiDomainDumpXML): Likewise.
* src/xenapi/xenapi_utils.c (createVMRecordFromXml): Likewise.
* src/security/security_selinux.c (SELinuxGenNewContext):
Likewise.
* src/qemu/qemu_command.c (qemuBuildCommandLine): Likewise.
* src/qemu/qemu_hotplug.c (qemuDomainChangeEjectableMedia):
Likewise.
* src/qemu/qemu_process.c (qemuProcessWaitForMonitor): Likewise.
* src/qemu/qemu_monitor_text.c (qemuMonitorTextGetPtyPaths):
Likewise.
* src/qemu/qemu_driver.c (qemudDomainShutdown)
(qemudDomainBlockStats, qemudDomainMemoryPeek): Likewise.
* src/storage/storage_backend_iscsi.c
(virStorageBackendCreateIfaceIQN): Likewise.
* src/node_device/node_device_udev.c (udevProcessPCI): Likewise.
2011-04-01 09:41:45 -06:00
|
|
|
char *p;
|
2017-10-16 11:53:33 +02:00
|
|
|
bool privileged;
|
|
|
|
|
|
|
|
nodeDeviceLock();
|
|
|
|
privileged = driver->privileged;
|
|
|
|
nodeDeviceUnlock();
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2019-03-21 10:12:40 +03:00
|
|
|
pci_dev->klass = -1;
|
|
|
|
if (udevGetIntProperty(device, "PCI_CLASS", &pci_dev->klass, 16) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-03-01 17:46:53 +01:00
|
|
|
if ((p = strrchr(def->sysfs_path, '/')) == NULL ||
|
2017-03-02 10:59:25 -05:00
|
|
|
virStrToLong_ui(p + 1, &p, 16, &pci_dev->domain) < 0 || p == NULL ||
|
|
|
|
virStrToLong_ui(p + 1, &p, 16, &pci_dev->bus) < 0 || p == NULL ||
|
|
|
|
virStrToLong_ui(p + 1, &p, 16, &pci_dev->slot) < 0 || p == NULL ||
|
|
|
|
virStrToLong_ui(p + 1, &p, 16, &pci_dev->function) < 0) {
|
2016-06-03 14:54:03 +02:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("failed to parse the PCI address from sysfs path: '%s'"),
|
2017-03-01 17:46:53 +01:00
|
|
|
def->sysfs_path);
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGetUintSysfsAttr(device, "vendor", &pci_dev->vendor, 16) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGetUintSysfsAttr(device, "device", &pci_dev->product, 16) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevTranslatePCIIds(pci_dev->vendor,
|
|
|
|
pci_dev->product,
|
|
|
|
&pci_dev->vendor_name,
|
|
|
|
&pci_dev->product_name) != 0) {
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 23:22:00 +01:00
|
|
|
}
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2014-11-13 15:24:17 +01:00
|
|
|
if (udevGenerateDeviceName(device, def, NULL) != 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:39:39 +02:00
|
|
|
/* The default value is -1, because it can't be 0
|
|
|
|
* as zero is valid node number. */
|
2017-03-02 10:59:25 -05:00
|
|
|
pci_dev->numa_node = -1;
|
2016-06-03 17:39:39 +02:00
|
|
|
if (udevGetIntSysfsAttr(device, "numa_node",
|
2017-03-02 10:59:25 -05:00
|
|
|
&pci_dev->numa_node, 10) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2014-05-07 18:07:12 +02:00
|
|
|
|
2018-01-12 13:14:26 +01:00
|
|
|
if (virNodeDeviceGetPCIDynamicCaps(def->sysfs_path, pci_dev) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2013-06-23 14:01:00 -04:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (!(pciDev = virPCIDeviceNew(pci_dev->domain,
|
|
|
|
pci_dev->bus,
|
|
|
|
pci_dev->slot,
|
|
|
|
pci_dev->function)))
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2014-05-15 10:13:45 +02:00
|
|
|
|
2014-06-30 15:18:23 +02:00
|
|
|
/* We need to be root to read PCI device configs */
|
2017-10-16 11:53:33 +02:00
|
|
|
if (privileged) {
|
2017-03-02 10:59:25 -05:00
|
|
|
if (virPCIGetHeaderType(pciDev, &pci_dev->hdrType) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2016-03-15 12:22:03 +01:00
|
|
|
|
2016-03-15 13:13:48 +01:00
|
|
|
if (virPCIDeviceIsPCIExpress(pciDev) > 0) {
|
|
|
|
if (VIR_ALLOC(pci_express) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2014-05-15 10:13:45 +02:00
|
|
|
|
2016-03-15 13:13:48 +01:00
|
|
|
if (virPCIDeviceHasPCIExpressLink(pciDev) > 0) {
|
|
|
|
if (VIR_ALLOC(pci_express->link_cap) < 0 ||
|
|
|
|
VIR_ALLOC(pci_express->link_sta) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2016-03-15 13:13:48 +01:00
|
|
|
|
|
|
|
if (virPCIDeviceGetLinkCapSta(pciDev,
|
|
|
|
&pci_express->link_cap->port,
|
|
|
|
&pci_express->link_cap->speed,
|
|
|
|
&pci_express->link_cap->width,
|
|
|
|
&pci_express->link_sta->speed,
|
|
|
|
&pci_express->link_sta->width) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2016-03-15 13:13:48 +01:00
|
|
|
|
|
|
|
pci_express->link_sta->port = -1; /* PCIe can't negotiate port. Yet :) */
|
|
|
|
}
|
2017-03-02 10:59:25 -05:00
|
|
|
pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCIE;
|
|
|
|
pci_dev->pci_express = pci_express;
|
2016-03-15 13:13:48 +01:00
|
|
|
pci_express = NULL;
|
2014-05-15 10:13:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
ret = 0;
|
|
|
|
|
2016-06-03 19:40:45 +02:00
|
|
|
cleanup:
|
2014-05-15 10:13:45 +02:00
|
|
|
virPCIDeviceFree(pciDev);
|
2014-07-22 22:38:30 -06:00
|
|
|
virPCIEDeviceInfoFree(pci_express);
|
2009-11-12 22:48:24 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
|
|
|
|
static int
|
|
|
|
drmGetMinorType(int minor)
|
2017-02-15 01:04:12 +04:00
|
|
|
{
|
|
|
|
int type = minor >> 6;
|
|
|
|
|
|
|
|
if (minor < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case VIR_NODE_DEV_DRM_PRIMARY:
|
|
|
|
case VIR_NODE_DEV_DRM_CONTROL:
|
|
|
|
case VIR_NODE_DEV_DRM_RENDER:
|
|
|
|
return type;
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
|
|
|
|
static int
|
|
|
|
udevProcessDRMDevice(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def)
|
2017-02-15 01:04:12 +04:00
|
|
|
{
|
2017-03-02 10:59:25 -05:00
|
|
|
virNodeDevCapDRMPtr drm = &def->caps->data.drm;
|
2017-02-15 01:04:12 +04:00
|
|
|
int minor;
|
|
|
|
|
|
|
|
if (udevGenerateDeviceName(device, def, NULL) != 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (udevGetIntProperty(device, "MINOR", &minor, 10) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if ((minor = drmGetMinorType(minor)) == -1)
|
|
|
|
return -1;
|
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
drm->type = minor;
|
2017-02-15 01:04:12 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
|
|
|
|
static int
|
|
|
|
udevProcessUSBDevice(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2017-03-02 10:59:25 -05:00
|
|
|
virNodeDevCapUSBDevPtr usb_dev = &def->caps->data.usb_dev;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGetUintProperty(device, "BUSNUM", &usb_dev->bus, 10) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGetUintProperty(device, "DEVNUM", &usb_dev->device, 10) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGetUintProperty(device, "ID_VENDOR_ID", &usb_dev->vendor, 16) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 15:54:19 +02:00
|
|
|
if (udevGetStringProperty(device,
|
|
|
|
"ID_VENDOR_FROM_DATABASE",
|
2017-03-02 10:59:25 -05:00
|
|
|
&usb_dev->vendor_name) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2016-06-03 15:54:19 +02:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (!usb_dev->vendor_name &&
|
2016-06-03 17:27:48 +02:00
|
|
|
udevGetStringSysfsAttr(device, "manufacturer",
|
2017-03-02 10:59:25 -05:00
|
|
|
&usb_dev->vendor_name) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGetUintProperty(device, "ID_MODEL_ID", &usb_dev->product, 16) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 15:54:19 +02:00
|
|
|
if (udevGetStringProperty(device,
|
|
|
|
"ID_MODEL_FROM_DATABASE",
|
2017-03-02 10:59:25 -05:00
|
|
|
&usb_dev->product_name) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2016-06-03 15:54:19 +02:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (!usb_dev->product_name &&
|
2016-06-03 17:27:48 +02:00
|
|
|
udevGetStringSysfsAttr(device, "product",
|
2017-03-02 10:59:25 -05:00
|
|
|
&usb_dev->product_name) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2014-11-13 15:24:17 +01:00
|
|
|
if (udevGenerateDeviceName(device, def, NULL) != 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 19:37:06 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
udevProcessUSBInterface(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2017-03-02 10:59:25 -05:00
|
|
|
virNodeDevCapUSBIfPtr usb_if = &def->caps->data.usb_if;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 18:49:40 +02:00
|
|
|
if (udevGetUintSysfsAttr(device, "bInterfaceNumber",
|
2017-03-02 10:59:25 -05:00
|
|
|
&usb_if->number, 16) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 18:49:40 +02:00
|
|
|
if (udevGetUintSysfsAttr(device, "bInterfaceClass",
|
2019-03-12 11:08:00 +03:00
|
|
|
&usb_if->klass, 16) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 18:49:40 +02:00
|
|
|
if (udevGetUintSysfsAttr(device, "bInterfaceSubClass",
|
2017-03-02 10:59:25 -05:00
|
|
|
&usb_if->subclass, 16) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 18:49:40 +02:00
|
|
|
if (udevGetUintSysfsAttr(device, "bInterfaceProtocol",
|
2017-03-02 10:59:25 -05:00
|
|
|
&usb_if->protocol, 16) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2014-11-13 15:24:17 +01:00
|
|
|
if (udevGenerateDeviceName(device, def, NULL) != 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 19:37:06 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
udevProcessNetworkInterface(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2010-05-27 10:44:02 -04:00
|
|
|
const char *devtype = udev_device_get_devtype(device);
|
2017-03-02 10:59:25 -05:00
|
|
|
virNodeDevCapNetPtr net = &def->caps->data.net;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2010-05-27 10:44:02 -04:00
|
|
|
if (devtype && STREQ(devtype, "wlan")) {
|
2017-03-02 10:59:25 -05:00
|
|
|
net->subtype = VIR_NODE_DEV_CAP_NET_80211;
|
2010-05-27 10:44:02 -04:00
|
|
|
} else {
|
2017-03-02 10:59:25 -05:00
|
|
|
net->subtype = VIR_NODE_DEV_CAP_NET_80203;
|
2010-05-27 10:44:02 -04:00
|
|
|
}
|
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
if (udevGetStringProperty(device,
|
|
|
|
"INTERFACE",
|
2017-03-02 10:59:25 -05:00
|
|
|
&net->ifname) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:27:48 +02:00
|
|
|
if (udevGetStringSysfsAttr(device, "address",
|
2017-03-02 10:59:25 -05:00
|
|
|
&net->address) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGetUintSysfsAttr(device, "addr_len", &net->address_len, 0) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGenerateDeviceName(device, def, net->address) != 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (virNetDevGetLinkInfo(net->ifname, &net->lnk) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2014-06-05 17:36:31 +02:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (virNetDevGetFeatures(net->ifname, &net->features) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 19:37:06 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
2019-10-14 14:45:33 +02:00
|
|
|
udevProcessSCSIHost(struct udev_device *device G_GNUC_UNUSED,
|
2017-05-12 11:49:18 -04:00
|
|
|
virNodeDeviceDefPtr def)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2017-03-02 10:59:25 -05:00
|
|
|
virNodeDevCapSCSIHostPtr scsi_host = &def->caps->data.scsi_host;
|
2009-11-12 22:48:24 +01:00
|
|
|
char *filename = NULL;
|
2016-06-03 14:41:28 +02:00
|
|
|
char *str;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2013-04-25 14:24:42 -06:00
|
|
|
filename = last_component(def->sysfs_path);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 14:54:03 +02:00
|
|
|
if (!(str = STRSKIP(filename, "host")) ||
|
2017-03-02 10:59:25 -05:00
|
|
|
virStrToLong_ui(str, NULL, 0, &scsi_host->host) < 0) {
|
2016-06-03 14:54:03 +02:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("failed to parse SCSI host '%s'"),
|
|
|
|
filename);
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
2018-01-12 16:23:30 +01:00
|
|
|
virNodeDeviceGetSCSIHostCaps(&def->caps->data.scsi_host);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2014-11-13 15:24:17 +01:00
|
|
|
if (udevGenerateDeviceName(device, def, NULL) != 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 19:37:06 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
udevProcessSCSITarget(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def)
|
2009-11-12 23:03:47 +01:00
|
|
|
{
|
|
|
|
const char *sysname = NULL;
|
2017-03-02 10:59:25 -05:00
|
|
|
virNodeDevCapSCSITargetPtr scsi_target = &def->caps->data.scsi_target;
|
2009-11-12 23:03:47 +01:00
|
|
|
|
|
|
|
sysname = udev_device_get_sysname(device);
|
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (VIR_STRDUP(scsi_target->name, sysname) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 23:03:47 +01:00
|
|
|
|
2018-01-12 13:14:26 +01:00
|
|
|
virNodeDeviceGetSCSITargetCaps(def->sysfs_path, &def->caps->data.scsi_target);
|
2017-05-22 08:38:25 +02:00
|
|
|
|
2014-11-13 15:24:17 +01:00
|
|
|
if (udevGenerateDeviceName(device, def, NULL) != 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 23:03:47 +01:00
|
|
|
|
2016-06-03 19:37:06 +02:00
|
|
|
return 0;
|
2009-11-12 23:03:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
2019-10-14 14:45:33 +02:00
|
|
|
udevGetSCSIType(virNodeDeviceDefPtr def G_GNUC_UNUSED,
|
2017-05-12 11:49:18 -04:00
|
|
|
unsigned int type,
|
|
|
|
char **typestring)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
int foundtype = 1;
|
|
|
|
|
|
|
|
*typestring = NULL;
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case TYPE_DISK:
|
2013-05-03 14:44:20 +02:00
|
|
|
ignore_value(VIR_STRDUP(*typestring, "disk"));
|
2009-11-12 22:48:24 +01:00
|
|
|
break;
|
|
|
|
case TYPE_TAPE:
|
2013-05-03 14:44:20 +02:00
|
|
|
ignore_value(VIR_STRDUP(*typestring, "tape"));
|
2009-11-12 22:48:24 +01:00
|
|
|
break;
|
|
|
|
case TYPE_PROCESSOR:
|
2013-05-03 14:44:20 +02:00
|
|
|
ignore_value(VIR_STRDUP(*typestring, "processor"));
|
2009-11-12 22:48:24 +01:00
|
|
|
break;
|
|
|
|
case TYPE_WORM:
|
2013-05-03 14:44:20 +02:00
|
|
|
ignore_value(VIR_STRDUP(*typestring, "worm"));
|
2009-11-12 22:48:24 +01:00
|
|
|
break;
|
|
|
|
case TYPE_ROM:
|
2013-05-03 14:44:20 +02:00
|
|
|
ignore_value(VIR_STRDUP(*typestring, "cdrom"));
|
2009-11-12 22:48:24 +01:00
|
|
|
break;
|
|
|
|
case TYPE_SCANNER:
|
2013-05-03 14:44:20 +02:00
|
|
|
ignore_value(VIR_STRDUP(*typestring, "scanner"));
|
2009-11-12 22:48:24 +01:00
|
|
|
break;
|
|
|
|
case TYPE_MOD:
|
2013-05-03 14:44:20 +02:00
|
|
|
ignore_value(VIR_STRDUP(*typestring, "mod"));
|
2009-11-12 22:48:24 +01:00
|
|
|
break;
|
|
|
|
case TYPE_MEDIUM_CHANGER:
|
2013-05-03 14:44:20 +02:00
|
|
|
ignore_value(VIR_STRDUP(*typestring, "changer"));
|
2009-11-12 22:48:24 +01:00
|
|
|
break;
|
|
|
|
case TYPE_ENCLOSURE:
|
2013-05-03 14:44:20 +02:00
|
|
|
ignore_value(VIR_STRDUP(*typestring, "enclosure"));
|
2009-11-12 22:48:24 +01:00
|
|
|
break;
|
2011-02-07 17:25:06 +00:00
|
|
|
case TYPE_RAID:
|
2013-05-03 14:44:20 +02:00
|
|
|
ignore_value(VIR_STRDUP(*typestring, "raid"));
|
2011-02-07 17:25:06 +00:00
|
|
|
break;
|
2009-11-12 22:48:24 +01:00
|
|
|
case TYPE_NO_LUN:
|
|
|
|
default:
|
|
|
|
foundtype = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*typestring == NULL) {
|
|
|
|
if (foundtype == 1) {
|
|
|
|
ret = -1;
|
|
|
|
} else {
|
2011-02-07 17:25:06 +00:00
|
|
|
VIR_DEBUG("Failed to find SCSI device type %d for %s",
|
|
|
|
type, def->sysfs_path);
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
2019-10-14 14:45:33 +02:00
|
|
|
udevProcessSCSIDevice(struct udev_device *device G_GNUC_UNUSED,
|
2017-05-12 11:49:18 -04:00
|
|
|
virNodeDeviceDefPtr def)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
unsigned int tmp = 0;
|
2017-03-02 10:59:25 -05:00
|
|
|
virNodeDevCapSCSIPtr scsi = &def->caps->data.scsi;
|
2009-11-12 22:48:24 +01:00
|
|
|
char *filename = NULL, *p = NULL;
|
|
|
|
|
2013-04-25 14:24:42 -06:00
|
|
|
filename = last_component(def->sysfs_path);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (virStrToLong_ui(filename, &p, 10, &scsi->host) < 0 || p == NULL ||
|
|
|
|
virStrToLong_ui(p + 1, &p, 10, &scsi->bus) < 0 || p == NULL ||
|
|
|
|
virStrToLong_ui(p + 1, &p, 10, &scsi->target) < 0 || p == NULL ||
|
|
|
|
virStrToLong_ui(p + 1, &p, 10, &scsi->lun) < 0) {
|
2016-06-03 14:54:03 +02:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("failed to parse the SCSI address from filename: '%s'"),
|
|
|
|
filename);
|
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
2016-06-03 18:49:40 +02:00
|
|
|
if (udev_device_get_sysattr_value(device, "type")) {
|
|
|
|
if (udevGetUintSysfsAttr(device, "type", &tmp, 0) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2016-06-03 18:49:40 +02:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGetSCSIType(def, tmp, &scsi->type) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
2014-11-13 15:24:17 +01:00
|
|
|
if (udevGenerateDeviceName(device, def, NULL) != 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
2016-06-03 19:40:45 +02:00
|
|
|
cleanup:
|
2009-11-12 22:48:24 +01:00
|
|
|
if (ret != 0) {
|
2016-06-03 19:03:38 +02:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Failed to process SCSI device with sysfs path '%s'"),
|
|
|
|
def->sysfs_path);
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
udevProcessDisk(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2017-03-02 10:59:25 -05:00
|
|
|
virNodeDevCapStoragePtr storage = &def->caps->data.storage;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGetUint64SysfsAttr(device, "size", &storage->num_blocks) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 18:54:43 +02:00
|
|
|
if (udevGetUint64SysfsAttr(device, "queue/logical_block_size",
|
2017-03-02 10:59:25 -05:00
|
|
|
&storage->logical_block_size) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
storage->size = storage->num_blocks * storage->logical_block_size;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 19:37:06 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
udevProcessRemoveableMedia(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def,
|
|
|
|
int has_media)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2017-03-02 10:59:25 -05:00
|
|
|
virNodeDevCapStoragePtr storage = &def->caps->data.storage;
|
2016-06-03 19:38:59 +02:00
|
|
|
int is_removable = 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:39:39 +02:00
|
|
|
if (udevGetIntSysfsAttr(device, "removable", &is_removable, 0) < 0)
|
|
|
|
return -1;
|
|
|
|
if (is_removable == 1)
|
2009-11-12 22:48:24 +01:00
|
|
|
def->caps->data.storage.flags |= VIR_NODE_DEV_CAP_STORAGE_REMOVABLE;
|
|
|
|
|
2016-06-03 19:38:59 +02:00
|
|
|
if (!has_media)
|
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 19:38:59 +02:00
|
|
|
def->caps->data.storage.flags |=
|
|
|
|
VIR_NODE_DEV_CAP_STORAGE_REMOVABLE_MEDIA_AVAILABLE;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 19:38:59 +02:00
|
|
|
if (udevGetStringProperty(device, "ID_FS_LABEL",
|
2017-03-02 10:59:25 -05:00
|
|
|
&storage->media_label) < 0)
|
2016-06-03 19:38:59 +02:00
|
|
|
return -1;
|
2009-12-14 14:58:23 +01:00
|
|
|
|
2016-06-03 19:38:59 +02:00
|
|
|
if (udevGetUint64SysfsAttr(device, "size",
|
2017-03-02 10:59:25 -05:00
|
|
|
&storage->num_blocks) < 0)
|
2016-06-03 19:38:59 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 19:38:59 +02:00
|
|
|
if (udevGetUint64SysfsAttr(device, "queue/logical_block_size",
|
2017-03-02 10:59:25 -05:00
|
|
|
&storage->logical_block_size) < 0)
|
2016-06-03 19:38:59 +02:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 19:38:59 +02:00
|
|
|
/* XXX This calculation is wrong for the qemu virtual cdrom
|
|
|
|
* which reports the size in 512 byte blocks, but the logical
|
|
|
|
* block size as 2048. I don't have a physical cdrom on a
|
|
|
|
* devel system to see how they behave. */
|
|
|
|
def->caps->data.storage.removable_media_size =
|
|
|
|
def->caps->data.storage.num_blocks *
|
|
|
|
def->caps->data.storage.logical_block_size;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 19:38:59 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
|
|
|
|
static int
|
|
|
|
udevProcessCDROM(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def)
|
2010-01-12 14:56:56 -05:00
|
|
|
{
|
|
|
|
int has_media = 0;
|
|
|
|
|
|
|
|
/* NB: the drive_type string provided by udev is different from
|
|
|
|
* that provided by HAL; now it's "cd" instead of "cdrom" We
|
|
|
|
* change it to cdrom to preserve compatibility with earlier
|
|
|
|
* versions of libvirt. */
|
|
|
|
VIR_FREE(def->caps->data.storage.drive_type);
|
2013-05-03 14:44:20 +02:00
|
|
|
if (VIR_STRDUP(def->caps->data.storage.drive_type, "cdrom") < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2010-01-12 14:56:56 -05:00
|
|
|
|
2016-06-03 16:10:21 +02:00
|
|
|
if (udevHasDeviceProperty(device, "ID_CDROM_MEDIA") &&
|
|
|
|
udevGetIntProperty(device, "ID_CDROM_MEDIA", &has_media, 0) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2010-01-12 14:56:56 -05:00
|
|
|
|
2016-06-03 19:37:06 +02:00
|
|
|
return udevProcessRemoveableMedia(device, def, has_media);
|
2010-01-12 14:56:56 -05:00
|
|
|
}
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
|
|
|
|
static int
|
|
|
|
udevProcessFloppy(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def)
|
2010-01-12 14:56:56 -05:00
|
|
|
{
|
|
|
|
int has_media = 0;
|
|
|
|
|
2016-06-03 16:10:21 +02:00
|
|
|
if (udevHasDeviceProperty(device, "ID_CDROM_MEDIA")) {
|
2010-01-12 14:56:56 -05:00
|
|
|
/* USB floppy */
|
2016-06-03 16:10:21 +02:00
|
|
|
if (udevGetIntProperty(device, "DKD_MEDIA_AVAILABLE", &has_media, 0) < 0)
|
|
|
|
return -1;
|
2016-06-03 15:51:28 +02:00
|
|
|
} else if (udevHasDeviceProperty(device, "ID_FS_LABEL")) {
|
2010-01-12 14:56:56 -05:00
|
|
|
/* Legacy floppy */
|
|
|
|
has_media = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return udevProcessRemoveableMedia(device, def, has_media);
|
|
|
|
}
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2013-04-03 18:20:41 +01:00
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
udevProcessSD(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def)
|
2013-04-03 18:20:41 +01:00
|
|
|
{
|
2017-03-02 10:59:25 -05:00
|
|
|
virNodeDevCapStoragePtr storage = &def->caps->data.storage;
|
2013-04-03 18:20:41 +01:00
|
|
|
|
2016-06-03 18:54:43 +02:00
|
|
|
if (udevGetUint64SysfsAttr(device, "size",
|
2017-03-02 10:59:25 -05:00
|
|
|
&storage->num_blocks) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2013-04-03 18:20:41 +01:00
|
|
|
|
2016-06-03 18:54:43 +02:00
|
|
|
if (udevGetUint64SysfsAttr(device, "queue/logical_block_size",
|
2017-03-02 10:59:25 -05:00
|
|
|
&storage->logical_block_size) < 0)
|
2016-06-03 19:37:06 +02:00
|
|
|
return -1;
|
2013-04-03 18:20:41 +01:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
storage->size = storage->num_blocks * storage->logical_block_size;
|
2013-04-03 18:20:41 +01:00
|
|
|
|
2016-06-03 19:37:06 +02:00
|
|
|
return 0;
|
2013-04-03 18:20:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
/* This function exists to deal with the case in which a driver does
|
|
|
|
* not provide a device type in the usual place, but udev told us it's
|
|
|
|
* a storage device, and we can make a good guess at what kind of
|
|
|
|
* storage device it is from other information that is provided. */
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
udevKludgeStorageType(virNodeDeviceDefPtr def)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2011-02-09 16:41:30 +00:00
|
|
|
VIR_DEBUG("Could not find definitive storage type for device "
|
|
|
|
"with sysfs path '%s', trying to guess it",
|
|
|
|
def->sysfs_path);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 18:04:23 +02:00
|
|
|
/* virtio disk */
|
|
|
|
if (STRPREFIX(def->caps->data.storage.block, "/dev/vd") &&
|
|
|
|
VIR_STRDUP(def->caps->data.storage.drive_type, "disk") > 0) {
|
2009-11-12 22:48:24 +01:00
|
|
|
VIR_DEBUG("Found storage type '%s' for device "
|
2010-04-02 21:44:04 +02:00
|
|
|
"with sysfs path '%s'",
|
2009-11-12 22:48:24 +01:00
|
|
|
def->caps->data.storage.drive_type,
|
|
|
|
def->sysfs_path);
|
2016-06-03 18:04:23 +02:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
2016-06-03 18:04:23 +02:00
|
|
|
VIR_DEBUG("Could not determine storage type "
|
|
|
|
"for device with sysfs path '%s'", def->sysfs_path);
|
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
udevProcessStorage(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2017-03-02 10:59:25 -05:00
|
|
|
virNodeDevCapStoragePtr storage = &def->caps->data.storage;
|
2009-11-12 22:48:24 +01:00
|
|
|
int ret = -1;
|
2009-12-07 19:00:11 +01:00
|
|
|
const char* devnode;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2009-12-07 19:00:11 +01:00
|
|
|
devnode = udev_device_get_devnode(device);
|
2012-10-17 10:23:12 +01:00
|
|
|
if (!devnode) {
|
2010-01-19 14:17:20 +01:00
|
|
|
VIR_DEBUG("No devnode for '%s'", udev_device_get_devpath(device));
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-12-07 19:00:11 +01:00
|
|
|
}
|
2013-05-03 14:44:20 +02:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (VIR_STRDUP(storage->block, devnode) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2010-01-12 15:01:21 -05:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGetStringProperty(device, "ID_BUS", &storage->bus) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGetStringProperty(device, "ID_SERIAL", &storage->serial) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2016-06-03 15:54:19 +02:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGetStringSysfsAttr(device, "device/vendor", &storage->vendor) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2016-06-03 19:33:05 +02:00
|
|
|
if (def->caps->data.storage.vendor)
|
|
|
|
virTrimSpaces(def->caps->data.storage.vendor, NULL);
|
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGetStringSysfsAttr(device, "device/model", &storage->model) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2016-06-03 19:33:05 +02:00
|
|
|
if (def->caps->data.storage.model)
|
|
|
|
virTrimSpaces(def->caps->data.storage.model, NULL);
|
2009-11-12 22:48:24 +01:00
|
|
|
/* There is no equivalent of the hotpluggable property in libudev,
|
|
|
|
* but storage is going toward a world in which hotpluggable is
|
|
|
|
* expected, so I don't see a problem with not having a property
|
|
|
|
* for it. */
|
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGetStringProperty(device, "ID_TYPE", &storage->drive_type) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2016-06-03 15:54:19 +02:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (!storage->drive_type ||
|
2014-04-23 12:42:01 +02:00
|
|
|
STREQ(def->caps->data.storage.drive_type, "generic")) {
|
2016-06-03 18:24:43 +02:00
|
|
|
int val = 0;
|
|
|
|
const char *str = NULL;
|
2010-01-12 14:56:56 -05:00
|
|
|
|
|
|
|
/* All floppy drives have the ID_DRIVE_FLOPPY prop. This is
|
|
|
|
* needed since legacy floppies don't have a drive_type */
|
2016-06-03 16:10:21 +02:00
|
|
|
if (udevGetIntProperty(device, "ID_DRIVE_FLOPPY", &val, 0) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2016-06-03 18:24:43 +02:00
|
|
|
else if (val == 1)
|
|
|
|
str = "floppy";
|
2010-01-12 14:56:56 -05:00
|
|
|
|
2016-06-03 18:24:43 +02:00
|
|
|
if (!str) {
|
2016-06-03 16:10:21 +02:00
|
|
|
if (udevGetIntProperty(device, "ID_CDROM", &val, 0) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2016-06-03 18:24:43 +02:00
|
|
|
else if (val == 1)
|
|
|
|
str = "cd";
|
|
|
|
}
|
2014-04-23 12:42:01 +02:00
|
|
|
|
2016-06-03 18:24:43 +02:00
|
|
|
if (!str) {
|
2016-06-03 16:10:21 +02:00
|
|
|
if (udevGetIntProperty(device, "ID_DRIVE_FLASH_SD", &val, 0) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2016-06-03 18:24:43 +02:00
|
|
|
if (val == 1)
|
|
|
|
str = "sd";
|
|
|
|
}
|
2013-04-03 18:20:41 +01:00
|
|
|
|
2016-06-03 18:24:43 +02:00
|
|
|
if (str) {
|
2017-03-02 10:59:25 -05:00
|
|
|
if (VIR_STRDUP(storage->drive_type, str) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2010-01-12 14:56:56 -05:00
|
|
|
} else {
|
|
|
|
/* If udev doesn't have it, perhaps we can guess it. */
|
2014-11-13 15:24:17 +01:00
|
|
|
if (udevKludgeStorageType(def) != 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (STREQ(def->caps->data.storage.drive_type, "cd")) {
|
|
|
|
ret = udevProcessCDROM(device, def);
|
|
|
|
} else if (STREQ(def->caps->data.storage.drive_type, "disk")) {
|
|
|
|
ret = udevProcessDisk(device, def);
|
2010-01-12 14:56:56 -05:00
|
|
|
} else if (STREQ(def->caps->data.storage.drive_type, "floppy")) {
|
|
|
|
ret = udevProcessFloppy(device, def);
|
2013-04-03 18:20:41 +01:00
|
|
|
} else if (STREQ(def->caps->data.storage.drive_type, "sd")) {
|
|
|
|
ret = udevProcessSD(device, def);
|
2009-11-12 22:48:24 +01:00
|
|
|
} else {
|
2011-02-09 16:41:30 +00:00
|
|
|
VIR_DEBUG("Unsupported storage type '%s'",
|
|
|
|
def->caps->data.storage.drive_type);
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (udevGenerateDeviceName(device, def, storage->serial) != 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 19:40:45 +02:00
|
|
|
cleanup:
|
2013-04-03 18:20:41 +01:00
|
|
|
VIR_DEBUG("Storage ret=%d", ret);
|
2009-11-12 22:48:24 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
|
2013-06-03 18:05:32 +08:00
|
|
|
static int
|
2014-03-13 11:55:46 +00:00
|
|
|
udevProcessSCSIGeneric(struct udev_device *dev,
|
2013-06-03 18:05:32 +08:00
|
|
|
virNodeDeviceDefPtr def)
|
|
|
|
{
|
2016-06-03 15:54:19 +02:00
|
|
|
if (udevGetStringProperty(dev, "DEVNAME", &def->caps->data.sg.path) < 0 ||
|
|
|
|
!def->caps->data.sg.path)
|
2013-06-03 18:05:32 +08:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (udevGenerateDeviceName(dev, def, NULL) != 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
|
2017-03-06 17:18:48 +01:00
|
|
|
static int
|
|
|
|
udevProcessMediatedDevice(struct udev_device *dev,
|
|
|
|
virNodeDeviceDefPtr def)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
const char *uuidstr = NULL;
|
|
|
|
int iommugrp = -1;
|
|
|
|
char *linkpath = NULL;
|
2017-05-18 14:43:05 +02:00
|
|
|
char *canonicalpath = NULL;
|
2017-03-06 17:18:48 +01:00
|
|
|
virNodeDevCapMdevPtr data = &def->caps->data.mdev;
|
|
|
|
|
2017-06-20 16:15:22 +02:00
|
|
|
/* Because of a kernel uevent race, we might get the 'add' event prior to
|
|
|
|
* the sysfs tree being ready, so any attempt to access any sysfs attribute
|
|
|
|
* would result in ENOENT and us dropping the device, so let's work around
|
|
|
|
* it by waiting for the attributes to become available.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (virAsprintf(&linkpath, "%s/mdev_type",
|
|
|
|
udev_device_get_syspath(dev)) < 0)
|
2017-03-06 17:18:48 +01:00
|
|
|
goto cleanup;
|
|
|
|
|
2017-06-20 16:15:22 +02:00
|
|
|
if (virFileWaitForExists(linkpath, 1, 100) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("failed to wait for file '%s' to appear"),
|
|
|
|
linkpath);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2017-06-20 16:50:26 +02:00
|
|
|
if (virFileResolveLink(linkpath, &canonicalpath) < 0) {
|
|
|
|
virReportSystemError(errno, _("failed to resolve '%s'"), linkpath);
|
2017-03-06 17:18:48 +01:00
|
|
|
goto cleanup;
|
2017-06-20 16:50:26 +02:00
|
|
|
}
|
2017-03-06 17:18:48 +01:00
|
|
|
|
2017-05-18 14:43:05 +02:00
|
|
|
if (VIR_STRDUP(data->type, last_component(canonicalpath)) < 0)
|
2017-03-06 17:18:48 +01:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
uuidstr = udev_device_get_sysname(dev);
|
|
|
|
if ((iommugrp = virMediatedDeviceGetIOMMUGroupNum(uuidstr)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (udevGenerateDeviceName(dev, def, NULL) != 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
data->iommuGroupNumber = iommugrp;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(linkpath);
|
2017-05-18 14:43:05 +02:00
|
|
|
VIR_FREE(canonicalpath);
|
2017-03-06 17:18:48 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-05-22 08:38:22 +02:00
|
|
|
|
|
|
|
static int
|
|
|
|
udevProcessCCW(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def)
|
|
|
|
{
|
|
|
|
int online;
|
|
|
|
char *p;
|
|
|
|
virNodeDevCapDataPtr data = &def->caps->data;
|
|
|
|
|
|
|
|
/* process only online devices to keep the list sane */
|
|
|
|
if (udevGetIntSysfsAttr(device, "online", &online, 0) < 0 || online != 1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if ((p = strrchr(def->sysfs_path, '/')) == NULL ||
|
|
|
|
virStrToLong_ui(p + 1, &p, 16, &data->ccw_dev.cssid) < 0 || p == NULL ||
|
|
|
|
virStrToLong_ui(p + 1, &p, 16, &data->ccw_dev.ssid) < 0 || p == NULL ||
|
|
|
|
virStrToLong_ui(p + 1, &p, 16, &data->ccw_dev.devno) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("failed to parse the CCW address from sysfs path: '%s'"),
|
|
|
|
def->sysfs_path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevGenerateDeviceName(device, def, NULL) != 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-15 01:04:10 +04:00
|
|
|
static int
|
|
|
|
udevGetDeviceNodes(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def)
|
|
|
|
{
|
|
|
|
const char *devnode = NULL;
|
|
|
|
struct udev_list_entry *list_entry = NULL;
|
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
devnode = udev_device_get_devnode(device);
|
|
|
|
|
|
|
|
if (VIR_STRDUP(def->devnode, devnode) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device))
|
|
|
|
n++;
|
|
|
|
|
|
|
|
if (VIR_ALLOC_N(def->devlinks, n + 1) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
n = 0;
|
|
|
|
udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device)) {
|
|
|
|
if (VIR_STRDUP(def->devlinks[n++], udev_list_entry_get_name(list_entry)) < 0)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
|
2013-06-03 18:05:31 +08:00
|
|
|
static int
|
|
|
|
udevGetDeviceType(struct udev_device *device,
|
2014-05-11 12:08:48 -03:00
|
|
|
virNodeDevCapType *type)
|
2013-06-03 18:05:31 +08:00
|
|
|
{
|
|
|
|
const char *devtype = NULL;
|
2013-06-03 18:05:32 +08:00
|
|
|
char *subsystem = NULL;
|
2013-06-03 18:05:31 +08:00
|
|
|
int ret = -1;
|
2010-05-27 10:44:02 -04:00
|
|
|
|
2013-06-03 18:05:31 +08:00
|
|
|
devtype = udev_device_get_devtype(device);
|
|
|
|
*type = 0;
|
|
|
|
|
|
|
|
if (devtype) {
|
|
|
|
if (STREQ(devtype, "usb_device"))
|
|
|
|
*type = VIR_NODE_DEV_CAP_USB_DEV;
|
|
|
|
else if (STREQ(devtype, "usb_interface"))
|
|
|
|
*type = VIR_NODE_DEV_CAP_USB_INTERFACE;
|
|
|
|
else if (STREQ(devtype, "scsi_host"))
|
|
|
|
*type = VIR_NODE_DEV_CAP_SCSI_HOST;
|
|
|
|
else if (STREQ(devtype, "scsi_target"))
|
|
|
|
*type = VIR_NODE_DEV_CAP_SCSI_TARGET;
|
|
|
|
else if (STREQ(devtype, "scsi_device"))
|
|
|
|
*type = VIR_NODE_DEV_CAP_SCSI;
|
|
|
|
else if (STREQ(devtype, "disk"))
|
|
|
|
*type = VIR_NODE_DEV_CAP_STORAGE;
|
|
|
|
else if (STREQ(devtype, "wlan"))
|
|
|
|
*type = VIR_NODE_DEV_CAP_NET;
|
2017-02-15 01:04:12 +04:00
|
|
|
else if (STREQ(devtype, "drm_minor"))
|
|
|
|
*type = VIR_NODE_DEV_CAP_DRM;
|
2013-06-03 18:05:31 +08:00
|
|
|
} else {
|
|
|
|
/* PCI devices don't set the DEVTYPE property. */
|
2013-06-18 16:39:24 +08:00
|
|
|
if (udevHasDeviceProperty(device, "PCI_CLASS"))
|
2013-06-03 18:05:31 +08:00
|
|
|
*type = VIR_NODE_DEV_CAP_PCI_DEV;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2013-06-03 18:05:31 +08:00
|
|
|
/* Wired network interfaces don't set the DEVTYPE property,
|
|
|
|
* USB devices also have an INTERFACE property, but they do
|
|
|
|
* set DEVTYPE, so if devtype is NULL and the INTERFACE
|
|
|
|
* property exists, we have a network device. */
|
2013-06-18 16:39:24 +08:00
|
|
|
if (udevHasDeviceProperty(device, "INTERFACE"))
|
2013-06-03 18:05:31 +08:00
|
|
|
*type = VIR_NODE_DEV_CAP_NET;
|
2013-06-03 18:05:32 +08:00
|
|
|
|
2017-05-22 08:38:22 +02:00
|
|
|
/* The following devices do not set the DEVTYPE property, therefore
|
|
|
|
* we need to rely on the SUBSYSTEM property */
|
2016-06-03 15:54:19 +02:00
|
|
|
if (udevGetStringProperty(device, "SUBSYSTEM", &subsystem) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (STREQ_NULLABLE(subsystem, "scsi_generic"))
|
2013-06-03 18:05:32 +08:00
|
|
|
*type = VIR_NODE_DEV_CAP_SCSI_GENERIC;
|
2017-03-06 17:18:48 +01:00
|
|
|
else if (STREQ_NULLABLE(subsystem, "mdev"))
|
|
|
|
*type = VIR_NODE_DEV_CAP_MDEV;
|
2017-05-22 08:38:22 +02:00
|
|
|
else if (STREQ_NULLABLE(subsystem, "ccw"))
|
|
|
|
*type = VIR_NODE_DEV_CAP_CCW_DEV;
|
2017-03-06 17:18:48 +01:00
|
|
|
|
2013-06-03 18:05:32 +08:00
|
|
|
VIR_FREE(subsystem);
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
2013-06-03 18:05:31 +08:00
|
|
|
if (!*type)
|
|
|
|
VIR_DEBUG("Could not determine device type for device "
|
|
|
|
"with sysfs name '%s'",
|
|
|
|
udev_device_get_sysname(device));
|
|
|
|
else
|
|
|
|
ret = 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
udevGetDeviceDetails(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2015-05-08 12:55:00 -04:00
|
|
|
switch (def->caps->data.type) {
|
2009-11-12 22:48:24 +01:00
|
|
|
case VIR_NODE_DEV_CAP_PCI_DEV:
|
2017-02-24 10:26:42 +01:00
|
|
|
return udevProcessPCI(device, def);
|
2009-11-12 22:48:24 +01:00
|
|
|
case VIR_NODE_DEV_CAP_USB_DEV:
|
2017-02-24 10:26:42 +01:00
|
|
|
return udevProcessUSBDevice(device, def);
|
2009-11-12 22:48:24 +01:00
|
|
|
case VIR_NODE_DEV_CAP_USB_INTERFACE:
|
2017-02-24 10:26:42 +01:00
|
|
|
return udevProcessUSBInterface(device, def);
|
2009-11-12 22:48:24 +01:00
|
|
|
case VIR_NODE_DEV_CAP_NET:
|
2017-02-24 10:26:42 +01:00
|
|
|
return udevProcessNetworkInterface(device, def);
|
2009-11-12 22:48:24 +01:00
|
|
|
case VIR_NODE_DEV_CAP_SCSI_HOST:
|
2017-02-24 10:26:42 +01:00
|
|
|
return udevProcessSCSIHost(device, def);
|
2009-11-12 23:03:47 +01:00
|
|
|
case VIR_NODE_DEV_CAP_SCSI_TARGET:
|
2017-02-24 10:26:42 +01:00
|
|
|
return udevProcessSCSITarget(device, def);
|
2009-11-12 22:48:24 +01:00
|
|
|
case VIR_NODE_DEV_CAP_SCSI:
|
2017-02-24 10:26:42 +01:00
|
|
|
return udevProcessSCSIDevice(device, def);
|
2009-11-12 22:48:24 +01:00
|
|
|
case VIR_NODE_DEV_CAP_STORAGE:
|
2017-02-24 10:26:42 +01:00
|
|
|
return udevProcessStorage(device, def);
|
2013-06-03 18:05:32 +08:00
|
|
|
case VIR_NODE_DEV_CAP_SCSI_GENERIC:
|
2017-02-24 10:26:42 +01:00
|
|
|
return udevProcessSCSIGeneric(device, def);
|
2017-02-15 01:04:12 +04:00
|
|
|
case VIR_NODE_DEV_CAP_DRM:
|
2017-02-24 10:26:42 +01:00
|
|
|
return udevProcessDRMDevice(device, def);
|
2017-03-06 17:20:00 +01:00
|
|
|
case VIR_NODE_DEV_CAP_MDEV:
|
2017-03-06 17:18:48 +01:00
|
|
|
return udevProcessMediatedDevice(device, def);
|
2017-05-22 08:38:22 +02:00
|
|
|
case VIR_NODE_DEV_CAP_CCW_DEV:
|
|
|
|
return udevProcessCCW(device, def);
|
2017-03-06 17:20:00 +01:00
|
|
|
case VIR_NODE_DEV_CAP_MDEV_TYPES:
|
2017-02-24 10:26:42 +01:00
|
|
|
case VIR_NODE_DEV_CAP_SYSTEM:
|
|
|
|
case VIR_NODE_DEV_CAP_FC_HOST:
|
|
|
|
case VIR_NODE_DEV_CAP_VPORTS:
|
|
|
|
case VIR_NODE_DEV_CAP_LAST:
|
2009-11-12 22:48:24 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-02-24 10:26:42 +01:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
udevRemoveOneDevice(struct udev_device *device)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2017-05-12 12:45:16 -04:00
|
|
|
virNodeDeviceObjPtr obj = NULL;
|
2017-06-03 07:36:01 -04:00
|
|
|
virNodeDeviceDefPtr def;
|
2016-07-28 14:02:55 +02:00
|
|
|
virObjectEventPtr event = NULL;
|
2009-11-12 22:48:24 +01:00
|
|
|
const char *name = NULL;
|
|
|
|
|
|
|
|
name = udev_device_get_syspath(device);
|
2017-05-12 14:47:17 -04:00
|
|
|
if (!(obj = virNodeDeviceObjListFindBySysfsPath(driver->devs, name))) {
|
2011-02-09 16:41:30 +00:00
|
|
|
VIR_DEBUG("Failed to find device to remove that has udev name '%s'",
|
|
|
|
name);
|
2017-06-03 07:36:01 -04:00
|
|
|
return -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
2017-05-12 12:45:16 -04:00
|
|
|
def = virNodeDeviceObjGetDef(obj);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-06-03 07:36:01 -04:00
|
|
|
event = virNodeDeviceEventLifecycleNew(def->name,
|
2016-07-28 14:02:55 +02:00
|
|
|
VIR_NODE_DEVICE_EVENT_DELETED,
|
|
|
|
0);
|
|
|
|
|
|
|
|
VIR_DEBUG("Removing device '%s' with sysfs path '%s'",
|
2017-06-03 07:36:01 -04:00
|
|
|
def->name, name);
|
2017-05-12 14:47:17 -04:00
|
|
|
virNodeDeviceObjListRemove(driver->devs, obj);
|
2017-05-15 11:00:59 -04:00
|
|
|
virObjectUnref(obj);
|
2016-07-28 14:02:55 +02:00
|
|
|
|
2018-06-11 15:38:17 -04:00
|
|
|
virObjectEventStateQueue(driver->nodeDeviceEventState, event);
|
2017-06-03 07:36:01 -04:00
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
udevSetParent(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
|
|
|
struct udev_device *parent_device = NULL;
|
|
|
|
const char *parent_sysfs_path = NULL;
|
2017-05-12 12:45:16 -04:00
|
|
|
virNodeDeviceObjPtr obj = NULL;
|
2017-06-03 07:36:01 -04:00
|
|
|
virNodeDeviceDefPtr objdef;
|
2009-11-12 22:48:24 +01:00
|
|
|
int ret = -1;
|
|
|
|
|
2010-05-27 17:36:54 -04:00
|
|
|
parent_device = device;
|
|
|
|
do {
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2010-05-27 17:36:54 -04:00
|
|
|
parent_device = udev_device_get_parent(parent_device);
|
2014-11-13 15:24:17 +01:00
|
|
|
if (parent_device == NULL)
|
2010-05-27 17:36:54 -04:00
|
|
|
break;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2010-05-27 17:36:54 -04:00
|
|
|
parent_sysfs_path = udev_device_get_syspath(parent_device);
|
|
|
|
if (parent_sysfs_path == NULL) {
|
2012-07-18 12:42:38 +01:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Could not get syspath for parent of '%s'"),
|
|
|
|
udev_device_get_syspath(parent_device));
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2010-05-27 17:36:54 -04:00
|
|
|
}
|
|
|
|
|
2017-05-12 14:47:17 -04:00
|
|
|
if ((obj = virNodeDeviceObjListFindBySysfsPath(driver->devs,
|
|
|
|
parent_sysfs_path))) {
|
2017-05-12 12:45:16 -04:00
|
|
|
objdef = virNodeDeviceObjGetDef(obj);
|
2017-06-03 07:36:01 -04:00
|
|
|
if (VIR_STRDUP(def->parent, objdef->name) < 0) {
|
2017-05-15 11:00:59 -04:00
|
|
|
virNodeDeviceObjEndAPI(&obj);
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2010-05-27 17:36:54 -04:00
|
|
|
}
|
2017-05-15 11:00:59 -04:00
|
|
|
virNodeDeviceObjEndAPI(&obj);
|
2010-05-27 17:36:54 -04:00
|
|
|
|
2013-05-03 14:44:20 +02:00
|
|
|
if (VIR_STRDUP(def->parent_sysfs_path, parent_sysfs_path) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2010-05-27 17:36:54 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
} while (def->parent == NULL && parent_device != NULL);
|
|
|
|
|
2013-05-03 14:44:20 +02:00
|
|
|
if (!def->parent && VIR_STRDUP(def->parent, "computer") < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
2016-06-03 19:40:45 +02:00
|
|
|
cleanup:
|
2009-11-12 22:48:24 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
udevAddOneDevice(struct udev_device *device)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
|
|
|
virNodeDeviceDefPtr def = NULL;
|
2017-05-12 12:45:16 -04:00
|
|
|
virNodeDeviceObjPtr obj = NULL;
|
2017-06-03 07:36:01 -04:00
|
|
|
virNodeDeviceDefPtr objdef;
|
2016-07-28 14:02:55 +02:00
|
|
|
virObjectEventPtr event = NULL;
|
|
|
|
bool new_device = true;
|
2009-11-12 22:48:24 +01:00
|
|
|
int ret = -1;
|
|
|
|
|
2013-07-04 12:11:59 +02:00
|
|
|
if (VIR_ALLOC(def) != 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2013-05-03 14:44:20 +02:00
|
|
|
if (VIR_STRDUP(def->sysfs_path, udev_device_get_syspath(device)) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2013-05-03 14:44:20 +02:00
|
|
|
|
2016-06-03 15:54:19 +02:00
|
|
|
if (udevGetStringProperty(device, "DRIVER", &def->driver) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2013-07-04 12:11:59 +02:00
|
|
|
if (VIR_ALLOC(def->caps) != 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2015-05-08 12:55:00 -04:00
|
|
|
if (udevGetDeviceType(device, &def->caps->data.type) != 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-02-15 01:04:10 +04:00
|
|
|
if (udevGetDeviceNodes(device, def) != 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2014-11-13 15:24:17 +01:00
|
|
|
if (udevGetDeviceDetails(device, def) != 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2014-11-13 15:24:17 +01:00
|
|
|
if (udevSetParent(device, def) != 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-05-12 14:47:17 -04:00
|
|
|
if ((obj = virNodeDeviceObjListFindByName(driver->devs, def->name))) {
|
2017-05-15 11:00:59 -04:00
|
|
|
virNodeDeviceObjEndAPI(&obj);
|
2016-07-28 14:02:55 +02:00
|
|
|
new_device = false;
|
|
|
|
}
|
|
|
|
|
2010-05-28 22:22:05 -04:00
|
|
|
/* If this is a device change, the old definition will be freed
|
|
|
|
* and the current definition will take its place. */
|
2017-05-12 14:47:17 -04:00
|
|
|
if (!(obj = virNodeDeviceObjListAssignDef(driver->devs, def)))
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2017-05-12 12:45:16 -04:00
|
|
|
objdef = virNodeDeviceObjGetDef(obj);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-07-28 14:02:55 +02:00
|
|
|
if (new_device)
|
2017-06-03 07:36:01 -04:00
|
|
|
event = virNodeDeviceEventLifecycleNew(objdef->name,
|
2016-07-28 14:02:55 +02:00
|
|
|
VIR_NODE_DEVICE_EVENT_CREATED,
|
|
|
|
0);
|
2016-08-11 17:15:23 +02:00
|
|
|
else
|
2017-06-03 07:36:01 -04:00
|
|
|
event = virNodeDeviceEventUpdateNew(objdef->name);
|
2016-07-28 14:02:55 +02:00
|
|
|
|
2017-05-15 11:00:59 -04:00
|
|
|
virNodeDeviceObjEndAPI(&obj);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
2016-06-03 19:40:45 +02:00
|
|
|
cleanup:
|
2018-06-11 15:38:17 -04:00
|
|
|
virObjectEventStateQueue(driver->nodeDeviceEventState, event);
|
2016-07-28 14:02:55 +02:00
|
|
|
|
2010-05-28 22:22:05 -04:00
|
|
|
if (ret != 0) {
|
2013-04-03 18:20:41 +01:00
|
|
|
VIR_DEBUG("Discarding device %d %p %s", ret, def,
|
2013-05-03 14:44:20 +02:00
|
|
|
def ? NULLSTR(def->sysfs_path) : "");
|
2010-05-28 22:22:05 -04:00
|
|
|
virNodeDeviceDefFree(def);
|
|
|
|
}
|
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
udevProcessDeviceListEntry(struct udev *udev,
|
|
|
|
struct udev_list_entry *list_entry)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
|
|
|
struct udev_device *device;
|
|
|
|
const char *name = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
name = udev_list_entry_get_name(list_entry);
|
|
|
|
|
|
|
|
device = udev_device_new_from_syspath(udev, name);
|
2010-05-28 22:22:05 -04:00
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
if (device != NULL) {
|
|
|
|
if (udevAddOneDevice(device) != 0) {
|
2011-02-09 16:41:30 +00:00
|
|
|
VIR_DEBUG("Failed to create node device for udev device '%s'",
|
|
|
|
name);
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
|
2010-05-28 22:22:05 -04:00
|
|
|
udev_device_unref(device);
|
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-06-02 18:09:56 +02:00
|
|
|
/* We do not care about every device (see udevGetDeviceType).
|
|
|
|
* Do not bother enumerating over subsystems that do not
|
|
|
|
* contain interesting devices.
|
|
|
|
*/
|
|
|
|
const char *subsystem_blacklist[] = {
|
|
|
|
"acpi", "tty", "vc", "i2c",
|
|
|
|
};
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
udevEnumerateAddMatches(struct udev_enumerate *udev_enumerate)
|
2016-06-02 18:09:56 +02:00
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
2019-10-15 13:55:26 +02:00
|
|
|
for (i = 0; i < G_N_ELEMENTS(subsystem_blacklist); i++) {
|
2016-06-02 18:09:56 +02:00
|
|
|
const char *s = subsystem_blacklist[i];
|
|
|
|
if (udev_enumerate_add_nomatch_subsystem(udev_enumerate, s) < 0) {
|
|
|
|
virReportSystemError(errno, "%s", _("failed to add susbsystem filter"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
udevEnumerateDevices(struct udev *udev)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
|
|
|
struct udev_enumerate *udev_enumerate = NULL;
|
|
|
|
struct udev_list_entry *list_entry = NULL;
|
2016-06-02 18:09:56 +02:00
|
|
|
int ret = -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
|
|
|
udev_enumerate = udev_enumerate_new(udev);
|
2016-06-02 18:09:56 +02:00
|
|
|
if (udevEnumerateAddMatches(udev_enumerate) < 0)
|
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2019-02-14 10:01:01 +01:00
|
|
|
if (udev_enumerate_scan_devices(udev_enumerate) < 0)
|
|
|
|
VIR_WARN("udev scan devices failed");
|
2009-11-12 22:48:24 +01:00
|
|
|
|
|
|
|
udev_list_entry_foreach(list_entry,
|
|
|
|
udev_enumerate_get_list_entry(udev_enumerate)) {
|
|
|
|
|
|
|
|
udevProcessDeviceListEntry(udev, list_entry);
|
|
|
|
}
|
|
|
|
|
2019-02-14 10:01:01 +01:00
|
|
|
ret = 0;
|
2016-06-03 19:40:45 +02:00
|
|
|
cleanup:
|
2009-11-12 22:48:24 +01:00
|
|
|
udev_enumerate_unref(udev_enumerate);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static void
|
|
|
|
udevPCITranslateDeinit(void)
|
2016-06-03 11:03:41 +02:00
|
|
|
{
|
|
|
|
#if defined __s390__ || defined __s390x_
|
|
|
|
/* Nothing was initialized, nothing needs to be cleaned up */
|
|
|
|
#else
|
|
|
|
/* pci_system_cleanup returns void */
|
|
|
|
pci_system_cleanup();
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
nodeStateCleanup(void)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2017-06-28 14:34:41 +02:00
|
|
|
udevEventDataPtr priv = NULL;
|
|
|
|
|
2016-06-03 12:38:40 +02:00
|
|
|
if (!driver)
|
|
|
|
return -1;
|
2010-01-26 02:58:37 +01:00
|
|
|
|
2017-06-28 14:34:41 +02:00
|
|
|
priv = driver->privateData;
|
|
|
|
if (priv) {
|
|
|
|
virObjectLock(priv);
|
|
|
|
priv->threadQuit = true;
|
|
|
|
virCondSignal(&priv->threadCond);
|
|
|
|
virObjectUnlock(priv);
|
|
|
|
virThreadJoin(&priv->th);
|
|
|
|
}
|
|
|
|
|
|
|
|
virObjectUnref(priv);
|
2016-10-11 09:48:36 +02:00
|
|
|
virObjectUnref(driver->nodeDeviceEventState);
|
2016-07-28 14:02:55 +02:00
|
|
|
|
2017-05-12 13:51:25 -04:00
|
|
|
virNodeDeviceObjListFree(driver->devs);
|
2019-05-23 11:34:08 +01:00
|
|
|
|
|
|
|
if (driver->lockFD != -1)
|
|
|
|
virPidFileRelease(driver->stateDir, "driver", driver->lockFD);
|
|
|
|
|
|
|
|
VIR_FREE(driver->stateDir);
|
2016-06-03 12:38:40 +02:00
|
|
|
virMutexDestroy(&driver->lock);
|
|
|
|
VIR_FREE(driver);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 12:38:40 +02:00
|
|
|
udevPCITranslateDeinit();
|
|
|
|
return 0;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-25 17:41:31 +02:00
|
|
|
static int
|
|
|
|
udevHandleOneDevice(struct udev_device *device)
|
|
|
|
{
|
|
|
|
const char *action = udev_device_get_action(device);
|
|
|
|
|
|
|
|
VIR_DEBUG("udev action: '%s'", action);
|
|
|
|
|
|
|
|
if (STREQ(action, "add") || STREQ(action, "change"))
|
|
|
|
return udevAddOneDevice(device);
|
|
|
|
|
|
|
|
if (STREQ(action, "remove"))
|
|
|
|
return udevRemoveOneDevice(device);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-10-06 15:21:55 +02:00
|
|
|
/* the caller must be holding the udevEventData object lock prior to calling
|
|
|
|
* this function
|
|
|
|
*/
|
2017-07-26 15:16:09 +02:00
|
|
|
static bool
|
2017-10-06 15:21:55 +02:00
|
|
|
udevEventMonitorSanityCheck(udevEventDataPtr priv,
|
2017-07-26 15:16:09 +02:00
|
|
|
int fd)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2017-07-26 15:16:09 +02:00
|
|
|
int rc = -1;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-10-06 15:21:55 +02:00
|
|
|
rc = udev_monitor_get_fd(priv->udev_monitor);
|
2017-07-26 15:16:09 +02:00
|
|
|
if (fd != rc) {
|
2016-06-03 19:03:38 +02:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("File descriptor returned by udev %d does not "
|
|
|
|
"match node device file descriptor %d"),
|
2017-07-26 15:16:09 +02:00
|
|
|
fd, rc);
|
2017-06-28 15:39:51 +02:00
|
|
|
|
|
|
|
/* this is a non-recoverable error, let's remove the handle, so that we
|
|
|
|
* don't get in here again because of some spurious behaviour and report
|
|
|
|
* the same error multiple times
|
|
|
|
*/
|
|
|
|
virEventRemoveHandle(priv->watch);
|
|
|
|
priv->watch = -1;
|
|
|
|
|
2017-07-26 15:16:09 +02:00
|
|
|
return false;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
2017-07-26 15:16:09 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-10-18 10:22:18 -04:00
|
|
|
/**
|
|
|
|
* udevEventHandleThread
|
|
|
|
* @opaque: unused
|
|
|
|
*
|
|
|
|
* Thread to handle the udevEventHandleCallback processing when udev
|
|
|
|
* tells us there's a device change for us (add, modify, delete, etc).
|
|
|
|
*
|
|
|
|
* Once notified there is data to be processed, the actual @device
|
|
|
|
* data retrieval by libudev may be delayed due to how threads are
|
|
|
|
* scheduled. In fact, the event loop could be scheduled earlier than
|
|
|
|
* the handler thread, thus potentially emitting the very same event
|
|
|
|
* the handler thread is currently trying to process, simply because
|
|
|
|
* the data hadn't been retrieved from the socket.
|
|
|
|
*
|
|
|
|
* NB: Some older distros, such as CentOS 6, libudev opens sockets
|
|
|
|
* without the NONBLOCK flag which might cause issues with event
|
|
|
|
* based algorithm. Although the issue can be mitigated by resetting
|
|
|
|
* priv->dataReady for each event found; however, the scheduler issues
|
|
|
|
* would still come into play.
|
|
|
|
*/
|
2017-07-26 15:16:09 +02:00
|
|
|
static void
|
2019-10-14 14:45:33 +02:00
|
|
|
udevEventHandleThread(void *opaque G_GNUC_UNUSED)
|
2017-07-26 15:16:09 +02:00
|
|
|
{
|
2017-10-06 15:21:55 +02:00
|
|
|
udevEventDataPtr priv = driver->privateData;
|
2017-07-26 15:16:09 +02:00
|
|
|
struct udev_device *device = NULL;
|
|
|
|
|
2017-06-28 14:34:41 +02:00
|
|
|
/* continue rather than break from the loop on non-fatal errors */
|
|
|
|
while (1) {
|
|
|
|
virObjectLock(priv);
|
|
|
|
while (!priv->dataReady && !priv->threadQuit) {
|
|
|
|
if (virCondWait(&priv->threadCond, &priv->parent.lock)) {
|
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("handler failed to wait on condition"));
|
|
|
|
virObjectUnlock(priv);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (priv->threadQuit) {
|
|
|
|
virObjectUnlock(priv);
|
|
|
|
return;
|
|
|
|
}
|
2017-07-26 15:16:09 +02:00
|
|
|
|
2017-06-28 14:34:41 +02:00
|
|
|
errno = 0;
|
|
|
|
device = udev_monitor_receive_device(priv->udev_monitor);
|
2017-10-06 15:21:55 +02:00
|
|
|
virObjectUnlock(priv);
|
|
|
|
|
2017-06-28 14:34:41 +02:00
|
|
|
if (!device) {
|
|
|
|
if (errno == 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("failed to receive device from udev monitor"));
|
|
|
|
return;
|
|
|
|
}
|
2017-07-26 15:16:09 +02:00
|
|
|
|
2017-06-28 14:34:41 +02:00
|
|
|
/* POSIX allows both EAGAIN and EWOULDBLOCK to be used
|
|
|
|
* interchangeably when the read would block or timeout was fired
|
|
|
|
*/
|
|
|
|
VIR_WARNINGS_NO_WLOGICALOP_EQUAL_EXPR
|
|
|
|
if (errno != EAGAIN && errno != EWOULDBLOCK) {
|
|
|
|
VIR_WARNINGS_RESET
|
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("failed to receive device from udev "
|
|
|
|
"monitor"));
|
|
|
|
return;
|
|
|
|
}
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2018-10-18 10:22:18 -04:00
|
|
|
/* Trying to move the reset of the @priv->dataReady flag to
|
|
|
|
* after the udev_monitor_receive_device wouldn't help much
|
|
|
|
* due to event mgmt and scheduler timing. */
|
2017-06-28 14:34:41 +02:00
|
|
|
virObjectLock(priv);
|
|
|
|
priv->dataReady = false;
|
|
|
|
virObjectUnlock(priv);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
udevHandleOneDevice(device);
|
|
|
|
udev_device_unref(device);
|
2018-10-18 10:22:18 -04:00
|
|
|
|
|
|
|
/* Instead of waiting for the next event after processing @device
|
|
|
|
* data, let's keep reading from the udev monitor and only wait
|
|
|
|
* for the next event once either a EAGAIN or a EWOULDBLOCK error
|
|
|
|
* is encountered. */
|
2017-06-28 14:34:41 +02:00
|
|
|
}
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-06-28 14:20:37 +02:00
|
|
|
static void
|
2019-10-14 14:45:33 +02:00
|
|
|
udevEventHandleCallback(int watch G_GNUC_UNUSED,
|
2017-06-28 14:20:37 +02:00
|
|
|
int fd,
|
2019-10-14 14:45:33 +02:00
|
|
|
int events G_GNUC_UNUSED,
|
|
|
|
void *data G_GNUC_UNUSED)
|
2017-06-28 14:20:37 +02:00
|
|
|
{
|
|
|
|
udevEventDataPtr priv = driver->privateData;
|
|
|
|
|
|
|
|
virObjectLock(priv);
|
|
|
|
|
2017-06-28 14:34:41 +02:00
|
|
|
if (!udevEventMonitorSanityCheck(priv, fd))
|
|
|
|
priv->threadQuit = true;
|
|
|
|
else
|
|
|
|
priv->dataReady = true;
|
|
|
|
|
|
|
|
virCondSignal(&priv->threadCond);
|
|
|
|
virObjectUnlock(priv);
|
2017-06-28 14:20:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-07-19 16:48:49 +02:00
|
|
|
/* DMI is intel-compatible specific */
|
|
|
|
#if defined(__x86_64__) || defined(__i386__) || defined(__amd64__)
|
2010-03-04 13:17:24 -05:00
|
|
|
static void
|
2017-03-04 10:42:33 -05:00
|
|
|
udevGetDMIData(virNodeDevCapSystemPtr syscap)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2017-10-06 15:21:55 +02:00
|
|
|
udevEventDataPtr priv = driver->privateData;
|
2009-11-12 22:48:24 +01:00
|
|
|
struct udev *udev = NULL;
|
|
|
|
struct udev_device *device = NULL;
|
2017-03-04 10:42:33 -05:00
|
|
|
virNodeDevCapSystemHardwarePtr hardware = &syscap->hardware;
|
|
|
|
virNodeDevCapSystemFirmwarePtr firmware = &syscap->firmware;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-10-18 14:58:51 +02:00
|
|
|
virObjectLock(priv);
|
2017-10-06 15:21:55 +02:00
|
|
|
udev = udev_monitor_get_udev(priv->udev_monitor);
|
2010-03-04 13:17:24 -05:00
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
device = udev_device_new_from_syspath(udev, DMI_DEVPATH);
|
|
|
|
if (device == NULL) {
|
2010-01-07 10:13:51 +01:00
|
|
|
device = udev_device_new_from_syspath(udev, DMI_DEVPATH_FALLBACK);
|
|
|
|
if (device == NULL) {
|
2016-06-03 19:03:38 +02:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Failed to get udev device for syspath '%s' or '%s'"),
|
|
|
|
DMI_DEVPATH, DMI_DEVPATH_FALLBACK);
|
2017-10-18 14:58:51 +02:00
|
|
|
virObjectUnlock(priv);
|
2016-06-03 19:40:45 +02:00
|
|
|
return;
|
2010-01-07 10:13:51 +01:00
|
|
|
}
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
2017-10-18 14:58:51 +02:00
|
|
|
virObjectUnlock(priv);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:27:48 +02:00
|
|
|
if (udevGetStringSysfsAttr(device, "product_name",
|
2017-03-04 10:42:33 -05:00
|
|
|
&syscap->product_name) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2016-06-03 17:27:48 +02:00
|
|
|
if (udevGetStringSysfsAttr(device, "sys_vendor",
|
2017-03-02 10:59:25 -05:00
|
|
|
&hardware->vendor_name) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2016-06-03 17:27:48 +02:00
|
|
|
if (udevGetStringSysfsAttr(device, "product_version",
|
2017-03-02 10:59:25 -05:00
|
|
|
&hardware->version) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2016-06-03 17:27:48 +02:00
|
|
|
if (udevGetStringSysfsAttr(device, "product_serial",
|
2017-03-02 10:59:25 -05:00
|
|
|
&hardware->serial) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-03-02 10:59:25 -05:00
|
|
|
if (virGetHostUUID(hardware->uuid))
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 17:27:48 +02:00
|
|
|
if (udevGetStringSysfsAttr(device, "bios_vendor",
|
2017-03-02 10:59:25 -05:00
|
|
|
&firmware->vendor_name) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2016-06-03 17:27:48 +02:00
|
|
|
if (udevGetStringSysfsAttr(device, "bios_version",
|
2017-03-02 10:59:25 -05:00
|
|
|
&firmware->version) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2016-06-03 17:27:48 +02:00
|
|
|
if (udevGetStringSysfsAttr(device, "bios_date",
|
2017-03-02 10:59:25 -05:00
|
|
|
&firmware->release_date) < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 19:40:45 +02:00
|
|
|
cleanup:
|
2014-11-13 15:24:17 +01:00
|
|
|
if (device != NULL)
|
2010-03-04 13:17:24 -05:00
|
|
|
udev_device_unref(device);
|
|
|
|
return;
|
|
|
|
}
|
2011-07-19 16:48:49 +02:00
|
|
|
#endif
|
2010-03-04 13:17:24 -05:00
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
udevSetupSystemDev(void)
|
2010-03-04 13:17:24 -05:00
|
|
|
{
|
|
|
|
virNodeDeviceDefPtr def = NULL;
|
2017-05-12 12:45:16 -04:00
|
|
|
virNodeDeviceObjPtr obj = NULL;
|
2010-03-04 13:17:24 -05:00
|
|
|
int ret = -1;
|
|
|
|
|
2016-06-03 19:42:11 +02:00
|
|
|
if (VIR_ALLOC(def) < 0)
|
|
|
|
return -1;
|
2010-03-04 13:17:24 -05:00
|
|
|
|
2013-05-03 14:44:20 +02:00
|
|
|
if (VIR_STRDUP(def->name, "computer") < 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2010-03-04 13:17:24 -05:00
|
|
|
|
2013-07-04 12:11:59 +02:00
|
|
|
if (VIR_ALLOC(def->caps) != 0)
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2010-03-04 13:17:24 -05:00
|
|
|
|
2011-07-19 16:48:49 +02:00
|
|
|
#if defined(__x86_64__) || defined(__i386__) || defined(__amd64__)
|
2017-03-02 10:59:25 -05:00
|
|
|
udevGetDMIData(&def->caps->data.system);
|
2011-07-19 16:48:49 +02:00
|
|
|
#endif
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-05-12 14:47:17 -04:00
|
|
|
if (!(obj = virNodeDeviceObjListAssignDef(driver->devs, def)))
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-05-15 11:00:59 -04:00
|
|
|
virNodeDeviceObjEndAPI(&obj);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
2016-06-03 19:40:45 +02:00
|
|
|
cleanup:
|
2014-11-13 15:24:17 +01:00
|
|
|
if (ret == -1)
|
2010-03-04 13:17:24 -05:00
|
|
|
virNodeDeviceDefFree(def);
|
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
|
2017-11-22 11:55:10 -05:00
|
|
|
static void
|
|
|
|
nodeStateInitializeEnumerate(void *opaque)
|
|
|
|
{
|
|
|
|
struct udev *udev = opaque;
|
|
|
|
udevEventDataPtr priv = driver->privateData;
|
|
|
|
|
|
|
|
/* Populate with known devices */
|
|
|
|
if (udevEnumerateDevices(udev) != 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
error:
|
|
|
|
virObjectLock(priv);
|
2019-02-20 11:05:45 +01:00
|
|
|
ignore_value(virEventRemoveHandle(priv->watch));
|
|
|
|
priv->watch = -1;
|
2017-11-22 11:55:10 -05:00
|
|
|
priv->threadQuit = true;
|
2019-02-20 11:05:46 +01:00
|
|
|
virCondSignal(&priv->threadCond);
|
2017-11-22 11:55:10 -05:00
|
|
|
virObjectUnlock(priv);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
2019-10-14 14:45:33 +02:00
|
|
|
udevPCITranslateInit(bool privileged G_GNUC_UNUSED)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2011-04-22 14:24:54 +02:00
|
|
|
#if defined __s390__ || defined __s390x_
|
|
|
|
/* On s390(x) system there is no PCI bus.
|
|
|
|
* Therefore there is nothing to initialize here. */
|
|
|
|
#else
|
2016-06-03 19:03:38 +02:00
|
|
|
int rc;
|
2011-02-07 17:04:35 +00:00
|
|
|
|
2016-06-03 19:03:38 +02:00
|
|
|
if ((rc = pci_system_init()) != 0) {
|
2011-03-16 15:47:32 -06:00
|
|
|
/* Ignore failure as non-root; udev is not as helpful in that
|
|
|
|
* situation, but a non-privileged user won't benefit much
|
|
|
|
* from udev in the first place. */
|
2011-09-05 21:29:06 +02:00
|
|
|
if (errno != ENOENT && (privileged || errno != EACCES)) {
|
2016-06-03 19:03:38 +02:00
|
|
|
virReportSystemError(rc, "%s",
|
|
|
|
_("Failed to initialize libpciaccess"));
|
2016-06-03 11:03:41 +02:00
|
|
|
return -1;
|
2011-03-16 15:47:32 -06:00
|
|
|
}
|
2011-02-07 17:04:35 +00:00
|
|
|
}
|
2011-04-22 14:24:54 +02:00
|
|
|
#endif
|
2016-06-03 11:03:41 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
|
|
|
|
static int
|
|
|
|
nodeStateInitialize(bool privileged,
|
2019-10-14 14:45:33 +02:00
|
|
|
virStateInhibitCallback callback G_GNUC_UNUSED,
|
|
|
|
void *opaque G_GNUC_UNUSED)
|
2016-06-03 11:03:41 +02:00
|
|
|
{
|
2017-10-06 15:21:55 +02:00
|
|
|
udevEventDataPtr priv = NULL;
|
2016-06-03 11:03:41 +02:00
|
|
|
struct udev *udev = NULL;
|
2017-11-22 11:55:10 -05:00
|
|
|
virThread enumThread;
|
2016-06-03 11:03:41 +02:00
|
|
|
|
2017-10-06 15:21:55 +02:00
|
|
|
if (VIR_ALLOC(driver) < 0)
|
2019-07-23 13:05:18 +01:00
|
|
|
return VIR_DRV_STATE_INIT_ERROR;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2019-05-23 11:34:08 +01:00
|
|
|
driver->lockFD = -1;
|
2014-11-17 16:30:27 +00:00
|
|
|
if (virMutexInit(&driver->lock) < 0) {
|
2016-06-03 19:03:38 +02:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Unable to initialize mutex"));
|
2014-11-17 16:30:27 +00:00
|
|
|
VIR_FREE(driver);
|
2019-07-23 13:05:18 +01:00
|
|
|
return VIR_DRV_STATE_INIT_ERROR;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
2017-11-22 10:34:11 -05:00
|
|
|
driver->privileged = privileged;
|
|
|
|
|
2019-05-23 11:34:08 +01:00
|
|
|
if (privileged) {
|
|
|
|
if (virAsprintf(&driver->stateDir,
|
2019-08-20 16:05:12 +01:00
|
|
|
"%s/libvirt/nodedev", RUNSTATEDIR) < 0)
|
2019-05-23 11:34:08 +01:00
|
|
|
goto cleanup;
|
|
|
|
} else {
|
|
|
|
VIR_AUTOFREE(char *) rundir = NULL;
|
|
|
|
|
|
|
|
if (!(rundir = virGetUserRuntimeDirectory()))
|
|
|
|
goto cleanup;
|
|
|
|
if (virAsprintf(&driver->stateDir, "%s/nodedev/run", rundir) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virFileMakePathWithMode(driver->stateDir, S_IRWXU) < 0) {
|
|
|
|
virReportSystemError(errno, _("cannot create state directory '%s'"),
|
|
|
|
driver->stateDir);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((driver->lockFD =
|
2019-07-11 15:57:34 +01:00
|
|
|
virPidFileAcquire(driver->stateDir, "driver", false, getpid())) < 0)
|
2019-05-23 11:34:08 +01:00
|
|
|
goto cleanup;
|
|
|
|
|
2017-10-06 15:21:55 +02:00
|
|
|
if (!(driver->devs = virNodeDeviceObjListNew()) ||
|
|
|
|
!(priv = udevEventDataNew()))
|
2017-10-18 12:51:40 +02:00
|
|
|
goto cleanup;
|
2017-05-12 13:51:25 -04:00
|
|
|
|
2017-10-06 15:21:55 +02:00
|
|
|
driver->privateData = priv;
|
2016-07-28 14:02:55 +02:00
|
|
|
driver->nodeDeviceEventState = virObjectEventStateNew();
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 12:57:41 +02:00
|
|
|
if (udevPCITranslateInit(privileged) < 0)
|
2017-10-18 12:51:40 +02:00
|
|
|
goto cleanup;
|
2016-06-03 12:20:41 +02:00
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
udev = udev_new();
|
2017-02-10 10:44:44 +01:00
|
|
|
if (!udev) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("failed to create udev context"));
|
2017-10-18 12:51:40 +02:00
|
|
|
goto cleanup;
|
2017-02-10 10:44:44 +01:00
|
|
|
}
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-10-06 15:21:55 +02:00
|
|
|
virObjectLock(priv);
|
|
|
|
|
2010-01-26 02:58:37 +01:00
|
|
|
priv->udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
|
2017-10-06 15:21:55 +02:00
|
|
|
if (!priv->udev_monitor) {
|
2016-06-03 19:03:38 +02:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("udev_monitor_new_from_netlink returned NULL"));
|
2017-07-26 10:45:11 +02:00
|
|
|
goto unlock;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
2010-01-26 02:58:37 +01:00
|
|
|
udev_monitor_enable_receiving(priv->udev_monitor);
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-05-09 10:09:07 +08:00
|
|
|
/* mimic udevd's behaviour and override the systems rmem_max limit in case
|
|
|
|
* there's a significant number of device 'add' events
|
|
|
|
*/
|
|
|
|
if (geteuid() == 0)
|
|
|
|
udev_monitor_set_receive_buffer_size(priv->udev_monitor,
|
|
|
|
128 * 1024 * 1024);
|
|
|
|
|
2017-06-28 14:34:41 +02:00
|
|
|
if (virThreadCreate(&priv->th, true, udevEventHandleThread, NULL) < 0) {
|
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("failed to create udev handler thread"));
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
/* We register the monitor with the event callback so we are
|
|
|
|
* notified by udev of device changes before we enumerate existing
|
|
|
|
* devices because libvirt will simply recreate the device if we
|
|
|
|
* try to register it twice, i.e., if the device appears between
|
|
|
|
* the time we register the callback and the time we begin
|
|
|
|
* enumeration. The alternative is to register the callback after
|
|
|
|
* we enumerate, in which case we will fail to create any devices
|
|
|
|
* that appear while the enumeration is taking place. */
|
2010-01-26 02:58:37 +01:00
|
|
|
priv->watch = virEventAddHandle(udev_monitor_get_fd(priv->udev_monitor),
|
|
|
|
VIR_EVENT_HANDLE_READABLE,
|
|
|
|
udevEventHandleCallback, NULL, NULL);
|
2016-06-03 10:21:23 +02:00
|
|
|
if (priv->watch == -1)
|
2017-07-26 10:45:11 +02:00
|
|
|
goto unlock;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-10-18 14:58:51 +02:00
|
|
|
virObjectUnlock(priv);
|
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
/* Create a fictional 'computer' device to root the device tree. */
|
2016-06-03 10:21:23 +02:00
|
|
|
if (udevSetupSystemDev() != 0)
|
2017-10-18 14:58:51 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2017-11-22 11:55:10 -05:00
|
|
|
if (virThreadCreate(&enumThread, false, nodeStateInitializeEnumerate,
|
|
|
|
udev) < 0) {
|
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("failed to create udev enumerate thread"));
|
2016-06-03 19:40:45 +02:00
|
|
|
goto cleanup;
|
2017-11-22 11:55:10 -05:00
|
|
|
}
|
2016-06-03 10:21:23 +02:00
|
|
|
|
2019-07-23 13:05:18 +01:00
|
|
|
return VIR_DRV_STATE_INIT_COMPLETE;
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2016-06-03 19:40:45 +02:00
|
|
|
cleanup:
|
2017-06-29 16:51:54 -04:00
|
|
|
nodeStateCleanup();
|
2019-07-23 13:05:18 +01:00
|
|
|
return VIR_DRV_STATE_INIT_ERROR;
|
2017-07-26 10:45:11 +02:00
|
|
|
|
|
|
|
unlock:
|
2017-10-18 12:51:40 +02:00
|
|
|
virObjectUnlock(priv);
|
2017-07-26 10:45:11 +02:00
|
|
|
goto cleanup;
|
2009-11-12 22:48:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
static int
|
|
|
|
nodeStateReload(void)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-04-23 11:15:48 +01:00
|
|
|
static virNodeDeviceDriver udevNodeDeviceDriver = {
|
2015-01-20 16:16:26 +00:00
|
|
|
.name = "udev",
|
2013-04-22 18:26:01 +01:00
|
|
|
.nodeNumOfDevices = nodeNumOfDevices, /* 0.7.3 */
|
|
|
|
.nodeListDevices = nodeListDevices, /* 0.7.3 */
|
2013-04-29 10:20:17 +01:00
|
|
|
.connectListAllNodeDevices = nodeConnectListAllNodeDevices, /* 0.10.2 */
|
2016-07-28 14:02:55 +02:00
|
|
|
.connectNodeDeviceEventRegisterAny = nodeConnectNodeDeviceEventRegisterAny, /* 2.2.0 */
|
|
|
|
.connectNodeDeviceEventDeregisterAny = nodeConnectNodeDeviceEventDeregisterAny, /* 2.2.0 */
|
2013-04-22 18:26:01 +01:00
|
|
|
.nodeDeviceLookupByName = nodeDeviceLookupByName, /* 0.7.3 */
|
|
|
|
.nodeDeviceLookupSCSIHostByWWN = nodeDeviceLookupSCSIHostByWWN, /* 1.0.2 */
|
|
|
|
.nodeDeviceGetXMLDesc = nodeDeviceGetXMLDesc, /* 0.7.3 */
|
|
|
|
.nodeDeviceGetParent = nodeDeviceGetParent, /* 0.7.3 */
|
|
|
|
.nodeDeviceNumOfCaps = nodeDeviceNumOfCaps, /* 0.7.3 */
|
|
|
|
.nodeDeviceListCaps = nodeDeviceListCaps, /* 0.7.3 */
|
|
|
|
.nodeDeviceCreateXML = nodeDeviceCreateXML, /* 0.7.3 */
|
|
|
|
.nodeDeviceDestroy = nodeDeviceDestroy, /* 0.7.3 */
|
2009-11-12 22:48:24 +01:00
|
|
|
};
|
|
|
|
|
2018-01-26 11:16:00 +00:00
|
|
|
|
|
|
|
static virHypervisorDriver udevHypervisorDriver = {
|
|
|
|
.name = "nodedev",
|
|
|
|
.connectOpen = nodeConnectOpen, /* 4.1.0 */
|
|
|
|
.connectClose = nodeConnectClose, /* 4.1.0 */
|
|
|
|
.connectIsEncrypted = nodeConnectIsEncrypted, /* 4.1.0 */
|
|
|
|
.connectIsSecure = nodeConnectIsSecure, /* 4.1.0 */
|
|
|
|
.connectIsAlive = nodeConnectIsAlive, /* 4.1.0 */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static virConnectDriver udevConnectDriver = {
|
2018-03-28 10:53:31 +01:00
|
|
|
.localOnly = true,
|
2018-03-27 15:51:45 +01:00
|
|
|
.uriSchemes = (const char *[]){ "nodedev", NULL },
|
2018-01-26 11:16:00 +00:00
|
|
|
.hypervisorDriver = &udevHypervisorDriver,
|
|
|
|
.nodeDeviceDriver = &udevNodeDeviceDriver,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2009-11-12 22:48:24 +01:00
|
|
|
static virStateDriver udevStateDriver = {
|
2010-01-26 03:13:08 +01:00
|
|
|
.name = "udev",
|
2013-04-29 10:20:17 +01:00
|
|
|
.stateInitialize = nodeStateInitialize, /* 0.7.3 */
|
|
|
|
.stateCleanup = nodeStateCleanup, /* 0.7.3 */
|
|
|
|
.stateReload = nodeStateReload, /* 0.7.3 */
|
2009-11-12 22:48:24 +01:00
|
|
|
};
|
|
|
|
|
2017-05-12 11:49:18 -04:00
|
|
|
|
|
|
|
int
|
|
|
|
udevNodeRegister(void)
|
2009-11-12 22:48:24 +01:00
|
|
|
{
|
2011-05-09 17:24:09 +08:00
|
|
|
VIR_DEBUG("Registering udev node device backend");
|
2009-11-12 22:48:24 +01:00
|
|
|
|
2018-01-26 11:16:00 +00:00
|
|
|
if (virRegisterConnectDriver(&udevConnectDriver, false) < 0)
|
|
|
|
return -1;
|
2015-01-20 16:16:26 +00:00
|
|
|
if (virSetSharedNodeDeviceDriver(&udevNodeDeviceDriver) < 0)
|
2009-11-12 22:48:24 +01:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
return virRegisterStateDriver(&udevStateDriver);
|
|
|
|
}
|