1 /* SPDX-License-Identifier: GPL-2.0-or-later */
8 #include <sys/sysmacros.h>
11 #include "sd-device.h"
14 #include "alloc-util.h"
15 #include "ansi-color.h"
16 #include "device-enumerator-private.h"
17 #include "device-private.h"
18 #include "device-util.h"
19 #include "dirent-util.h"
20 #include "errno-util.h"
23 #include "glyph-util.h"
25 #include "parse-argument.h"
26 #include "sort-util.h"
27 #include "static-destruct.h"
28 #include "string-table.h"
29 #include "string-util.h"
31 #include "time-util.h"
32 #include "udev-util.h"
34 #include "udevadm-util.h"
36 typedef enum ActionType
{
38 ACTION_ATTRIBUTE_WALK
,
39 ACTION_DEVICE_ID_FILE
,
45 typedef enum QueryType
{
52 _QUERY_TYPE_INVALID
= -EINVAL
,
55 static char **arg_properties
= NULL
;
56 static bool arg_root
= false;
57 static bool arg_export
= false;
58 static bool arg_value
= false;
59 static const char *arg_export_prefix
= NULL
;
60 static usec_t arg_wait_for_initialization_timeout
= 0;
61 static PagerFlags arg_pager_flags
= 0;
62 static sd_json_format_flags_t arg_json_format_flags
= SD_JSON_FORMAT_OFF
;
63 static ActionType arg_action_type
= ACTION_QUERY
;
64 static QueryType arg_query
= QUERY_ALL
;
65 static char **arg_devices
= NULL
;
66 static char *arg_name
= NULL
;
67 static char **arg_attr_match
= NULL
;
68 static char **arg_attr_nomatch
= NULL
;
69 static char **arg_name_match
= NULL
;
70 static char **arg_parent_match
= NULL
;
71 static char **arg_property_match
= NULL
;
72 static char **arg_subsystem_match
= NULL
;
73 static char **arg_subsystem_nomatch
= NULL
;
74 static char **arg_sysname_match
= NULL
;
75 static char **arg_tag_match
= NULL
;
76 static int arg_initialized_match
= -1;
78 STATIC_DESTRUCTOR_REGISTER(arg_properties
, strv_freep
);
79 STATIC_DESTRUCTOR_REGISTER(arg_devices
, strv_freep
);
80 STATIC_DESTRUCTOR_REGISTER(arg_name
, freep
);
81 STATIC_DESTRUCTOR_REGISTER(arg_attr_match
, strv_freep
);
82 STATIC_DESTRUCTOR_REGISTER(arg_attr_nomatch
, strv_freep
);
83 STATIC_DESTRUCTOR_REGISTER(arg_name_match
, strv_freep
);
84 STATIC_DESTRUCTOR_REGISTER(arg_parent_match
, strv_freep
);
85 STATIC_DESTRUCTOR_REGISTER(arg_property_match
, strv_freep
);
86 STATIC_DESTRUCTOR_REGISTER(arg_subsystem_match
, strv_freep
);
87 STATIC_DESTRUCTOR_REGISTER(arg_subsystem_nomatch
, strv_freep
);
88 STATIC_DESTRUCTOR_REGISTER(arg_sysname_match
, strv_freep
);
89 STATIC_DESTRUCTOR_REGISTER(arg_tag_match
, strv_freep
);
91 /* Put a limit on --tree descent level to not exhaust our stack */
92 #define TREE_DEPTH_MAX 64
94 static const char *query_type_table
[_QUERY_TYPE_MAX
] = {
95 [QUERY_NAME
] = "name",
96 [QUERY_PATH
] = "path",
97 [QUERY_SYMLINK
] = "symlink",
98 [QUERY_PROPERTY
] = "property",
102 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(query_type
, QueryType
);
104 static bool skip_attribute(const char *name
) {
107 /* Those are either displayed separately or should not be shown at all. */
108 return STR_IN_SET(name
,
118 typedef struct SysAttr
{
123 static int sysattr_compare(const SysAttr
*a
, const SysAttr
*b
) {
127 return strcmp(a
->name
, b
->name
);
130 static int print_all_attributes(sd_device
*device
, bool is_parent
) {
131 _cleanup_free_ SysAttr
*sysattrs
= NULL
;
142 (void) sd_device_get_devpath(device
, &value
);
143 printf(" looking at %sdevice '%s':\n", is_parent
? "parent " : "", strempty(value
));
146 (void) sd_device_get_sysname(device
, &value
);
147 printf(" %s==\"%s\"\n", is_parent
? "KERNELS" : "KERNEL", strempty(value
));
150 (void) sd_device_get_subsystem(device
, &value
);
151 printf(" %s==\"%s\"\n", is_parent
? "SUBSYSTEMS" : "SUBSYSTEM", strempty(value
));
154 (void) sd_device_get_driver(device
, &value
);
155 printf(" %s==\"%s\"\n", is_parent
? "DRIVERS" : "DRIVER", strempty(value
));
157 FOREACH_DEVICE_SYSATTR(device
, name
) {
160 if (skip_attribute(name
))
163 r
= sd_device_get_sysattr_value(device
, name
, &value
);
165 /* skip any values that look like a path */
169 /* skip nonprintable attributes */
171 while (len
> 0 && isprint((unsigned char) value
[len
-1]))
176 } else if (ERRNO_IS_PRIVILEGE(r
))
177 value
= "(not readable)";
181 if (!GREEDY_REALLOC(sysattrs
, n_items
+ 1))
184 sysattrs
[n_items
] = (SysAttr
) {
191 typesafe_qsort(sysattrs
, n_items
, sysattr_compare
);
193 FOREACH_ARRAY(i
, sysattrs
, n_items
)
194 printf(" %s{%s}==\"%s\"\n", is_parent
? "ATTRS" : "ATTR", i
->name
, i
->value
);
199 static int print_device_chain(sd_device
*device
) {
200 sd_device
*child
, *parent
;
206 "Udevadm info starts with the device specified by the devpath and then\n"
207 "walks up the chain of parent devices. It prints for every device\n"
208 "found, all possible attributes in the udev rules key format.\n"
209 "A rule to match, can be composed by the attributes of the device\n"
210 "and the attributes from one single parent device.\n"
213 r
= print_all_attributes(device
, /* is_parent = */ false);
217 for (child
= device
; sd_device_get_parent(child
, &parent
) >= 0; child
= parent
) {
218 r
= print_all_attributes(parent
, /* is_parent = */ true);
226 static int print_all_attributes_in_json(sd_device
*device
, bool is_parent
) {
227 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*v
= NULL
, *w
= NULL
;
228 _cleanup_free_ SysAttr
*sysattrs
= NULL
;
236 (void) sd_device_get_devpath(device
, &value
);
237 r
= sd_json_variant_set_field_string(&v
, "DEVPATH", value
);
242 (void) sd_device_get_sysname(device
, &value
);
243 r
= sd_json_variant_set_field_string(&v
, is_parent
? "KERNELS" : "KERNEL", value
);
248 (void) sd_device_get_subsystem(device
, &value
);
249 r
= sd_json_variant_set_field_string(&v
, is_parent
? "SUBSYSTEMS" : "SUBSYSTEM", value
);
254 (void) sd_device_get_driver(device
, &value
);
255 r
= sd_json_variant_set_field_string(&v
, is_parent
? "DRIVERS" : "DRIVER", value
);
259 FOREACH_DEVICE_SYSATTR(device
, name
) {
262 if (skip_attribute(name
))
265 r
= sd_device_get_sysattr_value(device
, name
, &value
);
267 /* skip any values that look like a path */
271 /* skip nonprintable attributes */
273 while (len
> 0 && isprint((unsigned char) value
[len
-1]))
278 } else if (ERRNO_IS_PRIVILEGE(r
))
279 value
= "(not readable)";
283 if (!GREEDY_REALLOC(sysattrs
, n_items
+ 1))
286 sysattrs
[n_items
] = (SysAttr
) {
293 typesafe_qsort(sysattrs
, n_items
, sysattr_compare
);
295 FOREACH_ARRAY(i
, sysattrs
, n_items
) {
296 r
= sd_json_variant_set_field_string(&w
, i
->name
, i
->value
);
301 r
= sd_json_variant_set_field(&v
, is_parent
? "ATTRS" : "ATTR", w
);
305 return sd_json_variant_dump(v
, arg_json_format_flags
, stdout
, NULL
);
308 static int print_device_chain_in_json(sd_device
*device
) {
309 sd_device
*child
, *parent
;
314 arg_json_format_flags
|=SD_JSON_FORMAT_SEQ
;
316 r
= print_all_attributes_in_json(device
, /* is_parent = */ false);
320 for (child
= device
; sd_device_get_parent(child
, &parent
) >= 0; child
= parent
) {
321 r
= print_all_attributes_in_json(parent
, /* is_parent = */ true);
329 static int print_record(sd_device
*device
, const char *prefix
) {
330 const char *str
, *subsys
;
337 prefix
= strempty(prefix
);
339 /* We don't show syspath here, because it's identical to devpath (modulo the "/sys" prefix).
341 * We don't show action/seqnum here because that only makes sense for records synthesized from
342 * uevents, not for those synthesized from database entries.
344 * We don't show sysattrs here, because they can be expensive and potentially issue expensive driver
347 * Coloring: let's be conservative with coloring. Let's use it to group related fields. Right now:
349 * • white for fields that give the device a name
350 * • green for fields that categorize the device into subsystem/devtype and similar
351 * • cyan for fields about associated device nodes/symlinks/network interfaces and such
352 * • magenta for block device diskseq
353 * • yellow for driver info
354 * • no color for regular properties */
356 assert_se(sd_device_get_devpath(device
, &str
) >= 0);
357 printf("%sP: %s%s%s\n", prefix
, ansi_highlight_white(), str
, ansi_normal());
359 if (sd_device_get_sysname(device
, &str
) >= 0)
360 printf("%sM: %s%s%s\n", prefix
, ansi_highlight_white(), str
, ansi_normal());
362 if (sd_device_get_sysnum(device
, &str
) >= 0)
363 printf("%sR: %s%s%s\n", prefix
, ansi_highlight_white(), str
, ansi_normal());
365 if (sd_device_get_device_id(device
, &str
) >= 0)
366 printf("%sJ: %s%s%s\n", prefix
, ansi_highlight_white(), str
, ansi_normal());
368 if (sd_device_get_subsystem(device
, &subsys
) >= 0)
369 printf("%sU: %s%s%s\n", prefix
, ansi_highlight_green(), subsys
, ansi_normal());
371 if (sd_device_get_driver_subsystem(device
, &str
) >= 0)
372 printf("%sB: %s%s%s\n", prefix
, ansi_highlight_green(), str
, ansi_normal());
374 if (sd_device_get_devtype(device
, &str
) >= 0)
375 printf("%sT: %s%s%s\n", prefix
, ansi_highlight_green(), str
, ansi_normal());
377 if (sd_device_get_devnum(device
, &devnum
) >= 0)
378 printf("%sD: %s%c %u:%u%s\n",
380 ansi_highlight_cyan(),
381 streq_ptr(subsys
, "block") ? 'b' : 'c', major(devnum
), minor(devnum
),
384 if (sd_device_get_ifindex(device
, &ifi
) >= 0)
385 printf("%sI: %s%i%s\n", prefix
, ansi_highlight_cyan(), ifi
, ansi_normal());
387 if (sd_device_get_devname(device
, &str
) >= 0) {
390 assert_se(val
= path_startswith(str
, "/dev/"));
391 printf("%sN: %s%s%s\n", prefix
, ansi_highlight_cyan(), val
, ansi_normal());
393 if (device_get_devlink_priority(device
, &i
) >= 0)
394 printf("%sL: %s%i%s\n", prefix
, ansi_highlight_cyan(), i
, ansi_normal());
396 FOREACH_DEVICE_DEVLINK(device
, link
) {
397 assert_se(val
= path_startswith(link
, "/dev/"));
398 printf("%sS: %s%s%s\n", prefix
, ansi_highlight_cyan(), val
, ansi_normal());
402 if (sd_device_get_diskseq(device
, &q
) >= 0)
403 printf("%sQ: %s%" PRIu64
"%s\n", prefix
, ansi_highlight_magenta(), q
, ansi_normal());
405 if (sd_device_get_driver(device
, &str
) >= 0)
406 printf("%sV: %s%s%s\n", prefix
, ansi_highlight_yellow4(), str
, ansi_normal());
408 FOREACH_DEVICE_PROPERTY(device
, key
, val
)
409 printf("%sE: %s=%s\n", prefix
, key
, val
);
416 static int record_to_json(sd_device
*device
, sd_json_variant
**ret
) {
417 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*v
= NULL
;
424 /* We don't show any shorthand fields here as done in print_record() except for SYSNAME, SYSNUM,
425 * DRIVER_SUBSYSTEM, and DEVICE_ID, as all the other ones have a matching property which will already
428 if (sd_device_get_sysname(device
, &str
) >= 0) {
429 r
= sd_json_variant_set_field_string(&v
, "SYSNAME", str
);
434 if (sd_device_get_sysnum(device
, &str
) >= 0) {
435 r
= sd_json_variant_set_field_string(&v
, "SYSNUM", str
);
440 if (sd_device_get_driver_subsystem(device
, &str
) >= 0) {
441 r
= sd_json_variant_set_field_string(&v
, "DRIVER_SUBSYSTEM", str
);
446 if (sd_device_get_device_id(device
, &str
) >= 0) {
447 r
= sd_json_variant_set_field_string(&v
, "DEVICE_ID", str
);
452 FOREACH_DEVICE_PROPERTY(device
, key
, val
) {
453 r
= sd_json_variant_set_field_string(&v
, key
, val
);
462 static int stat_device(void) {
467 if (stat(arg_name
, &statbuf
) != 0)
471 const char *prefix
= arg_export_prefix
?: "INFO_";
472 printf("%sMAJOR=%u\n"
474 prefix
, major(statbuf
.st_dev
),
475 prefix
, minor(statbuf
.st_dev
));
477 printf("%u:%u\n", major(statbuf
.st_dev
), minor(statbuf
.st_dev
));
481 static int add_match_parent(sd_device_enumerator
*e
, const char *s
, const char *prefix
) {
482 _cleanup_(sd_device_unrefp
) sd_device
*dev
= NULL
;
488 r
= find_device(s
, prefix
, &dev
);
490 return log_error_errno(r
, "Failed to open the device '%s': %m", s
);
492 r
= device_enumerator_add_match_parent_incremental(e
, dev
);
494 return log_error_errno(r
, "Failed to add parent match '%s': %m", s
);
499 static int setup_matches(sd_device_enumerator
*e
) {
504 STRV_FOREACH(n
, arg_name_match
) {
505 r
= add_match_parent(e
, *n
, "/dev");
510 STRV_FOREACH(p
, arg_parent_match
) {
511 r
= add_match_parent(e
, *p
, "/sys");
516 STRV_FOREACH(s
, arg_subsystem_match
) {
517 r
= sd_device_enumerator_add_match_subsystem(e
, *s
, /* match= */ true);
519 return log_error_errno(r
, "Failed to add subsystem match '%s': %m", *s
);
522 STRV_FOREACH(s
, arg_subsystem_nomatch
) {
523 r
= sd_device_enumerator_add_match_subsystem(e
, *s
, /* match= */ false);
525 return log_error_errno(r
, "Failed to add negative subsystem match '%s': %m", *s
);
528 STRV_FOREACH(a
, arg_attr_match
) {
529 _cleanup_free_
char *k
= NULL
, *v
= NULL
;
531 r
= parse_key_value_argument(*a
, /* require_value= */ true, &k
, &v
);
535 r
= sd_device_enumerator_add_match_sysattr(e
, k
, v
, /* match= */ true);
537 return log_error_errno(r
, "Failed to add sysattr match '%s=%s': %m", k
, v
);
540 STRV_FOREACH(a
, arg_attr_nomatch
) {
541 _cleanup_free_
char *k
= NULL
, *v
= NULL
;
543 r
= parse_key_value_argument(*a
, /* require_value= */ true, &k
, &v
);
547 r
= sd_device_enumerator_add_match_sysattr(e
, k
, v
, /* match= */ false);
549 return log_error_errno(r
, "Failed to add negative sysattr match '%s=%s': %m", k
, v
);
552 STRV_FOREACH(p
, arg_property_match
) {
553 _cleanup_free_
char *k
= NULL
, *v
= NULL
;
555 r
= parse_key_value_argument(*p
, /* require_value= */ true, &k
, &v
);
559 r
= sd_device_enumerator_add_match_property_required(e
, k
, v
);
561 return log_error_errno(r
, "Failed to add property match '%s=%s': %m", k
, v
);
564 STRV_FOREACH(t
, arg_tag_match
) {
565 r
= sd_device_enumerator_add_match_tag(e
, *t
);
567 return log_error_errno(r
, "Failed to add tag match '%s': %m", *t
);
570 STRV_FOREACH(s
, arg_sysname_match
) {
571 r
= sd_device_enumerator_add_match_sysname(e
, *s
);
573 return log_error_errno(r
, "Failed to add sysname match '%s': %m", *s
);
576 if (arg_initialized_match
!= -1) {
577 r
= device_enumerator_add_match_is_initialized(e
, arg_initialized_match
);
579 return log_error_errno(r
, "Failed to set initialized filter: %m");
585 static int export_devices(void) {
586 _cleanup_(sd_device_enumerator_unrefp
) sd_device_enumerator
*e
= NULL
;
590 r
= sd_device_enumerator_new(&e
);
592 return log_error_errno(r
, "Failed to create device enumerator: %m");
594 r
= sd_device_enumerator_allow_uninitialized(e
);
596 return log_error_errno(r
, "Failed to allow uninitialized devices: %m");
598 r
= setup_matches(e
);
602 r
= device_enumerator_scan_devices(e
);
604 return log_error_errno(r
, "Failed to scan devices: %m");
606 FOREACH_DEVICE_AND_SUBSYSTEM(e
, d
)
607 if (sd_json_format_enabled(arg_json_format_flags
)) {
608 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*v
= NULL
;
610 r
= record_to_json(d
, &v
);
614 (void) sd_json_variant_dump(v
, arg_json_format_flags
, stdout
, NULL
);
616 (void) print_record(d
, NULL
);
621 static void cleanup_dir(DIR *dir
, mode_t mask
, int depth
) {
627 FOREACH_DIRENT_ALL(dent
, dir
, break) {
630 if (dot_or_dot_dot(dent
->d_name
))
632 if (fstatat(dirfd(dir
), dent
->d_name
, &stats
, AT_SYMLINK_NOFOLLOW
) < 0)
634 if ((stats
.st_mode
& mask
) != 0)
636 if (S_ISDIR(stats
.st_mode
)) {
637 _cleanup_closedir_
DIR *subdir
= NULL
;
639 subdir
= xopendirat(dirfd(dir
), dent
->d_name
, O_NOFOLLOW
);
641 log_debug_errno(errno
, "Failed to open subdirectory '%s', ignoring: %m", dent
->d_name
);
643 cleanup_dir(subdir
, mask
, depth
-1);
645 (void) unlinkat(dirfd(dir
), dent
->d_name
, AT_REMOVEDIR
);
647 (void) unlinkat(dirfd(dir
), dent
->d_name
, 0);
652 * Assume that dir is a directory with file names matching udev data base
653 * entries for devices in /run/udev/data (such as "b8:16"), and removes
654 * all files except those that haven't been deleted in /run/udev/data
655 * (i.e. they were skipped during db cleanup because of the db_persist flag).
657 static void cleanup_dir_after_db_cleanup(DIR *dir
, DIR *datadir
) {
661 FOREACH_DIRENT_ALL(dent
, dir
, break) {
662 if (dot_or_dot_dot(dent
->d_name
))
665 if (faccessat(dirfd(datadir
), dent
->d_name
, F_OK
, AT_SYMLINK_NOFOLLOW
) >= 0)
666 /* The corresponding udev database file still exists.
667 * Assuming the persistent flag is set for the database. */
670 (void) unlinkat(dirfd(dir
), dent
->d_name
, 0);
674 static void cleanup_dirs_after_db_cleanup(DIR *dir
, DIR *datadir
) {
678 FOREACH_DIRENT_ALL(dent
, dir
, break) {
681 if (dot_or_dot_dot(dent
->d_name
))
683 if (fstatat(dirfd(dir
), dent
->d_name
, &stats
, AT_SYMLINK_NOFOLLOW
) < 0)
685 if (S_ISDIR(stats
.st_mode
)) {
686 _cleanup_closedir_
DIR *subdir
= NULL
;
688 subdir
= xopendirat(dirfd(dir
), dent
->d_name
, O_NOFOLLOW
);
690 log_debug_errno(errno
, "Failed to open subdirectory '%s', ignoring: %m", dent
->d_name
);
692 cleanup_dir_after_db_cleanup(subdir
, datadir
);
694 (void) unlinkat(dirfd(dir
), dent
->d_name
, AT_REMOVEDIR
);
696 (void) unlinkat(dirfd(dir
), dent
->d_name
, 0);
700 static int cleanup_db(void) {
701 _cleanup_closedir_
DIR *dir1
= NULL
, *dir2
= NULL
, *dir3
= NULL
, *dir4
= NULL
;
703 dir1
= opendir("/run/udev/data");
705 cleanup_dir(dir1
, S_ISVTX
, 1);
707 dir2
= opendir("/run/udev/links");
709 cleanup_dirs_after_db_cleanup(dir2
, dir1
);
711 dir3
= opendir("/run/udev/tags");
713 cleanup_dirs_after_db_cleanup(dir3
, dir1
);
715 dir4
= opendir("/run/udev/static_node-tags");
717 cleanup_dir(dir4
, 0, 2);
719 /* Do not remove /run/udev/watch. It will be handled by udevd well on restart.
720 * And should not be removed by external program when udevd is running. */
725 static int query_device(QueryType query
, sd_device
* device
) {
734 r
= sd_device_get_devname(device
, &node
);
736 return log_error_errno(r
, "No device node found: %m");
739 assert_se(node
= path_startswith(node
, "/dev/"));
740 printf("%s\n", node
);
744 case QUERY_SYMLINK
: {
745 const char *prefix
= "";
747 FOREACH_DEVICE_DEVLINK(device
, devlink
) {
749 assert_se(devlink
= path_startswith(devlink
, "/dev/"));
750 printf("%s%s", prefix
, devlink
);
760 r
= sd_device_get_devpath(device
, &devpath
);
762 return log_error_errno(r
, "Failed to get device path: %m");
764 printf("%s\n", devpath
);
769 FOREACH_DEVICE_PROPERTY(device
, key
, value
) {
770 if (arg_properties
&& !strv_contains(arg_properties
, key
))
774 printf("%s%s='%s'\n", strempty(arg_export_prefix
), key
, value
);
776 printf("%s\n", value
);
778 printf("%s=%s\n", key
, value
);
784 if (sd_json_format_enabled(arg_json_format_flags
)) {
785 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*v
= NULL
;
787 r
= record_to_json(device
, &v
);
791 (void) sd_json_variant_dump(v
, arg_json_format_flags
, stdout
, NULL
);
793 return print_record(device
, NULL
);
798 assert_not_reached();
802 static int help(void) {
803 printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
804 "Query sysfs or the udev database.\n\n"
805 " -h --help Print this message\n"
806 " -V --version Print version of the program\n"
807 " -q --query=TYPE Query device information:\n"
808 " name Name of device node\n"
809 " symlink Pointing to node\n"
810 " path sysfs device path\n"
811 " property The device properties\n"
813 " --property=NAME Show only properties by this name\n"
814 " --value When showing properties, print only their values\n"
815 " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
816 " -n --name=NAME Node or symlink name used for query or attribute walk\n"
817 " -r --root Prepend dev directory to path names\n"
818 " -a --attribute-walk Print all key matches walking along the chain\n"
819 " of parent devices\n"
820 " -t --tree Show tree of devices\n"
821 " -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
822 " -x --export Export key/value pairs\n"
823 " -P --export-prefix Export the key name with a prefix\n"
824 " -e --export-db Export the content of the udev database\n"
825 " -c --cleanup-db Clean up the udev database\n"
826 " -w --wait-for-initialization[=SECONDS]\n"
827 " Wait for device to be initialized\n"
828 " --no-pager Do not pipe output into a pager\n"
829 " --json=pretty|short|off Generate JSON output\n"
830 " --subsystem-match=SUBSYSTEM\n"
831 " Query devices matching a subsystem\n"
832 " --subsystem-nomatch=SUBSYSTEM\n"
833 " Query devices not matching a subsystem\n"
834 " --attr-match=FILE[=VALUE]\n"
835 " Query devices that match an attribute\n"
836 " --attr-nomatch=FILE[=VALUE]\n"
837 " Query devices that do not match an attribute\n"
838 " --property-match=KEY=VALUE\n"
839 " Query devices with matching properties\n"
840 " --tag-match=TAG Query devices with a matching tag\n"
841 " --sysname-match=NAME Query devices with this /sys path\n"
842 " --name-match=NAME Query devices with this /dev name\n"
843 " --parent-match=NAME Query devices with this parent device\n"
844 " --initialized-match Query devices that are already initialized\n"
845 " --initialized-nomatch Query devices that are not initialized yet\n",
846 program_invocation_short_name
);
851 static int draw_tree(
853 sd_device
*const array
[], size_t n
,
857 static int output_tree_device(
862 sd_device
*const array
[], size_t n
,
865 _cleanup_free_
char *subprefix
= NULL
, *subsubprefix
= NULL
;
870 prefix
= strempty(prefix
);
872 printf("%s%s%s\n", prefix
, glyph(more
? GLYPH_TREE_BRANCH
: GLYPH_TREE_RIGHT
), str
);
874 subprefix
= strjoin(prefix
, glyph(more
? GLYPH_TREE_VERTICAL
: GLYPH_TREE_SPACE
));
878 subsubprefix
= strjoin(subprefix
, glyph(GLYPH_VERTICAL_DOTTED
), " ");
882 (void) print_record(device
, subsubprefix
);
884 return draw_tree(device
, array
, n
, subprefix
, level
+ 1);
887 static int draw_tree(
889 sd_device
*const array
[], size_t n
,
893 const char *parent_path
;
903 r
= sd_device_get_devpath(parent
, &parent_path
);
905 return log_error_errno(r
, "Failed to get sysfs path of parent device: %m");
909 if (level
> TREE_DEPTH_MAX
) {
910 log_warning("Eliding tree below '%s', too deep.", strna(parent_path
));
915 sd_device
*device
= array
[i
];
916 const char *device_path
, *str
;
920 r
= sd_device_get_devpath(device
, &device_path
);
922 return log_error_errno(r
, "Failed to get sysfs path of enumerated device: %m");
924 /* Scan through the subsequent devices looking children of the device we are looking at. */
925 for (j
= i
+ 1; j
< n
; j
++) {
926 sd_device
*next
= array
[j
];
927 const char *next_path
;
929 r
= sd_device_get_devpath(next
, &next_path
);
931 return log_error_errno(r
, "Failed to get sysfs of child device: %m");
933 if (!path_startswith(next_path
, device_path
)) {
934 more
= !parent_path
|| path_startswith(next_path
, parent_path
);
939 /* Determine the string to display for this node. If we are at the top of the tree, the full
940 * device path so far, otherwise just the part suffixing the parent's device path. */
941 str
= parent
? ASSERT_PTR(path_startswith(device_path
, parent_path
)) : device_path
;
943 r
= output_tree_device(device
, str
, prefix
, more
, array
+ i
+ 1, j
- i
- 1, level
);
953 static int print_tree(sd_device
* below
) {
954 _cleanup_(sd_device_enumerator_unrefp
) sd_device_enumerator
*e
= NULL
;
955 const char *below_path
;
961 r
= sd_device_get_devpath(below
, &below_path
);
963 return log_error_errno(r
, "Failed to get sysfs path of device: %m");
968 r
= sd_device_enumerator_new(&e
);
970 return log_error_errno(r
, "Failed to allocate device enumerator: %m");
973 r
= sd_device_enumerator_add_match_parent(e
, below
);
975 return log_error_errno(r
, "Failed to install parent enumerator match: %m");
978 r
= sd_device_enumerator_allow_uninitialized(e
);
980 return log_error_errno(r
, "Failed to enable enumeration of uninitialized devices: %m");
982 r
= device_enumerator_scan_devices_and_subsystems(e
);
984 return log_error_errno(r
, "Failed to scan for devices and subsystems: %m");
987 /* This must be called after device_enumerator_scan_devices_and_subsystems(). */
988 r
= device_enumerator_add_parent_devices(e
, below
);
990 return log_error_errno(r
, "Failed to add parent devices: %m");
993 assert_se(array
= device_enumerator_get_devices(e
, &n
));
996 log_info("No items.");
1000 r
= draw_tree(NULL
, array
, n
, NULL
, 0);
1004 printf("\n%zu items shown.\n", n
);
1008 static int parse_argv(int argc
, char *argv
[]) {
1011 ARG_PROPERTY
= 0x100,
1015 ARG_SUBSYSTEM_MATCH
,
1016 ARG_SUBSYSTEM_NOMATCH
,
1024 ARG_INITIALIZED_MATCH
,
1025 ARG_INITIALIZED_NOMATCH
,
1028 static const struct option options
[] = {
1029 { "attribute-walk", no_argument
, NULL
, 'a' },
1030 { "tree", no_argument
, NULL
, 't' },
1031 { "cleanup-db", no_argument
, NULL
, 'c' },
1032 { "device-id-of-file", required_argument
, NULL
, 'd' },
1033 { "export", no_argument
, NULL
, 'x' },
1034 { "export-db", no_argument
, NULL
, 'e' },
1035 { "export-prefix", required_argument
, NULL
, 'P' },
1036 { "help", no_argument
, NULL
, 'h' },
1037 { "name", required_argument
, NULL
, 'n' },
1038 { "path", required_argument
, NULL
, 'p' },
1039 { "property", required_argument
, NULL
, ARG_PROPERTY
},
1040 { "query", required_argument
, NULL
, 'q' },
1041 { "root", no_argument
, NULL
, 'r' },
1042 { "value", no_argument
, NULL
, ARG_VALUE
},
1043 { "version", no_argument
, NULL
, 'V' },
1044 { "wait-for-initialization", optional_argument
, NULL
, 'w' },
1045 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
1046 { "json", required_argument
, NULL
, ARG_JSON
},
1047 { "subsystem-match", required_argument
, NULL
, ARG_SUBSYSTEM_MATCH
},
1048 { "subsystem-nomatch", required_argument
, NULL
, ARG_SUBSYSTEM_NOMATCH
},
1049 { "attr-match", required_argument
, NULL
, ARG_ATTR_MATCH
},
1050 { "attr-nomatch", required_argument
, NULL
, ARG_ATTR_NOMATCH
},
1051 { "property-match", required_argument
, NULL
, ARG_PROPERTY_MATCH
},
1052 { "tag-match", required_argument
, NULL
, ARG_TAG_MATCH
},
1053 { "sysname-match", required_argument
, NULL
, ARG_SYSNAME_MATCH
},
1054 { "name-match", required_argument
, NULL
, ARG_NAME_MATCH
},
1055 { "parent-match", required_argument
, NULL
, ARG_PARENT_MATCH
},
1056 { "initialized-match", no_argument
, NULL
, ARG_INITIALIZED_MATCH
},
1057 { "initialized-nomatch", no_argument
, NULL
, ARG_INITIALIZED_NOMATCH
},
1066 while ((c
= getopt_long(argc
, argv
, "atced:n:p:q:rxP:w::Vh", options
, NULL
)) >= 0)
1070 /* Make sure that if the empty property list was specified, we won't show any
1072 if (isempty(optarg
) && !arg_properties
) {
1073 arg_properties
= new0(char*, 1);
1074 if (!arg_properties
)
1077 r
= strv_split_and_extend(&arg_properties
, optarg
, ",", true);
1089 const char *prefix
= c
== 'n' ? "/dev/" : "/sys/";
1092 path
= path_join(path_startswith(optarg
, prefix
) ? NULL
: prefix
, optarg
);
1096 r
= strv_consume(&arg_devices
, path
);
1103 arg_query
= query_type_from_string(optarg
);
1104 if (arg_query
< 0) {
1105 if (streq(optarg
, "env")) /* deprecated */
1106 arg_query
= QUERY_PROPERTY
;
1108 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Unknown query type '%s'", optarg
);
1117 arg_action_type
= ACTION_DEVICE_ID_FILE
;
1118 r
= free_and_strdup(&arg_name
, optarg
);
1124 arg_action_type
= ACTION_ATTRIBUTE_WALK
;
1128 arg_action_type
= ACTION_TREE
;
1132 arg_action_type
= ACTION_EXPORT
;
1136 arg_action_type
= ACTION_CLEANUP_DB
;
1145 arg_export_prefix
= optarg
;
1150 r
= parse_sec(optarg
, &arg_wait_for_initialization_timeout
);
1152 return log_error_errno(r
, "Failed to parse timeout value: %m");
1154 arg_wait_for_initialization_timeout
= USEC_INFINITY
;
1158 return print_version();
1164 arg_pager_flags
|= PAGER_DISABLE
;
1168 r
= parse_json_argument(optarg
, &arg_json_format_flags
);
1173 case ARG_SUBSYSTEM_MATCH
:
1174 r
= strv_extend(&arg_subsystem_match
, optarg
);
1179 case ARG_SUBSYSTEM_NOMATCH
:
1180 r
= strv_extend(&arg_subsystem_nomatch
, optarg
);
1185 case ARG_ATTR_MATCH
:
1186 if (!strchr(optarg
, '='))
1187 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1188 "Expected <ATTR>=<value> instead of '%s'", optarg
);
1190 r
= strv_extend(&arg_attr_match
, optarg
);
1195 case ARG_ATTR_NOMATCH
:
1196 if (!strchr(optarg
, '='))
1197 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1198 "Expected <ATTR>=<value> instead of '%s'", optarg
);
1200 r
= strv_extend(&arg_attr_nomatch
, optarg
);
1205 case ARG_PROPERTY_MATCH
:
1206 if (!strchr(optarg
, '='))
1207 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1208 "Expected <PROPERTY>=<value> instead of '%s'", optarg
);
1210 r
= strv_extend(&arg_property_match
, optarg
);
1216 r
= strv_extend(&arg_tag_match
, optarg
);
1221 case ARG_SYSNAME_MATCH
:
1222 r
= strv_extend(&arg_sysname_match
, optarg
);
1227 case ARG_NAME_MATCH
:
1228 r
= strv_extend(&arg_name_match
, optarg
);
1233 case ARG_PARENT_MATCH
:
1234 r
= strv_extend(&arg_parent_match
, optarg
);
1239 case ARG_INITIALIZED_MATCH
:
1240 arg_initialized_match
= MATCH_INITIALIZED_YES
;
1243 case ARG_INITIALIZED_NOMATCH
:
1244 arg_initialized_match
= MATCH_INITIALIZED_NO
;
1251 assert_not_reached();
1254 r
= strv_extend_strv(&arg_devices
, argv
+ optind
, /* filter_duplicates= */ false);
1256 return log_error_errno(r
, "Failed to build argument list: %m");
1258 if (IN_SET(arg_action_type
, ACTION_DEVICE_ID_FILE
, ACTION_CLEANUP_DB
) && !strv_isempty(arg_devices
))
1259 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1260 "Devices are not allowed with -d/--device-id-of-file and -c/--cleanup-db.");
1262 if (!IN_SET(arg_action_type
, ACTION_DEVICE_ID_FILE
, ACTION_CLEANUP_DB
, ACTION_EXPORT
, ACTION_TREE
) &&
1263 strv_isempty(arg_devices
))
1264 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1265 "A device name or path is required");
1267 if (IN_SET(arg_action_type
, ACTION_ATTRIBUTE_WALK
, ACTION_TREE
) && strv_length(arg_devices
) > 1)
1268 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1269 "Only one device may be specified with -a/--attribute-walk and -t/--tree");
1271 if (arg_export
&& arg_value
)
1272 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1273 "-x/--export or -P/--export-prefix cannot be used with --value");
1278 int info_main(int argc
, char *argv
[], void *userdata
) {
1281 r
= parse_argv(argc
, argv
);
1285 if (arg_action_type
== ACTION_CLEANUP_DB
)
1286 return cleanup_db();
1288 if (arg_action_type
== ACTION_DEVICE_ID_FILE
)
1289 return stat_device();
1291 pager_open(arg_pager_flags
);
1293 if (arg_action_type
== ACTION_EXPORT
)
1294 return export_devices();
1296 if (strv_isempty(arg_devices
)) {
1297 assert(arg_action_type
== ACTION_TREE
);
1298 return print_tree(NULL
);
1302 STRV_FOREACH(p
, arg_devices
) {
1303 _cleanup_(sd_device_unrefp
) sd_device
*device
= NULL
;
1305 r
= find_device(*p
, /* prefix = */ NULL
, &device
);
1308 log_error_errno(r
, "Bad argument \"%s\", expected an absolute path in /dev/ or /sys/, device ID, or a unit name: %m", *p
);
1310 log_error_errno(r
, "Unknown device \"%s\": %m", *p
);
1316 if (arg_wait_for_initialization_timeout
> 0) {
1319 r
= device_wait_for_initialization(
1322 arg_wait_for_initialization_timeout
,
1327 sd_device_unref(device
);
1331 if (arg_action_type
== ACTION_QUERY
)
1332 r
= query_device(arg_query
, device
);
1333 else if (arg_action_type
== ACTION_ATTRIBUTE_WALK
) {
1334 if (sd_json_format_enabled(arg_json_format_flags
))
1335 r
= print_device_chain_in_json(device
);
1337 r
= print_device_chain(device
);
1338 } else if (arg_action_type
== ACTION_TREE
)
1339 r
= print_tree(device
);
1341 assert_not_reached();