/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2017-2020 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include "block.h" #include "ibpi.h" #include "status.h" #include "sysfs.h" #include "udev.h" #include "utils.h" static struct udev_monitor *udev_monitor; static int _compare(const struct block_device *bd, const char *syspath) { if (!bd || !syspath) return 0; if (strcmp(bd->sysfs_path, syspath) == 0) { return 1; } else { struct block_device *bd_new; int ret; bd_new = block_device_init(sysfs_get_cntrl_devices(), syspath); if (!bd_new) return 0; ret = block_compare(bd, bd_new); block_device_fini(bd_new); return ret; } } static int create_udev_monitor(void) { int res; struct udev *udev = udev_new(); if (!udev) { log_error("Failed to create udev context instance."); return -1; } udev_monitor = udev_monitor_new_from_netlink(udev, "udev"); if (!udev_monitor) { log_error("Failed to create udev monitor object."); udev_unref(udev); return -1; } res = udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "block", "disk"); if (res < 0) { log_error("Failed to modify udev monitor filters."); stop_udev_monitor(); return -1; } res = udev_monitor_enable_receiving(udev_monitor); if (res < 0) { log_error("Failed to switch udev monitor to listening mode."); stop_udev_monitor(); return -1; } return udev_monitor_get_fd(udev_monitor); } void stop_udev_monitor(void) { if (udev_monitor) { struct udev *udev = udev_monitor_get_udev(udev_monitor); udev_monitor_unref(udev_monitor); if (udev) udev_unref(udev); } } int get_udev_monitor(void) { if (udev_monitor) return udev_monitor_get_fd(udev_monitor); return create_udev_monitor(); } static int _check_raid(const char *path) { char *t = strrchr(path, '/'); if (t == NULL) return 0; return strncmp(t + 1, "md", 2) == 0; } static enum udev_action _get_udev_action(const char *action) { enum udev_action ret = UDEV_ACTION_UNKNOWN; if (strncmp(action, "add", 3) == 0) ret = UDEV_ACTION_ADD; else if (strncmp(action, "remove", 6) == 0) ret = UDEV_ACTION_REMOVE; return ret; } static void _clear_raid_dev_info(struct block_device *block, char *raid_dev) { if (block->raid_dev && block->raid_dev->sysfs_path) { char *tmp = strrchr(block->raid_dev->sysfs_path, '/'); if (tmp == NULL) { log_debug("Device: %s have wrong raid_dev path: %s", block->sysfs_path, block->raid_dev->sysfs_path); return; } if (strcmp(raid_dev, tmp + 1) == 0) { log_debug("CLEAR raid_dev %s in %s ", raid_dev, block->sysfs_path); raid_device_fini(block->raid_dev); block->raid_dev = NULL; } } } int handle_udev_event(struct list *ledmon_block_list) { struct udev_device *dev; int status = -1; dev = udev_monitor_receive_device(udev_monitor); if (dev) { const char *action = udev_device_get_action(dev); enum udev_action act = _get_udev_action(action); const char *syspath = udev_device_get_syspath(dev); struct block_device *block = NULL; if (act == UDEV_ACTION_UNKNOWN) { status = 1; goto exit; } list_for_each(ledmon_block_list, block) { if (_compare(block, syspath)) break; block = NULL; } if (!block) { if (act == UDEV_ACTION_REMOVE && _check_raid(syspath)) { /*ledmon is interested about removed arrays*/ char *dev_name; dev_name = strrchr(syspath, '/') + 1; log_debug("REMOVED %s", dev_name); list_for_each(ledmon_block_list, block) _clear_raid_dev_info(block, dev_name); status = 0; goto exit; } status = 1; goto exit; } if (act == UDEV_ACTION_ADD) { log_debug("ADDED %s", block->sysfs_path); if (block->ibpi == IBPI_PATTERN_FAILED_DRIVE || block->ibpi == IBPI_PATTERN_REMOVED || block->ibpi == IBPI_PATTERN_UNKNOWN) block->ibpi = IBPI_PATTERN_ADDED; } else if (act == UDEV_ACTION_REMOVE) { log_debug("REMOVED %s", block->sysfs_path); block->ibpi = IBPI_PATTERN_REMOVED; } else { /* not interesting event */ status = 1; goto exit; } status = 0; } else { return -1; } exit: udev_device_unref(dev); return status; }