diff --git a/src/libsystemd/sd-device/device-private.c b/src/libsystemd/sd-device/device-private.c index cac7b98518..e612dbe0a0 100644 --- a/src/libsystemd/sd-device/device-private.c +++ b/src/libsystemd/sd-device/device-private.c @@ -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); - if (handle) - *handle = device->watch_handle; + 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; + } + + 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; - device->watch_handle = handle; + 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; + + 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) diff --git a/src/libsystemd/sd-device/device-private.h b/src/libsystemd/sd-device/device-private.h index 8eafc7ef74..fe268d7f2f 100644 --- a/src/libsystemd/sd-device/device-private.h +++ b/src/libsystemd/sd-device/device-private.h @@ -12,18 +12,22 @@ 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); diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index 06ef3698a5..f2e8c2112c 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -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); diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c index c613916d18..f40b3120ca 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -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; } diff --git a/src/udev/udev-watch.c b/src/udev/udev-watch.c index f5085107be..7f15c87870 100644 --- a/src/udev/udev-watch.c +++ b/src/udev/udev-watch.c @@ -5,52 +5,55 @@ */ #include -#include #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); - goto unlink; - } + /* For backward compatibility, read symlink from watch handle to device id, and ignore + * the opposite direction symlink. */ - r = sd_device_new_from_device_id(&dev, id); + if (safe_atoi(ent->d_name, &wd) < 0) + goto unlink; + + 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); + 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); - device_set_watch_handle(dev, wd); + (void) device_set_watch_handle(dev, -1); + return r; + } - xsprintf(filename, "/run/udev/watch/%d", wd); - r = mkdir_parents(filename, 0755); + r = device_set_watch_handle(dev, wd); if (r < 0) - return log_device_error_errno(dev, r, "Failed to create parent directory of '%s': %m", filename); - (void) unlink(filename); - - r = device_get_device_id(dev, &id); - 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; -} diff --git a/src/udev/udev-watch.h b/src/udev/udev-watch.h index f5c55794c4..d211d99f49 100644 --- a/src/udev/udev-watch.h +++ b/src/udev/udev-watch.h @@ -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); diff --git a/src/udev/udevd.c b/src/udev/udevd.c index 9689e15b1c..5574abce9d 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -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;