#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"
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) {
if (!set_isempty(device->current_tags))
return true;
- if (device->watch_handle >= 0)
- return true;
-
return false;
}
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)
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);
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);
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");
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)
int udev_event_process_inotify_watch(UdevEvent *event, int inotify_fd) {
sd_device *dev;
- int r;
assert(event);
assert(inotify_fd >= 0);
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;
}
*/
#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;
}
}
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);
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);
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;
-}
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);
_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;