]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/udevadm-info.c
udevadm: add more assertions
[thirdparty/systemd.git] / src / udev / udevadm-info.c
CommitLineData
f13467ec 1/* SPDX-License-Identifier: GPL-2.0-or-later */
be9b51f6 2
034f35d7 3#include <ctype.h>
87171e46 4#include <errno.h>
e6c1a2bd 5#include <fcntl.h>
07630cea
LP
6#include <getopt.h>
7#include <stddef.h>
8#include <stdio.h>
492e76c9 9#include <sys/stat.h>
07630cea 10#include <unistd.h>
be9b51f6 11
13aca847
YW
12#include "sd-device.h"
13
c4abe719 14#include "alloc-util.h"
13aca847
YW
15#include "device-enumerator-private.h"
16#include "device-private.h"
17#include "device-util.h"
8fb3f009 18#include "dirent-util.h"
3ffd4af2 19#include "fd-util.h"
21df1465 20#include "sort-util.h"
6c1482b2 21#include "static-destruct.h"
0f4b93c4 22#include "string-table.h"
07630cea 23#include "string-util.h"
ae760f4b 24#include "udev-util.h"
d6170d27 25#include "udevadm-util.h"
0f4b93c4 26#include "udevadm.h"
be9b51f6 27
668e7c0c
ZJS
28typedef enum ActionType {
29 ACTION_QUERY,
30 ACTION_ATTRIBUTE_WALK,
31 ACTION_DEVICE_ID_FILE,
32} ActionType;
33
34typedef enum QueryType {
35 QUERY_NAME,
36 QUERY_PATH,
37 QUERY_SYMLINK,
38 QUERY_PROPERTY,
39 QUERY_ALL,
40} QueryType;
41
6c1482b2 42static char **arg_properties = NULL;
668e7c0c
ZJS
43static bool arg_root = false;
44static bool arg_export = false;
6c1482b2 45static bool arg_value = false;
668e7c0c 46static const char *arg_export_prefix = NULL;
ae760f4b 47static usec_t arg_wait_for_initialization_timeout = 0;
668e7c0c 48
9ec6e95b 49static bool skip_attribute(const char *name) {
4881a0d2
YW
50 assert(name);
51
5992f362
ZJS
52 /* Those are either displayed separately or should not be shown at all. */
53 return STR_IN_SET(name,
54 "uevent",
55 "dev",
56 "modalias",
57 "resource",
58 "driver",
59 "subsystem",
60 "module");
20bee04c
KS
61}
62
21df1465
YW
63typedef struct SysAttr {
64 const char *name;
65 const char *value;
66} SysAttr;
67
6c1482b2
FS
68STATIC_DESTRUCTOR_REGISTER(arg_properties, strv_freep);
69
21df1465 70static int sysattr_compare(const SysAttr *a, const SysAttr *b) {
4881a0d2
YW
71 assert(a);
72 assert(b);
73
21df1465
YW
74 return strcmp(a->name, b->name);
75}
76
77static int print_all_attributes(sd_device *device, bool is_parent) {
78 _cleanup_free_ SysAttr *sysattrs = NULL;
13aca847 79 const char *name, *value;
319a4f4b 80 size_t n_items = 0;
3a90bef5 81 int r;
912541b0 82
4881a0d2
YW
83 assert(device);
84
21df1465
YW
85 value = NULL;
86 (void) sd_device_get_devpath(device, &value);
87 printf(" looking at %sdevice '%s':\n", is_parent ? "parent " : "", strempty(value));
88
89 value = NULL;
90 (void) sd_device_get_sysname(device, &value);
91 printf(" %s==\"%s\"\n", is_parent ? "KERNELS" : "KERNEL", strempty(value));
92
93 value = NULL;
94 (void) sd_device_get_subsystem(device, &value);
95 printf(" %s==\"%s\"\n", is_parent ? "SUBSYSTEMS" : "SUBSYSTEM", strempty(value));
96
97 value = NULL;
98 (void) sd_device_get_driver(device, &value);
99 printf(" %s==\"%s\"\n", is_parent ? "DRIVERS" : "DRIVER", strempty(value));
100
6d6308f6 101 FOREACH_DEVICE_SYSATTR(device, name) {
912541b0
KS
102 size_t len;
103
912541b0
KS
104 if (skip_attribute(name))
105 continue;
106
3a90bef5
YW
107 r = sd_device_get_sysattr_value(device, name, &value);
108 if (r >= 0) {
109 /* skip any values that look like a path */
110 if (value[0] == '/')
111 continue;
112
113 /* skip nonprintable attributes */
114 len = strlen(value);
115 while (len > 0 && isprint((unsigned char) value[len-1]))
116 len--;
117 if (len > 0)
118 continue;
119
120 } else if (r == -EPERM)
121 value = "(write-only)";
122 else
912541b0 123 continue;
912541b0 124
319a4f4b 125 if (!GREEDY_REALLOC(sysattrs, n_items + 1))
21df1465
YW
126 return log_oom();
127
128 sysattrs[n_items] = (SysAttr) {
129 .name = name,
130 .value = value,
131 };
132 n_items++;
912541b0 133 }
21df1465
YW
134
135 typesafe_qsort(sysattrs, n_items, sysattr_compare);
136
137 for (size_t i = 0; i < n_items; i++)
138 printf(" %s{%s}==\"%s\"\n", is_parent ? "ATTRS" : "ATTR", sysattrs[i].name, sysattrs[i].value);
139
d539f791 140 puts("");
21df1465
YW
141
142 return 0;
be9b51f6
KS
143}
144
13aca847
YW
145static int print_device_chain(sd_device *device) {
146 sd_device *child, *parent;
21df1465 147 int r;
912541b0 148
4881a0d2
YW
149 assert(device);
150
912541b0
KS
151 printf("\n"
152 "Udevadm info starts with the device specified by the devpath and then\n"
153 "walks up the chain of parent devices. It prints for every device\n"
154 "found, all possible attributes in the udev rules key format.\n"
155 "A rule to match, can be composed by the attributes of the device\n"
156 "and the attributes from one single parent device.\n"
157 "\n");
158
21df1465
YW
159 r = print_all_attributes(device, false);
160 if (r < 0)
161 return r;
912541b0 162
13aca847 163 for (child = device; sd_device_get_parent(child, &parent) >= 0; child = parent) {
21df1465
YW
164 r = print_all_attributes(parent, true);
165 if (r < 0)
166 return r;
13aca847 167 }
912541b0
KS
168
169 return 0;
1aa1e248
KS
170}
171
668e7c0c 172static int print_record(sd_device *device) {
13aca847 173 const char *str, *val;
912541b0 174 int i;
912541b0 175
4881a0d2
YW
176 assert(device);
177
13aca847
YW
178 (void) sd_device_get_devpath(device, &str);
179 printf("P: %s\n", str);
912541b0 180
d539f791
ZJS
181 if (sd_device_get_devname(device, &str) >= 0) {
182 assert_se(val = path_startswith(str, "/dev/"));
183 printf("N: %s\n", val);
184 }
912541b0 185
13aca847 186 if (device_get_devlink_priority(device, &i) >= 0)
912541b0
KS
187 printf("L: %i\n", i);
188
d539f791
ZJS
189 FOREACH_DEVICE_DEVLINK(device, str) {
190 assert_se(val = path_startswith(str, "/dev/"));
191 printf("S: %s\n", val);
192 }
13aca847
YW
193
194 FOREACH_DEVICE_PROPERTY(device, str, val)
195 printf("E: %s=%s\n", str, val);
912541b0 196
d539f791 197 puts("");
668e7c0c 198 return 0;
31de3a2b
KS
199}
200
9ec6e95b 201static int stat_device(const char *name, bool export, const char *prefix) {
912541b0
KS
202 struct stat statbuf;
203
4881a0d2
YW
204 assert(name);
205
912541b0 206 if (stat(name, &statbuf) != 0)
755700bb 207 return -errno;
912541b0
KS
208
209 if (export) {
13aca847 210 if (!prefix)
912541b0 211 prefix = "INFO_";
1fa2f38f
ZJS
212 printf("%sMAJOR=%u\n"
213 "%sMINOR=%u\n",
912541b0
KS
214 prefix, major(statbuf.st_dev),
215 prefix, minor(statbuf.st_dev));
216 } else
1fa2f38f 217 printf("%u:%u\n", major(statbuf.st_dev), minor(statbuf.st_dev));
912541b0 218 return 0;
f338bac8
KS
219}
220
2024ed61 221static int export_devices(void) {
13aca847
YW
222 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
223 sd_device *d;
224 int r;
912541b0 225
13aca847
YW
226 r = sd_device_enumerator_new(&e);
227 if (r < 0)
df5a4889 228 return log_oom();
755700bb 229
13aca847
YW
230 r = sd_device_enumerator_allow_uninitialized(e);
231 if (r < 0)
df5a4889 232 return log_error_errno(r, "Failed to set allowing uninitialized flag: %m");
912541b0 233
13aca847
YW
234 r = device_enumerator_scan_devices(e);
235 if (r < 0)
df5a4889 236 return log_error_errno(r, "Failed to scan devices: %m");
13aca847
YW
237
238 FOREACH_DEVICE_AND_SUBSYSTEM(e, d)
df5a4889 239 (void) print_record(d);
755700bb 240
912541b0 241 return 0;
bf7ad0ea
KS
242}
243
9ec6e95b 244static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
4881a0d2
YW
245 assert(dir);
246
912541b0
KS
247 if (depth <= 0)
248 return;
249
8fb3f009 250 FOREACH_DIRENT_ALL(dent, dir, break) {
912541b0
KS
251 struct stat stats;
252
28d6e854 253 if (dot_or_dot_dot(dent->d_name))
912541b0 254 continue;
28d6e854 255 if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) < 0)
912541b0
KS
256 continue;
257 if ((stats.st_mode & mask) != 0)
258 continue;
259 if (S_ISDIR(stats.st_mode)) {
13aca847 260 _cleanup_closedir_ DIR *dir2 = NULL;
912541b0
KS
261
262 dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
13aca847 263 if (dir2)
912541b0 264 cleanup_dir(dir2, mask, depth-1);
200c7fa6
LP
265
266 (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
267 } else
268 (void) unlinkat(dirfd(dir), dent->d_name, 0);
912541b0 269 }
9ead6627
KS
270}
271
7ec62414
MW
272/*
273 * Assume that dir is a directory with file names matching udev data base
274 * entries for devices in /run/udev/data (such as "b8:16"), and removes
275 * all files except those that haven't been deleted in /run/udev/data
276 * (i.e. they were skipped during db cleanup because of the db_persist flag).
7ec62414 277 */
636ab001 278static void cleanup_dir_after_db_cleanup(DIR *dir, DIR *datadir) {
9e0bd1d6
YW
279 assert(dir);
280 assert(datadir);
7ec62414
MW
281
282 FOREACH_DIRENT_ALL(dent, dir, break) {
7ec62414
MW
283 if (dot_or_dot_dot(dent->d_name))
284 continue;
636ab001
YW
285
286 if (faccessat(dirfd(datadir), dent->d_name, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
287 /* The corresponding udev database file still exists.
288 * Assuming the parsistent flag is set for the database. */
7ec62414 289 continue;
7ec62414 290
636ab001 291 (void) unlinkat(dirfd(dir), dent->d_name, 0);
7ec62414 292 }
7ec62414
MW
293}
294
295static void cleanup_dirs_after_db_cleanup(DIR *dir, DIR *datadir) {
9e0bd1d6
YW
296 assert(dir);
297 assert(datadir);
7ec62414
MW
298
299 FOREACH_DIRENT_ALL(dent, dir, break) {
300 struct stat stats;
301
302 if (dot_or_dot_dot(dent->d_name))
303 continue;
304 if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) < 0)
305 continue;
306 if (S_ISDIR(stats.st_mode)) {
307 _cleanup_closedir_ DIR *dir2 = NULL;
308
309 dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
636ab001
YW
310 if (dir2)
311 cleanup_dir_after_db_cleanup(dir2, datadir);
312
313 (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
7ec62414
MW
314 } else
315 (void) unlinkat(dirfd(dir), dent->d_name, 0);
316 }
317}
318
2024ed61 319static void cleanup_db(void) {
bd979801 320 _cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL;
912541b0 321
755700bb 322 dir1 = opendir("/run/udev/data");
13aca847 323 if (dir1)
755700bb 324 cleanup_dir(dir1, S_ISVTX, 1);
912541b0 325
755700bb 326 dir2 = opendir("/run/udev/links");
13aca847 327 if (dir2)
7ec62414 328 cleanup_dirs_after_db_cleanup(dir2, dir1);
912541b0 329
755700bb 330 dir3 = opendir("/run/udev/tags");
13aca847 331 if (dir3)
7ec62414 332 cleanup_dirs_after_db_cleanup(dir3, dir1);
912541b0 333
755700bb 334 dir4 = opendir("/run/udev/static_node-tags");
13aca847 335 if (dir4)
755700bb 336 cleanup_dir(dir4, 0, 2);
84b6ad70 337
bd979801
YW
338 /* Do not remove /run/udev/watch. It will be handled by udevd well on restart.
339 * And should not be removed by external program when udevd is running. */
9ead6627
KS
340}
341
668e7c0c
ZJS
342static int query_device(QueryType query, sd_device* device) {
343 int r;
344
345 assert(device);
346
347 switch(query) {
348 case QUERY_NAME: {
349 const char *node;
350
351 r = sd_device_get_devname(device, &node);
352 if (r < 0)
353 return log_error_errno(r, "No device node found: %m");
354
d539f791
ZJS
355 if (!arg_root)
356 assert_se(node = path_startswith(node, "/dev/"));
357 printf("%s\n", node);
668e7c0c
ZJS
358 return 0;
359 }
360
361 case QUERY_SYMLINK: {
d539f791 362 const char *devlink, *prefix = "";
668e7c0c
ZJS
363
364 FOREACH_DEVICE_DEVLINK(device, devlink) {
d539f791
ZJS
365 if (!arg_root)
366 assert_se(devlink = path_startswith(devlink, "/dev/"));
367 printf("%s%s", prefix, devlink);
368 prefix = " ";
668e7c0c 369 }
d539f791 370 puts("");
668e7c0c
ZJS
371 return 0;
372 }
373
374 case QUERY_PATH: {
375 const char *devpath;
5ac0162c 376
668e7c0c
ZJS
377 r = sd_device_get_devpath(device, &devpath);
378 if (r < 0)
379 return log_error_errno(r, "Failed to get device path: %m");
380
381 printf("%s\n", devpath);
382 return 0;
383 }
384
385 case QUERY_PROPERTY: {
386 const char *key, *value;
387
6c1482b2
FS
388 FOREACH_DEVICE_PROPERTY(device, key, value) {
389 if (arg_properties && !strv_contains(arg_properties, key))
390 continue;
391
668e7c0c
ZJS
392 if (arg_export)
393 printf("%s%s='%s'\n", strempty(arg_export_prefix), key, value);
6c1482b2
FS
394 else if (arg_value)
395 printf("%s\n", value);
668e7c0c
ZJS
396 else
397 printf("%s=%s\n", key, value);
6c1482b2
FS
398 }
399
668e7c0c
ZJS
400 return 0;
401 }
402
403 case QUERY_ALL:
404 return print_record(device);
668e7c0c 405
4881a0d2
YW
406 default:
407 assert_not_reached();
408 }
668e7c0c
ZJS
409}
410
411static int help(void) {
5ac0162c
LP
412 printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
413 "Query sysfs or the udev database.\n\n"
414 " -h --help Print this message\n"
73527992 415 " -V --version Print version of the program\n"
5ac0162c
LP
416 " -q --query=TYPE Query device information:\n"
417 " name Name of device node\n"
418 " symlink Pointing to node\n"
419 " path sysfs device path\n"
420 " property The device properties\n"
421 " all All values\n"
6c1482b2
FS
422 " --property=NAME Show only properties by this name\n"
423 " --value When showing properties, print only their values\n"
5ac0162c
LP
424 " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
425 " -n --name=NAME Node or symlink name used for query or attribute walk\n"
426 " -r --root Prepend dev directory to path names\n"
427 " -a --attribute-walk Print all key matches walking along the chain\n"
428 " of parent devices\n"
429 " -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
430 " -x --export Export key/value pairs\n"
431 " -P --export-prefix Export the key name with a prefix\n"
432 " -e --export-db Export the content of the udev database\n"
433 " -c --cleanup-db Clean up the udev database\n"
ae760f4b 434 " -w --wait-for-initialization[=SECONDS]\n"
bc556335
DDM
435 " Wait for device to be initialized\n",
436 program_invocation_short_name);
ee4a776d
YW
437
438 return 0;
5ac0162c
LP
439}
440
3d05193e 441int info_main(int argc, char *argv[], void *userdata) {
3c79311a 442 _cleanup_strv_free_ char **devices = NULL;
c4abe719 443 _cleanup_free_ char *name = NULL;
ee4a776d 444 int c, r;
912541b0 445
6c1482b2
FS
446 enum {
447 ARG_PROPERTY = 0x100,
448 ARG_VALUE,
449 };
450
912541b0 451 static const struct option options[] = {
6c1482b2
FS
452 { "attribute-walk", no_argument, NULL, 'a' },
453 { "cleanup-db", no_argument, NULL, 'c' },
454 { "device-id-of-file", required_argument, NULL, 'd' },
455 { "export", no_argument, NULL, 'x' },
456 { "export-db", no_argument, NULL, 'e' },
457 { "export-prefix", required_argument, NULL, 'P' },
458 { "help", no_argument, NULL, 'h' },
459 { "name", required_argument, NULL, 'n' },
460 { "path", required_argument, NULL, 'p' },
461 { "property", required_argument, NULL, ARG_PROPERTY },
462 { "query", required_argument, NULL, 'q' },
463 { "root", no_argument, NULL, 'r' },
464 { "value", no_argument, NULL, ARG_VALUE },
465 { "version", no_argument, NULL, 'V' },
466 { "wait-for-initialization", optional_argument, NULL, 'w' },
912541b0
KS
467 {}
468 };
469
668e7c0c
ZJS
470 ActionType action = ACTION_QUERY;
471 QueryType query = QUERY_ALL;
912541b0 472
ae760f4b 473 while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:w::Vh", options, NULL)) >= 0)
7643ac9a 474 switch (c) {
6c1482b2
FS
475 case ARG_PROPERTY:
476 /* Make sure that if the empty property list was specified, we won't show any
477 properties. */
478 if (isempty(optarg) && !arg_properties) {
479 arg_properties = new0(char*, 1);
480 if (!arg_properties)
481 return log_oom();
482 } else {
483 r = strv_split_and_extend(&arg_properties, optarg, ",", true);
484 if (r < 0)
485 return log_oom();
486 }
487 break;
488 case ARG_VALUE:
489 arg_value = true;
490 break;
ee4a776d 491 case 'n':
3c79311a
ZJS
492 case 'p': {
493 const char *prefix = c == 'n' ? "/dev/" : "/sys/";
494 char *path;
4f5d327a 495
3c79311a
ZJS
496 path = path_join(path_startswith(optarg, prefix) ? NULL : prefix, optarg);
497 if (!path)
498 return log_oom();
4f5d327a 499
3c79311a 500 r = strv_consume(&devices, path);
13aca847 501 if (r < 0)
3c79311a 502 return log_oom();
912541b0 503 break;
3c79311a
ZJS
504 }
505
912541b0
KS
506 case 'q':
507 action = ACTION_QUERY;
44433ebd 508 if (streq(optarg, "property") || streq(optarg, "env"))
912541b0 509 query = QUERY_PROPERTY;
44433ebd 510 else if (streq(optarg, "name"))
912541b0 511 query = QUERY_NAME;
44433ebd 512 else if (streq(optarg, "symlink"))
912541b0 513 query = QUERY_SYMLINK;
44433ebd 514 else if (streq(optarg, "path"))
912541b0 515 query = QUERY_PATH;
44433ebd 516 else if (streq(optarg, "all"))
912541b0 517 query = QUERY_ALL;
47c8fcbe
YW
518 else
519 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "unknown query type");
912541b0
KS
520 break;
521 case 'r':
668e7c0c 522 arg_root = true;
912541b0 523 break;
912541b0
KS
524 case 'd':
525 action = ACTION_DEVICE_ID_FILE;
1cb7d29d
YW
526 r = free_and_strdup(&name, optarg);
527 if (r < 0)
c4abe719 528 return log_oom();
912541b0
KS
529 break;
530 case 'a':
531 action = ACTION_ATTRIBUTE_WALK;
532 break;
533 case 'e':
ee4a776d 534 return export_devices();
912541b0 535 case 'c':
2024ed61 536 cleanup_db();
44433ebd 537 return 0;
912541b0 538 case 'x':
668e7c0c 539 arg_export = true;
912541b0
KS
540 break;
541 case 'P':
2277e845 542 arg_export = true;
668e7c0c 543 arg_export_prefix = optarg;
912541b0 544 break;
ae760f4b
YW
545 case 'w':
546 if (optarg) {
547 r = parse_sec(optarg, &arg_wait_for_initialization_timeout);
548 if (r < 0)
549 return log_error_errno(r, "Failed to parse timeout value: %m");
550 } else
551 arg_wait_for_initialization_timeout = USEC_INFINITY;
552 break;
912541b0 553 case 'V':
51b006e1 554 return print_version();
912541b0 555 case 'h':
ee4a776d
YW
556 return help();
557 case '?':
558 return -EINVAL;
912541b0 559 default:
04499a70 560 assert_not_reached();
912541b0 561 }
912541b0 562
3c79311a
ZJS
563 if (action == ACTION_DEVICE_ID_FILE) {
564 if (argv[optind])
668e7c0c 565 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
3c79311a
ZJS
566 "Positional arguments are not allowed with -d/--device-id-of-file.");
567 assert(name);
568 return stat_device(name, arg_export, arg_export_prefix);
668e7c0c 569 }
13aca847 570
3c79311a
ZJS
571 r = strv_extend_strv(&devices, argv + optind, false);
572 if (r < 0)
573 return log_error_errno(r, "Failed to build argument list: %m");
19a29798 574
3c79311a
ZJS
575 if (strv_isempty(devices))
576 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
577 "A device name or path is required");
578 if (action == ACTION_ATTRIBUTE_WALK && strv_length(devices) > 1)
579 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
580 "Only one device may be specified with -a/--attribute-walk");
668e7c0c 581
6c1482b2
FS
582 if (arg_export && arg_value)
583 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
584 "-x/--export or -P/--export-prefix cannot be used with --value");
585
3c79311a
ZJS
586 char **p;
587 STRV_FOREACH(p, devices) {
588 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
589
590 r = find_device(*p, NULL, &device);
591 if (r == -EINVAL)
592 return log_error_errno(r, "Bad argument \"%s\", expected an absolute path in /dev/ or /sys or a unit name: %m", *p);
593 if (r < 0)
594 return log_error_errno(r, "Unknown device \"%s\": %m", *p);
595
ae760f4b 596 if (arg_wait_for_initialization_timeout > 0) {
7a555216
YW
597 sd_device *d;
598
9e3d9067
LP
599 r = device_wait_for_initialization(
600 device,
601 NULL,
602 usec_add(now(CLOCK_MONOTONIC), arg_wait_for_initialization_timeout),
603 &d);
ae760f4b
YW
604 if (r < 0)
605 return r;
7a555216
YW
606
607 sd_device_unref(device);
608 device = d;
ae760f4b
YW
609 }
610
3c79311a
ZJS
611 if (action == ACTION_QUERY)
612 r = query_device(query, device);
613 else if (action == ACTION_ATTRIBUTE_WALK)
614 r = print_device_chain(device);
615 else
04499a70 616 assert_not_reached();
3c79311a
ZJS
617 if (r < 0)
618 return r;
912541b0 619 }
87171e46 620
3c79311a 621 return 0;
87171e46 622}