#include "sd-event.h"
#include "alloc-util.h"
+#include "blockdev-util.h"
#include "cgroup-setup.h"
#include "cgroup-util.h"
#include "cpu-set-util.h"
#include "udev-builtin.h"
#include "udev-ctrl.h"
#include "udev-event.h"
+#include "udev-node.h"
#include "udev-util.h"
#include "udev-watch.h"
#include "user-util.h"
usec_t last_usec;
+ bool udev_node_needs_cleanup;
bool stop_exec_queue;
bool exit;
} Manager;
const char *devpath;
const char *devpath_old;
const char *devnode;
+
+ /* Used when the device is locked by another program. */
usec_t retry_again_next_usec;
usec_t retry_again_timeout_usec;
+ sd_event_source *retry_event_source;
sd_event_source *timeout_warning_event;
sd_event_source *timeout_event;
/* Do not use sd_event_source_disable_unref() here, as this is called by both workers and the
* main process. */
+ sd_event_source_unref(event->retry_event_source);
sd_event_source_unref(event->timeout_warning_event);
sd_event_source_unref(event->timeout_event);
mac_selinux_maybe_reload();
/* Nothing changed. It is not necessary to reload. */
- if (!udev_rules_should_reload(manager->rules) && !udev_builtin_validate())
+ if (!udev_rules_should_reload(manager->rules) && !udev_builtin_should_reload())
return;
sd_notify(false,
}
static int on_kill_workers_event(sd_event_source *s, uint64_t usec, void *userdata) {
- Manager *manager = userdata;
-
- assert(manager);
+ Manager *manager = ASSERT_PTR(userdata);
log_debug("Cleanup idle workers");
manager_kill_workers(manager, false);
if (device_for_action(dev, SD_DEVICE_REMOVE))
goto irrelevant;
- r = sd_device_get_subsystem(dev, &val);
- if (r < 0)
- return log_device_debug_errno(dev, r, "Failed to get subsystem: %m");
-
- if (!streq(val, "block"))
- goto irrelevant;
-
r = sd_device_get_sysname(dev, &val);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to get sysname: %m");
if (STARTSWITH_SET(val, "dm-", "md", "drbd"))
goto irrelevant;
- r = sd_device_get_devtype(dev, &val);
- if (r < 0 && r != -ENOENT)
- return log_device_debug_errno(dev, r, "Failed to get devtype: %m");
- if (r >= 0 && streq(val, "partition")) {
- r = sd_device_get_parent(dev, &dev);
- if (r == -ENOENT) /* The device may be already removed. */
- goto irrelevant;
- if (r < 0)
- return log_device_debug_errno(dev, r, "Failed to get parent device: %m");
- }
+ r = block_device_get_whole_disk(dev, &dev);
+ if (IN_SET(r,
+ -ENOTBLK, /* The device is not a block device. */
+ -ENODEV /* The whole disk device was not found, it may already be removed. */))
+ goto irrelevant;
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to get whole disk device: %m");
r = sd_device_get_devname(dev, &val);
- if (r == -ENOENT)
- goto irrelevant;
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to get devname: %m");
}
static int worker_lock_whole_disk(sd_device *dev, int *ret_fd) {
- _cleanup_close_ int fd = -1;
+ _cleanup_close_ int fd = -EBADF;
sd_device *dev_whole_disk;
const char *val;
int r;
if (r == 0)
goto nolock;
- fd = sd_device_open(dev_whole_disk, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
+ fd = sd_device_open(dev_whole_disk, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (fd < 0) {
bool ignore = ERRNO_IS_DEVICE_ABSENT(fd);
return 1;
nolock:
- *ret_fd = -1;
+ *ret_fd = -EBADF;
return 0;
}
static int worker_mark_block_device_read_only(sd_device *dev) {
- _cleanup_close_ int fd = -1;
+ _cleanup_close_ int fd = -EBADF;
const char *val;
int state = 1, r;
if (STARTSWITH_SET(val, "dm-", "md", "drbd", "loop", "nbd", "zram"))
return 0;
- fd = sd_device_open(dev, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
+ fd = sd_device_open(dev, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (fd < 0)
return log_device_debug_errno(dev, fd, "Failed to open '%s', ignoring: %m", val);
static int worker_process_device(Manager *manager, sd_device *dev) {
_cleanup_(udev_event_freep) UdevEvent *udev_event = NULL;
- _cleanup_close_ int fd_lock = -1;
+ _cleanup_close_ int fd_lock = -EBADF;
int r;
assert(manager);
/* in case rtnl was initialized */
manager->rtnl = sd_netlink_ref(udev_event->rtnl);
- r = udev_event_process_inotify_watch(udev_event, manager->inotify_fd);
- if (r < 0)
- return r;
+ if (udev_event->inotify_watch) {
+ r = udev_watch_begin(manager->inotify_fd, dev);
+ if (r < 0 && r != -ENOENT) /* The device may be already removed, ignore -ENOENT. */
+ log_device_warning_errno(dev, r, "Failed to add inotify watch, ignoring: %m");
+ }
log_device_uevent(dev, "Device processed");
return 0;
}
static int worker_device_monitor_handler(sd_device_monitor *monitor, sd_device *dev, void *userdata) {
- Manager *manager = userdata;
+ Manager *manager = ASSERT_PTR(userdata);
int r;
assert(dev);
- assert(manager);
r = worker_process_device(manager, dev);
if (r == EVENT_RESULT_TRY_AGAIN)
if (r < 0)
return log_error_errno(r, "Failed to start device monitor: %m");
- (void) sd_event_source_set_description(sd_device_monitor_get_event_source(monitor), "worker-device-monitor");
-
/* Process first device */
(void) worker_device_monitor_handler(monitor, dev, manager);
}
static int on_event_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
- Event *event = userdata;
+ Event *event = ASSERT_PTR(userdata);
- assert(event);
assert(event->worker);
kill_and_sigcont(event->worker->pid, arg_timeout_signal);
}
static int on_event_timeout_warning(sd_event_source *s, uint64_t usec, void *userdata) {
- Event *event = userdata;
+ Event *event = ASSERT_PTR(userdata);
- assert(event);
assert(event->worker);
log_device_warning(event->dev, "Worker ["PID_FMT"] processing SEQNUM=%"PRIu64" is taking a long time", event->worker->pid, event->seqnum);
if (r < 0)
return r;
+ (void) sd_device_monitor_set_description(worker_monitor, "worker");
+
/* allow the main daemon netlink address to send devices to the worker */
r = device_monitor_allow_unicast_sender(worker_monitor, manager->monitor);
if (r < 0)
log_device_uevent(event->dev, "Device ready for processing");
+ (void) event_source_disable(event->retry_event_source);
+
manager = event->manager;
HASHMAP_FOREACH(worker, manager->workers) {
if (worker->state != WORKER_IDLE)
if (r < 0)
return r;
- if (event->retry_again_next_usec <= now_usec)
+ if (event->retry_again_next_usec > now_usec)
return true;
}
if (!manager->events || manager->exit || manager->stop_exec_queue)
return 0;
+ /* To make the stack directory /run/udev/links cleaned up later. */
+ manager->udev_node_needs_cleanup = true;
+
r = event_source_disable(manager->kill_workers_event);
if (r < 0)
log_warning_errno(r, "Failed to disable event source for cleaning up idle workers, ignoring: %m");
return 0;
}
+static int on_event_retry(sd_event_source *s, uint64_t usec, void *userdata) {
+ /* This does nothing. The on_post() callback will start the event if there exists an idle worker. */
+ return 1;
+}
+
static int event_requeue(Event *event) {
usec_t now_usec;
int r;
if (event->retry_again_timeout_usec == 0)
event->retry_again_timeout_usec = usec_add(now_usec, EVENT_RETRY_TIMEOUT_USEC);
+ r = event_reset_time_relative(event->manager->event, &event->retry_event_source,
+ CLOCK_MONOTONIC, EVENT_RETRY_INTERVAL_USEC, 0,
+ on_event_retry, NULL,
+ 0, "retry-event", true);
+ if (r < 0)
+ return log_device_warning_errno(event->dev, r, "Failed to reset timer event source for retrying event, "
+ "skipping event (SEQNUM=%"PRIu64", ACTION=%s): %m",
+ event->seqnum, strna(device_action_to_string(event->action)));
+
if (event->worker && event->worker->event == event)
event->worker->event = NULL;
event->worker = NULL;
}
static int on_uevent(sd_device_monitor *monitor, sd_device *dev, void *userdata) {
- Manager *manager = userdata;
+ Manager *manager = ASSERT_PTR(userdata);
int r;
- assert(manager);
-
DEVICE_TRACE_POINT(kernel_uevent_received, dev);
device_ensure_usec_initialized(dev, NULL);
(void) event_queue_assume_block_device_unlocked(manager, dev);
- /* we have fresh events, try to schedule them */
- event_queue_start(manager);
-
return 1;
}
static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- Manager *manager = userdata;
-
- assert(manager);
+ Manager *manager = ASSERT_PTR(userdata);
for (;;) {
EventResult result;
event_free(worker->event);
}
- /* we have free workers, try to schedule events */
- event_queue_start(manager);
-
return 1;
}
/* receive the udevd message from userspace */
static int on_ctrl_msg(UdevCtrl *uctrl, UdevCtrlMessageType type, const UdevCtrlMessageValue *value, void *userdata) {
- Manager *manager = userdata;
+ Manager *manager = ASSERT_PTR(userdata);
int r;
assert(value);
- assert(manager);
switch (type) {
case UDEV_CTRL_SET_LOG_LEVEL:
+ if ((value->intval & LOG_PRIMASK) != value->intval) {
+ log_debug("Received invalid udev control message (SET_LOG_LEVEL, %i), ignoring.", value->intval);
+ break;
+ }
+
log_debug("Received udev control message (SET_LOG_LEVEL), setting log_level=%i", value->intval);
+
+ r = log_get_max_level();
+ if (r == value->intval)
+ break;
+
log_set_max_level(value->intval);
manager->log_level = value->intval;
manager_kill_workers(manager, false);
}
static int synthesize_change(sd_device *dev) {
- const char *subsystem, *sysname, *devtype;
- int r;
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ bool part_table_read;
+ const char *sysname;
+ sd_device *d;
+ int r, k;
- r = sd_device_get_subsystem(dev, &subsystem);
+ r = sd_device_get_sysname(dev, &sysname);
if (r < 0)
return r;
- r = sd_device_get_devtype(dev, &devtype);
+ if (startswith(sysname, "dm-") || block_device_is_whole_disk(dev) <= 0)
+ return synthesize_change_one(dev, dev);
+
+ r = blockdev_reread_partition_table(dev);
if (r < 0)
- return r;
+ log_device_debug_errno(dev, r, "Failed to re-read partition table, ignoring: %m");
+ part_table_read = r >= 0;
- r = sd_device_get_sysname(dev, &sysname);
+ /* search for partitions */
+ r = partition_enumerator_new(dev, &e);
if (r < 0)
return r;
- if (streq_ptr(subsystem, "block") &&
- streq_ptr(devtype, "disk") &&
- !startswith(sysname, "dm-")) {
- _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
- bool part_table_read = false, has_partitions = false;
- sd_device *d;
- int fd;
-
- /* Try to re-read the partition table. This only succeeds if none of the devices is
- * busy. The kernel returns 0 if no partition table is found, and we will not get an
- * event for the disk. */
- fd = sd_device_open(dev, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
- if (fd >= 0) {
- r = flock(fd, LOCK_EX|LOCK_NB);
- if (r >= 0)
- r = ioctl(fd, BLKRRPART, 0);
-
- close(fd);
- if (r >= 0)
- part_table_read = true;
- }
-
- /* search for partitions */
- r = sd_device_enumerator_new(&e);
- if (r < 0)
- return r;
-
- r = sd_device_enumerator_allow_uninitialized(e);
- if (r < 0)
- return r;
-
- r = sd_device_enumerator_add_match_parent(e, dev);
- if (r < 0)
- return r;
-
- r = sd_device_enumerator_add_match_subsystem(e, "block", true);
- if (r < 0)
- return r;
-
- FOREACH_DEVICE(e, d) {
- const char *t;
-
- if (sd_device_get_devtype(d, &t) < 0 || !streq(t, "partition"))
- continue;
-
- has_partitions = true;
- break;
- }
-
- /* We have partitions and re-read the table, the kernel already sent out a "change"
- * event for the disk, and "remove/add" for all partitions. */
- if (part_table_read && has_partitions)
- return 0;
-
- /* We have partitions but re-reading the partition table did not work, synthesize
- * "change" for the disk and all partitions. */
- (void) synthesize_change_one(dev, dev);
-
- FOREACH_DEVICE(e, d) {
- const char *t;
-
- if (sd_device_get_devtype(d, &t) < 0 || !streq(t, "partition"))
- continue;
-
- (void) synthesize_change_one(dev, d);
- }
+ /* We have partitions and re-read the table, the kernel already sent out a "change"
+ * event for the disk, and "remove/add" for all partitions. */
+ if (part_table_read && sd_device_enumerator_get_device_first(e))
+ return 0;
- } else
- (void) synthesize_change_one(dev, dev);
+ /* We have partitions but re-reading the partition table did not work, synthesize
+ * "change" for the disk and all partitions. */
+ r = synthesize_change_one(dev, dev);
+ FOREACH_DEVICE(e, d) {
+ k = synthesize_change_one(dev, d);
+ if (k < 0 && r >= 0)
+ r = k;
+ }
- return 0;
+ return r;
}
static int on_inotify(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- Manager *manager = userdata;
+ Manager *manager = ASSERT_PTR(userdata);
union inotify_event_buffer buffer;
ssize_t l;
int r;
- assert(manager);
-
- r = event_source_disable(manager->kill_workers_event);
- if (r < 0)
- log_warning_errno(r, "Failed to disable event source for cleaning up idle workers, ignoring: %m");
-
l = read(fd, &buffer, sizeof(buffer));
if (l < 0) {
if (ERRNO_IS_TRANSIENT(errno))
- return 1;
+ return 0;
return log_error_errno(errno, "Failed to read inotify fd: %m");
}
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
const char *devnode;
+ /* Do not handle IN_IGNORED here. Especially, do not try to call udev_watch_end() from the
+ * main process. Otherwise, the pair of the symlinks may become inconsistent, and several
+ * garbage may remain. The old symlinks are removed by a worker that processes the
+ * corresponding 'remove' uevent;
+ * udev_event_execute_rules() -> event_execute_rules_on_remove() -> udev_watch_end(). */
+
+ if (!FLAGS_SET(e->mask, IN_CLOSE_WRITE))
+ continue;
+
r = device_new_from_watch_handle(&dev, e->wd);
if (r < 0) {
+ /* Device may be removed just after closed. */
log_debug_errno(r, "Failed to create sd_device object from watch handle, ignoring: %m");
continue;
}
- if (sd_device_get_devname(dev, &devnode) < 0)
+ r = sd_device_get_devname(dev, &devnode);
+ if (r < 0) {
+ /* Also here, device may be already removed. */
+ log_device_debug_errno(dev, r, "Failed to get device node, ignoring: %m");
continue;
-
- log_device_debug(dev, "Inotify event: %x for %s", e->mask, devnode);
- if (e->mask & IN_CLOSE_WRITE) {
- (void) event_queue_assume_block_device_unlocked(manager, dev);
- (void) synthesize_change(dev);
}
- /* Do not handle IN_IGNORED here. It should be handled by worker in 'remove' uevent;
- * udev_event_execute_rules() -> event_execute_rules_on_remove() -> udev_watch_end(). */
+ log_device_debug(dev, "Received inotify event for %s.", devnode);
+
+ (void) event_queue_assume_block_device_unlocked(manager, dev);
+ (void) synthesize_change(dev);
}
- return 1;
+ return 0;
}
static int on_sigterm(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
- Manager *manager = userdata;
-
- assert(manager);
+ Manager *manager = ASSERT_PTR(userdata);
manager_exit(manager);
}
static int on_sighup(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
- Manager *manager = userdata;
-
- assert(manager);
+ Manager *manager = ASSERT_PTR(userdata);
manager_reload(manager, /* force = */ true);
Manager *manager = ASSERT_PTR(worker->manager);
sd_device *dev = worker->event ? ASSERT_PTR(worker->event->dev) : NULL;
EventResult result;
- int r;
assert(si);
worker_free(worker);
- /* we can start new workers, try to schedule events */
- event_queue_start(manager);
-
- /* Disable unnecessary cleanup event */
- if (hashmap_isempty(manager->workers)) {
- r = event_source_disable(manager->kill_workers_event);
- if (r < 0)
- log_warning_errno(r, "Failed to disable event source for cleaning up idle workers, ignoring: %m");
- }
-
return 1;
}
static int on_post(sd_event_source *s, void *userdata) {
- Manager *manager = userdata;
-
- assert(manager);
+ Manager *manager = ASSERT_PTR(userdata);
if (manager->events) {
/* Try to process pending events if idle workers exist. Why is this necessary?
/* There are no idle workers. */
+ if (manager->udev_node_needs_cleanup) {
+ (void) udev_node_cleanup();
+ manager->udev_node_needs_cleanup = false;
+ }
+
if (manager->exit)
return sd_event_exit(manager->event, 0);
}
static int listen_fds(int *ret_ctrl, int *ret_netlink) {
- int ctrl_fd = -1, netlink_fd = -1;
+ int ctrl_fd = -EBADF, netlink_fd = -EBADF;
int fd, n;
assert(ret_ctrl);
}
r = cg_get_xattr_bool(SYSTEMD_CGROUP_CONTROLLER, cgroup, "trusted.delegate");
- if (IN_SET(r, 0, -ENODATA))
+ if (r == 0 || (r < 0 && ERRNO_IS_XATTR_ABSENT(r)))
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "The cgroup %s is not delegated to us.", cgroup);
if (r < 0)
return log_debug_errno(r, "Failed to read trusted.delegate attribute: %m");
return log_oom();
*manager = (Manager) {
- .inotify_fd = -1,
- .worker_watch = { -1, -1 },
+ .inotify_fd = -EBADF,
+ .worker_watch = { -EBADF, -EBADF },
.cgroup = TAKE_PTR(cgroup),
};
log_warning_errno(r, "Failed to set receive buffer size for device monitor, ignoring: %m");
}
+ (void) sd_device_monitor_set_description(manager->monitor, "manager");
+
r = device_monitor_enable_receiving(manager->monitor);
if (r < 0)
return log_error_errno(r, "Failed to bind netlink socket: %m");
if (r < 0)
return log_error_errno(r, "Failed to start device monitor: %m");
- (void) sd_event_source_set_description(sd_device_monitor_get_event_source(manager->monitor), "device-monitor");
-
r = sd_event_add_io(manager->event, NULL, fd_worker, EPOLLIN, on_worker, manager);
if (r < 0)
return log_error_errno(r, "Failed to create worker event source: %m");
int run_udevd(int argc, char *argv[]) {
_cleanup_(manager_freep) Manager *manager = NULL;
- int fd_ctrl = -1, fd_uevent = -1;
+ int fd_ctrl = -EBADF, fd_uevent = -EBADF;
int r;
log_set_target(LOG_TARGET_AUTO);