#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;
}
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 (!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");
}
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);
}
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;
/* 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:
}
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;
- sd_device *d;
-
- r = blockdev_reread_partition_table(dev);
- if (r < 0)
- log_device_debug_errno(dev, r, "Failed to re-read partition table, ignoring: %m");
- part_table_read = r >= 0;
-
- /* search for partitions */
- r = partition_enumerator_new(dev, &e);
- if (r < 0)
- return r;
-
- /* 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;
+ /* 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;
- /* 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)
- (void) synthesize_change_one(dev, d);
- } 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) {
_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 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);
}
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),
};
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);