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