2009-11-12 21:48:24 +00:00
|
|
|
/*
|
|
|
|
* node_device_udev.c: node device enumeration - libudev implementation
|
|
|
|
*
|
|
|
|
* Copyright (C) 2009 Red Hat
|
|
|
|
*
|
|
|
|
* 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, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*
|
|
|
|
* Author: Dave Allan <dallan@redhat.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
#include <libudev.h>
|
2009-11-12 22:22:00 +00:00
|
|
|
#include <pciaccess.h>
|
2009-11-12 21:48:24 +00:00
|
|
|
#include <scsi/scsi.h>
|
|
|
|
#include <c-ctype.h>
|
|
|
|
|
|
|
|
#include "node_device_udev.h"
|
|
|
|
#include "virterror_internal.h"
|
|
|
|
#include "node_device_conf.h"
|
|
|
|
#include "node_device_driver.h"
|
|
|
|
#include "driver.h"
|
|
|
|
#include "datatypes.h"
|
|
|
|
#include "logging.h"
|
|
|
|
#include "memory.h"
|
|
|
|
#include "uuid.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "buf.h"
|
2009-12-14 15:09:01 +00:00
|
|
|
#include "event.h"
|
2009-11-12 21:48:24 +00:00
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_NODEDEV
|
|
|
|
|
2010-01-26 01:58:37 +00:00
|
|
|
struct _udevPrivate {
|
|
|
|
struct udev_monitor *udev_monitor;
|
|
|
|
int watch;
|
|
|
|
};
|
|
|
|
|
2009-11-12 21:48:24 +00:00
|
|
|
static virDeviceMonitorStatePtr driverState = NULL;
|
|
|
|
|
|
|
|
static int udevStrToLong_ull(char const *s,
|
|
|
|
char **end_ptr,
|
|
|
|
int base,
|
|
|
|
unsigned long long *result)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
ret = virStrToLong_ull(s, end_ptr, base, result);
|
|
|
|
if (ret != 0) {
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_ERROR("Failed to convert '%s' to unsigned long long", s);
|
2009-11-12 21:48:24 +00:00
|
|
|
} else {
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_DEBUG("Converted '%s' to unsigned long %llu", s, *result);
|
2009-11-12 21:48:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int udevStrToLong_ui(char const *s,
|
|
|
|
char **end_ptr,
|
|
|
|
int base,
|
|
|
|
unsigned int *result)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
ret = virStrToLong_ui(s, end_ptr, base, result);
|
|
|
|
if (ret != 0) {
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_ERROR("Failed to convert '%s' to unsigned int", s);
|
2009-11-12 21:48:24 +00:00
|
|
|
} else {
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_DEBUG("Converted '%s' to unsigned int %u", s, *result);
|
2009-11-12 21:48:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int udevStrToLong_i(char const *s,
|
|
|
|
char **end_ptr,
|
|
|
|
int base,
|
|
|
|
int *result)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
ret = virStrToLong_i(s, end_ptr, base, result);
|
|
|
|
if (ret != 0) {
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_ERROR("Failed to convert '%s' to int", s);
|
2009-11-12 21:48:24 +00:00
|
|
|
} else {
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_DEBUG("Converted '%s' to int %u", s, *result);
|
2009-11-12 21:48:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* This function allocates memory from the heap for the property
|
|
|
|
* value. That memory must be later freed by some other code. */
|
|
|
|
static int udevGetDeviceProperty(struct udev_device *udev_device,
|
|
|
|
const char *property_key,
|
|
|
|
char **property_value)
|
|
|
|
{
|
|
|
|
const char *udev_value = NULL;
|
|
|
|
int ret = PROPERTY_FOUND;
|
|
|
|
|
|
|
|
udev_value = udev_device_get_property_value(udev_device, property_key);
|
|
|
|
if (udev_value == NULL) {
|
|
|
|
VIR_INFO(_("udev reports device '%s' does not have property '%s'"),
|
|
|
|
udev_device_get_sysname(udev_device), property_key);
|
|
|
|
ret = PROPERTY_MISSING;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If this allocation is changed, the comment at the beginning
|
|
|
|
* of the function must also be changed. */
|
|
|
|
*property_value = strdup(udev_value);
|
|
|
|
if (*property_value == NULL) {
|
|
|
|
VIR_ERROR("Failed to allocate memory for property value for "
|
|
|
|
"property key '%s' on device with sysname '%s'",
|
|
|
|
property_key, udev_device_get_sysname(udev_device));
|
|
|
|
virReportOOMError(NULL);
|
|
|
|
ret = PROPERTY_ERROR;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_DEBUG("Found property key '%s' value '%s' "
|
|
|
|
"for device with sysname '%s'\n",
|
|
|
|
property_key, *property_value,
|
|
|
|
udev_device_get_sysname(udev_device));
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int udevGetStringProperty(struct udev_device *udev_device,
|
|
|
|
const char *property_key,
|
|
|
|
char **value)
|
|
|
|
{
|
|
|
|
return udevGetDeviceProperty(udev_device, property_key, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int udevGetIntProperty(struct udev_device *udev_device,
|
|
|
|
const char *property_key,
|
|
|
|
int *value,
|
|
|
|
int base)
|
|
|
|
{
|
|
|
|
char *udev_value = NULL;
|
|
|
|
int ret = PROPERTY_FOUND;
|
|
|
|
|
|
|
|
ret = udevGetDeviceProperty(udev_device, property_key, &udev_value);
|
|
|
|
|
|
|
|
if (ret == PROPERTY_FOUND) {
|
|
|
|
if (udevStrToLong_i(udev_value, NULL, base, value) != 0) {
|
|
|
|
ret = PROPERTY_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(udev_value);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int udevGetUintProperty(struct udev_device *udev_device,
|
|
|
|
const char *property_key,
|
|
|
|
unsigned int *value,
|
|
|
|
int base)
|
|
|
|
{
|
|
|
|
char *udev_value = NULL;
|
|
|
|
int ret = PROPERTY_FOUND;
|
|
|
|
|
|
|
|
ret = udevGetDeviceProperty(udev_device, property_key, &udev_value);
|
|
|
|
|
|
|
|
if (ret == PROPERTY_FOUND) {
|
|
|
|
if (udevStrToLong_ui(udev_value, NULL, base, value) != 0) {
|
|
|
|
ret = PROPERTY_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(udev_value);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* This function allocates memory from the heap for the property
|
|
|
|
* value. That memory must be later freed by some other code. */
|
|
|
|
static int udevGetDeviceSysfsAttr(struct udev_device *udev_device,
|
|
|
|
const char *attr_name,
|
|
|
|
char **attr_value)
|
|
|
|
{
|
|
|
|
const char *udev_value = NULL;
|
|
|
|
int ret = PROPERTY_FOUND;
|
|
|
|
|
|
|
|
udev_value = udev_device_get_sysattr_value(udev_device, attr_name);
|
|
|
|
if (udev_value == NULL) {
|
|
|
|
VIR_INFO(_("udev reports device '%s' does not have sysfs attr '%s'"),
|
|
|
|
udev_device_get_sysname(udev_device), attr_name);
|
|
|
|
ret = PROPERTY_MISSING;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If this allocation is changed, the comment at the beginning
|
|
|
|
* of the function must also be changed. */
|
|
|
|
*attr_value = strdup(udev_value);
|
|
|
|
if (*attr_value == NULL) {
|
|
|
|
VIR_ERROR("Failed to allocate memory for sysfs attribute value for "
|
|
|
|
"sysfs attribute '%s' on device with sysname '%s'",
|
|
|
|
attr_name, udev_device_get_sysname(udev_device));
|
|
|
|
virReportOOMError(NULL);
|
|
|
|
ret = PROPERTY_ERROR;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_DEBUG("Found sysfs attribute '%s' value '%s' "
|
|
|
|
"for device with sysname '%s'\n",
|
|
|
|
attr_name, *attr_value,
|
|
|
|
udev_device_get_sysname(udev_device));
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int udevGetStringSysfsAttr(struct udev_device *udev_device,
|
|
|
|
const char *attr_name,
|
|
|
|
char **value)
|
|
|
|
{
|
|
|
|
char *tmp = NULL;
|
|
|
|
int ret = PROPERTY_MISSING;
|
|
|
|
|
|
|
|
ret = udevGetDeviceSysfsAttr(udev_device, attr_name, &tmp);
|
|
|
|
|
|
|
|
if (tmp != NULL && (STREQ(tmp, ""))) {
|
|
|
|
VIR_FREE(tmp);
|
|
|
|
tmp = NULL;
|
|
|
|
ret = PROPERTY_MISSING;
|
|
|
|
}
|
|
|
|
|
|
|
|
*value = tmp;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int udevGetIntSysfsAttr(struct udev_device *udev_device,
|
|
|
|
const char *attr_name,
|
|
|
|
int *value,
|
|
|
|
int base)
|
|
|
|
{
|
|
|
|
char *udev_value = NULL;
|
|
|
|
int ret = PROPERTY_FOUND;
|
|
|
|
|
|
|
|
ret = udevGetDeviceSysfsAttr(udev_device, attr_name, &udev_value);
|
|
|
|
|
|
|
|
if (ret == PROPERTY_FOUND) {
|
|
|
|
if (udevStrToLong_i(udev_value, NULL, base, value) != 0) {
|
|
|
|
ret = PROPERTY_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(udev_value);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int udevGetUintSysfsAttr(struct udev_device *udev_device,
|
|
|
|
const char *attr_name,
|
|
|
|
unsigned int *value,
|
|
|
|
int base)
|
|
|
|
{
|
|
|
|
char *udev_value = NULL;
|
|
|
|
int ret = PROPERTY_FOUND;
|
|
|
|
|
|
|
|
ret = udevGetDeviceSysfsAttr(udev_device, attr_name, &udev_value);
|
|
|
|
|
|
|
|
if (ret == PROPERTY_FOUND) {
|
|
|
|
if (udevStrToLong_ui(udev_value, NULL, base, value) != 0) {
|
|
|
|
ret = PROPERTY_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(udev_value);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int udevGetUint64SysfsAttr(struct udev_device *udev_device,
|
|
|
|
const char *attr_name,
|
|
|
|
unsigned long long *value)
|
|
|
|
{
|
|
|
|
char *udev_value = NULL;
|
|
|
|
int ret = PROPERTY_FOUND;
|
|
|
|
|
|
|
|
ret = udevGetDeviceSysfsAttr(udev_device, attr_name, &udev_value);
|
|
|
|
|
|
|
|
if (ret == PROPERTY_FOUND) {
|
|
|
|
if (udevStrToLong_ull(udev_value, NULL, 0, value) != 0) {
|
|
|
|
ret = PROPERTY_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(udev_value);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int udevGenerateDeviceName(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def,
|
|
|
|
const char *s)
|
|
|
|
{
|
|
|
|
int ret = 0, i = 0;
|
|
|
|
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
|
|
|
|
|
|
|
virBufferVSprintf(&buf, "%s_%s",
|
|
|
|
udev_device_get_subsystem(device),
|
|
|
|
udev_device_get_sysname(device));
|
|
|
|
|
|
|
|
if (s != NULL) {
|
|
|
|
virBufferVSprintf(&buf, "_%s", s);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virBufferError(&buf)) {
|
2009-12-09 23:00:50 +00:00
|
|
|
virBufferFreeAndReset(&buf);
|
2009-11-12 21:48:24 +00:00
|
|
|
VIR_ERROR("Buffer error when generating device name for device "
|
|
|
|
"with sysname '%s'\n", udev_device_get_sysname(device));
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
def->name = virBufferContentAndReset(&buf);
|
|
|
|
|
|
|
|
for (i = 0; i < strlen(def->name) ; i++) {
|
|
|
|
if (!(c_isalnum(*(def->name + i)))) {
|
|
|
|
*(def->name + i) = '_';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void udevLogFunction(struct udev *udev ATTRIBUTE_UNUSED,
|
|
|
|
int priority ATTRIBUTE_UNUSED,
|
|
|
|
const char *file,
|
|
|
|
int line,
|
|
|
|
const char *fn,
|
|
|
|
const char *fmt,
|
|
|
|
va_list args)
|
|
|
|
{
|
|
|
|
VIR_ERROR_INT(file, fn, line, fmt, args);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-12 22:22:00 +00:00
|
|
|
static int udevTranslatePCIIds(unsigned int vendor,
|
|
|
|
unsigned int product,
|
|
|
|
char **vendor_string,
|
|
|
|
char **product_string)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
struct pci_id_match m;
|
|
|
|
const char *vendor_name = NULL, *device_name = NULL;
|
|
|
|
|
|
|
|
if (pci_system_init() != 0) {
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_ERROR0("Failed to initialize libpciaccess");
|
2009-11-12 22:22:00 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
|
|
|
&vendor_name,
|
|
|
|
&device_name,
|
|
|
|
NULL,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
if (vendor_name != NULL) {
|
|
|
|
*vendor_string = strdup(vendor_name);
|
|
|
|
if (*vendor_string == NULL) {
|
|
|
|
virReportOOMError(NULL);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (device_name != NULL) {
|
|
|
|
*product_string = strdup(device_name);
|
|
|
|
if (*product_string == NULL) {
|
|
|
|
virReportOOMError(NULL);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* pci_system_cleanup returns void */
|
|
|
|
pci_system_cleanup();
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-12 21:48:24 +00:00
|
|
|
static int udevProcessPCI(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def)
|
|
|
|
{
|
|
|
|
const char *devpath = NULL;
|
|
|
|
union _virNodeDevCapData *data = &def->caps->data;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
devpath = udev_device_get_devpath(device);
|
|
|
|
|
|
|
|
if (udevGetUintProperty(device,
|
|
|
|
"PCI_CLASS",
|
|
|
|
&data->pci_dev.class,
|
|
|
|
16) == PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *p = strrchr(devpath, '/');
|
|
|
|
|
|
|
|
if ((p == NULL) || (udevStrToLong_ui(p+1,
|
|
|
|
&p,
|
|
|
|
16,
|
|
|
|
&data->pci_dev.domain) == -1)) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((p == NULL) || (udevStrToLong_ui(p+1,
|
|
|
|
&p,
|
|
|
|
16,
|
|
|
|
&data->pci_dev.bus) == -1)) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((p == NULL) || (udevStrToLong_ui(p+1,
|
|
|
|
&p,
|
|
|
|
16,
|
|
|
|
&data->pci_dev.slot) == -1)) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((p == NULL) || (udevStrToLong_ui(p+1,
|
|
|
|
&p,
|
|
|
|
16,
|
|
|
|
&data->pci_dev.function) == -1)) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevGetUintSysfsAttr(device,
|
|
|
|
"vendor",
|
|
|
|
&data->pci_dev.vendor,
|
2010-01-12 18:39:46 +00:00
|
|
|
16) == PROPERTY_ERROR) {
|
2009-11-12 21:48:24 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevGetUintSysfsAttr(device,
|
|
|
|
"device",
|
|
|
|
&data->pci_dev.product,
|
2010-01-12 18:39:46 +00:00
|
|
|
16) == PROPERTY_ERROR) {
|
2009-11-12 21:48:24 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-11-12 22:22:00 +00:00
|
|
|
if (udevTranslatePCIIds(data->pci_dev.vendor,
|
|
|
|
data->pci_dev.product,
|
|
|
|
&data->pci_dev.vendor_name,
|
|
|
|
&data->pci_dev.product_name) != 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
2009-11-12 21:48:24 +00:00
|
|
|
|
|
|
|
if (udevGenerateDeviceName(device, def, NULL) != 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int udevProcessUSBDevice(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def)
|
|
|
|
{
|
|
|
|
union _virNodeDevCapData *data = &def->caps->data;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (udevGetUintProperty(device,
|
|
|
|
"BUSNUM",
|
|
|
|
&data->usb_dev.bus,
|
|
|
|
0) == PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevGetUintProperty(device,
|
|
|
|
"DEVNUM",
|
|
|
|
&data->usb_dev.device,
|
|
|
|
0) == PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevGetUintProperty(device,
|
|
|
|
"ID_VENDOR_ID",
|
|
|
|
&data->usb_dev.vendor,
|
|
|
|
16) == PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevGetStringSysfsAttr(device,
|
|
|
|
"manufacturer",
|
|
|
|
&data->usb_dev.vendor_name) == PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevGetUintProperty(device,
|
|
|
|
"ID_MODEL_ID",
|
|
|
|
&data->usb_dev.product,
|
2010-01-12 18:39:46 +00:00
|
|
|
16) == PROPERTY_ERROR) {
|
2009-11-12 21:48:24 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevGetStringSysfsAttr(device,
|
|
|
|
"product",
|
|
|
|
&data->usb_dev.product_name) == PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevGenerateDeviceName(device, def, NULL) != 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* XXX Is 10 the correct base for the Number/Class/SubClass/Protocol
|
|
|
|
* conversions? */
|
|
|
|
static int udevProcessUSBInterface(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
union _virNodeDevCapData *data = &def->caps->data;
|
|
|
|
|
|
|
|
if (udevGetUintSysfsAttr(device,
|
|
|
|
"bInterfaceNumber",
|
|
|
|
&data->usb_if.number,
|
|
|
|
10) == PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevGetUintSysfsAttr(device,
|
|
|
|
"bInterfaceClass",
|
|
|
|
&data->usb_if._class,
|
|
|
|
10) == PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevGetUintSysfsAttr(device,
|
|
|
|
"bInterfaceSubClass",
|
|
|
|
&data->usb_if.subclass,
|
|
|
|
10) == PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevGetUintSysfsAttr(device,
|
|
|
|
"bInterfaceProtocol",
|
|
|
|
&data->usb_if.protocol,
|
|
|
|
10) == PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevGenerateDeviceName(device, def, NULL) != 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int udevProcessNetworkInterface(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
union _virNodeDevCapData *data = &def->caps->data;
|
|
|
|
|
|
|
|
if (udevGetStringProperty(device,
|
|
|
|
"INTERFACE",
|
|
|
|
&data->net.ifname) == PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevGetStringSysfsAttr(device,
|
|
|
|
"address",
|
|
|
|
&data->net.address) == PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevGetUintSysfsAttr(device,
|
|
|
|
"addr_len",
|
|
|
|
&data->net.address_len,
|
|
|
|
0) == PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevGenerateDeviceName(device, def, data->net.address) != 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int udevProcessSCSIHost(struct udev_device *device ATTRIBUTE_UNUSED,
|
|
|
|
virNodeDeviceDefPtr def)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
union _virNodeDevCapData *data = &def->caps->data;
|
|
|
|
char *filename = NULL;
|
|
|
|
|
|
|
|
filename = basename(def->sysfs_path);
|
|
|
|
|
|
|
|
if (!STRPREFIX(filename, "host")) {
|
|
|
|
VIR_ERROR("SCSI host found, but its udev name '%s' does "
|
|
|
|
"not begin with 'host'\n", filename);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevStrToLong_ui(filename + strlen("host"),
|
|
|
|
NULL,
|
|
|
|
0,
|
|
|
|
&data->scsi_host.host) == -1) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
check_fc_host(&def->caps->data);
|
|
|
|
check_vport_capable(&def->caps->data);
|
|
|
|
|
|
|
|
if (udevGenerateDeviceName(device, def, NULL) != 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-12 22:03:47 +00:00
|
|
|
static int udevProcessSCSITarget(struct udev_device *device ATTRIBUTE_UNUSED,
|
|
|
|
virNodeDeviceDefPtr def)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
const char *sysname = NULL;
|
|
|
|
union _virNodeDevCapData *data = &def->caps->data;
|
|
|
|
|
|
|
|
sysname = udev_device_get_sysname(device);
|
|
|
|
|
|
|
|
data->scsi_target.name = strdup(sysname);
|
|
|
|
if (data->scsi_target.name == NULL) {
|
|
|
|
virReportOOMError(NULL);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevGenerateDeviceName(device, def, NULL) != 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-12 21:48:24 +00:00
|
|
|
static int udevGetSCSIType(unsigned int type, char **typestring)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
int foundtype = 1;
|
|
|
|
|
|
|
|
*typestring = NULL;
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case TYPE_DISK:
|
|
|
|
*typestring = strdup("disk");
|
|
|
|
break;
|
|
|
|
case TYPE_TAPE:
|
|
|
|
*typestring = strdup("tape");
|
|
|
|
break;
|
|
|
|
case TYPE_PROCESSOR:
|
|
|
|
*typestring = strdup("processor");
|
|
|
|
break;
|
|
|
|
case TYPE_WORM:
|
|
|
|
*typestring = strdup("worm");
|
|
|
|
break;
|
|
|
|
case TYPE_ROM:
|
|
|
|
*typestring = strdup("cdrom");
|
|
|
|
break;
|
|
|
|
case TYPE_SCANNER:
|
|
|
|
*typestring = strdup("scanner");
|
|
|
|
break;
|
|
|
|
case TYPE_MOD:
|
|
|
|
*typestring = strdup("mod");
|
|
|
|
break;
|
|
|
|
case TYPE_MEDIUM_CHANGER:
|
|
|
|
*typestring = strdup("changer");
|
|
|
|
break;
|
|
|
|
case TYPE_ENCLOSURE:
|
|
|
|
*typestring = strdup("enclosure");
|
|
|
|
break;
|
|
|
|
case TYPE_NO_LUN:
|
|
|
|
default:
|
|
|
|
foundtype = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*typestring == NULL) {
|
|
|
|
if (foundtype == 1) {
|
|
|
|
ret = -1;
|
|
|
|
virReportOOMError(NULL);
|
|
|
|
} else {
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_ERROR("Failed to find SCSI device type %d", type);
|
2009-11-12 21:48:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int udevProcessSCSIDevice(struct udev_device *device ATTRIBUTE_UNUSED,
|
|
|
|
virNodeDeviceDefPtr def)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
unsigned int tmp = 0;
|
|
|
|
union _virNodeDevCapData *data = &def->caps->data;
|
|
|
|
char *filename = NULL, *p = NULL;
|
|
|
|
|
|
|
|
filename = basename(def->sysfs_path);
|
|
|
|
|
|
|
|
if (udevStrToLong_ui(filename, &p, 10, &data->scsi.host) == -1) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((p == NULL) || (udevStrToLong_ui(p+1,
|
|
|
|
&p,
|
|
|
|
10,
|
|
|
|
&data->scsi.bus) == -1)) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((p == NULL) || (udevStrToLong_ui(p+1,
|
|
|
|
&p,
|
|
|
|
10,
|
|
|
|
&data->scsi.target) == -1)) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((p == NULL) || (udevStrToLong_ui(p+1,
|
|
|
|
&p,
|
|
|
|
10,
|
|
|
|
&data->scsi.lun) == -1)) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (udevGetUintSysfsAttr(device, "type", &tmp, 0)) {
|
|
|
|
case PROPERTY_FOUND:
|
|
|
|
if (udevGetSCSIType(tmp, &data->scsi.type) == -1) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case PROPERTY_MISSING:
|
|
|
|
break; /* No type is not an error */
|
|
|
|
case PROPERTY_ERROR:
|
|
|
|
default:
|
|
|
|
goto out;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevGenerateDeviceName(device, def, NULL) != 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (ret != 0) {
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_ERROR("Failed to process SCSI device with sysfs path '%s'",
|
2009-11-12 21:48:24 +00:00
|
|
|
def->sysfs_path);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int udevProcessDisk(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def)
|
|
|
|
{
|
|
|
|
union _virNodeDevCapData *data = &def->caps->data;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
data->storage.drive_type = strdup("disk");
|
|
|
|
if (data->storage.drive_type == NULL) {
|
|
|
|
virReportOOMError(NULL);
|
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevGetUint64SysfsAttr(device,
|
|
|
|
"size",
|
|
|
|
&data->storage.num_blocks) == PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevGetUint64SysfsAttr(device,
|
|
|
|
"queue/logical_block_size",
|
|
|
|
&data->storage.logical_block_size)
|
|
|
|
== PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
data->storage.size = data->storage.num_blocks *
|
|
|
|
data->storage.logical_block_size;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-01-12 19:56:56 +00:00
|
|
|
static int udevProcessRemoveableMedia(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def,
|
|
|
|
int has_media)
|
2009-11-12 21:48:24 +00:00
|
|
|
{
|
|
|
|
union _virNodeDevCapData *data = &def->caps->data;
|
|
|
|
int tmp_int = 0, ret = 0;
|
|
|
|
|
|
|
|
if ((udevGetIntSysfsAttr(device, "removable", &tmp_int, 0) == PROPERTY_FOUND) &&
|
|
|
|
(tmp_int == 1)) {
|
|
|
|
def->caps->data.storage.flags |= VIR_NODE_DEV_CAP_STORAGE_REMOVABLE;
|
|
|
|
}
|
|
|
|
|
2010-01-12 19:56:56 +00:00
|
|
|
if (has_media) {
|
2009-11-12 21:48:24 +00:00
|
|
|
|
|
|
|
def->caps->data.storage.flags |=
|
|
|
|
VIR_NODE_DEV_CAP_STORAGE_REMOVABLE_MEDIA_AVAILABLE;
|
|
|
|
|
2009-12-14 13:58:23 +00:00
|
|
|
if (udevGetStringProperty(device, "ID_FS_LABEL",
|
|
|
|
&data->storage.media_label) == PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-11-12 21:48:24 +00:00
|
|
|
if (udevGetUint64SysfsAttr(device,
|
|
|
|
"size",
|
|
|
|
&data->storage.num_blocks) == PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevGetUint64SysfsAttr(device,
|
|
|
|
"queue/logical_block_size",
|
|
|
|
&data->storage.logical_block_size) == PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-01-12 19:56:56 +00:00
|
|
|
static int udevProcessCDROM(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
int tmp_int = 0;
|
|
|
|
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);
|
|
|
|
def->caps->data.storage.drive_type = strdup("cdrom");
|
|
|
|
if (def->caps->data.storage.drive_type == NULL) {
|
|
|
|
virReportOOMError(NULL);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((udevGetIntProperty(device, "ID_CDROM_MEDIA",
|
|
|
|
&tmp_int, 0) == PROPERTY_FOUND))
|
|
|
|
has_media = tmp_int;
|
|
|
|
|
|
|
|
ret = udevProcessRemoveableMedia(device, def, has_media);
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int udevProcessFloppy(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def)
|
|
|
|
{
|
|
|
|
int tmp_int = 0;
|
|
|
|
int has_media = 0;
|
|
|
|
char *tmp_str = NULL;
|
|
|
|
|
|
|
|
if ((udevGetIntProperty(device, "DKD_MEDIA_AVAILABLE",
|
|
|
|
&tmp_int, 0) == PROPERTY_FOUND))
|
|
|
|
/* USB floppy */
|
|
|
|
has_media = tmp_int;
|
|
|
|
else if (udevGetStringProperty(device, "ID_FS_LABEL",
|
|
|
|
&tmp_str) == PROPERTY_FOUND) {
|
|
|
|
/* Legacy floppy */
|
|
|
|
has_media = 1;
|
|
|
|
VIR_FREE(tmp_str);
|
|
|
|
}
|
|
|
|
|
|
|
|
return udevProcessRemoveableMedia(device, def, has_media);
|
|
|
|
}
|
2009-11-12 21:48:24 +00: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. */
|
|
|
|
static int udevKludgeStorageType(virNodeDeviceDefPtr def)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
VIR_INFO("Could not find definitive storage type for device "
|
|
|
|
"with sysfs path '%s', trying to guess it\n",
|
|
|
|
def->sysfs_path);
|
|
|
|
|
|
|
|
if (STRPREFIX(def->caps->data.storage.block, "/dev/vd")) {
|
|
|
|
/* virtio disk */
|
|
|
|
def->caps->data.storage.drive_type = strdup("disk");
|
|
|
|
if (def->caps->data.storage.drive_type != NULL) {
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret != 0) {
|
|
|
|
VIR_INFO("Could not determine storage type for device "
|
|
|
|
"with sysfs path '%s'\n", def->sysfs_path);
|
|
|
|
} else {
|
|
|
|
VIR_DEBUG("Found storage type '%s' for device "
|
|
|
|
"with sysfs path '%s'\n",
|
|
|
|
def->caps->data.storage.drive_type,
|
|
|
|
def->sysfs_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void udevStripSpaces(char *s)
|
|
|
|
{
|
|
|
|
if (s == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (virFileStripSuffix(s, " ")) {
|
|
|
|
/* do nothing */
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int udevProcessStorage(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def)
|
|
|
|
{
|
|
|
|
union _virNodeDevCapData *data = &def->caps->data;
|
|
|
|
int ret = -1;
|
2009-12-07 18:00:11 +00:00
|
|
|
const char* devnode;
|
2009-11-12 21:48:24 +00:00
|
|
|
|
2009-12-07 18:00:11 +00:00
|
|
|
devnode = udev_device_get_devnode(device);
|
|
|
|
if(!devnode) {
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_DEBUG("No devnode for '%s'", udev_device_get_devpath(device));
|
2009-12-07 18:00:11 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
data->storage.block = strdup(devnode);
|
2010-01-12 20:01:21 +00:00
|
|
|
|
2009-11-12 21:48:24 +00:00
|
|
|
if (udevGetStringProperty(device,
|
|
|
|
"ID_BUS",
|
|
|
|
&data->storage.bus) == PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (udevGetStringProperty(device,
|
|
|
|
"ID_SERIAL",
|
|
|
|
&data->storage.serial) == PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (udevGetStringSysfsAttr(device,
|
|
|
|
"device/vendor",
|
|
|
|
&data->storage.vendor) == PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
udevStripSpaces(def->caps->data.storage.vendor);
|
|
|
|
if (udevGetStringSysfsAttr(device,
|
|
|
|
"device/model",
|
|
|
|
&data->storage.model) == PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
udevStripSpaces(def->caps->data.storage.model);
|
|
|
|
/* 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. */
|
|
|
|
|
|
|
|
if (udevGetStringProperty(device,
|
|
|
|
"ID_TYPE",
|
|
|
|
&data->storage.drive_type) != PROPERTY_FOUND) {
|
2010-01-12 19:56:56 +00:00
|
|
|
int tmp_int = 0;
|
|
|
|
|
|
|
|
/* All floppy drives have the ID_DRIVE_FLOPPY prop. This is
|
|
|
|
* needed since legacy floppies don't have a drive_type */
|
|
|
|
if ((udevGetIntProperty(device, "ID_DRIVE_FLOPPY",
|
|
|
|
&tmp_int, 0) == PROPERTY_FOUND) &&
|
|
|
|
(tmp_int == 1)) {
|
|
|
|
|
|
|
|
data->storage.drive_type = strdup("floppy");
|
|
|
|
if (!data->storage.drive_type)
|
|
|
|
goto out;
|
|
|
|
} else {
|
|
|
|
|
|
|
|
/* If udev doesn't have it, perhaps we can guess it. */
|
|
|
|
if (udevKludgeStorageType(def) != 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
2009-11-12 21:48:24 +00: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 19:56:56 +00:00
|
|
|
} else if (STREQ(def->caps->data.storage.drive_type, "floppy")) {
|
|
|
|
ret = udevProcessFloppy(device, def);
|
2009-11-12 21:48:24 +00:00
|
|
|
} else {
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_INFO("Unsupported storage type '%s'",
|
2009-11-12 21:48:24 +00:00
|
|
|
def->caps->data.storage.drive_type);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevGenerateDeviceName(device, def, data->storage.serial) != 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int udevGetDeviceType(struct udev_device *device,
|
|
|
|
enum virNodeDevCapType *type)
|
|
|
|
{
|
|
|
|
const char *devtype = NULL;
|
|
|
|
char *tmp_string = NULL;
|
|
|
|
unsigned int tmp = 0;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
devtype = udev_device_get_devtype(device);
|
|
|
|
|
|
|
|
if (devtype != NULL && STREQ(devtype, "usb_device")) {
|
|
|
|
*type = VIR_NODE_DEV_CAP_USB_DEV;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (devtype != NULL && STREQ(devtype, "usb_interface")) {
|
|
|
|
*type = VIR_NODE_DEV_CAP_USB_INTERFACE;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (devtype != NULL && STREQ(devtype, "scsi_host")) {
|
|
|
|
*type = VIR_NODE_DEV_CAP_SCSI_HOST;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-11-12 22:03:47 +00:00
|
|
|
if (devtype != NULL && STREQ(devtype, "scsi_target")) {
|
|
|
|
*type = VIR_NODE_DEV_CAP_SCSI_TARGET;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-11-12 21:48:24 +00:00
|
|
|
if (devtype != NULL && STREQ(devtype, "scsi_device")) {
|
|
|
|
*type = VIR_NODE_DEV_CAP_SCSI;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (devtype != NULL && STREQ(devtype, "disk")) {
|
|
|
|
*type = VIR_NODE_DEV_CAP_STORAGE;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevGetUintProperty(device, "PCI_CLASS", &tmp, 16) == PROPERTY_FOUND) {
|
|
|
|
*type = VIR_NODE_DEV_CAP_PCI_DEV;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* It does not appear that network interfaces set the device type
|
|
|
|
* property. */
|
|
|
|
if (devtype == NULL &&
|
|
|
|
udevGetStringProperty(device,
|
|
|
|
"INTERFACE",
|
|
|
|
&tmp_string) == PROPERTY_FOUND) {
|
|
|
|
VIR_FREE(tmp_string);
|
|
|
|
*type = VIR_NODE_DEV_CAP_NET;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_INFO("Could not determine device type for device "
|
|
|
|
"with sysfs path '%s'\n",
|
|
|
|
udev_device_get_sysname(device));
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int udevGetDeviceDetails(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
switch (def->caps->type) {
|
|
|
|
case VIR_NODE_DEV_CAP_SYSTEM:
|
|
|
|
/* There's no libudev equivalent of system, so ignore it. */
|
|
|
|
break;
|
|
|
|
case VIR_NODE_DEV_CAP_PCI_DEV:
|
|
|
|
ret = udevProcessPCI(device, def);
|
|
|
|
break;
|
|
|
|
case VIR_NODE_DEV_CAP_USB_DEV:
|
|
|
|
ret = udevProcessUSBDevice(device, def);
|
|
|
|
break;
|
|
|
|
case VIR_NODE_DEV_CAP_USB_INTERFACE:
|
|
|
|
ret = udevProcessUSBInterface(device, def);
|
|
|
|
break;
|
|
|
|
case VIR_NODE_DEV_CAP_NET:
|
|
|
|
ret = udevProcessNetworkInterface(device, def);
|
|
|
|
break;
|
|
|
|
case VIR_NODE_DEV_CAP_SCSI_HOST:
|
|
|
|
ret = udevProcessSCSIHost(device, def);
|
|
|
|
break;
|
2009-11-12 22:03:47 +00:00
|
|
|
case VIR_NODE_DEV_CAP_SCSI_TARGET:
|
|
|
|
ret = udevProcessSCSITarget(device, def);
|
|
|
|
break;
|
2009-11-12 21:48:24 +00:00
|
|
|
case VIR_NODE_DEV_CAP_SCSI:
|
|
|
|
ret = udevProcessSCSIDevice(device, def);
|
|
|
|
break;
|
|
|
|
case VIR_NODE_DEV_CAP_STORAGE:
|
|
|
|
ret = udevProcessStorage(device, def);
|
|
|
|
break;
|
|
|
|
default:
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_ERROR("Unknown device type %d", def->caps->type);
|
2009-11-12 21:48:24 +00:00
|
|
|
ret = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int udevRemoveOneDevice(struct udev_device *device)
|
|
|
|
{
|
|
|
|
virNodeDeviceObjPtr dev = NULL;
|
|
|
|
const char *name = NULL;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
name = udev_device_get_syspath(device);
|
|
|
|
dev = virNodeDeviceFindBySysfsPath(&driverState->devs, name);
|
|
|
|
if (dev != NULL) {
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_DEBUG("Removing device '%s' with sysfs path '%s'",
|
2009-11-12 21:48:24 +00:00
|
|
|
dev->def->name, name);
|
|
|
|
virNodeDeviceObjRemove(&driverState->devs, dev);
|
|
|
|
} else {
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_INFO("Failed to find device to remove that has udev name '%s'",
|
2009-11-12 21:48:24 +00:00
|
|
|
name);
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int udevSetParent(struct udev_device *device,
|
|
|
|
virNodeDeviceDefPtr def)
|
|
|
|
{
|
|
|
|
struct udev_device *parent_device = NULL;
|
|
|
|
const char *parent_sysfs_path = NULL;
|
|
|
|
virNodeDeviceObjPtr dev = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
parent_device = udev_device_get_parent(device);
|
|
|
|
if (parent_device == NULL) {
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_INFO("Could not find udev parent for device with sysfs path '%s'",
|
2009-11-12 21:48:24 +00:00
|
|
|
udev_device_get_syspath(device));
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
parent_sysfs_path = udev_device_get_syspath(parent_device);
|
|
|
|
if (parent_sysfs_path == NULL) {
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_INFO("Could not get syspath for parent of '%s'",
|
2009-11-12 21:48:24 +00:00
|
|
|
udev_device_get_syspath(device));
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
def->parent_sysfs_path = strdup(parent_sysfs_path);
|
|
|
|
if (def->parent_sysfs_path == NULL) {
|
|
|
|
virReportOOMError(NULL);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev = virNodeDeviceFindBySysfsPath(&driverState->devs, parent_sysfs_path);
|
|
|
|
if (dev == NULL) {
|
|
|
|
def->parent = strdup("computer");
|
|
|
|
} else {
|
|
|
|
def->parent = strdup(dev->def->name);
|
|
|
|
virNodeDeviceObjUnlock(dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (def->parent == NULL) {
|
|
|
|
virReportOOMError(NULL);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int udevAddOneDevice(struct udev_device *device)
|
|
|
|
{
|
|
|
|
virNodeDeviceDefPtr def = NULL;
|
|
|
|
virNodeDeviceObjPtr dev = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (VIR_ALLOC(def) != 0) {
|
|
|
|
virReportOOMError(NULL);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
def->sysfs_path = strdup(udev_device_get_syspath(device));
|
|
|
|
if (udevGetStringProperty(device,
|
|
|
|
"DRIVER",
|
|
|
|
&def->driver) == PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_ALLOC(def->caps) != 0) {
|
|
|
|
virReportOOMError(NULL);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevGetDeviceType(device, &def->caps->type) != 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevGetDeviceDetails(device, def) != 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevSetParent(device, def) != 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev = virNodeDeviceAssignDef(NULL, &driverState->devs, def);
|
|
|
|
if (dev == NULL) {
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_ERROR("Failed to create device for '%s'", def->name);
|
2009-11-12 21:48:24 +00:00
|
|
|
virNodeDeviceDefFree(def);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
virNodeDeviceObjUnlock(dev);
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int udevProcessDeviceListEntry(struct udev *udev,
|
|
|
|
struct udev_list_entry *list_entry)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
if (device != NULL) {
|
|
|
|
if (udevAddOneDevice(device) != 0) {
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_INFO("Failed to create node device for udev device '%s'",
|
2009-11-12 21:48:24 +00:00
|
|
|
name);
|
|
|
|
}
|
|
|
|
udev_device_unref(device);
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int udevEnumerateDevices(struct udev *udev)
|
|
|
|
{
|
|
|
|
struct udev_enumerate *udev_enumerate = NULL;
|
|
|
|
struct udev_list_entry *list_entry = NULL;
|
|
|
|
const char *name = NULL;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
udev_enumerate = udev_enumerate_new(udev);
|
|
|
|
|
|
|
|
ret = udev_enumerate_scan_devices(udev_enumerate);
|
|
|
|
if (0 != ret) {
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_ERROR("udev scan devices returned %d", ret);
|
2009-11-12 21:48:24 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
udev_list_entry_foreach(list_entry,
|
|
|
|
udev_enumerate_get_list_entry(udev_enumerate)) {
|
|
|
|
|
|
|
|
udevProcessDeviceListEntry(udev, list_entry);
|
|
|
|
name = udev_list_entry_get_name(list_entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
udev_enumerate_unref(udev_enumerate);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int udevDeviceMonitorShutdown(void)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
2010-01-26 01:58:37 +00:00
|
|
|
udevPrivate *priv = NULL;
|
2009-11-12 21:48:24 +00:00
|
|
|
struct udev_monitor *udev_monitor = NULL;
|
|
|
|
struct udev *udev = NULL;
|
|
|
|
|
|
|
|
if (driverState) {
|
|
|
|
nodeDeviceLock(driverState);
|
2010-01-26 01:58:37 +00:00
|
|
|
|
|
|
|
priv = driverState->privateData;
|
|
|
|
|
|
|
|
if (priv->watch != -1)
|
|
|
|
virEventRemoveHandle(priv->watch);
|
|
|
|
|
2009-11-12 21:48:24 +00:00
|
|
|
udev_monitor = DRV_STATE_UDEV_MONITOR(driverState);
|
|
|
|
|
|
|
|
if (udev_monitor != NULL) {
|
|
|
|
udev = udev_monitor_get_udev(udev_monitor);
|
|
|
|
udev_monitor_unref(udev_monitor);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udev != NULL) {
|
|
|
|
udev_unref(udev);
|
|
|
|
}
|
|
|
|
|
|
|
|
virNodeDeviceObjListFree(&driverState->devs);
|
|
|
|
nodeDeviceUnlock(driverState);
|
|
|
|
virMutexDestroy(&driverState->lock);
|
|
|
|
VIR_FREE(driverState);
|
2010-01-26 01:58:37 +00:00
|
|
|
VIR_FREE(priv);
|
2009-11-12 21:48:24 +00:00
|
|
|
} else {
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void udevEventHandleCallback(int watch ATTRIBUTE_UNUSED,
|
|
|
|
int fd,
|
|
|
|
int events ATTRIBUTE_UNUSED,
|
|
|
|
void *data ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
struct udev_device *device = NULL;
|
|
|
|
struct udev_monitor *udev_monitor = DRV_STATE_UDEV_MONITOR(driverState);
|
|
|
|
const char *action = NULL;
|
|
|
|
int udev_fd = -1;
|
|
|
|
|
|
|
|
udev_fd = udev_monitor_get_fd(udev_monitor);
|
|
|
|
if (fd != udev_fd) {
|
|
|
|
VIR_ERROR("File descriptor returned by udev %d does not "
|
|
|
|
"match node device file descriptor %d", fd, udev_fd);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
device = udev_monitor_receive_device(udev_monitor);
|
|
|
|
if (device == NULL) {
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_ERROR0("udev_monitor_receive_device returned NULL");
|
2009-11-12 21:48:24 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
action = udev_device_get_action(device);
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_DEBUG("udev action: '%s'", action);
|
2009-11-12 21:48:24 +00:00
|
|
|
|
|
|
|
if (STREQ(action, "add") || STREQ(action, "change")) {
|
|
|
|
udevAddOneDevice(device);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (STREQ(action, "remove")) {
|
|
|
|
udevRemoveOneDevice(device);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int udevSetupSystemDev(void)
|
|
|
|
{
|
|
|
|
virNodeDeviceDefPtr def = NULL;
|
|
|
|
virNodeDeviceObjPtr dev = NULL;
|
|
|
|
struct udev *udev = NULL;
|
|
|
|
struct udev_device *device = NULL;
|
|
|
|
union _virNodeDevCapData *data = NULL;
|
|
|
|
char *tmp = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (VIR_ALLOC(def) != 0) {
|
|
|
|
virReportOOMError(NULL);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
def->name = strdup("computer");
|
|
|
|
if (def->name == NULL) {
|
|
|
|
virReportOOMError(NULL);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_ALLOC(def->caps) != 0) {
|
|
|
|
virReportOOMError(NULL);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
udev = udev_monitor_get_udev(DRV_STATE_UDEV_MONITOR(driverState));
|
|
|
|
device = udev_device_new_from_syspath(udev, DMI_DEVPATH);
|
|
|
|
if (device == NULL) {
|
2010-01-07 09:13:51 +00:00
|
|
|
device = udev_device_new_from_syspath(udev, DMI_DEVPATH_FALLBACK);
|
|
|
|
if (device == NULL) {
|
2010-01-21 14:45:44 +00:00
|
|
|
VIR_ERROR("Failed to get udev device for syspath '%s' or '%s'",
|
|
|
|
DMI_DEVPATH, DMI_DEVPATH_FALLBACK);
|
2010-01-07 09:13:51 +00:00
|
|
|
goto out;
|
|
|
|
}
|
2009-11-12 21:48:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
data = &def->caps->data;
|
|
|
|
|
|
|
|
if (udevGetStringSysfsAttr(device,
|
|
|
|
"product_name",
|
|
|
|
&data->system.product_name) == PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (udevGetStringSysfsAttr(device,
|
|
|
|
"sys_vendor",
|
|
|
|
&data->system.hardware.vendor_name)
|
|
|
|
== PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (udevGetStringSysfsAttr(device,
|
|
|
|
"product_version",
|
|
|
|
&data->system.hardware.version)
|
|
|
|
== PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (udevGetStringSysfsAttr(device,
|
|
|
|
"product_serial",
|
|
|
|
&data->system.hardware.serial)
|
|
|
|
== PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udevGetStringSysfsAttr(device,
|
|
|
|
"product_uuid",
|
|
|
|
&tmp) == PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
virUUIDParse(tmp, def->caps->data.system.hardware.uuid);
|
|
|
|
VIR_FREE(tmp);
|
|
|
|
|
|
|
|
if (udevGetStringSysfsAttr(device,
|
|
|
|
"bios_vendor",
|
|
|
|
&data->system.firmware.vendor_name)
|
|
|
|
== PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (udevGetStringSysfsAttr(device,
|
|
|
|
"bios_version",
|
|
|
|
&data->system.firmware.version)
|
|
|
|
== PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (udevGetStringSysfsAttr(device,
|
|
|
|
"bios_date",
|
|
|
|
&data->system.firmware.release_date)
|
|
|
|
== PROPERTY_ERROR) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
udev_device_unref(device);
|
|
|
|
|
|
|
|
dev = virNodeDeviceAssignDef(NULL, &driverState->devs, def);
|
|
|
|
if (dev == NULL) {
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_ERROR("Failed to create device for '%s'", def->name);
|
2009-11-12 21:48:24 +00:00
|
|
|
virNodeDeviceDefFree(def);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
virNodeDeviceObjUnlock(dev);
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int udevDeviceMonitorStartup(int privileged ATTRIBUTE_UNUSED)
|
|
|
|
{
|
2010-01-26 01:58:37 +00:00
|
|
|
udevPrivate *priv = NULL;
|
2009-11-12 21:48:24 +00:00
|
|
|
struct udev *udev = NULL;
|
|
|
|
int ret = 0;
|
|
|
|
|
2010-01-26 01:58:37 +00:00
|
|
|
if (VIR_ALLOC(priv) < 0) {
|
|
|
|
virReportOOMError(NULL);
|
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
priv->watch = -1;
|
|
|
|
|
2009-11-12 21:48:24 +00:00
|
|
|
if (VIR_ALLOC(driverState) < 0) {
|
|
|
|
virReportOOMError(NULL);
|
2010-01-26 01:58:37 +00:00
|
|
|
VIR_FREE(priv);
|
2009-11-12 21:48:24 +00:00
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virMutexInit(&driverState->lock) < 0) {
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_ERROR0("Failed to initialize mutex for driverState");
|
2010-01-26 01:58:37 +00:00
|
|
|
VIR_FREE(priv);
|
2009-11-12 21:48:24 +00:00
|
|
|
VIR_FREE(driverState);
|
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
nodeDeviceLock(driverState);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* http://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/libudev-udev.html#udev-new
|
|
|
|
*
|
|
|
|
* indicates no return value other than success, so we don't check
|
|
|
|
* its return value.
|
|
|
|
*/
|
|
|
|
udev = udev_new();
|
|
|
|
udev_set_log_fn(udev, udevLogFunction);
|
|
|
|
|
2010-01-26 01:58:37 +00:00
|
|
|
priv->udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
|
|
|
|
if (priv->udev_monitor == NULL) {
|
|
|
|
VIR_FREE(priv);
|
|
|
|
nodeDeviceUnlock(driverState);
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_ERROR0("udev_monitor_new_from_netlink returned NULL");
|
2009-11-12 21:48:24 +00:00
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-01-26 01:58:37 +00:00
|
|
|
udev_monitor_enable_receiving(priv->udev_monitor);
|
2009-11-12 21:48:24 +00:00
|
|
|
|
|
|
|
/* udev can be retrieved from udev_monitor */
|
2010-01-26 01:58:37 +00:00
|
|
|
driverState->privateData = priv;
|
2009-11-12 21:48:24 +00: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 01:58:37 +00:00
|
|
|
priv->watch = virEventAddHandle(udev_monitor_get_fd(priv->udev_monitor),
|
|
|
|
VIR_EVENT_HANDLE_READABLE,
|
|
|
|
udevEventHandleCallback, NULL, NULL);
|
|
|
|
if (priv->watch == -1) {
|
|
|
|
nodeDeviceUnlock(driverState);
|
2009-11-12 21:48:24 +00:00
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-01-26 01:58:37 +00:00
|
|
|
nodeDeviceUnlock(driverState);
|
|
|
|
|
2009-11-12 21:48:24 +00:00
|
|
|
/* Create a fictional 'computer' device to root the device tree. */
|
|
|
|
if (udevSetupSystemDev() != 0) {
|
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Populate with known devices */
|
|
|
|
|
|
|
|
if (udevEnumerateDevices(udev) != 0) {
|
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (ret == -1) {
|
|
|
|
udevDeviceMonitorShutdown();
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int udevDeviceMonitorReload(void)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int udevDeviceMonitorActive(void)
|
|
|
|
{
|
|
|
|
/* Always ready to deal with a shutdown */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static virDrvOpenStatus udevNodeDrvOpen(virConnectPtr conn,
|
|
|
|
virConnectAuthPtr auth ATTRIBUTE_UNUSED,
|
|
|
|
int flags ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
if (driverState == NULL) {
|
|
|
|
return VIR_DRV_OPEN_DECLINED;
|
|
|
|
}
|
|
|
|
|
|
|
|
conn->devMonPrivateData = driverState;
|
|
|
|
|
|
|
|
return VIR_DRV_OPEN_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int udevNodeDrvClose(virConnectPtr conn)
|
|
|
|
{
|
|
|
|
conn->devMonPrivateData = NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static virDeviceMonitor udevDeviceMonitor = {
|
|
|
|
.name = "udevDeviceMonitor",
|
|
|
|
.open = udevNodeDrvOpen,
|
|
|
|
.close = udevNodeDrvClose,
|
|
|
|
};
|
|
|
|
|
|
|
|
static virStateDriver udevStateDriver = {
|
2010-01-26 02:13:08 +00:00
|
|
|
.name = "udev",
|
2009-11-12 21:48:24 +00:00
|
|
|
.initialize = udevDeviceMonitorStartup,
|
|
|
|
.cleanup = udevDeviceMonitorShutdown,
|
|
|
|
.reload = udevDeviceMonitorReload,
|
|
|
|
.active = udevDeviceMonitorActive,
|
|
|
|
};
|
|
|
|
|
|
|
|
int udevNodeRegister(void)
|
|
|
|
{
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_DEBUG0("Registering udev node device backend");
|
2009-11-12 21:48:24 +00:00
|
|
|
|
|
|
|
registerCommonNodeFuncs(&udevDeviceMonitor);
|
|
|
|
if (virRegisterDeviceMonitor(&udevDeviceMonitor) < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return virRegisterStateDriver(&udevStateDriver);
|
|
|
|
}
|