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
skip_attribute(const char * name)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
sysattr_compare(const SysAttr * a,const SysAttr * b)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
print_all_attributes(sd_device * device,bool is_parent)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
print_device_chain(sd_device * device)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
print_record(sd_device * device,const char * prefix)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
stat_device(const char * name,bool export,const char * prefix)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
export_devices(void)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
cleanup_dir(DIR * dir,mode_t mask,int depth)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 */
cleanup_dir_after_db_cleanup(DIR * dir,DIR * datadir)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
cleanup_dirs_after_db_cleanup(DIR * dir,DIR * datadir)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
cleanup_db(void)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
query_device(QueryType query,sd_device * device)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
help(void)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
output_tree_device(sd_device * device,const char * str,const char * prefix,bool more,sd_device * const array[],size_t n,unsigned level)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
draw_tree(sd_device * parent,sd_device * const array[],size_t n,const char * prefix,unsigned level)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
print_tree(sd_device * below)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 if (below) {
641 /* This must be called after device_enumerator_scan_devices_and_subsystems(). */
642 r = device_enumerator_add_parent_devices(e, below);
643 if (r < 0)
644 return log_error_errno(r, "Failed to add parent devices: %m");
645 }
646
647 assert_se(array = device_enumerator_get_devices(e, &n));
648
649 if (n == 0) {
650 log_info("No items.");
651 return 0;
652 }
653
654 r = draw_tree(NULL, array, n, NULL, 0);
655 if (r < 0)
656 return r;
657
658 printf("\n%zu items shown.\n", n);
659 return 0;
660 }
661
info_main(int argc,char * argv[],void * userdata)662 int info_main(int argc, char *argv[], void *userdata) {
663 _cleanup_strv_free_ char **devices = NULL;
664 _cleanup_free_ char *name = NULL;
665 int c, r, ret;
666
667 enum {
668 ARG_PROPERTY = 0x100,
669 ARG_VALUE,
670 };
671
672 static const struct option options[] = {
673 { "attribute-walk", no_argument, NULL, 'a' },
674 { "tree", no_argument, NULL, 't' },
675 { "cleanup-db", no_argument, NULL, 'c' },
676 { "device-id-of-file", required_argument, NULL, 'd' },
677 { "export", no_argument, NULL, 'x' },
678 { "export-db", no_argument, NULL, 'e' },
679 { "export-prefix", required_argument, NULL, 'P' },
680 { "help", no_argument, NULL, 'h' },
681 { "name", required_argument, NULL, 'n' },
682 { "path", required_argument, NULL, 'p' },
683 { "property", required_argument, NULL, ARG_PROPERTY },
684 { "query", required_argument, NULL, 'q' },
685 { "root", no_argument, NULL, 'r' },
686 { "value", no_argument, NULL, ARG_VALUE },
687 { "version", no_argument, NULL, 'V' },
688 { "wait-for-initialization", optional_argument, NULL, 'w' },
689 {}
690 };
691
692 ActionType action = ACTION_QUERY;
693 QueryType query = QUERY_ALL;
694
695 while ((c = getopt_long(argc, argv, "atced:n:p:q:rxP:w::Vh", options, NULL)) >= 0)
696 switch (c) {
697 case ARG_PROPERTY:
698 /* Make sure that if the empty property list was specified, we won't show any
699 properties. */
700 if (isempty(optarg) && !arg_properties) {
701 arg_properties = new0(char*, 1);
702 if (!arg_properties)
703 return log_oom();
704 } else {
705 r = strv_split_and_extend(&arg_properties, optarg, ",", true);
706 if (r < 0)
707 return log_oom();
708 }
709 break;
710 case ARG_VALUE:
711 arg_value = true;
712 break;
713 case 'n':
714 case 'p': {
715 const char *prefix = c == 'n' ? "/dev/" : "/sys/";
716 char *path;
717
718 path = path_join(path_startswith(optarg, prefix) ? NULL : prefix, optarg);
719 if (!path)
720 return log_oom();
721
722 r = strv_consume(&devices, path);
723 if (r < 0)
724 return log_oom();
725 break;
726 }
727
728 case 'q':
729 action = ACTION_QUERY;
730 if (streq(optarg, "property") || streq(optarg, "env"))
731 query = QUERY_PROPERTY;
732 else if (streq(optarg, "name"))
733 query = QUERY_NAME;
734 else if (streq(optarg, "symlink"))
735 query = QUERY_SYMLINK;
736 else if (streq(optarg, "path"))
737 query = QUERY_PATH;
738 else if (streq(optarg, "all"))
739 query = QUERY_ALL;
740 else
741 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "unknown query type");
742 break;
743 case 'r':
744 arg_root = true;
745 break;
746 case 'd':
747 action = ACTION_DEVICE_ID_FILE;
748 r = free_and_strdup(&name, optarg);
749 if (r < 0)
750 return log_oom();
751 break;
752 case 'a':
753 action = ACTION_ATTRIBUTE_WALK;
754 break;
755 case 't':
756 action = ACTION_TREE;
757 break;
758 case 'e':
759 return export_devices();
760 case 'c':
761 cleanup_db();
762 return 0;
763 case 'x':
764 arg_export = true;
765 break;
766 case 'P':
767 arg_export = true;
768 arg_export_prefix = optarg;
769 break;
770 case 'w':
771 if (optarg) {
772 r = parse_sec(optarg, &arg_wait_for_initialization_timeout);
773 if (r < 0)
774 return log_error_errno(r, "Failed to parse timeout value: %m");
775 } else
776 arg_wait_for_initialization_timeout = USEC_INFINITY;
777 break;
778 case 'V':
779 return print_version();
780 case 'h':
781 return help();
782 case '?':
783 return -EINVAL;
784 default:
785 assert_not_reached();
786 }
787
788 if (action == ACTION_DEVICE_ID_FILE) {
789 if (argv[optind])
790 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
791 "Positional arguments are not allowed with -d/--device-id-of-file.");
792 assert(name);
793 return stat_device(name, arg_export, arg_export_prefix);
794 }
795
796 r = strv_extend_strv(&devices, argv + optind, false);
797 if (r < 0)
798 return log_error_errno(r, "Failed to build argument list: %m");
799
800 if (action != ACTION_TREE && strv_isempty(devices))
801 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
802 "A device name or path is required");
803 if (IN_SET(action, ACTION_ATTRIBUTE_WALK, ACTION_TREE) && strv_length(devices) > 1)
804 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
805 "Only one device may be specified with -a/--attribute-walk and -t/--tree");
806
807 if (arg_export && arg_value)
808 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
809 "-x/--export or -P/--export-prefix cannot be used with --value");
810
811 if (strv_isempty(devices)) {
812 assert(action == ACTION_TREE);
813 pager_open(0);
814 return print_tree(NULL);
815 }
816
817 ret = 0;
818 STRV_FOREACH(p, devices) {
819 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
820
821 r = find_device(*p, NULL, &device);
822 if (r < 0) {
823 if (r == -EINVAL)
824 log_error_errno(r, "Bad argument \"%s\", expected an absolute path in /dev/ or /sys/ or a unit name: %m", *p);
825 else
826 log_error_errno(r, "Unknown device \"%s\": %m", *p);
827
828 if (ret == 0)
829 ret = r;
830 continue;
831 }
832
833 if (arg_wait_for_initialization_timeout > 0) {
834 sd_device *d;
835
836 r = device_wait_for_initialization(
837 device,
838 NULL,
839 usec_add(now(CLOCK_MONOTONIC), arg_wait_for_initialization_timeout),
840 &d);
841 if (r < 0)
842 return r;
843
844 sd_device_unref(device);
845 device = d;
846 }
847
848 if (action == ACTION_QUERY)
849 r = query_device(query, device);
850 else if (action == ACTION_ATTRIBUTE_WALK)
851 r = print_device_chain(device);
852 else if (action == ACTION_TREE)
853 r = print_tree(device);
854 else
855 assert_not_reached();
856 if (r < 0)
857 return r;
858 }
859
860 return ret;
861 }
862