blob: 0cc8279aec2296b6ec365631d9bd0ff312868404 [file] [log] [blame]
/*
* Copyright (C) 2014 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License v.2.1.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "lib.h"
#include "device.h"
#ifdef UDEV_SYNC_SUPPORT
#include <libudev.h>
#endif
struct ext_registry_item {
const char *name;
struct dev_ext *(* dev_ext_get) (struct device *dev);
int (*dev_ext_release) (struct device *dev);
};
#define EXT_REGISTER(id,name) [id] = { #name, &_dev_ext_get_ ## name, &_dev_ext_release_ ## name }
/*
* DEV_EXT_NONE
*/
static struct dev_ext *_dev_ext_get_none(struct device *dev)
{
dev->ext.handle = NULL;
return &dev->ext;
}
static int _dev_ext_release_none(struct device *dev)
{
dev->ext.handle = NULL;
return 1;
}
/*
* DEV_EXT_UDEV
*/
static struct dev_ext *_dev_ext_get_udev(struct device *dev)
{
#ifdef UDEV_SYNC_SUPPORT
struct udev *udev;
struct udev_device *udev_device;
if (dev->ext.handle)
return &dev->ext;
if (!(udev = udev_get_library_context()))
return_NULL;
if (!(udev_device = udev_device_new_from_devnum(udev, 'b', dev->dev)))
return_NULL;
#ifdef HAVE_LIBUDEV_UDEV_DEVICE_GET_IS_INITIALIZED
if (!udev_device_get_is_initialized(udev_device)) {
/* Timeout or some other udev db inconsistency! */
log_error("Udev database has incomplete information about device %s.", dev_name(dev));
return NULL;
}
#endif
dev->ext.handle = (void *) udev_device;
return &dev->ext;
#else
return NULL;
#endif
}
static int _dev_ext_release_udev(struct device *dev)
{
#ifdef UDEV_SYNC_SUPPORT
if (!dev->ext.handle)
return 1;
/* udev_device_unref can't fail - it has no return value */
udev_device_unref((struct udev_device *) dev->ext.handle);
dev->ext.handle = NULL;
return 1;
#else
return 0;
#endif
}
static struct ext_registry_item _ext_registry[DEV_EXT_NUM] = {
EXT_REGISTER(DEV_EXT_NONE, none),
EXT_REGISTER(DEV_EXT_UDEV, udev)
};
const char *dev_ext_name(struct device *dev)
{
return _ext_registry[dev->ext.src].name;
}
static const char *_ext_attached_msg = "External handle attached to device";
struct dev_ext *dev_ext_get(struct device *dev)
{
struct dev_ext *ext;
void *handle_ptr;
handle_ptr = dev->ext.handle;
if (!(ext = _ext_registry[dev->ext.src].dev_ext_get(dev)))
log_error("Failed to get external handle for device %s [%s].",
dev_name(dev), dev_ext_name(dev));
else if (handle_ptr != dev->ext.handle)
log_debug_devs("%s %s [%s:%p]", _ext_attached_msg, dev_name(dev),
dev_ext_name(dev), dev->ext.handle);
return ext;
}
int dev_ext_release(struct device *dev)
{
int r;
void *handle_ptr;
if (!dev->ext.enabled ||
!dev->ext.handle)
return 1;
handle_ptr = dev->ext.handle;
if (!(r = _ext_registry[dev->ext.src].dev_ext_release(dev)))
log_error("Failed to release external handle for device %s [%s:%p].",
dev_name(dev), dev_ext_name(dev), dev->ext.handle);
else
log_debug_devs("External handle detached from device %s [%s:%p]",
dev_name(dev), dev_ext_name(dev), handle_ptr);
return r;
}
int dev_ext_enable(struct device *dev, dev_ext_t src)
{
if (dev->ext.enabled && (dev->ext.src != src) && !dev_ext_release(dev)) {
log_error("Failed to enable external handle for device %s [%s].",
dev_name(dev), _ext_registry[src].name);
return 0;
}
dev->ext.src = src;
dev->ext.enabled = 1;
return 1;
}
int dev_ext_disable(struct device *dev)
{
if (!dev->ext.enabled)
return 1;
if (!dev_ext_release(dev)) {
log_error("Failed to disable external handle for device %s [%s].",
dev_name(dev), dev_ext_name(dev));
return 0;
}
dev->ext.enabled = 0;
dev->ext.src = DEV_EXT_NONE;
return 1;
}