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;
55 /* Put a limit on --tree descent level to not exhaust our stack */
56 #define TREE_DEPTH_MAX 64
58 static bool skip_attribute(const char *name
) {
61 /* Those are either displayed separately or should not be shown at all. */
62 return STR_IN_SET(name
,
72 typedef struct SysAttr
{
77 STATIC_DESTRUCTOR_REGISTER(arg_properties
, strv_freep
);
79 static int sysattr_compare(const SysAttr
*a
, const SysAttr
*b
) {
83 return strcmp(a
->name
, b
->name
);
86 static int print_all_attributes(sd_device
*device
, bool is_parent
) {
87 _cleanup_free_ SysAttr
*sysattrs
= NULL
;
88 const char *name
, *value
;
95 (void) sd_device_get_devpath(device
, &value
);
96 printf(" looking at %sdevice '%s':\n", is_parent
? "parent " : "", strempty(value
));
99 (void) sd_device_get_sysname(device
, &value
);
100 printf(" %s==\"%s\"\n", is_parent
? "KERNELS" : "KERNEL", strempty(value
));
103 (void) sd_device_get_subsystem(device
, &value
);
104 printf(" %s==\"%s\"\n", is_parent
? "SUBSYSTEMS" : "SUBSYSTEM", strempty(value
));
107 (void) sd_device_get_driver(device
, &value
);
108 printf(" %s==\"%s\"\n", is_parent
? "DRIVERS" : "DRIVER", strempty(value
));
110 FOREACH_DEVICE_SYSATTR(device
, name
) {
113 if (skip_attribute(name
))
116 r
= sd_device_get_sysattr_value(device
, name
, &value
);
118 /* skip any values that look like a path */
122 /* skip nonprintable attributes */
124 while (len
> 0 && isprint((unsigned char) value
[len
-1]))
129 } else if (ERRNO_IS_PRIVILEGE(r
))
130 value
= "(not readable)";
134 if (!GREEDY_REALLOC(sysattrs
, n_items
+ 1))
137 sysattrs
[n_items
] = (SysAttr
) {
144 typesafe_qsort(sysattrs
, n_items
, sysattr_compare
);
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
);
154 static int print_device_chain(sd_device
*device
) {
155 sd_device
*child
, *parent
;
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"
168 r
= print_all_attributes(device
, false);
172 for (child
= device
; sd_device_get_parent(child
, &parent
) >= 0; child
= parent
) {
173 r
= print_all_attributes(parent
, true);
181 static int print_record(sd_device
*device
, const char *prefix
) {
182 const char *str
, *val
, *subsys
;
189 prefix
= strempty(prefix
);
191 /* We don't show syspath here, because it's identical to devpath (modulo the "/sys" prefix).
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.
196 * We don't show sysattrs here, because they can be expensive and potentially issue expensive driver
199 * Coloring: let's be conservative with coloring. Let's use it to group related fields. Right now:
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 */
208 assert_se(sd_device_get_devpath(device
, &str
) >= 0);
209 printf("%sP: %s%s%s\n", prefix
, ansi_highlight_white(), str
, ansi_normal());
211 if (sd_device_get_sysname(device
, &str
) >= 0)
212 printf("%sM: %s%s%s\n", prefix
, ansi_highlight_white(), str
, ansi_normal());
214 if (sd_device_get_sysnum(device
, &str
) >= 0)
215 printf("%sR: %s%s%s\n", prefix
, ansi_highlight_white(), str
, ansi_normal());
217 if (sd_device_get_subsystem(device
, &subsys
) >= 0)
218 printf("%sU: %s%s%s\n", prefix
, ansi_highlight_green(), subsys
, ansi_normal());
220 if (sd_device_get_devtype(device
, &str
) >= 0)
221 printf("%sT: %s%s%s\n", prefix
, ansi_highlight_green(), str
, ansi_normal());
223 if (sd_device_get_devnum(device
, &devnum
) >= 0)
224 printf("%sD: %s%c %u:%u%s\n",
226 ansi_highlight_cyan(),
227 streq_ptr(subsys
, "block") ? 'b' : 'c', major(devnum
), minor(devnum
),
230 if (sd_device_get_ifindex(device
, &ifi
) >= 0)
231 printf("%sI: %s%i%s\n", prefix
, ansi_highlight_cyan(), ifi
, ansi_normal());
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());
237 if (device_get_devlink_priority(device
, &i
) >= 0)
238 printf("%sL: %s%i%s\n", prefix
, ansi_highlight_cyan(), i
, ansi_normal());
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());
246 if (sd_device_get_diskseq(device
, &q
) >= 0)
247 printf("%sQ: %s%" PRIu64
"%s\n", prefix
, ansi_highlight_magenta(), q
, ansi_normal());
249 if (sd_device_get_driver(device
, &str
) >= 0)
250 printf("%sV: %s%s%s\n", prefix
, ansi_highlight_yellow4(), str
, ansi_normal());
252 FOREACH_DEVICE_PROPERTY(device
, str
, val
)
253 printf("%sE: %s=%s\n", prefix
, str
, val
);
260 static int stat_device(const char *name
, bool export
, const char *prefix
) {
265 if (stat(name
, &statbuf
) != 0)
271 printf("%sMAJOR=%u\n"
273 prefix
, major(statbuf
.st_dev
),
274 prefix
, minor(statbuf
.st_dev
));
276 printf("%u:%u\n", major(statbuf
.st_dev
), minor(statbuf
.st_dev
));
280 static int export_devices(void) {
281 _cleanup_(sd_device_enumerator_unrefp
) sd_device_enumerator
*e
= NULL
;
285 r
= sd_device_enumerator_new(&e
);
289 r
= sd_device_enumerator_allow_uninitialized(e
);
291 return log_error_errno(r
, "Failed to set allowing uninitialized flag: %m");
293 r
= device_enumerator_scan_devices(e
);
295 return log_error_errno(r
, "Failed to scan devices: %m");
297 FOREACH_DEVICE_AND_SUBSYSTEM(e
, d
)
298 (void) print_record(d
, NULL
);
303 static void cleanup_dir(DIR *dir
, mode_t mask
, int depth
) {
309 FOREACH_DIRENT_ALL(dent
, dir
, break) {
312 if (dot_or_dot_dot(dent
->d_name
))
314 if (fstatat(dirfd(dir
), dent
->d_name
, &stats
, AT_SYMLINK_NOFOLLOW
) < 0)
316 if ((stats
.st_mode
& mask
) != 0)
318 if (S_ISDIR(stats
.st_mode
)) {
319 _cleanup_closedir_
DIR *subdir
= NULL
;
321 subdir
= xopendirat(dirfd(dir
), dent
->d_name
, O_NOFOLLOW
);
323 log_debug_errno(errno
, "Failed to open subdirectory '%s', ignoring: %m", dent
->d_name
);
325 cleanup_dir(subdir
, mask
, depth
-1);
327 (void) unlinkat(dirfd(dir
), dent
->d_name
, AT_REMOVEDIR
);
329 (void) unlinkat(dirfd(dir
), dent
->d_name
, 0);
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).
339 static void cleanup_dir_after_db_cleanup(DIR *dir
, DIR *datadir
) {
343 FOREACH_DIRENT_ALL(dent
, dir
, break) {
344 if (dot_or_dot_dot(dent
->d_name
))
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. */
352 (void) unlinkat(dirfd(dir
), dent
->d_name
, 0);
356 static void cleanup_dirs_after_db_cleanup(DIR *dir
, DIR *datadir
) {
360 FOREACH_DIRENT_ALL(dent
, dir
, break) {
363 if (dot_or_dot_dot(dent
->d_name
))
365 if (fstatat(dirfd(dir
), dent
->d_name
, &stats
, AT_SYMLINK_NOFOLLOW
) < 0)
367 if (S_ISDIR(stats
.st_mode
)) {
368 _cleanup_closedir_
DIR *subdir
= NULL
;
370 subdir
= xopendirat(dirfd(dir
), dent
->d_name
, O_NOFOLLOW
);
372 log_debug_errno(errno
, "Failed to open subdirectory '%s', ignoring: %m", dent
->d_name
);
374 cleanup_dir_after_db_cleanup(subdir
, datadir
);
376 (void) unlinkat(dirfd(dir
), dent
->d_name
, AT_REMOVEDIR
);
378 (void) unlinkat(dirfd(dir
), dent
->d_name
, 0);
382 static void cleanup_db(void) {
383 _cleanup_closedir_
DIR *dir1
= NULL
, *dir2
= NULL
, *dir3
= NULL
, *dir4
= NULL
;
385 dir1
= opendir("/run/udev/data");
387 cleanup_dir(dir1
, S_ISVTX
, 1);
389 dir2
= opendir("/run/udev/links");
391 cleanup_dirs_after_db_cleanup(dir2
, dir1
);
393 dir3
= opendir("/run/udev/tags");
395 cleanup_dirs_after_db_cleanup(dir3
, dir1
);
397 dir4
= opendir("/run/udev/static_node-tags");
399 cleanup_dir(dir4
, 0, 2);
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. */
405 static int query_device(QueryType query
, sd_device
* device
) {
414 r
= sd_device_get_devname(device
, &node
);
416 return log_error_errno(r
, "No device node found: %m");
419 assert_se(node
= path_startswith(node
, "/dev/"));
420 printf("%s\n", node
);
424 case QUERY_SYMLINK
: {
425 const char *devlink
, *prefix
= "";
427 FOREACH_DEVICE_DEVLINK(device
, devlink
) {
429 assert_se(devlink
= path_startswith(devlink
, "/dev/"));
430 printf("%s%s", prefix
, devlink
);
440 r
= sd_device_get_devpath(device
, &devpath
);
442 return log_error_errno(r
, "Failed to get device path: %m");
444 printf("%s\n", devpath
);
448 case QUERY_PROPERTY
: {
449 const char *key
, *value
;
451 FOREACH_DEVICE_PROPERTY(device
, key
, value
) {
452 if (arg_properties
&& !strv_contains(arg_properties
, key
))
456 printf("%s%s='%s'\n", strempty(arg_export_prefix
), key
, value
);
458 printf("%s\n", value
);
460 printf("%s=%s\n", key
, value
);
467 return print_record(device
, NULL
);
470 assert_not_reached();
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"
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
);
505 static int draw_tree(
507 sd_device
*const array
[], size_t n
,
511 static int output_tree_device(
516 sd_device
*const array
[], size_t n
,
519 _cleanup_free_
char *subprefix
= NULL
, *subsubprefix
= NULL
;
524 prefix
= strempty(prefix
);
526 printf("%s%s%s\n", prefix
, special_glyph(more
? SPECIAL_GLYPH_TREE_BRANCH
: SPECIAL_GLYPH_TREE_RIGHT
), str
);
528 subprefix
= strjoin(prefix
, special_glyph(more
? SPECIAL_GLYPH_TREE_VERTICAL
: SPECIAL_GLYPH_TREE_SPACE
));
532 subsubprefix
= strjoin(subprefix
, special_glyph(SPECIAL_GLYPH_VERTICAL_DOTTED
), " ");
536 (void) print_record(device
, subsubprefix
);
538 return draw_tree(device
, array
, n
, subprefix
, level
+ 1);
541 static int draw_tree(
543 sd_device
*const array
[], size_t n
,
547 const char *parent_path
;
557 r
= sd_device_get_devpath(parent
, &parent_path
);
559 return log_error_errno(r
, "Failed to get sysfs path of parent device: %m");
563 if (level
> TREE_DEPTH_MAX
) {
564 log_warning("Eliding tree below '%s', too deep.", strna(parent_path
));
569 sd_device
*device
= array
[i
];
570 const char *device_path
, *str
;
574 r
= sd_device_get_devpath(device
, &device_path
);
576 return log_error_errno(r
, "Failed to get sysfs path of enumerated device: %m");
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
;
583 r
= sd_device_get_devpath(next
, &next_path
);
585 return log_error_errno(r
, "Failed to get sysfs of child device: %m");
587 if (!path_startswith(next_path
, device_path
)) {
588 more
= !parent_path
|| path_startswith(next_path
, parent_path
);
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
;
597 r
= output_tree_device(device
, str
, prefix
, more
, array
+ i
+ 1, j
- i
- 1, level
);
607 static int print_tree(sd_device
* below
) {
608 _cleanup_(sd_device_enumerator_unrefp
) sd_device_enumerator
*e
= NULL
;
609 const char *below_path
;
615 r
= sd_device_get_devpath(below
, &below_path
);
617 return log_error_errno(r
, "Failed to get sysfs path of device: %m");
622 r
= sd_device_enumerator_new(&e
);
624 return log_error_errno(r
, "Failed to allocate device enumerator: %m");
627 r
= sd_device_enumerator_add_match_parent(e
, below
);
629 return log_error_errno(r
, "Failed to install parent enumerator match: %m");
632 r
= sd_device_enumerator_allow_uninitialized(e
);
634 return log_error_errno(r
, "Failed to enable enumeration of uninitialized devices: %m");
636 r
= device_enumerator_scan_devices_and_subsystems(e
);
638 return log_error_errno(r
, "Failed to scan for devices and subsystems: %m");
640 assert_se(array
= device_enumerator_get_devices(e
, &n
));
643 log_info("No items.");
647 r
= draw_tree(NULL
, array
, n
, NULL
, 0);
651 printf("\n%zu items shown.\n", n
);
655 int info_main(int argc
, char *argv
[], void *userdata
) {
656 _cleanup_strv_free_
char **devices
= NULL
;
657 _cleanup_free_
char *name
= NULL
;
661 ARG_PROPERTY
= 0x100,
665 static const struct option options
[] = {
666 { "attribute-walk", no_argument
, NULL
, 'a' },
667 { "tree", no_argument
, NULL
, 't' },
668 { "cleanup-db", no_argument
, NULL
, 'c' },
669 { "device-id-of-file", required_argument
, NULL
, 'd' },
670 { "export", no_argument
, NULL
, 'x' },
671 { "export-db", no_argument
, NULL
, 'e' },
672 { "export-prefix", required_argument
, NULL
, 'P' },
673 { "help", no_argument
, NULL
, 'h' },
674 { "name", required_argument
, NULL
, 'n' },
675 { "path", required_argument
, NULL
, 'p' },
676 { "property", required_argument
, NULL
, ARG_PROPERTY
},
677 { "query", required_argument
, NULL
, 'q' },
678 { "root", no_argument
, NULL
, 'r' },
679 { "value", no_argument
, NULL
, ARG_VALUE
},
680 { "version", no_argument
, NULL
, 'V' },
681 { "wait-for-initialization", optional_argument
, NULL
, 'w' },
685 ActionType action
= ACTION_QUERY
;
686 QueryType query
= QUERY_ALL
;
688 while ((c
= getopt_long(argc
, argv
, "atced:n:p:q:rxP:w::Vh", options
, NULL
)) >= 0)
691 /* Make sure that if the empty property list was specified, we won't show any
693 if (isempty(optarg
) && !arg_properties
) {
694 arg_properties
= new0(char*, 1);
698 r
= strv_split_and_extend(&arg_properties
, optarg
, ",", true);
708 const char *prefix
= c
== 'n' ? "/dev/" : "/sys/";
711 path
= path_join(path_startswith(optarg
, prefix
) ? NULL
: prefix
, optarg
);
715 r
= strv_consume(&devices
, path
);
722 action
= ACTION_QUERY
;
723 if (streq(optarg
, "property") || streq(optarg
, "env"))
724 query
= QUERY_PROPERTY
;
725 else if (streq(optarg
, "name"))
727 else if (streq(optarg
, "symlink"))
728 query
= QUERY_SYMLINK
;
729 else if (streq(optarg
, "path"))
731 else if (streq(optarg
, "all"))
734 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "unknown query type");
740 action
= ACTION_DEVICE_ID_FILE
;
741 r
= free_and_strdup(&name
, optarg
);
746 action
= ACTION_ATTRIBUTE_WALK
;
749 action
= ACTION_TREE
;
752 return export_devices();
761 arg_export_prefix
= optarg
;
765 r
= parse_sec(optarg
, &arg_wait_for_initialization_timeout
);
767 return log_error_errno(r
, "Failed to parse timeout value: %m");
769 arg_wait_for_initialization_timeout
= USEC_INFINITY
;
772 return print_version();
778 assert_not_reached();
781 if (action
== ACTION_DEVICE_ID_FILE
) {
783 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
784 "Positional arguments are not allowed with -d/--device-id-of-file.");
786 return stat_device(name
, arg_export
, arg_export_prefix
);
789 r
= strv_extend_strv(&devices
, argv
+ optind
, false);
791 return log_error_errno(r
, "Failed to build argument list: %m");
793 if (action
!= ACTION_TREE
&& strv_isempty(devices
))
794 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
795 "A device name or path is required");
796 if (IN_SET(action
, ACTION_ATTRIBUTE_WALK
, ACTION_TREE
) && strv_length(devices
) > 1)
797 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
798 "Only one device may be specified with -a/--attribute-walk and -t/--tree");
800 if (arg_export
&& arg_value
)
801 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
802 "-x/--export or -P/--export-prefix cannot be used with --value");
804 if (strv_isempty(devices
)) {
805 assert(action
== ACTION_TREE
);
807 return print_tree(NULL
);
811 STRV_FOREACH(p
, devices
) {
812 _cleanup_(sd_device_unrefp
) sd_device
*device
= NULL
;
814 r
= find_device(*p
, NULL
, &device
);
817 log_error_errno(r
, "Bad argument \"%s\", expected an absolute path in /dev/ or /sys/ or a unit name: %m", *p
);
819 log_error_errno(r
, "Unknown device \"%s\": %m", *p
);
826 if (arg_wait_for_initialization_timeout
> 0) {
829 r
= device_wait_for_initialization(
832 usec_add(now(CLOCK_MONOTONIC
), arg_wait_for_initialization_timeout
),
837 sd_device_unref(device
);
841 if (action
== ACTION_QUERY
)
842 r
= query_device(query
, device
);
843 else if (action
== ACTION_ATTRIBUTE_WALK
)
844 r
= print_device_chain(device
);
845 else if (action
== ACTION_TREE
)
846 r
= print_tree(device
);
848 assert_not_reached();