diff --git a/src/udev/udev-manager.c b/src/udev/udev-manager.c index a696f6a69e..adfe8540b2 100644 --- a/src/udev/udev-manager.c +++ b/src/udev/udev-manager.c @@ -153,6 +153,7 @@ Manager* manager_free(Manager *manager) { sd_varlink_server_unref(manager->varlink_server); sd_event_source_unref(manager->inotify_event); + set_free(manager->synthesize_change_child_event_sources); sd_event_source_unref(manager->kill_workers_event); sd_event_unref(manager->event); @@ -895,9 +896,18 @@ static int synthesize_change_all(sd_device *dev) { return r; } -static int synthesize_change(sd_device *dev) { +static int synthesize_change_child_handler(sd_event_source *s, const siginfo_t *si, void *userdata) { + Manager *manager = ASSERT_PTR(userdata); + assert(s); + + sd_event_source_unref(set_remove(manager->synthesize_change_child_event_sources, s)); + return 0; +} + +static int synthesize_change(Manager *manager, sd_device *dev) { int r; + assert(manager); assert(dev); const char *sysname; @@ -908,7 +918,37 @@ static int synthesize_change(sd_device *dev) { if (startswith(sysname, "dm-") || block_device_is_whole_disk(dev) <= 0) return synthesize_change_one(dev, dev); - return synthesize_change_all(dev); + _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; + r = pidref_safe_fork( + "(udev-synth)", + FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE, + &pidref); + if (r < 0) + return r; + if (r == 0) { + /* child */ + (void) synthesize_change_all(dev); + _exit(EXIT_SUCCESS); + } + + _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; + r = event_add_child_pidref(manager->event, &s, &pidref, WEXITED, synthesize_change_child_handler, manager); + if (r < 0) { + log_debug_errno(r, "Failed to add child event source for "PID_FMT", ignoring: %m", pidref.pid); + return 0; + } + + r = sd_event_source_set_child_pidfd_own(s, true); + if (r < 0) + return r; + TAKE_PIDREF(pidref); + + r = set_ensure_put(&manager->synthesize_change_child_event_sources, &event_source_hash_ops, s); + if (r < 0) + return r; + TAKE_PTR(s); + + return 0; } static int on_inotify(sd_event_source *s, int fd, uint32_t revents, void *userdata) { @@ -955,7 +995,7 @@ static int on_inotify(sd_event_source *s, int fd, uint32_t revents, void *userda log_device_debug(dev, "Received inotify event for %s.", devnode); (void) event_queue_assume_block_device_unlocked(manager, dev); - (void) synthesize_change(dev); + (void) synthesize_change(manager, dev); } return 0; @@ -1055,7 +1095,7 @@ static int on_post(sd_event_source *s, void *userdata) { if (manager->exit) return sd_event_exit(manager->event, 0); - if (manager->cgroup) + if (manager->cgroup && set_isempty(manager->synthesize_change_child_event_sources)) /* cleanup possible left-over processes in our cgroup */ (void) cg_kill(manager->cgroup, SIGKILL, CGROUP_IGNORE_SELF, /* set=*/ NULL, /* kill_log= */ NULL, /* userdata= */ NULL); diff --git a/src/udev/udev-manager.h b/src/udev/udev-manager.h index 808f75d3e1..1681560533 100644 --- a/src/udev/udev-manager.h +++ b/src/udev/udev-manager.h @@ -35,6 +35,7 @@ typedef struct Manager { /* used by udev-watch */ int inotify_fd; sd_event_source *inotify_event; + Set *synthesize_change_child_event_sources; sd_event_source *kill_workers_event;