Blame src/udev.c

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