]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udev-watch.c
9d3e69bc9382fef13ec74c346e99b3f08b40e669
[thirdparty/systemd.git] / src / udev / udev-watch.c
1 /* SPDX-License-Identifier: GPL-2.0+ */
2 /*
3 * Copyright © 2004-2012 Kay Sievers <kay@vrfy.org>
4 * Copyright © 2009 Canonical Ltd.
5 * Copyright © 2009 Scott James Remnant <scott@netsplit.com>
6 *
7 */
8
9 #include <errno.h>
10 #include <stddef.h>
11 #include <stdio.h>
12 #include <sys/inotify.h>
13 #include <unistd.h>
14
15 #include "dirent-util.h"
16 #include "stdio-util.h"
17 #include "udev.h"
18
19 static int inotify_fd = -1;
20
21 /* inotify descriptor, will be shared with rules directory;
22 * set to cloexec since we need our children to be able to add
23 * watches for us
24 */
25 int udev_watch_init(struct udev *udev) {
26 inotify_fd = inotify_init1(IN_CLOEXEC);
27 if (inotify_fd < 0)
28 log_error_errno(errno, "inotify_init failed: %m");
29 return inotify_fd;
30 }
31
32 /* move any old watches directory out of the way, and then restore
33 * the watches
34 */
35 void udev_watch_restore(struct udev *udev) {
36 if (inotify_fd < 0)
37 return;
38
39 if (rename("/run/udev/watch", "/run/udev/watch.old") == 0) {
40 DIR *dir;
41 struct dirent *ent;
42
43 dir = opendir("/run/udev/watch.old");
44 if (dir == NULL) {
45 log_error_errno(errno, "unable to open old watches dir /run/udev/watch.old; old watches will not be restored: %m");
46 return;
47 }
48
49 FOREACH_DIRENT_ALL(ent, dir, break) {
50 char device[UTIL_PATH_SIZE];
51 ssize_t len;
52 struct udev_device *dev;
53
54 if (ent->d_name[0] == '.')
55 continue;
56
57 len = readlinkat(dirfd(dir), ent->d_name, device, sizeof(device));
58 if (len <= 0 || len == (ssize_t)sizeof(device))
59 goto unlink;
60 device[len] = '\0';
61
62 dev = udev_device_new_from_device_id(udev, device);
63 if (dev == NULL)
64 goto unlink;
65
66 log_debug("restoring old watch on '%s'", udev_device_get_devnode(dev));
67 udev_watch_begin(udev, dev);
68 udev_device_unref(dev);
69 unlink:
70 (void) unlinkat(dirfd(dir), ent->d_name, 0);
71 }
72
73 closedir(dir);
74 rmdir("/run/udev/watch.old");
75
76 } else if (errno != ENOENT)
77 log_error_errno(errno, "unable to move watches dir /run/udev/watch; old watches will not be restored: %m");
78 }
79
80 void udev_watch_begin(struct udev *udev, struct udev_device *dev) {
81 char filename[sizeof("/run/udev/watch/") + DECIMAL_STR_MAX(int)];
82 int wd;
83 int r;
84
85 if (inotify_fd < 0)
86 return;
87
88 log_debug("adding watch on '%s'", udev_device_get_devnode(dev));
89 wd = inotify_add_watch(inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE);
90 if (wd < 0) {
91 log_error_errno(errno, "inotify_add_watch(%d, %s, %o) failed: %m",
92 inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE);
93 return;
94 }
95
96 xsprintf(filename, "/run/udev/watch/%d", wd);
97 mkdir_parents(filename, 0755);
98 unlink(filename);
99 r = symlink(udev_device_get_id_filename(dev), filename);
100 if (r < 0)
101 log_error_errno(errno, "Failed to create symlink %s: %m", filename);
102
103 udev_device_set_watch_handle(dev, wd);
104 }
105
106 void udev_watch_end(struct udev *udev, struct udev_device *dev) {
107 int wd;
108 char filename[sizeof("/run/udev/watch/") + DECIMAL_STR_MAX(int)];
109
110 if (inotify_fd < 0)
111 return;
112
113 wd = udev_device_get_watch_handle(dev);
114 if (wd < 0)
115 return;
116
117 log_debug("removing watch on '%s'", udev_device_get_devnode(dev));
118 inotify_rm_watch(inotify_fd, wd);
119
120 xsprintf(filename, "/run/udev/watch/%d", wd);
121 unlink(filename);
122
123 udev_device_set_watch_handle(dev, -1);
124 }
125
126 struct udev_device *udev_watch_lookup(struct udev *udev, int wd) {
127 char filename[sizeof("/run/udev/watch/") + DECIMAL_STR_MAX(int)];
128 char device[UTIL_NAME_SIZE];
129 ssize_t len;
130
131 if (inotify_fd < 0 || wd < 0)
132 return NULL;
133
134 xsprintf(filename, "/run/udev/watch/%d", wd);
135 len = readlink(filename, device, sizeof(device));
136 if (len <= 0 || (size_t)len == sizeof(device))
137 return NULL;
138 device[len] = '\0';
139
140 return udev_device_new_from_device_id(udev, device);
141 }