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