1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2
3 #include <getopt.h>
4 #include <unistd.h>
5
6 #include "sd-event.h"
7
8 #include "alloc-util.h"
9 #include "chase-symlinks.h"
10 #include "device-util.h"
11 #include "errno-util.h"
12 #include "fd-util.h"
13 #include "fs-util.h"
14 #include "inotify-util.h"
15 #include "parse-util.h"
16 #include "path-util.h"
17 #include "static-destruct.h"
18 #include "string-table.h"
19 #include "strv.h"
20 #include "udev-util.h"
21 #include "udevadm.h"
22
23 typedef enum WaitUntil {
24 WAIT_UNTIL_INITIALIZED,
25 WAIT_UNTIL_ADDED,
26 WAIT_UNTIL_REMOVED,
27 _WAIT_UNTIL_MAX,
28 _WAIT_UNTIL_INVALID = -EINVAL,
29 } WaitUntil;
30
31 static WaitUntil arg_wait_until = WAIT_UNTIL_INITIALIZED;
32 static usec_t arg_timeout_usec = USEC_INFINITY;
33 static bool arg_settle = false;
34 static char **arg_devices = NULL;
35
36 STATIC_DESTRUCTOR_REGISTER(arg_devices, strv_freep);
37
38 static const char * const wait_until_table[_WAIT_UNTIL_MAX] = {
39 [WAIT_UNTIL_INITIALIZED] = "initialized",
40 [WAIT_UNTIL_ADDED] = "added",
41 [WAIT_UNTIL_REMOVED] = "removed",
42 };
43
44 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(wait_until, WaitUntil);
45
check_device(const char * path)46 static int check_device(const char *path) {
47 _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
48 int r;
49
50 assert(path);
51
52 if (arg_wait_until == WAIT_UNTIL_REMOVED) {
53 r = laccess(path, F_OK);
54 if (r == -ENOENT)
55 return true;
56 if (r < 0)
57 return r;
58 return false;
59 }
60
61 r = sd_device_new_from_path(&dev, path);
62 if (r == -ENODEV)
63 return false;
64 if (r < 0)
65 return r;
66
67 if (arg_wait_until == WAIT_UNTIL_INITIALIZED)
68 return sd_device_get_is_initialized(dev);
69
70 return true;
71 }
72
check(void)73 static bool check(void) {
74 int r;
75
76 if (arg_settle) {
77 r = udev_queue_is_empty();
78 if (r == 0)
79 return false;
80 if (r < 0)
81 log_warning_errno(r, "Failed to check if udev queue is empty, assuming empty: %m");
82 }
83
84 STRV_FOREACH(p, arg_devices) {
85 r = check_device(*p);
86 if (r <= 0) {
87 if (r < 0)
88 log_warning_errno(r, "Failed to check if device \"%s\" is %s, assuming not %s: %m",
89 *p,
90 wait_until_to_string(arg_wait_until),
91 wait_until_to_string(arg_wait_until));
92 return false;
93 }
94 }
95
96 return true;
97 }
98
check_and_exit(sd_event * event)99 static int check_and_exit(sd_event *event) {
100 assert(event);
101
102 if (check())
103 return sd_event_exit(event, 0);
104
105 return 0;
106 }
107
device_monitor_handler(sd_device_monitor * monitor,sd_device * device,void * userdata)108 static int device_monitor_handler(sd_device_monitor *monitor, sd_device *device, void *userdata) {
109 const char *name;
110 int r;
111
112 assert(monitor);
113 assert(device);
114
115 if (device_for_action(device, SD_DEVICE_REMOVE) != (arg_wait_until == WAIT_UNTIL_REMOVED))
116 return 0;
117
118 if (arg_wait_until == WAIT_UNTIL_REMOVED)
119 /* On removed event, the received device may not contain enough information.
120 * Let's unconditionally check all requested devices are removed. */
121 return check_and_exit(sd_device_monitor_get_event(monitor));
122
123 /* For other events, at first check if the received device matches with the requested devices,
124 * to avoid calling check() so many times within a short time. */
125
126 r = sd_device_get_sysname(device, &name);
127 if (r < 0) {
128 log_device_warning_errno(device, r, "Failed to get sysname of received device, ignoring: %m");
129 return 0;
130 }
131
132 STRV_FOREACH(p, arg_devices) {
133 const char *s;
134
135 if (!path_startswith(*p, "/sys"))
136 continue;
137
138 r = path_find_last_component(*p, false, NULL, &s);
139 if (r < 0) {
140 log_warning_errno(r, "Failed to extract filename from \"%s\", ignoring: %m", *p);
141 continue;
142 }
143 if (r == 0)
144 continue;
145
146 if (strneq(s, name, r))
147 return check_and_exit(sd_device_monitor_get_event(monitor));
148 }
149
150 r = sd_device_get_devname(device, &name);
151 if (r < 0) {
152 if (r != -ENOENT)
153 log_device_warning_errno(device, r, "Failed to get devname of received device, ignoring: %m");
154 return 0;
155 }
156
157 if (path_strv_contains(arg_devices, name))
158 return check_and_exit(sd_device_monitor_get_event(monitor));
159
160 STRV_FOREACH(p, arg_devices) {
161 const char *link;
162
163 if (!path_startswith(*p, "/dev"))
164 continue;
165
166 FOREACH_DEVICE_DEVLINK(device, link)
167 if (path_equal(*p, link))
168 return check_and_exit(sd_device_monitor_get_event(monitor));
169 }
170
171 return 0;
172 }
173
setup_monitor(sd_event * event,sd_device_monitor ** ret)174 static int setup_monitor(sd_event *event, sd_device_monitor **ret) {
175 _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL;
176 int r;
177
178 assert(event);
179 assert(ret);
180
181 r = sd_device_monitor_new(&monitor);
182 if (r < 0)
183 return r;
184
185 (void) sd_device_monitor_set_receive_buffer_size(monitor, 128*1024*1024);
186
187 r = sd_device_monitor_attach_event(monitor, event);
188 if (r < 0)
189 return r;
190
191 r = sd_device_monitor_start(monitor, device_monitor_handler, NULL);
192 if (r < 0)
193 return r;
194
195 r = sd_event_source_set_description(sd_device_monitor_get_event_source(monitor),
196 "device-monitor-event-source");
197 if (r < 0)
198 return r;
199
200 *ret = TAKE_PTR(monitor);
201 return 0;
202 }
203
on_inotify(sd_event_source * s,const struct inotify_event * event,void * userdata)204 static int on_inotify(sd_event_source *s, const struct inotify_event *event, void *userdata) {
205 return check_and_exit(sd_event_source_get_event(s));
206 }
207
setup_inotify(sd_event * event)208 static int setup_inotify(sd_event *event) {
209 _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
210 int r;
211
212 assert(event);
213
214 if (!arg_settle)
215 return 0;
216
217 r = sd_event_add_inotify(event, &s, "/run/udev" , IN_CREATE | IN_DELETE, on_inotify, NULL);
218 if (r < 0)
219 return r;
220
221 r = sd_event_source_set_description(s, "inotify-event-source");
222 if (r < 0)
223 return r;
224
225 return sd_event_source_set_floating(s, true);
226 }
227
setup_timer(sd_event * event)228 static int setup_timer(sd_event *event) {
229 _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
230 int r;
231
232 assert(event);
233
234 if (arg_timeout_usec == USEC_INFINITY)
235 return 0;
236
237 r = sd_event_add_time_relative(event, &s, CLOCK_BOOTTIME, arg_timeout_usec, 0,
238 NULL, INT_TO_PTR(-ETIMEDOUT));
239 if (r < 0)
240 return r;
241
242 r = sd_event_source_set_description(s, "timeout-event-source");
243 if (r < 0)
244 return r;
245
246 return sd_event_source_set_floating(s, true);
247 }
248
help(void)249 static int help(void) {
250 printf("%s wait [OPTIONS] DEVICE [DEVICE…]\n\n"
251 "Wait for devices or device symlinks being created.\n\n"
252 " -h --help Print this message\n"
253 " -V --version Print version of the program\n"
254 " -t --timeout=SEC Maximum time to wait for the device\n"
255 " --initialized=BOOL Wait for devices being initialized by systemd-udevd\n"
256 " --removed Wait for devices being removed\n"
257 " --settle Also wait for all queued events being processed\n",
258 program_invocation_short_name);
259
260 return 0;
261 }
262
parse_argv(int argc,char * argv[])263 static int parse_argv(int argc, char *argv[]) {
264 enum {
265 ARG_INITIALIZED = 0x100,
266 ARG_REMOVED,
267 ARG_SETTLE,
268 };
269
270 static const struct option options[] = {
271 { "timeout", required_argument, NULL, 't' },
272 { "initialized", required_argument, NULL, ARG_INITIALIZED },
273 { "removed", no_argument, NULL, ARG_REMOVED },
274 { "settle", no_argument, NULL, ARG_SETTLE },
275 { "help", no_argument, NULL, 'h' },
276 { "version", no_argument, NULL, 'V' },
277 {}
278 };
279
280 int c, r;
281
282 while ((c = getopt_long(argc, argv, "t:hV", options, NULL)) >= 0)
283 switch (c) {
284 case 't':
285 r = parse_sec(optarg, &arg_timeout_usec);
286 if (r < 0)
287 return log_error_errno(r, "Failed to parse -t/--timeout= parameter: %s", optarg);
288 break;
289
290 case ARG_INITIALIZED:
291 r = parse_boolean(optarg);
292 if (r < 0)
293 return log_error_errno(r, "Failed to parse --initialized= parameter: %s", optarg);
294 arg_wait_until = r ? WAIT_UNTIL_INITIALIZED : WAIT_UNTIL_ADDED;
295 break;
296
297 case ARG_REMOVED:
298 arg_wait_until = WAIT_UNTIL_REMOVED;
299 break;
300
301 case ARG_SETTLE:
302 arg_settle = true;
303 break;
304
305 case 'V':
306 return print_version();
307
308 case 'h':
309 return help();
310
311 case '?':
312 return -EINVAL;
313
314 default:
315 assert_not_reached();
316 }
317
318 if (optind >= argc)
319 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
320 "Too few arguments, expected at least one device path or device symlink.");
321
322 arg_devices = strv_copy(argv + optind);
323 if (!arg_devices)
324 return log_oom();
325
326 return 1; /* work to do */
327 }
328
wait_main(int argc,char * argv[],void * userdata)329 int wait_main(int argc, char *argv[], void *userdata) {
330 _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL;
331 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
332 int r;
333
334 r = parse_argv(argc, argv);
335 if (r <= 0)
336 return r;
337
338 STRV_FOREACH(p, arg_devices) {
339 path_simplify(*p);
340
341 if (!path_is_safe(*p))
342 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
343 "Device path cannot contain \"..\".");
344
345 if (!is_device_path(*p))
346 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
347 "Specified path \"%s\" does not start with \"/dev/\" or \"/sys/\".", *p);
348 }
349
350 /* Check before configuring event sources, as devices may be already initialized. */
351 if (check())
352 return 0;
353
354 r = sd_event_default(&event);
355 if (r < 0)
356 return log_error_errno(r, "Failed to initialize sd-event: %m");
357
358 r = setup_timer(event);
359 if (r < 0)
360 return log_error_errno(r, "Failed to set up timeout: %m");
361
362 r = setup_inotify(event);
363 if (r < 0)
364 return log_error_errno(r, "Failed to set up inotify: %m");
365
366 r = setup_monitor(event, &monitor);
367 if (r < 0)
368 return log_error_errno(r, "Failed to set up device monitor: %m");
369
370 /* Check before entering the event loop, as devices may be initialized during setting up event sources. */
371 if (check())
372 return 0;
373
374 r = sd_event_loop(event);
375 if (r == -ETIMEDOUT)
376 return log_error_errno(r, "Timed out for waiting devices being %s.",
377 wait_until_to_string(arg_wait_until));
378 if (r < 0)
379 return log_error_errno(r, "Event loop failed: %m");
380
381 return 0;
382 }
383