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