]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev,sd_device: also save map from device ID to watch handle in /run/udev/watch
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 7 Mar 2021 06:35:33 +0000 (15:35 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 30 Apr 2021 10:41:41 +0000 (19:41 +0900)
Previously, watch handle is saved in the udev databse. But in most cases,
the handle saved in the database is not updated. Especially, when udevd
is restarted, the inotify watch is restarted, but the database is not
updated.

Moreover, it is not necessary to save watch handle in the database, as
the handle is only take a effect during udevd is running, and the value
is meaningless when udevd is restarted.

So, this makes the opposite map from device ID to watch handle is saved
in /run/udev/watch as a symbolic link, and the handle not saved in the
database anymore.

Fixes #18525.

src/libsystemd/sd-device/device-private.c
src/libsystemd/sd-device/device-private.h
src/libsystemd/sd-device/sd-device.c
src/udev/udev-event.c
src/udev/udev-watch.c
src/udev/udev-watch.h
src/udev/udevd.c

index cac7b98518bd478de32dd75a2abed3e5eaa18052..e612dbe0a072ef5d8597487c529d3bd9ea63a650 100644 (file)
@@ -20,6 +20,7 @@
 #include "parse-util.h"
 #include "path-util.h"
 #include "set.h"
+#include "stdio-util.h"
 #include "string-table.h"
 #include "string-util.h"
 #include "strv.h"
@@ -568,28 +569,142 @@ int device_get_devlink_priority(sd_device *device, int *priority) {
         return 0;
 }
 
-int device_get_watch_handle(sd_device *device, int *handle) {
+int device_get_watch_handle(sd_device *device) {
+        char path_wd[STRLEN("/run/udev/watch/") + DECIMAL_STR_MAX(int)];
+        _cleanup_free_ char *buf = NULL;
+        const char *id, *path_id;
+        int wd, r;
+
+        assert(device);
+
+        if (device->watch_handle >= 0)
+                return device->watch_handle;
+
+        r = device_get_device_id(device, &id);
+        if (r < 0)
+                return r;
+
+        path_id = strjoina("/run/udev/watch/", id);
+        r = readlink_malloc(path_id, &buf);
+        if (r < 0)
+                return r;
+
+        r = safe_atoi(buf, &wd);
+        if (r < 0)
+                return r;
+
+        if (wd < 0)
+                return -EBADF;
+
+        buf = mfree(buf);
+        xsprintf(path_wd, "/run/udev/watch/%d", wd);
+        r = readlink_malloc(path_wd, &buf);
+        if (r < 0)
+                return r;
+
+        if (!streq(buf, id))
+                return -EBADF;
+
+        return device->watch_handle = wd;
+}
+
+static void device_remove_watch_handle(sd_device *device) {
+        const char *id;
+        int wd;
+
+        assert(device);
+
+        /* First, remove the symlink from handle to device id. */
+        wd = device_get_watch_handle(device);
+        if (wd >= 0) {
+                char path_wd[STRLEN("/run/udev/watch/") + DECIMAL_STR_MAX(int)];
+
+                xsprintf(path_wd, "/run/udev/watch/%d", wd);
+                if (unlink(path_wd) < 0 && errno != ENOENT)
+                        log_device_debug_errno(device, errno,
+                                               "sd-device: failed to remove %s, ignoring: %m",
+                                               path_wd);
+        }
+
+        /* Next, remove the symlink from device id to handle. */
+        if (device_get_device_id(device, &id) >= 0) {
+                const char *path_id;
+
+                path_id = strjoina("/run/udev/watch/", id);
+                if (unlink(path_id) < 0 && errno != ENOENT)
+                        log_device_debug_errno(device, errno,
+                                               "sd-device: failed to remove %s, ignoring: %m",
+                                               path_id);
+        }
+
+        device->watch_handle = -1;
+}
+
+int device_set_watch_handle(sd_device *device, int wd) {
+        char path_wd[STRLEN("/run/udev/watch/") + DECIMAL_STR_MAX(int)];
+        const char *id, *path_id;
         int r;
 
         assert(device);
 
-        r = device_read_db(device);
+        if (wd >= 0 && wd == device->watch_handle)
+                return 0;
+
+        device_remove_watch_handle(device);
+
+        if (wd < 0)
+                /* negative wd means that the caller requests to clear saved watch handle. */
+                return 0;
+
+        r = device_get_device_id(device, &id);
         if (r < 0)
                 return r;
 
-        if (device->watch_handle < 0)
-                return -ENOENT;
+        path_id = strjoina("/run/udev/watch/", id);
+        xsprintf(path_wd, "/run/udev/watch/%d", wd);
+
+        r = mkdir_parents(path_wd, 0755);
+        if (r < 0)
+                return r;
+
+        if (symlink(id, path_wd) < 0)
+                return -errno;
+
+        if (symlink(path_wd + STRLEN("/run/udev/watch/"), path_id) < 0) {
+                r = -errno;
+                if (unlink(path_wd) < 0 && errno != ENOENT)
+                        log_device_debug_errno(device, errno,
+                                               "sd-device: failed to remove %s, ignoring: %m",
+                                               path_wd);
+                return r;
+        }
 
-        if (handle)
-                *handle = device->watch_handle;
+        device->watch_handle = wd;
 
         return 0;
 }
 
-void device_set_watch_handle(sd_device *device, int handle) {
-        assert(device);
+int device_new_from_watch_handle_at(sd_device **ret, int dirfd, int wd) {
+        char path_wd[STRLEN("/run/udev/watch/") + DECIMAL_STR_MAX(int)];
+        _cleanup_free_ char *id = NULL;
+        int r;
+
+        assert(ret);
+
+        if (wd < 0)
+                return -EBADF;
+
+        if (dirfd >= 0) {
+                xsprintf(path_wd, "%d", wd);
+                r = readlinkat_malloc(dirfd, path_wd, &id);
+        } else {
+                xsprintf(path_wd, "/run/udev/watch/%d", wd);
+                r = readlink_malloc(path_wd, &id);
+        }
+        if (r < 0)
+                return r;
 
-        device->watch_handle = handle;
+        return sd_device_new_from_device_id(ret, id);
 }
 
 int device_rename(sd_device *device, const char *name) {
@@ -822,9 +937,6 @@ static bool device_has_info(sd_device *device) {
         if (!set_isempty(device->current_tags))
                 return true;
 
-        if (device->watch_handle >= 0)
-                return true;
-
         return false;
 }
 
@@ -899,9 +1011,6 @@ int device_update_db(sd_device *device) {
 
                         if (device->devlink_priority != 0)
                                 fprintf(f, "L:%i\n", device->devlink_priority);
-
-                        if (device->watch_handle >= 0)
-                                fprintf(f, "W:%i\n", device->watch_handle);
                 }
 
                 if (device->usec_initialized > 0)
index 8eafc7ef74b4403b16c4c13d5794b7ea23b19e8c..fe268d7f2f8a58581656adf34366e177fadeee38 100644 (file)
 
 int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len);
 int device_new_from_strv(sd_device **ret, char **strv);
+int device_new_from_watch_handle_at(sd_device **ret, int dirfd, int wd);
+static inline int device_new_from_watch_handle(sd_device **ret, int wd) {
+        return device_new_from_watch_handle_at(ret, -1, wd);
+}
 
 int device_get_device_id(sd_device *device, const char **ret);
 
 int device_get_devlink_priority(sd_device *device, int *priority);
-int device_get_watch_handle(sd_device *device, int *handle);
+int device_get_watch_handle(sd_device *device);
 int device_get_devnode_mode(sd_device *device, mode_t *mode);
 int device_get_devnode_uid(sd_device *device, uid_t *uid);
 int device_get_devnode_gid(sd_device *device, gid_t *gid);
 
 void device_seal(sd_device *device);
 void device_set_is_initialized(sd_device *device);
-void device_set_watch_handle(sd_device *device, int fd);
+int device_set_watch_handle(sd_device *device, int wd);
 void device_set_db_persist(sd_device *device);
 void device_set_devlink_priority(sd_device *device, int priority);
 int device_ensure_usec_initialized(sd_device *device, sd_device *device_old);
index 06ef3698a5164b5b464d127d6978d6b3840227c1..f2e8c2112cade93a8207ce5a080b484d66a74111 100644 (file)
@@ -1238,10 +1238,10 @@ static int handle_db_line(sd_device *device, char key, const char *value) {
 
                 break;
         case 'W':
-                r = safe_atoi(value, &device->watch_handle);
-                if (r < 0)
-                        return r;
-
+                /* Deprecated. Previously, watch handle is both saved in database and /run/udev/watch.
+                 * However, the handle saved in database may not be updated when the handle is updated
+                 * or removed. Moreover, it is not necessary to store the handle within the database,
+                 * as its value becomes meaningless when udevd is restarted. */
                 break;
         case 'V':
                 r = safe_atou(value, &device->database_version);
index c613916d18a9d692ad3b6361a2e22f9ae2ccb173..f40b3120ca5bce297efe9370a919779ebfbdef17 100644 (file)
@@ -997,6 +997,9 @@ int udev_event_execute_rules(
         if (action == SD_DEVICE_REMOVE)
                 return event_execute_rules_on_remove(event, inotify_fd, timeout_usec, timeout_signal, properties_list, rules);
 
+        /* Disable watch during event processing. */
+        (void) udev_watch_end(inotify_fd, event->dev);
+
         r = device_clone_with_db(dev, &event->dev_db_clone);
         if (r < 0)
                 return log_device_debug_errno(dev, r, "Failed to clone sd_device object: %m");
@@ -1005,9 +1008,6 @@ int udev_event_execute_rules(
         if (r < 0)
                 log_device_warning_errno(dev, r, "Failed to copy all tags from old database entry, ignoring: %m");
 
-        /* Disable watch during event processing. */
-        (void) udev_watch_end(inotify_fd, event->dev_db_clone);
-
         if (action == SD_DEVICE_MOVE) {
                 r = udev_event_on_move(event->dev);
                 if (r < 0)
@@ -1083,7 +1083,6 @@ void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec, int timeout_s
 
 int udev_event_process_inotify_watch(UdevEvent *event, int inotify_fd) {
         sd_device *dev;
-        int r;
 
         assert(event);
         assert(inotify_fd >= 0);
@@ -1095,16 +1094,10 @@ int udev_event_process_inotify_watch(UdevEvent *event, int inotify_fd) {
         if (device_for_action(dev, SD_DEVICE_REMOVE))
                 return 0;
 
-        if (!event->inotify_watch) {
+        if (event->inotify_watch)
+                (void) udev_watch_begin(inotify_fd, dev);
+        else
                 (void) udev_watch_end(inotify_fd, dev);
-                return 0;
-        }
-
-        (void) udev_watch_begin(inotify_fd, dev);
-
-        r = device_update_db(dev);
-        if (r < 0)
-                return log_device_debug_errno(dev, r, "Failed to update database under /run/udev/data/: %m");
 
         return 0;
 }
index f5085107be6be2cdd2bd948d76ffda719158b805..7f15c87870e8b745d73d047a2cb5cb071e886c23 100644 (file)
@@ -5,52 +5,55 @@
  */
 
 #include <sys/inotify.h>
-#include <unistd.h>
 
 #include "alloc-util.h"
 #include "device-private.h"
 #include "device-util.h"
 #include "dirent-util.h"
 #include "fs-util.h"
-#include "mkdir.h"
-#include "stdio-util.h"
+#include "parse-util.h"
 #include "udev-watch.h"
 
-/* Move any old watches directory out of the way, and then restore the watches. */
 int udev_watch_restore(int inotify_fd) {
         struct dirent *ent;
         DIR *dir;
         int r;
 
+        /* Move any old watches directory out of the way, and then restore the watches. */
+
         assert(inotify_fd >= 0);
 
         if (rename("/run/udev/watch", "/run/udev/watch.old") < 0) {
                 if (errno != ENOENT)
-                        return log_warning_errno(errno, "Failed to move watches directory /run/udev/watch. Old watches will not be restored: %m");
+                        return log_warning_errno(errno, "Failed to move watches directory /run/udev/watch. "
+                                                 "Old watches will not be restored: %m");
 
                 return 0;
         }
 
         dir = opendir("/run/udev/watch.old");
         if (!dir)
-                return log_warning_errno(errno, "Failed to open old watches directory /run/udev/watch.old. Old watches will not be restored: %m");
+                return log_warning_errno(errno, "Failed to open old watches directory /run/udev/watch.old. "
+                                         "Old watches will not be restored: %m");
 
         FOREACH_DIRENT_ALL(ent, dir, break) {
                 _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
-                _cleanup_free_ char *id = NULL;
+                int wd;
 
                 if (ent->d_name[0] == '.')
                         continue;
 
-                r = readlinkat_malloc(dirfd(dir), ent->d_name, &id);
-                if (r < 0) {
-                        log_debug_errno(r, "Failed to read link '/run/udev/watch.old/%s', ignoring: %m", ent->d_name);
+                /* For backward compatibility, read symlink from watch handle to device id, and ignore
+                 * the opposite direction symlink. */
+
+                if (safe_atoi(ent->d_name, &wd) < 0)
                         goto unlink;
-                }
 
-                r = sd_device_new_from_device_id(&dev, id);
+                r = device_new_from_watch_handle_at(&dev, dirfd(dir), wd);
                 if (r < 0) {
-                        log_debug_errno(r, "Failed to create sd_device object for '%s', ignoring: %m", id);
+                        log_full_errno(r == -ENODEV ? LOG_DEBUG : LOG_WARNING, r,
+                                       "Failed to create sd_device object from saved watch handle '%s', ignoring: %m",
+                                       ent->d_name);
                         goto unlink;
                 }
 
@@ -67,8 +70,7 @@ unlink:
 }
 
 int udev_watch_begin(int inotify_fd, sd_device *dev) {
-        char filename[STRLEN("/run/udev/watch/") + DECIMAL_STR_MAX(int)];
-        const char *devnode, *id;
+        const char *devnode;
         int wd, r;
 
         assert(inotify_fd >= 0);
@@ -76,35 +78,27 @@ int udev_watch_begin(int inotify_fd, sd_device *dev) {
 
         r = sd_device_get_devname(dev, &devnode);
         if (r < 0)
-                return log_device_error_errno(dev, r, "Failed to get device name: %m");
+                return log_device_debug_errno(dev, r, "Failed to get device name: %m");
 
         log_device_debug(dev, "Adding watch on '%s'", devnode);
         wd = inotify_add_watch(inotify_fd, devnode, IN_CLOSE_WRITE);
-        if (wd < 0)
-                return log_device_full_errno(dev, errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno,
-                                             "Failed to add device '%s' to watch: %m", devnode);
-
-        device_set_watch_handle(dev, wd);
+        if (wd < 0) {
+                r = log_device_full_errno(dev, errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
+                                          errno, "Failed to add device '%s' to watch: %m", devnode);
 
-        xsprintf(filename, "/run/udev/watch/%d", wd);
-        r = mkdir_parents(filename, 0755);
-        if (r < 0)
-                return log_device_error_errno(dev, r, "Failed to create parent directory of '%s': %m", filename);
-        (void) unlink(filename);
+                (void) device_set_watch_handle(dev, -1);
+                return r;
+        }
 
-        r = device_get_device_id(dev, &id);
+        r = device_set_watch_handle(dev, wd);
         if (r < 0)
-                return log_device_error_errno(dev, r, "Failed to get device id-filename: %m");
-
-        if (symlink(id, filename) < 0)
-                return log_device_error_errno(dev, errno, "Failed to create symlink %s: %m", filename);
+                return log_device_warning_errno(dev, r, "Failed to save watch handle in /run/udev/watch: %m");
 
         return 0;
 }
 
 int udev_watch_end(int inotify_fd, sd_device *dev) {
-        char filename[STRLEN("/run/udev/watch/") + DECIMAL_STR_MAX(int)];
-        int wd, r;
+        int wd;
 
         assert(dev);
 
@@ -115,43 +109,14 @@ int udev_watch_end(int inotify_fd, sd_device *dev) {
         if (sd_device_get_devname(dev, NULL) < 0)
                 return 0;
 
-        r = device_get_watch_handle(dev, &wd);
-        if (r == -ENOENT)
-                return 0;
-        if (r < 0)
-                return log_device_debug_errno(dev, r, "Failed to get watch handle, ignoring: %m");
-
-        log_device_debug(dev, "Removing watch");
-        (void) inotify_rm_watch(inotify_fd, wd);
-
-        xsprintf(filename, "/run/udev/watch/%d", wd);
-        (void) unlink(filename);
-
-        device_set_watch_handle(dev, -1);
+        wd = device_get_watch_handle(dev);
+        if (wd < 0)
+                log_device_debug_errno(dev, wd, "Failed to get watch handle, ignoring: %m");
+        else {
+                log_device_debug(dev, "Removing watch");
+                (void) inotify_rm_watch(inotify_fd, wd);
+        }
+        (void) device_set_watch_handle(dev, -1);
 
         return 0;
 }
-
-int udev_watch_lookup(int wd, sd_device **ret) {
-        char filename[STRLEN("/run/udev/watch/") + DECIMAL_STR_MAX(int)];
-        _cleanup_free_ char *id = NULL;
-        int r;
-
-        assert(wd >= 0);
-        assert(ret);
-
-        xsprintf(filename, "/run/udev/watch/%d", wd);
-        r = readlink_malloc(filename, &id);
-        if (r == -ENOENT)
-                return 0;
-        if (r < 0)
-                return log_debug_errno(r, "Failed to read link '%s': %m", filename);
-
-        r = sd_device_new_from_device_id(ret, id);
-        if (r == -ENODEV)
-                return 0;
-        if (r < 0)
-                return log_debug_errno(r, "Failed to create sd_device object for '%s': %m", id);
-
-        return 1;
-}
index f5c55794c40337db86613509f161d1dfc3b6e9a1..d211d99f49de7937d629e99575eb4b5ae97c07cb 100644 (file)
@@ -6,4 +6,3 @@
 int udev_watch_restore(int inotify_fd);
 int udev_watch_begin(int inotify_fd, sd_device *dev);
 int udev_watch_end(int inotify_fd, sd_device *dev);
-int udev_watch_lookup(int wd, sd_device **ret);
index 9689e15b1c45999cba6b9cfc8b6db7e7c3fb5e85..5574abce9d67b9000ea3d4f6eb8b82f118e78506 100644 (file)
@@ -1302,17 +1302,21 @@ static int on_inotify(sd_event_source *s, int fd, uint32_t revents, void *userda
                 _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
                 const char *devnode;
 
-                if (udev_watch_lookup(e->wd, &dev) <= 0)
+                r = device_new_from_watch_handle(&dev, e->wd);
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to create sd_device object from watch handle, ignoring: %m");
                         continue;
+                }
 
                 if (sd_device_get_devname(dev, &devnode) < 0)
                         continue;
 
                 log_device_debug(dev, "Inotify event: %x for %s", e->mask, devnode);
                 if (e->mask & IN_CLOSE_WRITE)
-                        synthesize_change(dev);
-                else if (e->mask & IN_IGNORED)
-                        udev_watch_end(manager->inotify_fd, 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(). */
         }
 
         return 1;