Line data Source code
1 : /* SPDX-License-Identifier: GPL-2.0+ */
2 : /*
3 : * Copyright © 2009 Canonical Ltd.
4 : * Copyright © 2009 Scott James Remnant <scott@netsplit.com>
5 : */
6 :
7 : #include <sys/inotify.h>
8 : #include <unistd.h>
9 :
10 : #include "alloc-util.h"
11 : #include "device-private.h"
12 : #include "device-util.h"
13 : #include "dirent-util.h"
14 : #include "fs-util.h"
15 : #include "mkdir.h"
16 : #include "stdio-util.h"
17 : #include "udev-watch.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 0 : int udev_watch_init(void) {
25 0 : inotify_fd = inotify_init1(IN_CLOEXEC);
26 0 : if (inotify_fd < 0)
27 0 : return -errno;
28 :
29 0 : return inotify_fd;
30 : }
31 :
32 : /* Move any old watches directory out of the way, and then restore the watches. */
33 0 : int udev_watch_restore(void) {
34 : struct dirent *ent;
35 : DIR *dir;
36 : int r;
37 :
38 0 : if (inotify_fd < 0)
39 0 : return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
40 : "Invalid inotify descriptor.");
41 :
42 0 : if (rename("/run/udev/watch", "/run/udev/watch.old") < 0) {
43 0 : if (errno != ENOENT)
44 0 : return log_warning_errno(errno, "Failed to move watches directory /run/udev/watch. Old watches will not be restored: %m");
45 :
46 0 : return 0;
47 : }
48 :
49 0 : dir = opendir("/run/udev/watch.old");
50 0 : if (!dir)
51 0 : return log_warning_errno(errno, "Failed to open old watches directory /run/udev/watch.old. Old watches will not be restored: %m");
52 :
53 0 : FOREACH_DIRENT_ALL(ent, dir, break) {
54 0 : _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
55 0 : _cleanup_free_ char *device = NULL;
56 :
57 0 : if (ent->d_name[0] == '.')
58 0 : continue;
59 :
60 0 : r = readlinkat_malloc(dirfd(dir), ent->d_name, &device);
61 0 : if (r < 0) {
62 0 : log_debug_errno(r, "Failed to read link '/run/udev/watch.old/%s', ignoring: %m", ent->d_name);
63 0 : goto unlink;
64 : }
65 :
66 0 : r = sd_device_new_from_device_id(&dev, device);
67 0 : if (r < 0) {
68 0 : log_debug_errno(r, "Failed to create sd_device object for '%s', ignoring: %m", device);
69 0 : goto unlink;
70 : }
71 :
72 0 : log_device_debug(dev, "Restoring old watch");
73 0 : (void) udev_watch_begin(dev);
74 0 : unlink:
75 0 : (void) unlinkat(dirfd(dir), ent->d_name, 0);
76 : }
77 :
78 0 : (void) closedir(dir);
79 0 : (void) rmdir("/run/udev/watch.old");
80 :
81 0 : return 0;
82 : }
83 :
84 0 : int udev_watch_begin(sd_device *dev) {
85 : char filename[STRLEN("/run/udev/watch/") + DECIMAL_STR_MAX(int)];
86 : const char *devnode, *id_filename;
87 : int wd, r;
88 :
89 0 : if (inotify_fd < 0)
90 0 : return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
91 : "Invalid inotify descriptor.");
92 :
93 0 : r = sd_device_get_devname(dev, &devnode);
94 0 : if (r < 0)
95 0 : return log_device_error_errno(dev, r, "Failed to get device name: %m");
96 :
97 0 : log_device_debug(dev, "Adding watch on '%s'", devnode);
98 0 : wd = inotify_add_watch(inotify_fd, devnode, IN_CLOSE_WRITE);
99 0 : if (wd < 0)
100 0 : return log_device_full(dev,
101 : errno == ENOENT ? LOG_DEBUG : LOG_ERR,
102 : errno,
103 : "Failed to add device '%s' to watch: %m", devnode);
104 :
105 0 : device_set_watch_handle(dev, wd);
106 :
107 0 : xsprintf(filename, "/run/udev/watch/%d", wd);
108 0 : r = mkdir_parents(filename, 0755);
109 0 : if (r < 0)
110 0 : return log_device_error_errno(dev, r, "Failed to create parent directory of '%s': %m", filename);
111 0 : (void) unlink(filename);
112 :
113 0 : r = device_get_id_filename(dev, &id_filename);
114 0 : if (r < 0)
115 0 : return log_device_error_errno(dev, r, "Failed to get device id-filename: %m");
116 :
117 0 : if (symlink(id_filename, filename) < 0)
118 0 : return log_device_error_errno(dev, errno, "Failed to create symlink %s: %m", filename);
119 :
120 0 : return 0;
121 : }
122 :
123 0 : int udev_watch_end(sd_device *dev) {
124 : char filename[STRLEN("/run/udev/watch/") + DECIMAL_STR_MAX(int)];
125 : int wd, r;
126 :
127 0 : if (inotify_fd < 0)
128 0 : return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
129 : "Invalid inotify descriptor.");
130 :
131 0 : r = device_get_watch_handle(dev, &wd);
132 0 : if (r == -ENOENT)
133 0 : return 0;
134 0 : if (r < 0)
135 0 : return log_device_debug_errno(dev, r, "Failed to get watch handle, ignoring: %m");
136 :
137 0 : log_device_debug(dev, "Removing watch");
138 0 : (void) inotify_rm_watch(inotify_fd, wd);
139 :
140 0 : xsprintf(filename, "/run/udev/watch/%d", wd);
141 0 : (void) unlink(filename);
142 :
143 0 : device_set_watch_handle(dev, -1);
144 :
145 0 : return 0;
146 : }
147 :
148 0 : int udev_watch_lookup(int wd, sd_device **ret) {
149 : char filename[STRLEN("/run/udev/watch/") + DECIMAL_STR_MAX(int)];
150 0 : _cleanup_free_ char *device = NULL;
151 : int r;
152 :
153 0 : assert(ret);
154 :
155 0 : if (inotify_fd < 0)
156 0 : return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
157 : "Invalid inotify descriptor.");
158 :
159 0 : if (wd < 0)
160 0 : return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
161 : "Invalid watch handle.");
162 :
163 0 : xsprintf(filename, "/run/udev/watch/%d", wd);
164 0 : r = readlink_malloc(filename, &device);
165 0 : if (r == -ENOENT)
166 0 : return 0;
167 0 : if (r < 0)
168 0 : return log_debug_errno(r, "Failed to read link '%s': %m", filename);
169 :
170 0 : r = sd_device_new_from_device_id(ret, device);
171 0 : if (r == -ENODEV)
172 0 : return 0;
173 0 : if (r < 0)
174 0 : return log_debug_errno(r, "Failed to create sd_device object for '%s': %m", device);
175 :
176 0 : return 1;
177 : }
|