]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev-watch: dump installed inotify watches on start and stop
authorYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 4 Apr 2025 13:42:24 +0000 (22:42 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 5 Apr 2025 08:33:31 +0000 (17:33 +0900)
src/udev/udev-manager.c
src/udev/udev-watch.c
src/udev/udev-watch.h

index c05e475dd622f2eb3fcf0ede98fb2fcc862d56ed..eddd07854d643bb878a59f7c5f8cc08c1141e152 100644 (file)
@@ -965,8 +965,10 @@ static int on_post(sd_event_source *s, void *userdata) {
         if (r < 0)
                 log_warning_errno(r, "Failed to disable timer event source for cleaning up idle workers, ignoring: %m");
 
-        if (manager->exit)
+        if (manager->exit) {
+                udev_watch_dump();
                 return sd_event_exit(manager->event, 0);
+        }
 
         if (manager->cgroup && set_isempty(manager->synthesize_change_child_event_sources))
                 /* cleanup possible left-over processes in our cgroup */
index a9b06f5eb8b7880896be863f2acfbab19bf4abdc..84a7f542b1f48be19ab29b2798e2770387598ec1 100644 (file)
@@ -46,6 +46,88 @@ static int device_new_from_watch_handle_at(sd_device **ret, int dirfd, int wd) {
         return sd_device_new_from_device_id(ret, id);
 }
 
+void udev_watch_dump(void) {
+        int r;
+
+        _cleanup_closedir_ DIR *dir = opendir("/run/udev/watch/");
+        if (!dir)
+                return (void) log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
+                                             "Failed to open old watches directory '/run/udev/watch/': %m");
+
+        _cleanup_set_free_ Set *pending_wds = NULL, *verified_wds = NULL;
+        FOREACH_DIRENT(de, dir, break) {
+                if (safe_atoi(de->d_name, NULL) >= 0) {
+                        /* This should be wd -> ID symlink */
+
+                        if (set_contains(verified_wds, de->d_name))
+                                continue;
+
+                        r = set_put_strdup(&pending_wds, de->d_name);
+                        if (r < 0)
+                                log_warning_errno(r, "Failed to store pending watch handle %s, ignoring: %m", de->d_name);
+                        continue;
+                }
+
+                _cleanup_free_ char *wd = NULL;
+                r = readlinkat_malloc(dirfd(dir), de->d_name, &wd);
+                if (r < 0) {
+                        log_warning_errno(r, "Found broken inotify watch, failed to read symlink %s, ignoring: %m", de->d_name);
+                        continue;
+                }
+
+                const char *devnode = NULL;
+                _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+                if (sd_device_new_from_device_id(&dev, de->d_name) >= 0)
+                        (void) sd_device_get_devname(dev, &devnode);
+
+                _cleanup_free_ char *id = NULL;
+                r = readlinkat_malloc(dirfd(dir), wd, &id);
+                if (r < 0) {
+                        log_warning_errno(r, "Found broken inotify watch %s on %s (%s), failed to read symlink %s, ignoring: %m",
+                                          wd, strna(devnode), de->d_name, wd);
+                        continue;
+                }
+
+                if (!streq(de->d_name, id)) {
+                        log_warning("Found broken inotify watch %s on %s (%s), broken symlink chain: %s → %s → %s",
+                                    wd, strna(devnode), de->d_name, de->d_name, wd, id);
+                        continue;
+                }
+
+                log_debug("Found inotify watch %s on %s (%s).", wd, strna(devnode), de->d_name);
+
+                free(set_remove(pending_wds, wd));
+
+                r = set_ensure_put(&verified_wds, &string_hash_ops_free, wd);
+                if (r < 0) {
+                        log_warning_errno(r, "Failed to store verified watch handle %s, ignoring: %m", wd);
+                        continue;
+                }
+                TAKE_PTR(wd);
+        }
+
+        const char *w;
+        SET_FOREACH(w, pending_wds) {
+                _cleanup_free_ char *id = NULL;
+                r = readlinkat_malloc(dirfd(dir), w, &id);
+                if (r < 0) {
+                        log_warning_errno(r, "Found broken inotify watch %s, failed to read symlink %s, ignoring: %m", w, w);
+                        continue;
+                }
+
+                const char *devnode = NULL;
+                _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+                if (sd_device_new_from_device_id(&dev, id) >= 0)
+                        (void) sd_device_get_devname(dev, &devnode);
+
+                _cleanup_free_ char *wd = NULL;
+                (void) readlinkat_malloc(dirfd(dir), id, &wd);
+
+                log_warning("Found broken inotify watch %s on %s (%s), broken symlink chain: %s → %s → %s",
+                            wd, strna(devnode), id, w, id, wd);
+        }
+}
+
 static int synthesize_change_one(sd_device *dev, sd_device *target) {
         int r;
 
@@ -290,6 +372,8 @@ int manager_start_inotify(Manager *manager) {
         if (r < 0)
                 return r;
 
+        udev_watch_dump();
+
         _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
         r = sd_event_add_io(manager->event, &s, manager->inotify_fd, EPOLLIN, on_inotify, manager);
         if (r < 0)
index 8365334e71265be728cd98a15567bb510876a4fa..fed5f9614c7d7840e44d88ace3ecc792cf4d32bc 100644 (file)
@@ -5,6 +5,8 @@
 
 typedef struct Manager Manager;
 
+void udev_watch_dump(void);
+
 int manager_init_inotify(Manager *manager, int fd);
 int manager_start_inotify(Manager *manager);