| /* |
| * 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; |
| } |