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"
21 #include "sort-util.h"
22 #include "static-destruct.h"
23 #include "string-table.h"
24 #include "string-util.h"
25 #include "udev-util.h"
26 #include "udevadm-util.h"
29 typedef enum ActionType
{
31 ACTION_ATTRIBUTE_WALK
,
32 ACTION_DEVICE_ID_FILE
,
35 typedef enum QueryType
{
43 static char **arg_properties
= NULL
;
44 static bool arg_root
= false;
45 static bool arg_export
= false;
46 static bool arg_value
= false;
47 static const char *arg_export_prefix
= NULL
;
48 static usec_t arg_wait_for_initialization_timeout
= 0;
50 static bool skip_attribute(const char *name
) {
53 /* Those are either displayed separately or should not be shown at all. */
54 return STR_IN_SET(name
,
64 typedef struct SysAttr
{
69 STATIC_DESTRUCTOR_REGISTER(arg_properties
, strv_freep
);
71 static int sysattr_compare(const SysAttr
*a
, const SysAttr
*b
) {
75 return strcmp(a
->name
, b
->name
);
78 static int print_all_attributes(sd_device
*device
, bool is_parent
) {
79 _cleanup_free_ SysAttr
*sysattrs
= NULL
;
80 const char *name
, *value
;
87 (void) sd_device_get_devpath(device
, &value
);
88 printf(" looking at %sdevice '%s':\n", is_parent
? "parent " : "", strempty(value
));
91 (void) sd_device_get_sysname(device
, &value
);
92 printf(" %s==\"%s\"\n", is_parent
? "KERNELS" : "KERNEL", strempty(value
));
95 (void) sd_device_get_subsystem(device
, &value
);
96 printf(" %s==\"%s\"\n", is_parent
? "SUBSYSTEMS" : "SUBSYSTEM", strempty(value
));
99 (void) sd_device_get_driver(device
, &value
);
100 printf(" %s==\"%s\"\n", is_parent
? "DRIVERS" : "DRIVER", strempty(value
));
102 FOREACH_DEVICE_SYSATTR(device
, name
) {
105 if (skip_attribute(name
))
108 r
= sd_device_get_sysattr_value(device
, name
, &value
);
110 /* skip any values that look like a path */
114 /* skip nonprintable attributes */
116 while (len
> 0 && isprint((unsigned char) value
[len
-1]))
121 } else if (ERRNO_IS_PRIVILEGE(r
))
122 value
= "(not readable)";
126 if (!GREEDY_REALLOC(sysattrs
, n_items
+ 1))
129 sysattrs
[n_items
] = (SysAttr
) {
136 typesafe_qsort(sysattrs
, n_items
, sysattr_compare
);
138 for (size_t i
= 0; i
< n_items
; i
++)
139 printf(" %s{%s}==\"%s\"\n", is_parent
? "ATTRS" : "ATTR", sysattrs
[i
].name
, sysattrs
[i
].value
);
146 static int print_device_chain(sd_device
*device
) {
147 sd_device
*child
, *parent
;
153 "Udevadm info starts with the device specified by the devpath and then\n"
154 "walks up the chain of parent devices. It prints for every device\n"
155 "found, all possible attributes in the udev rules key format.\n"
156 "A rule to match, can be composed by the attributes of the device\n"
157 "and the attributes from one single parent device.\n"
160 r
= print_all_attributes(device
, false);
164 for (child
= device
; sd_device_get_parent(child
, &parent
) >= 0; child
= parent
) {
165 r
= print_all_attributes(parent
, true);
173 static int print_record(sd_device
*device
) {
174 const char *str
, *val
;
179 (void) sd_device_get_devpath(device
, &str
);
180 printf("P: %s\n", str
);
182 if (sd_device_get_devname(device
, &str
) >= 0) {
183 assert_se(val
= path_startswith(str
, "/dev/"));
184 printf("N: %s\n", val
);
187 if (device_get_devlink_priority(device
, &i
) >= 0)
188 printf("L: %i\n", i
);
190 FOREACH_DEVICE_DEVLINK(device
, str
) {
191 assert_se(val
= path_startswith(str
, "/dev/"));
192 printf("S: %s\n", val
);
195 FOREACH_DEVICE_PROPERTY(device
, str
, val
)
196 printf("E: %s=%s\n", str
, val
);
202 static int stat_device(const char *name
, bool export
, const char *prefix
) {
207 if (stat(name
, &statbuf
) != 0)
213 printf("%sMAJOR=%u\n"
215 prefix
, major(statbuf
.st_dev
),
216 prefix
, minor(statbuf
.st_dev
));
218 printf("%u:%u\n", major(statbuf
.st_dev
), minor(statbuf
.st_dev
));
222 static int export_devices(void) {
223 _cleanup_(sd_device_enumerator_unrefp
) sd_device_enumerator
*e
= NULL
;
227 r
= sd_device_enumerator_new(&e
);
231 r
= sd_device_enumerator_allow_uninitialized(e
);
233 return log_error_errno(r
, "Failed to set allowing uninitialized flag: %m");
235 r
= device_enumerator_scan_devices(e
);
237 return log_error_errno(r
, "Failed to scan devices: %m");
239 FOREACH_DEVICE_AND_SUBSYSTEM(e
, d
)
240 (void) print_record(d
);
245 static void cleanup_dir(DIR *dir
, mode_t mask
, int depth
) {
251 FOREACH_DIRENT_ALL(dent
, dir
, break) {
254 if (dot_or_dot_dot(dent
->d_name
))
256 if (fstatat(dirfd(dir
), dent
->d_name
, &stats
, AT_SYMLINK_NOFOLLOW
) < 0)
258 if ((stats
.st_mode
& mask
) != 0)
260 if (S_ISDIR(stats
.st_mode
)) {
261 _cleanup_closedir_
DIR *dir2
= NULL
;
263 dir2
= fdopendir(openat(dirfd(dir
), dent
->d_name
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
));
265 cleanup_dir(dir2
, mask
, depth
-1);
267 (void) unlinkat(dirfd(dir
), dent
->d_name
, AT_REMOVEDIR
);
269 (void) unlinkat(dirfd(dir
), dent
->d_name
, 0);
274 * Assume that dir is a directory with file names matching udev data base
275 * entries for devices in /run/udev/data (such as "b8:16"), and removes
276 * all files except those that haven't been deleted in /run/udev/data
277 * (i.e. they were skipped during db cleanup because of the db_persist flag).
279 static void cleanup_dir_after_db_cleanup(DIR *dir
, DIR *datadir
) {
283 FOREACH_DIRENT_ALL(dent
, dir
, break) {
284 if (dot_or_dot_dot(dent
->d_name
))
287 if (faccessat(dirfd(datadir
), dent
->d_name
, F_OK
, AT_SYMLINK_NOFOLLOW
) >= 0)
288 /* The corresponding udev database file still exists.
289 * Assuming the parsistent flag is set for the database. */
292 (void) unlinkat(dirfd(dir
), dent
->d_name
, 0);
296 static void cleanup_dirs_after_db_cleanup(DIR *dir
, DIR *datadir
) {
300 FOREACH_DIRENT_ALL(dent
, dir
, break) {
303 if (dot_or_dot_dot(dent
->d_name
))
305 if (fstatat(dirfd(dir
), dent
->d_name
, &stats
, AT_SYMLINK_NOFOLLOW
) < 0)
307 if (S_ISDIR(stats
.st_mode
)) {
308 _cleanup_closedir_
DIR *dir2
= NULL
;
310 dir2
= fdopendir(openat(dirfd(dir
), dent
->d_name
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
));
312 cleanup_dir_after_db_cleanup(dir2
, datadir
);
314 (void) unlinkat(dirfd(dir
), dent
->d_name
, AT_REMOVEDIR
);
316 (void) unlinkat(dirfd(dir
), dent
->d_name
, 0);
320 static void cleanup_db(void) {
321 _cleanup_closedir_
DIR *dir1
= NULL
, *dir2
= NULL
, *dir3
= NULL
, *dir4
= NULL
;
323 dir1
= opendir("/run/udev/data");
325 cleanup_dir(dir1
, S_ISVTX
, 1);
327 dir2
= opendir("/run/udev/links");
329 cleanup_dirs_after_db_cleanup(dir2
, dir1
);
331 dir3
= opendir("/run/udev/tags");
333 cleanup_dirs_after_db_cleanup(dir3
, dir1
);
335 dir4
= opendir("/run/udev/static_node-tags");
337 cleanup_dir(dir4
, 0, 2);
339 /* Do not remove /run/udev/watch. It will be handled by udevd well on restart.
340 * And should not be removed by external program when udevd is running. */
343 static int query_device(QueryType query
, sd_device
* device
) {
352 r
= sd_device_get_devname(device
, &node
);
354 return log_error_errno(r
, "No device node found: %m");
357 assert_se(node
= path_startswith(node
, "/dev/"));
358 printf("%s\n", node
);
362 case QUERY_SYMLINK
: {
363 const char *devlink
, *prefix
= "";
365 FOREACH_DEVICE_DEVLINK(device
, devlink
) {
367 assert_se(devlink
= path_startswith(devlink
, "/dev/"));
368 printf("%s%s", prefix
, devlink
);
378 r
= sd_device_get_devpath(device
, &devpath
);
380 return log_error_errno(r
, "Failed to get device path: %m");
382 printf("%s\n", devpath
);
386 case QUERY_PROPERTY
: {
387 const char *key
, *value
;
389 FOREACH_DEVICE_PROPERTY(device
, key
, value
) {
390 if (arg_properties
&& !strv_contains(arg_properties
, key
))
394 printf("%s%s='%s'\n", strempty(arg_export_prefix
), key
, value
);
396 printf("%s\n", value
);
398 printf("%s=%s\n", key
, value
);
405 return print_record(device
);
408 assert_not_reached();
412 static int help(void) {
413 printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
414 "Query sysfs or the udev database.\n\n"
415 " -h --help Print this message\n"
416 " -V --version Print version of the program\n"
417 " -q --query=TYPE Query device information:\n"
418 " name Name of device node\n"
419 " symlink Pointing to node\n"
420 " path sysfs device path\n"
421 " property The device properties\n"
423 " --property=NAME Show only properties by this name\n"
424 " --value When showing properties, print only their values\n"
425 " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
426 " -n --name=NAME Node or symlink name used for query or attribute walk\n"
427 " -r --root Prepend dev directory to path names\n"
428 " -a --attribute-walk Print all key matches walking along the chain\n"
429 " of parent devices\n"
430 " -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
431 " -x --export Export key/value pairs\n"
432 " -P --export-prefix Export the key name with a prefix\n"
433 " -e --export-db Export the content of the udev database\n"
434 " -c --cleanup-db Clean up the udev database\n"
435 " -w --wait-for-initialization[=SECONDS]\n"
436 " Wait for device to be initialized\n",
437 program_invocation_short_name
);
442 int info_main(int argc
, char *argv
[], void *userdata
) {
443 _cleanup_strv_free_
char **devices
= NULL
;
444 _cleanup_free_
char *name
= NULL
;
448 ARG_PROPERTY
= 0x100,
452 static const struct option options
[] = {
453 { "attribute-walk", no_argument
, NULL
, 'a' },
454 { "cleanup-db", no_argument
, NULL
, 'c' },
455 { "device-id-of-file", required_argument
, NULL
, 'd' },
456 { "export", no_argument
, NULL
, 'x' },
457 { "export-db", no_argument
, NULL
, 'e' },
458 { "export-prefix", required_argument
, NULL
, 'P' },
459 { "help", no_argument
, NULL
, 'h' },
460 { "name", required_argument
, NULL
, 'n' },
461 { "path", required_argument
, NULL
, 'p' },
462 { "property", required_argument
, NULL
, ARG_PROPERTY
},
463 { "query", required_argument
, NULL
, 'q' },
464 { "root", no_argument
, NULL
, 'r' },
465 { "value", no_argument
, NULL
, ARG_VALUE
},
466 { "version", no_argument
, NULL
, 'V' },
467 { "wait-for-initialization", optional_argument
, NULL
, 'w' },
471 ActionType action
= ACTION_QUERY
;
472 QueryType query
= QUERY_ALL
;
474 while ((c
= getopt_long(argc
, argv
, "aced:n:p:q:rxP:w::Vh", options
, NULL
)) >= 0)
477 /* Make sure that if the empty property list was specified, we won't show any
479 if (isempty(optarg
) && !arg_properties
) {
480 arg_properties
= new0(char*, 1);
484 r
= strv_split_and_extend(&arg_properties
, optarg
, ",", true);
494 const char *prefix
= c
== 'n' ? "/dev/" : "/sys/";
497 path
= path_join(path_startswith(optarg
, prefix
) ? NULL
: prefix
, optarg
);
501 r
= strv_consume(&devices
, path
);
508 action
= ACTION_QUERY
;
509 if (streq(optarg
, "property") || streq(optarg
, "env"))
510 query
= QUERY_PROPERTY
;
511 else if (streq(optarg
, "name"))
513 else if (streq(optarg
, "symlink"))
514 query
= QUERY_SYMLINK
;
515 else if (streq(optarg
, "path"))
517 else if (streq(optarg
, "all"))
520 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "unknown query type");
526 action
= ACTION_DEVICE_ID_FILE
;
527 r
= free_and_strdup(&name
, optarg
);
532 action
= ACTION_ATTRIBUTE_WALK
;
535 return export_devices();
544 arg_export_prefix
= optarg
;
548 r
= parse_sec(optarg
, &arg_wait_for_initialization_timeout
);
550 return log_error_errno(r
, "Failed to parse timeout value: %m");
552 arg_wait_for_initialization_timeout
= USEC_INFINITY
;
555 return print_version();
561 assert_not_reached();
564 if (action
== ACTION_DEVICE_ID_FILE
) {
566 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
567 "Positional arguments are not allowed with -d/--device-id-of-file.");
569 return stat_device(name
, arg_export
, arg_export_prefix
);
572 r
= strv_extend_strv(&devices
, argv
+ optind
, false);
574 return log_error_errno(r
, "Failed to build argument list: %m");
576 if (strv_isempty(devices
))
577 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
578 "A device name or path is required");
579 if (action
== ACTION_ATTRIBUTE_WALK
&& strv_length(devices
) > 1)
580 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
581 "Only one device may be specified with -a/--attribute-walk");
583 if (arg_export
&& arg_value
)
584 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
585 "-x/--export or -P/--export-prefix cannot be used with --value");
588 STRV_FOREACH(p
, devices
) {
589 _cleanup_(sd_device_unrefp
) sd_device
*device
= NULL
;
591 r
= find_device(*p
, NULL
, &device
);
593 return log_error_errno(r
, "Bad argument \"%s\", expected an absolute path in /dev/ or /sys or a unit name: %m", *p
);
595 return log_error_errno(r
, "Unknown device \"%s\": %m", *p
);
597 if (arg_wait_for_initialization_timeout
> 0) {
600 r
= device_wait_for_initialization(
603 usec_add(now(CLOCK_MONOTONIC
), arg_wait_for_initialization_timeout
),
608 sd_device_unref(device
);
612 if (action
== ACTION_QUERY
)
613 r
= query_device(query
, device
);
614 else if (action
== ACTION_ATTRIBUTE_WALK
)
615 r
= print_device_chain(device
);
617 assert_not_reached();