]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udevadm-info.c
Merge pull request #22759 from msekletar/issue-18077-long-sysfs-paths-hashing
[thirdparty/systemd.git] / src / udev / udevadm-info.c
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2
3 #include <ctype.h>
4 #include <errno.h>
5 #include <fcntl.h>
6 #include <getopt.h>
7 #include <stddef.h>
8 #include <stdio.h>
9 #include <sys/stat.h>
10 #include <unistd.h>
11
12 #include "sd-device.h"
13
14 #include "alloc-util.h"
15 #include "device-enumerator-private.h"
16 #include "device-private.h"
17 #include "device-util.h"
18 #include "dirent-util.h"
19 #include "errno-util.h"
20 #include "fd-util.h"
21 #include "fileio.h"
22 #include "glyph-util.h"
23 #include "pager.h"
24 #include "sort-util.h"
25 #include "static-destruct.h"
26 #include "string-table.h"
27 #include "string-util.h"
28 #include "terminal-util.h"
29 #include "udev-util.h"
30 #include "udevadm.h"
31 #include "udevadm-util.h"
32
33 typedef enum ActionType {
34 ACTION_QUERY,
35 ACTION_ATTRIBUTE_WALK,
36 ACTION_DEVICE_ID_FILE,
37 ACTION_TREE,
38 } ActionType;
39
40 typedef enum QueryType {
41 QUERY_NAME,
42 QUERY_PATH,
43 QUERY_SYMLINK,
44 QUERY_PROPERTY,
45 QUERY_ALL,
46 } QueryType;
47
48 static char **arg_properties = NULL;
49 static bool arg_root = false;
50 static bool arg_export = false;
51 static bool arg_value = false;
52 static const char *arg_export_prefix = NULL;
53 static usec_t arg_wait_for_initialization_timeout = 0;
54
55 /* Put a limit on --tree descent level to not exhaust our stack */
56 #define TREE_DEPTH_MAX 64
57
58 static bool skip_attribute(const char *name) {
59 assert(name);
60
61 /* Those are either displayed separately or should not be shown at all. */
62 return STR_IN_SET(name,
63 "uevent",
64 "dev",
65 "modalias",
66 "resource",
67 "driver",
68 "subsystem",
69 "module");
70 }
71
72 typedef struct SysAttr {
73 const char *name;
74 const char *value;
75 } SysAttr;
76
77 STATIC_DESTRUCTOR_REGISTER(arg_properties, strv_freep);
78
79 static int sysattr_compare(const SysAttr *a, const SysAttr *b) {
80 assert(a);
81 assert(b);
82
83 return strcmp(a->name, b->name);
84 }
85
86 static int print_all_attributes(sd_device *device, bool is_parent) {
87 _cleanup_free_ SysAttr *sysattrs = NULL;
88 const char *name, *value;
89 size_t n_items = 0;
90 int r;
91
92 assert(device);
93
94 value = NULL;
95 (void) sd_device_get_devpath(device, &value);
96 printf(" looking at %sdevice '%s':\n", is_parent ? "parent " : "", strempty(value));
97
98 value = NULL;
99 (void) sd_device_get_sysname(device, &value);
100 printf(" %s==\"%s\"\n", is_parent ? "KERNELS" : "KERNEL", strempty(value));
101
102 value = NULL;
103 (void) sd_device_get_subsystem(device, &value);
104 printf(" %s==\"%s\"\n", is_parent ? "SUBSYSTEMS" : "SUBSYSTEM", strempty(value));
105
106 value = NULL;
107 (void) sd_device_get_driver(device, &value);
108 printf(" %s==\"%s\"\n", is_parent ? "DRIVERS" : "DRIVER", strempty(value));
109
110 FOREACH_DEVICE_SYSATTR(device, name) {
111 size_t len;
112
113 if (skip_attribute(name))
114 continue;
115
116 r = sd_device_get_sysattr_value(device, name, &value);
117 if (r >= 0) {
118 /* skip any values that look like a path */
119 if (value[0] == '/')
120 continue;
121
122 /* skip nonprintable attributes */
123 len = strlen(value);
124 while (len > 0 && isprint((unsigned char) value[len-1]))
125 len--;
126 if (len > 0)
127 continue;
128
129 } else if (ERRNO_IS_PRIVILEGE(r))
130 value = "(not readable)";
131 else
132 continue;
133
134 if (!GREEDY_REALLOC(sysattrs, n_items + 1))
135 return log_oom();
136
137 sysattrs[n_items] = (SysAttr) {
138 .name = name,
139 .value = value,
140 };
141 n_items++;
142 }
143
144 typesafe_qsort(sysattrs, n_items, sysattr_compare);
145
146 for (size_t i = 0; i < n_items; i++)
147 printf(" %s{%s}==\"%s\"\n", is_parent ? "ATTRS" : "ATTR", sysattrs[i].name, sysattrs[i].value);
148
149 puts("");
150
151 return 0;
152 }
153
154 static int print_device_chain(sd_device *device) {
155 sd_device *child, *parent;
156 int r;
157
158 assert(device);
159
160 printf("\n"
161 "Udevadm info starts with the device specified by the devpath and then\n"
162 "walks up the chain of parent devices. It prints for every device\n"
163 "found, all possible attributes in the udev rules key format.\n"
164 "A rule to match, can be composed by the attributes of the device\n"
165 "and the attributes from one single parent device.\n"
166 "\n");
167
168 r = print_all_attributes(device, false);
169 if (r < 0)
170 return r;
171
172 for (child = device; sd_device_get_parent(child, &parent) >= 0; child = parent) {
173 r = print_all_attributes(parent, true);
174 if (r < 0)
175 return r;
176 }
177
178 return 0;
179 }
180
181 static int print_record(sd_device *device, const char *prefix) {
182 const char *str, *val, *subsys;
183 dev_t devnum;
184 uint64_t q;
185 int i, ifi;
186
187 assert(device);
188
189 prefix = strempty(prefix);
190
191 /* We don't show syspath here, because it's identical to devpath (modulo the "/sys" prefix).
192 *
193 * We don't show action/seqnum here because that only makes sense for records synthesized from
194 * uevents, not for those synthesized from database entries.
195 *
196 * We don't show sysattrs here, because they can be expensive and potentially issue expensive driver
197 * IO.
198 *
199 * Coloring: let's be conservative with coloring. Let's use it to group related fields. Right now:
200 *
201 * • white for fields that give the device a name
202 * • green for fields that categorize the device into subsystem/devtype and similar
203 * • cyan for fields about associated device nodes/symlinks/network interfaces and such
204 * • magenta for block device diskseq
205 * • yellow for driver info
206 * • no color for regular properties */
207
208 assert_se(sd_device_get_devpath(device, &str) >= 0);
209 printf("%sP: %s%s%s\n", prefix, ansi_highlight_white(), str, ansi_normal());
210
211 if (sd_device_get_sysname(device, &str) >= 0)
212 printf("%sM: %s%s%s\n", prefix, ansi_highlight_white(), str, ansi_normal());
213
214 if (sd_device_get_sysnum(device, &str) >= 0)
215 printf("%sR: %s%s%s\n", prefix, ansi_highlight_white(), str, ansi_normal());
216
217 if (sd_device_get_subsystem(device, &subsys) >= 0)
218 printf("%sU: %s%s%s\n", prefix, ansi_highlight_green(), subsys, ansi_normal());
219
220 if (sd_device_get_devtype(device, &str) >= 0)
221 printf("%sT: %s%s%s\n", prefix, ansi_highlight_green(), str, ansi_normal());
222
223 if (sd_device_get_devnum(device, &devnum) >= 0)
224 printf("%sD: %s%c %u:%u%s\n",
225 prefix,
226 ansi_highlight_cyan(),
227 streq_ptr(subsys, "block") ? 'b' : 'c', major(devnum), minor(devnum),
228 ansi_normal());
229
230 if (sd_device_get_ifindex(device, &ifi) >= 0)
231 printf("%sI: %s%i%s\n", prefix, ansi_highlight_cyan(), ifi, ansi_normal());
232
233 if (sd_device_get_devname(device, &str) >= 0) {
234 assert_se(val = path_startswith(str, "/dev/"));
235 printf("%sN: %s%s%s\n", prefix, ansi_highlight_cyan(), val, ansi_normal());
236
237 if (device_get_devlink_priority(device, &i) >= 0)
238 printf("%sL: %s%i%s\n", prefix, ansi_highlight_cyan(), i, ansi_normal());
239
240 FOREACH_DEVICE_DEVLINK(device, str) {
241 assert_se(val = path_startswith(str, "/dev/"));
242 printf("%sS: %s%s%s\n", prefix, ansi_highlight_cyan(), val, ansi_normal());
243 }
244 }
245
246 if (sd_device_get_diskseq(device, &q) >= 0)
247 printf("%sQ: %s%" PRIu64 "%s\n", prefix, ansi_highlight_magenta(), q, ansi_normal());
248
249 if (sd_device_get_driver(device, &str) >= 0)
250 printf("%sV: %s%s%s\n", prefix, ansi_highlight_yellow4(), str, ansi_normal());
251
252 FOREACH_DEVICE_PROPERTY(device, str, val)
253 printf("%sE: %s=%s\n", prefix, str, val);
254
255 if (isempty(prefix))
256 puts("");
257 return 0;
258 }
259
260 static int stat_device(const char *name, bool export, const char *prefix) {
261 struct stat statbuf;
262
263 assert(name);
264
265 if (stat(name, &statbuf) != 0)
266 return -errno;
267
268 if (export) {
269 if (!prefix)
270 prefix = "INFO_";
271 printf("%sMAJOR=%u\n"
272 "%sMINOR=%u\n",
273 prefix, major(statbuf.st_dev),
274 prefix, minor(statbuf.st_dev));
275 } else
276 printf("%u:%u\n", major(statbuf.st_dev), minor(statbuf.st_dev));
277 return 0;
278 }
279
280 static int export_devices(void) {
281 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
282 sd_device *d;
283 int r;
284
285 r = sd_device_enumerator_new(&e);
286 if (r < 0)
287 return log_oom();
288
289 r = sd_device_enumerator_allow_uninitialized(e);
290 if (r < 0)
291 return log_error_errno(r, "Failed to set allowing uninitialized flag: %m");
292
293 r = device_enumerator_scan_devices(e);
294 if (r < 0)
295 return log_error_errno(r, "Failed to scan devices: %m");
296
297 FOREACH_DEVICE_AND_SUBSYSTEM(e, d)
298 (void) print_record(d, NULL);
299
300 return 0;
301 }
302
303 static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
304 assert(dir);
305
306 if (depth <= 0)
307 return;
308
309 FOREACH_DIRENT_ALL(dent, dir, break) {
310 struct stat stats;
311
312 if (dot_or_dot_dot(dent->d_name))
313 continue;
314 if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) < 0)
315 continue;
316 if ((stats.st_mode & mask) != 0)
317 continue;
318 if (S_ISDIR(stats.st_mode)) {
319 _cleanup_closedir_ DIR *subdir = NULL;
320
321 subdir = xopendirat(dirfd(dir), dent->d_name, O_NOFOLLOW);
322 if (!subdir)
323 log_debug_errno(errno, "Failed to open subdirectory '%s', ignoring: %m", dent->d_name);
324 else
325 cleanup_dir(subdir, mask, depth-1);
326
327 (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
328 } else
329 (void) unlinkat(dirfd(dir), dent->d_name, 0);
330 }
331 }
332
333 /*
334 * Assume that dir is a directory with file names matching udev data base
335 * entries for devices in /run/udev/data (such as "b8:16"), and removes
336 * all files except those that haven't been deleted in /run/udev/data
337 * (i.e. they were skipped during db cleanup because of the db_persist flag).
338 */
339 static void cleanup_dir_after_db_cleanup(DIR *dir, DIR *datadir) {
340 assert(dir);
341 assert(datadir);
342
343 FOREACH_DIRENT_ALL(dent, dir, break) {
344 if (dot_or_dot_dot(dent->d_name))
345 continue;
346
347 if (faccessat(dirfd(datadir), dent->d_name, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
348 /* The corresponding udev database file still exists.
349 * Assuming the parsistent flag is set for the database. */
350 continue;
351
352 (void) unlinkat(dirfd(dir), dent->d_name, 0);
353 }
354 }
355
356 static void cleanup_dirs_after_db_cleanup(DIR *dir, DIR *datadir) {
357 assert(dir);
358 assert(datadir);
359
360 FOREACH_DIRENT_ALL(dent, dir, break) {
361 struct stat stats;
362
363 if (dot_or_dot_dot(dent->d_name))
364 continue;
365 if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) < 0)
366 continue;
367 if (S_ISDIR(stats.st_mode)) {
368 _cleanup_closedir_ DIR *subdir = NULL;
369
370 subdir = xopendirat(dirfd(dir), dent->d_name, O_NOFOLLOW);
371 if (!subdir)
372 log_debug_errno(errno, "Failed to open subdirectory '%s', ignoring: %m", dent->d_name);
373 else
374 cleanup_dir_after_db_cleanup(subdir, datadir);
375
376 (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
377 } else
378 (void) unlinkat(dirfd(dir), dent->d_name, 0);
379 }
380 }
381
382 static void cleanup_db(void) {
383 _cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL;
384
385 dir1 = opendir("/run/udev/data");
386 if (dir1)
387 cleanup_dir(dir1, S_ISVTX, 1);
388
389 dir2 = opendir("/run/udev/links");
390 if (dir2)
391 cleanup_dirs_after_db_cleanup(dir2, dir1);
392
393 dir3 = opendir("/run/udev/tags");
394 if (dir3)
395 cleanup_dirs_after_db_cleanup(dir3, dir1);
396
397 dir4 = opendir("/run/udev/static_node-tags");
398 if (dir4)
399 cleanup_dir(dir4, 0, 2);
400
401 /* Do not remove /run/udev/watch. It will be handled by udevd well on restart.
402 * And should not be removed by external program when udevd is running. */
403 }
404
405 static int query_device(QueryType query, sd_device* device) {
406 int r;
407
408 assert(device);
409
410 switch (query) {
411 case QUERY_NAME: {
412 const char *node;
413
414 r = sd_device_get_devname(device, &node);
415 if (r < 0)
416 return log_error_errno(r, "No device node found: %m");
417
418 if (!arg_root)
419 assert_se(node = path_startswith(node, "/dev/"));
420 printf("%s\n", node);
421 return 0;
422 }
423
424 case QUERY_SYMLINK: {
425 const char *devlink, *prefix = "";
426
427 FOREACH_DEVICE_DEVLINK(device, devlink) {
428 if (!arg_root)
429 assert_se(devlink = path_startswith(devlink, "/dev/"));
430 printf("%s%s", prefix, devlink);
431 prefix = " ";
432 }
433 puts("");
434 return 0;
435 }
436
437 case QUERY_PATH: {
438 const char *devpath;
439
440 r = sd_device_get_devpath(device, &devpath);
441 if (r < 0)
442 return log_error_errno(r, "Failed to get device path: %m");
443
444 printf("%s\n", devpath);
445 return 0;
446 }
447
448 case QUERY_PROPERTY: {
449 const char *key, *value;
450
451 FOREACH_DEVICE_PROPERTY(device, key, value) {
452 if (arg_properties && !strv_contains(arg_properties, key))
453 continue;
454
455 if (arg_export)
456 printf("%s%s='%s'\n", strempty(arg_export_prefix), key, value);
457 else if (arg_value)
458 printf("%s\n", value);
459 else
460 printf("%s=%s\n", key, value);
461 }
462
463 return 0;
464 }
465
466 case QUERY_ALL:
467 return print_record(device, NULL);
468
469 default:
470 assert_not_reached();
471 }
472 }
473
474 static int help(void) {
475 printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
476 "Query sysfs or the udev database.\n\n"
477 " -h --help Print this message\n"
478 " -V --version Print version of the program\n"
479 " -q --query=TYPE Query device information:\n"
480 " name Name of device node\n"
481 " symlink Pointing to node\n"
482 " path sysfs device path\n"
483 " property The device properties\n"
484 " all All values\n"
485 " --property=NAME Show only properties by this name\n"
486 " --value When showing properties, print only their values\n"
487 " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
488 " -n --name=NAME Node or symlink name used for query or attribute walk\n"
489 " -r --root Prepend dev directory to path names\n"
490 " -a --attribute-walk Print all key matches walking along the chain\n"
491 " of parent devices\n"
492 " -t --tree Show tree of devices\n"
493 " -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
494 " -x --export Export key/value pairs\n"
495 " -P --export-prefix Export the key name with a prefix\n"
496 " -e --export-db Export the content of the udev database\n"
497 " -c --cleanup-db Clean up the udev database\n"
498 " -w --wait-for-initialization[=SECONDS]\n"
499 " Wait for device to be initialized\n",
500 program_invocation_short_name);
501
502 return 0;
503 }
504
505 static int draw_tree(
506 sd_device *parent,
507 sd_device *const array[], size_t n,
508 const char *prefix,
509 unsigned level);
510
511 static int output_tree_device(
512 sd_device *device,
513 const char *str,
514 const char *prefix,
515 bool more,
516 sd_device *const array[], size_t n,
517 unsigned level) {
518
519 _cleanup_free_ char *subprefix = NULL, *subsubprefix = NULL;
520
521 assert(device);
522 assert(str);
523
524 prefix = strempty(prefix);
525
526 printf("%s%s%s\n", prefix, special_glyph(more ? SPECIAL_GLYPH_TREE_BRANCH : SPECIAL_GLYPH_TREE_RIGHT), str);
527
528 subprefix = strjoin(prefix, special_glyph(more ? SPECIAL_GLYPH_TREE_VERTICAL : SPECIAL_GLYPH_TREE_SPACE));
529 if (!subprefix)
530 return log_oom();
531
532 subsubprefix = strjoin(subprefix, special_glyph(SPECIAL_GLYPH_VERTICAL_DOTTED), " ");
533 if (!subsubprefix)
534 return log_oom();
535
536 (void) print_record(device, subsubprefix);
537
538 return draw_tree(device, array, n, subprefix, level + 1);
539 }
540
541 static int draw_tree(
542 sd_device *parent,
543 sd_device *const array[], size_t n,
544 const char *prefix,
545 unsigned level) {
546
547 const char *parent_path;
548 size_t i = 0;
549 int r;
550
551 if (n == 0)
552 return 0;
553
554 assert(array);
555
556 if (parent) {
557 r = sd_device_get_devpath(parent, &parent_path);
558 if (r < 0)
559 return log_error_errno(r, "Failed to get sysfs path of parent device: %m");
560 } else
561 parent_path = NULL;
562
563 if (level > TREE_DEPTH_MAX) {
564 log_warning("Eliding tree below '%s', too deep.", strna(parent_path));
565 return 0;
566 }
567
568 while (i < n) {
569 sd_device *device = array[i];
570 const char *device_path, *str;
571 bool more = false;
572 size_t j;
573
574 r = sd_device_get_devpath(device, &device_path);
575 if (r < 0)
576 return log_error_errno(r, "Failed to get sysfs path of enumerated device: %m");
577
578 /* Scan through the subsequent devices looking children of the device we are looking at. */
579 for (j = i + 1; j < n; j++) {
580 sd_device *next = array[j];
581 const char *next_path;
582
583 r = sd_device_get_devpath(next, &next_path);
584 if (r < 0)
585 return log_error_errno(r, "Failed to get sysfs of child device: %m");
586
587 if (!path_startswith(next_path, device_path)) {
588 more = !parent_path || path_startswith(next_path, parent_path);
589 break;
590 }
591 }
592
593 /* Determine the string to display for this node. If we are at the top of the tree, the full
594 * device path so far, otherwise just the part suffixing the parent's device path. */
595 str = parent ? ASSERT_PTR(path_startswith(device_path, parent_path)) : device_path;
596
597 r = output_tree_device(device, str, prefix, more, array + i + 1, j - i - 1, level);
598 if (r < 0)
599 return r;
600
601 i = j;
602 }
603
604 return 0;
605 }
606
607 static int print_tree(sd_device* below) {
608 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
609 const char *below_path;
610 sd_device **array;
611 size_t n = 0;
612 int r;
613
614 if (below) {
615 r = sd_device_get_devpath(below, &below_path);
616 if (r < 0)
617 return log_error_errno(r, "Failed to get sysfs path of device: %m");
618
619 } else
620 below_path = NULL;
621
622 r = sd_device_enumerator_new(&e);
623 if (r < 0)
624 return log_error_errno(r, "Failed to allocate device enumerator: %m");
625
626 if (below) {
627 r = sd_device_enumerator_add_match_parent(e, below);
628 if (r < 0)
629 return log_error_errno(r, "Failed to install parent enumerator match: %m");
630 }
631
632 r = sd_device_enumerator_allow_uninitialized(e);
633 if (r < 0)
634 return log_error_errno(r, "Failed to enable enumeration of uninitialized devices: %m");
635
636 r = device_enumerator_scan_devices_and_subsystems(e);
637 if (r < 0)
638 return log_error_errno(r, "Failed to scan for devices and subsystems: %m");
639
640 assert_se(array = device_enumerator_get_devices(e, &n));
641
642 if (n == 0) {
643 log_info("No items.");
644 return 0;
645 }
646
647 r = draw_tree(NULL, array, n, NULL, 0);
648 if (r < 0)
649 return r;
650
651 printf("\n%zu items shown.\n", n);
652 return 0;
653 }
654
655 int info_main(int argc, char *argv[], void *userdata) {
656 _cleanup_strv_free_ char **devices = NULL;
657 _cleanup_free_ char *name = NULL;
658 int c, r, ret;
659
660 enum {
661 ARG_PROPERTY = 0x100,
662 ARG_VALUE,
663 };
664
665 static const struct option options[] = {
666 { "attribute-walk", no_argument, NULL, 'a' },
667 { "tree", no_argument, NULL, 't' },
668 { "cleanup-db", no_argument, NULL, 'c' },
669 { "device-id-of-file", required_argument, NULL, 'd' },
670 { "export", no_argument, NULL, 'x' },
671 { "export-db", no_argument, NULL, 'e' },
672 { "export-prefix", required_argument, NULL, 'P' },
673 { "help", no_argument, NULL, 'h' },
674 { "name", required_argument, NULL, 'n' },
675 { "path", required_argument, NULL, 'p' },
676 { "property", required_argument, NULL, ARG_PROPERTY },
677 { "query", required_argument, NULL, 'q' },
678 { "root", no_argument, NULL, 'r' },
679 { "value", no_argument, NULL, ARG_VALUE },
680 { "version", no_argument, NULL, 'V' },
681 { "wait-for-initialization", optional_argument, NULL, 'w' },
682 {}
683 };
684
685 ActionType action = ACTION_QUERY;
686 QueryType query = QUERY_ALL;
687
688 while ((c = getopt_long(argc, argv, "atced:n:p:q:rxP:w::Vh", options, NULL)) >= 0)
689 switch (c) {
690 case ARG_PROPERTY:
691 /* Make sure that if the empty property list was specified, we won't show any
692 properties. */
693 if (isempty(optarg) && !arg_properties) {
694 arg_properties = new0(char*, 1);
695 if (!arg_properties)
696 return log_oom();
697 } else {
698 r = strv_split_and_extend(&arg_properties, optarg, ",", true);
699 if (r < 0)
700 return log_oom();
701 }
702 break;
703 case ARG_VALUE:
704 arg_value = true;
705 break;
706 case 'n':
707 case 'p': {
708 const char *prefix = c == 'n' ? "/dev/" : "/sys/";
709 char *path;
710
711 path = path_join(path_startswith(optarg, prefix) ? NULL : prefix, optarg);
712 if (!path)
713 return log_oom();
714
715 r = strv_consume(&devices, path);
716 if (r < 0)
717 return log_oom();
718 break;
719 }
720
721 case 'q':
722 action = ACTION_QUERY;
723 if (streq(optarg, "property") || streq(optarg, "env"))
724 query = QUERY_PROPERTY;
725 else if (streq(optarg, "name"))
726 query = QUERY_NAME;
727 else if (streq(optarg, "symlink"))
728 query = QUERY_SYMLINK;
729 else if (streq(optarg, "path"))
730 query = QUERY_PATH;
731 else if (streq(optarg, "all"))
732 query = QUERY_ALL;
733 else
734 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "unknown query type");
735 break;
736 case 'r':
737 arg_root = true;
738 break;
739 case 'd':
740 action = ACTION_DEVICE_ID_FILE;
741 r = free_and_strdup(&name, optarg);
742 if (r < 0)
743 return log_oom();
744 break;
745 case 'a':
746 action = ACTION_ATTRIBUTE_WALK;
747 break;
748 case 't':
749 action = ACTION_TREE;
750 break;
751 case 'e':
752 return export_devices();
753 case 'c':
754 cleanup_db();
755 return 0;
756 case 'x':
757 arg_export = true;
758 break;
759 case 'P':
760 arg_export = true;
761 arg_export_prefix = optarg;
762 break;
763 case 'w':
764 if (optarg) {
765 r = parse_sec(optarg, &arg_wait_for_initialization_timeout);
766 if (r < 0)
767 return log_error_errno(r, "Failed to parse timeout value: %m");
768 } else
769 arg_wait_for_initialization_timeout = USEC_INFINITY;
770 break;
771 case 'V':
772 return print_version();
773 case 'h':
774 return help();
775 case '?':
776 return -EINVAL;
777 default:
778 assert_not_reached();
779 }
780
781 if (action == ACTION_DEVICE_ID_FILE) {
782 if (argv[optind])
783 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
784 "Positional arguments are not allowed with -d/--device-id-of-file.");
785 assert(name);
786 return stat_device(name, arg_export, arg_export_prefix);
787 }
788
789 r = strv_extend_strv(&devices, argv + optind, false);
790 if (r < 0)
791 return log_error_errno(r, "Failed to build argument list: %m");
792
793 if (action != ACTION_TREE && strv_isempty(devices))
794 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
795 "A device name or path is required");
796 if (IN_SET(action, ACTION_ATTRIBUTE_WALK, ACTION_TREE) && strv_length(devices) > 1)
797 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
798 "Only one device may be specified with -a/--attribute-walk and -t/--tree");
799
800 if (arg_export && arg_value)
801 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
802 "-x/--export or -P/--export-prefix cannot be used with --value");
803
804 if (strv_isempty(devices)) {
805 assert(action == ACTION_TREE);
806 pager_open(0);
807 return print_tree(NULL);
808 }
809
810 ret = 0;
811 STRV_FOREACH(p, devices) {
812 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
813
814 r = find_device(*p, NULL, &device);
815 if (r < 0) {
816 if (r == -EINVAL)
817 log_error_errno(r, "Bad argument \"%s\", expected an absolute path in /dev/ or /sys/ or a unit name: %m", *p);
818 else
819 log_error_errno(r, "Unknown device \"%s\": %m", *p);
820
821 if (ret == 0)
822 ret = r;
823 continue;
824 }
825
826 if (arg_wait_for_initialization_timeout > 0) {
827 sd_device *d;
828
829 r = device_wait_for_initialization(
830 device,
831 NULL,
832 usec_add(now(CLOCK_MONOTONIC), arg_wait_for_initialization_timeout),
833 &d);
834 if (r < 0)
835 return r;
836
837 sd_device_unref(device);
838 device = d;
839 }
840
841 if (action == ACTION_QUERY)
842 r = query_device(query, device);
843 else if (action == ACTION_ATTRIBUTE_WALK)
844 r = print_device_chain(device);
845 else if (action == ACTION_TREE)
846 r = print_tree(device);
847 else
848 assert_not_reached();
849 if (r < 0)
850 return r;
851 }
852
853 return ret;
854 }