1 /* SPDX-License-Identifier: GPL-2.0-or-later */
12 #include "sd-device.h"
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"
22 #include "glyph-util.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"
31 #include "udevadm-util.h"
33 typedef enum ActionType
{
35 ACTION_ATTRIBUTE_WALK
,
36 ACTION_DEVICE_ID_FILE
,
40 typedef enum QueryType
{
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;
56 /* Put a limit on --tree descent level to not exhaust our stack */
57 #define TREE_DEPTH_MAX 64
59 static bool skip_attribute(const char *name
) {
62 /* Those are either displayed separately or should not be shown at all. */
63 return STR_IN_SET(name
,
73 typedef struct SysAttr
{
78 STATIC_DESTRUCTOR_REGISTER(arg_properties
, strv_freep
);
80 static int sysattr_compare(const SysAttr
*a
, const SysAttr
*b
) {
84 return strcmp(a
->name
, b
->name
);
87 static int print_all_attributes(sd_device
*device
, bool is_parent
) {
88 _cleanup_free_ SysAttr
*sysattrs
= NULL
;
89 const char *name
, *value
;
96 (void) sd_device_get_devpath(device
, &value
);
97 printf(" looking at %sdevice '%s':\n", is_parent
? "parent " : "", strempty(value
));
100 (void) sd_device_get_sysname(device
, &value
);
101 printf(" %s==\"%s\"\n", is_parent
? "KERNELS" : "KERNEL", strempty(value
));
104 (void) sd_device_get_subsystem(device
, &value
);
105 printf(" %s==\"%s\"\n", is_parent
? "SUBSYSTEMS" : "SUBSYSTEM", strempty(value
));
108 (void) sd_device_get_driver(device
, &value
);
109 printf(" %s==\"%s\"\n", is_parent
? "DRIVERS" : "DRIVER", strempty(value
));
111 FOREACH_DEVICE_SYSATTR(device
, name
) {
114 if (skip_attribute(name
))
117 r
= sd_device_get_sysattr_value(device
, name
, &value
);
119 /* skip any values that look like a path */
123 /* skip nonprintable attributes */
125 while (len
> 0 && isprint((unsigned char) value
[len
-1]))
130 } else if (ERRNO_IS_PRIVILEGE(r
))
131 value
= "(not readable)";
135 if (!GREEDY_REALLOC(sysattrs
, n_items
+ 1))
138 sysattrs
[n_items
] = (SysAttr
) {
145 typesafe_qsort(sysattrs
, n_items
, sysattr_compare
);
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
);
155 static int print_device_chain(sd_device
*device
) {
156 sd_device
*child
, *parent
;
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"
169 r
= print_all_attributes(device
, false);
173 for (child
= device
; sd_device_get_parent(child
, &parent
) >= 0; child
= parent
) {
174 r
= print_all_attributes(parent
, true);
182 static int print_record(sd_device
*device
, const char *prefix
) {
183 const char *str
, *val
, *subsys
;
190 prefix
= strempty(prefix
);
192 /* We don't show syspath here, because it's identical to devpath (modulo the "/sys" prefix).
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.
197 * We don't show sysattrs here, because they can be expensive and potentially issue expensive driver
200 * Coloring: let's be conservative with coloring. Let's use it to group related fields. Right now:
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 */
209 assert_se(sd_device_get_devpath(device
, &str
) >= 0);
210 printf("%sP: %s%s%s\n", prefix
, ansi_highlight_white(), str
, ansi_normal());
212 if (sd_device_get_sysname(device
, &str
) >= 0)
213 printf("%sM: %s%s%s\n", prefix
, ansi_highlight_white(), str
, ansi_normal());
215 if (sd_device_get_sysnum(device
, &str
) >= 0)
216 printf("%sR: %s%s%s\n", prefix
, ansi_highlight_white(), str
, ansi_normal());
218 if (sd_device_get_subsystem(device
, &subsys
) >= 0)
219 printf("%sU: %s%s%s\n", prefix
, ansi_highlight_green(), subsys
, ansi_normal());
221 if (sd_device_get_devtype(device
, &str
) >= 0)
222 printf("%sT: %s%s%s\n", prefix
, ansi_highlight_green(), str
, ansi_normal());
224 if (sd_device_get_devnum(device
, &devnum
) >= 0)
225 printf("%sD: %s%c %u:%u%s\n",
227 ansi_highlight_cyan(),
228 streq_ptr(subsys
, "block") ? 'b' : 'c', major(devnum
), minor(devnum
),
231 if (sd_device_get_ifindex(device
, &ifi
) >= 0)
232 printf("%sI: %s%i%s\n", prefix
, ansi_highlight_cyan(), ifi
, ansi_normal());
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());
238 if (device_get_devlink_priority(device
, &i
) >= 0)
239 printf("%sL: %s%i%s\n", prefix
, ansi_highlight_cyan(), i
, ansi_normal());
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());
247 if (sd_device_get_diskseq(device
, &q
) >= 0)
248 printf("%sQ: %s%" PRIu64
"%s\n", prefix
, ansi_highlight_magenta(), q
, ansi_normal());
250 if (sd_device_get_driver(device
, &str
) >= 0)
251 printf("%sV: %s%s%s\n", prefix
, ansi_highlight_yellow4(), str
, ansi_normal());
253 FOREACH_DEVICE_PROPERTY(device
, str
, val
)
254 printf("%sE: %s=%s\n", prefix
, str
, val
);
261 static int stat_device(const char *name
, bool export
, const char *prefix
) {
266 if (stat(name
, &statbuf
) != 0)
272 printf("%sMAJOR=%u\n"
274 prefix
, major(statbuf
.st_dev
),
275 prefix
, minor(statbuf
.st_dev
));
277 printf("%u:%u\n", major(statbuf
.st_dev
), minor(statbuf
.st_dev
));
281 static int export_devices(void) {
282 _cleanup_(sd_device_enumerator_unrefp
) sd_device_enumerator
*e
= NULL
;
286 r
= sd_device_enumerator_new(&e
);
290 r
= sd_device_enumerator_allow_uninitialized(e
);
292 return log_error_errno(r
, "Failed to set allowing uninitialized flag: %m");
294 r
= device_enumerator_scan_devices(e
);
296 return log_error_errno(r
, "Failed to scan devices: %m");
298 pager_open(arg_pager_flags
);
300 FOREACH_DEVICE_AND_SUBSYSTEM(e
, d
)
301 (void) print_record(d
, NULL
);
306 static void cleanup_dir(DIR *dir
, mode_t mask
, int depth
) {
312 FOREACH_DIRENT_ALL(dent
, dir
, break) {
315 if (dot_or_dot_dot(dent
->d_name
))
317 if (fstatat(dirfd(dir
), dent
->d_name
, &stats
, AT_SYMLINK_NOFOLLOW
) < 0)
319 if ((stats
.st_mode
& mask
) != 0)
321 if (S_ISDIR(stats
.st_mode
)) {
322 _cleanup_closedir_
DIR *subdir
= NULL
;
324 subdir
= xopendirat(dirfd(dir
), dent
->d_name
, O_NOFOLLOW
);
326 log_debug_errno(errno
, "Failed to open subdirectory '%s', ignoring: %m", dent
->d_name
);
328 cleanup_dir(subdir
, mask
, depth
-1);
330 (void) unlinkat(dirfd(dir
), dent
->d_name
, AT_REMOVEDIR
);
332 (void) unlinkat(dirfd(dir
), dent
->d_name
, 0);
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).
342 static void cleanup_dir_after_db_cleanup(DIR *dir
, DIR *datadir
) {
346 FOREACH_DIRENT_ALL(dent
, dir
, break) {
347 if (dot_or_dot_dot(dent
->d_name
))
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. */
355 (void) unlinkat(dirfd(dir
), dent
->d_name
, 0);
359 static void cleanup_dirs_after_db_cleanup(DIR *dir
, DIR *datadir
) {
363 FOREACH_DIRENT_ALL(dent
, dir
, break) {
366 if (dot_or_dot_dot(dent
->d_name
))
368 if (fstatat(dirfd(dir
), dent
->d_name
, &stats
, AT_SYMLINK_NOFOLLOW
) < 0)
370 if (S_ISDIR(stats
.st_mode
)) {
371 _cleanup_closedir_
DIR *subdir
= NULL
;
373 subdir
= xopendirat(dirfd(dir
), dent
->d_name
, O_NOFOLLOW
);
375 log_debug_errno(errno
, "Failed to open subdirectory '%s', ignoring: %m", dent
->d_name
);
377 cleanup_dir_after_db_cleanup(subdir
, datadir
);
379 (void) unlinkat(dirfd(dir
), dent
->d_name
, AT_REMOVEDIR
);
381 (void) unlinkat(dirfd(dir
), dent
->d_name
, 0);
385 static void cleanup_db(void) {
386 _cleanup_closedir_
DIR *dir1
= NULL
, *dir2
= NULL
, *dir3
= NULL
, *dir4
= NULL
;
388 dir1
= opendir("/run/udev/data");
390 cleanup_dir(dir1
, S_ISVTX
, 1);
392 dir2
= opendir("/run/udev/links");
394 cleanup_dirs_after_db_cleanup(dir2
, dir1
);
396 dir3
= opendir("/run/udev/tags");
398 cleanup_dirs_after_db_cleanup(dir3
, dir1
);
400 dir4
= opendir("/run/udev/static_node-tags");
402 cleanup_dir(dir4
, 0, 2);
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. */
408 static int query_device(QueryType query
, sd_device
* device
) {
417 r
= sd_device_get_devname(device
, &node
);
419 return log_error_errno(r
, "No device node found: %m");
422 assert_se(node
= path_startswith(node
, "/dev/"));
423 printf("%s\n", node
);
427 case QUERY_SYMLINK
: {
428 const char *devlink
, *prefix
= "";
430 FOREACH_DEVICE_DEVLINK(device
, devlink
) {
432 assert_se(devlink
= path_startswith(devlink
, "/dev/"));
433 printf("%s%s", prefix
, devlink
);
443 r
= sd_device_get_devpath(device
, &devpath
);
445 return log_error_errno(r
, "Failed to get device path: %m");
447 printf("%s\n", devpath
);
451 case QUERY_PROPERTY
: {
452 const char *key
, *value
;
454 FOREACH_DEVICE_PROPERTY(device
, key
, value
) {
455 if (arg_properties
&& !strv_contains(arg_properties
, key
))
459 printf("%s%s='%s'\n", strempty(arg_export_prefix
), key
, value
);
461 printf("%s\n", value
);
463 printf("%s=%s\n", key
, value
);
470 return print_record(device
, NULL
);
473 assert_not_reached();
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"
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
);
509 static int draw_tree(
511 sd_device
*const array
[], size_t n
,
515 static int output_tree_device(
520 sd_device
*const array
[], size_t n
,
523 _cleanup_free_
char *subprefix
= NULL
, *subsubprefix
= NULL
;
528 prefix
= strempty(prefix
);
530 printf("%s%s%s\n", prefix
, special_glyph(more
? SPECIAL_GLYPH_TREE_BRANCH
: SPECIAL_GLYPH_TREE_RIGHT
), str
);
532 subprefix
= strjoin(prefix
, special_glyph(more
? SPECIAL_GLYPH_TREE_VERTICAL
: SPECIAL_GLYPH_TREE_SPACE
));
536 subsubprefix
= strjoin(subprefix
, special_glyph(SPECIAL_GLYPH_VERTICAL_DOTTED
), " ");
540 (void) print_record(device
, subsubprefix
);
542 return draw_tree(device
, array
, n
, subprefix
, level
+ 1);
545 static int draw_tree(
547 sd_device
*const array
[], size_t n
,
551 const char *parent_path
;
561 r
= sd_device_get_devpath(parent
, &parent_path
);
563 return log_error_errno(r
, "Failed to get sysfs path of parent device: %m");
567 if (level
> TREE_DEPTH_MAX
) {
568 log_warning("Eliding tree below '%s', too deep.", strna(parent_path
));
573 sd_device
*device
= array
[i
];
574 const char *device_path
, *str
;
578 r
= sd_device_get_devpath(device
, &device_path
);
580 return log_error_errno(r
, "Failed to get sysfs path of enumerated device: %m");
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
;
587 r
= sd_device_get_devpath(next
, &next_path
);
589 return log_error_errno(r
, "Failed to get sysfs of child device: %m");
591 if (!path_startswith(next_path
, device_path
)) {
592 more
= !parent_path
|| path_startswith(next_path
, parent_path
);
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
;
601 r
= output_tree_device(device
, str
, prefix
, more
, array
+ i
+ 1, j
- i
- 1, level
);
611 static int print_tree(sd_device
* below
) {
612 _cleanup_(sd_device_enumerator_unrefp
) sd_device_enumerator
*e
= NULL
;
613 const char *below_path
;
619 r
= sd_device_get_devpath(below
, &below_path
);
621 return log_error_errno(r
, "Failed to get sysfs path of device: %m");
626 r
= sd_device_enumerator_new(&e
);
628 return log_error_errno(r
, "Failed to allocate device enumerator: %m");
631 r
= sd_device_enumerator_add_match_parent(e
, below
);
633 return log_error_errno(r
, "Failed to install parent enumerator match: %m");
636 r
= sd_device_enumerator_allow_uninitialized(e
);
638 return log_error_errno(r
, "Failed to enable enumeration of uninitialized devices: %m");
640 r
= device_enumerator_scan_devices_and_subsystems(e
);
642 return log_error_errno(r
, "Failed to scan for devices and subsystems: %m");
645 /* This must be called after device_enumerator_scan_devices_and_subsystems(). */
646 r
= device_enumerator_add_parent_devices(e
, below
);
648 return log_error_errno(r
, "Failed to add parent devices: %m");
651 assert_se(array
= device_enumerator_get_devices(e
, &n
));
654 log_info("No items.");
658 r
= draw_tree(NULL
, array
, n
, NULL
, 0);
662 printf("\n%zu items shown.\n", n
);
666 int info_main(int argc
, char *argv
[], void *userdata
) {
667 _cleanup_strv_free_
char **devices
= NULL
;
668 _cleanup_free_
char *name
= NULL
;
672 ARG_PROPERTY
= 0x100,
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
},
698 ActionType action
= ACTION_QUERY
;
699 QueryType query
= QUERY_ALL
;
701 while ((c
= getopt_long(argc
, argv
, "atced:n:p:q:rxP:w::Vh", options
, NULL
)) >= 0)
704 /* Make sure that if the empty property list was specified, we won't show any
706 if (isempty(optarg
) && !arg_properties
) {
707 arg_properties
= new0(char*, 1);
711 r
= strv_split_and_extend(&arg_properties
, optarg
, ",", true);
721 const char *prefix
= c
== 'n' ? "/dev/" : "/sys/";
724 path
= path_join(path_startswith(optarg
, prefix
) ? NULL
: prefix
, optarg
);
728 r
= strv_consume(&devices
, path
);
735 action
= ACTION_QUERY
;
736 if (streq(optarg
, "property") || streq(optarg
, "env"))
737 query
= QUERY_PROPERTY
;
738 else if (streq(optarg
, "name"))
740 else if (streq(optarg
, "symlink"))
741 query
= QUERY_SYMLINK
;
742 else if (streq(optarg
, "path"))
744 else if (streq(optarg
, "all"))
747 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "unknown query type");
753 action
= ACTION_DEVICE_ID_FILE
;
754 r
= free_and_strdup(&name
, optarg
);
759 action
= ACTION_ATTRIBUTE_WALK
;
762 action
= ACTION_TREE
;
765 return export_devices();
774 arg_export_prefix
= optarg
;
778 r
= parse_sec(optarg
, &arg_wait_for_initialization_timeout
);
780 return log_error_errno(r
, "Failed to parse timeout value: %m");
782 arg_wait_for_initialization_timeout
= USEC_INFINITY
;
785 return print_version();
789 arg_pager_flags
|= PAGER_DISABLE
;
794 assert_not_reached();
797 if (action
== ACTION_DEVICE_ID_FILE
) {
799 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
800 "Positional arguments are not allowed with -d/--device-id-of-file.");
802 return stat_device(name
, arg_export
, arg_export_prefix
);
805 r
= strv_extend_strv(&devices
, argv
+ optind
, false);
807 return log_error_errno(r
, "Failed to build argument list: %m");
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");
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");
820 pager_open(arg_pager_flags
);
822 if (strv_isempty(devices
)) {
823 assert(action
== ACTION_TREE
);
824 return print_tree(NULL
);
828 STRV_FOREACH(p
, devices
) {
829 _cleanup_(sd_device_unrefp
) sd_device
*device
= NULL
;
831 r
= find_device(*p
, NULL
, &device
);
834 log_error_errno(r
, "Bad argument \"%s\", expected an absolute path in /dev/ or /sys/ or a unit name: %m", *p
);
836 log_error_errno(r
, "Unknown device \"%s\": %m", *p
);
843 if (arg_wait_for_initialization_timeout
> 0) {
846 r
= device_wait_for_initialization(
849 arg_wait_for_initialization_timeout
,
854 sd_device_unref(device
);
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
);
865 assert_not_reached();