1 /* SPDX-License-Identifier: GPL-2.0+ */
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"
20 #include "sort-util.h"
21 #include "string-table.h"
22 #include "string-util.h"
23 #include "udev-util.h"
24 #include "udevadm-util.h"
27 typedef enum ActionType
{
29 ACTION_ATTRIBUTE_WALK
,
30 ACTION_DEVICE_ID_FILE
,
33 typedef enum QueryType
{
41 static bool arg_root
= false;
42 static bool arg_export
= false;
43 static const char *arg_export_prefix
= NULL
;
44 static usec_t arg_wait_for_initialization_timeout
= 0;
46 static bool skip_attribute(const char *name
) {
47 static const char* const skip
[] = {
57 return string_table_lookup(skip
, ELEMENTSOF(skip
), name
) >= 0;
60 typedef struct SysAttr
{
65 static int sysattr_compare(const SysAttr
*a
, const SysAttr
*b
) {
66 return strcmp(a
->name
, b
->name
);
69 static int print_all_attributes(sd_device
*device
, bool is_parent
) {
70 _cleanup_free_ SysAttr
*sysattrs
= NULL
;
71 size_t n_items
= 0, n_allocated
= 0;
72 const char *name
, *value
;
75 (void) sd_device_get_devpath(device
, &value
);
76 printf(" looking at %sdevice '%s':\n", is_parent
? "parent " : "", strempty(value
));
79 (void) sd_device_get_sysname(device
, &value
);
80 printf(" %s==\"%s\"\n", is_parent
? "KERNELS" : "KERNEL", strempty(value
));
83 (void) sd_device_get_subsystem(device
, &value
);
84 printf(" %s==\"%s\"\n", is_parent
? "SUBSYSTEMS" : "SUBSYSTEM", strempty(value
));
87 (void) sd_device_get_driver(device
, &value
);
88 printf(" %s==\"%s\"\n", is_parent
? "DRIVERS" : "DRIVER", strempty(value
));
90 FOREACH_DEVICE_SYSATTR(device
, name
) {
93 if (skip_attribute(name
))
96 if (sd_device_get_sysattr_value(device
, name
, &value
) < 0)
99 /* skip any values that look like a path */
103 /* skip nonprintable attributes */
105 while (len
> 0 && isprint(value
[len
-1]))
110 if (!GREEDY_REALLOC(sysattrs
, n_allocated
, n_items
+ 1))
113 sysattrs
[n_items
] = (SysAttr
) {
120 typesafe_qsort(sysattrs
, n_items
, sysattr_compare
);
122 for (size_t i
= 0; i
< n_items
; i
++)
123 printf(" %s{%s}==\"%s\"\n", is_parent
? "ATTRS" : "ATTR", sysattrs
[i
].name
, sysattrs
[i
].value
);
130 static int print_device_chain(sd_device
*device
) {
131 sd_device
*child
, *parent
;
135 "Udevadm info starts with the device specified by the devpath and then\n"
136 "walks up the chain of parent devices. It prints for every device\n"
137 "found, all possible attributes in the udev rules key format.\n"
138 "A rule to match, can be composed by the attributes of the device\n"
139 "and the attributes from one single parent device.\n"
142 r
= print_all_attributes(device
, false);
146 for (child
= device
; sd_device_get_parent(child
, &parent
) >= 0; child
= parent
) {
147 r
= print_all_attributes(parent
, true);
155 static int print_record(sd_device
*device
) {
156 const char *str
, *val
;
159 (void) sd_device_get_devpath(device
, &str
);
160 printf("P: %s\n", str
);
162 if (sd_device_get_devname(device
, &str
) >= 0) {
163 assert_se(val
= path_startswith(str
, "/dev/"));
164 printf("N: %s\n", val
);
167 if (device_get_devlink_priority(device
, &i
) >= 0)
168 printf("L: %i\n", i
);
170 FOREACH_DEVICE_DEVLINK(device
, str
) {
171 assert_se(val
= path_startswith(str
, "/dev/"));
172 printf("S: %s\n", val
);
175 FOREACH_DEVICE_PROPERTY(device
, str
, val
)
176 printf("E: %s=%s\n", str
, val
);
182 static int stat_device(const char *name
, bool export
, const char *prefix
) {
185 if (stat(name
, &statbuf
) != 0)
191 printf("%sMAJOR=%u\n"
193 prefix
, major(statbuf
.st_dev
),
194 prefix
, minor(statbuf
.st_dev
));
196 printf("%u:%u\n", major(statbuf
.st_dev
), minor(statbuf
.st_dev
));
200 static int export_devices(void) {
201 _cleanup_(sd_device_enumerator_unrefp
) sd_device_enumerator
*e
= NULL
;
205 r
= sd_device_enumerator_new(&e
);
209 r
= sd_device_enumerator_allow_uninitialized(e
);
211 return log_error_errno(r
, "Failed to set allowing uninitialized flag: %m");
213 r
= device_enumerator_scan_devices(e
);
215 return log_error_errno(r
, "Failed to scan devices: %m");
217 FOREACH_DEVICE_AND_SUBSYSTEM(e
, d
)
218 (void) print_record(d
);
223 static void cleanup_dir(DIR *dir
, mode_t mask
, int depth
) {
229 FOREACH_DIRENT_ALL(dent
, dir
, break) {
232 if (dent
->d_name
[0] == '.')
234 if (fstatat(dirfd(dir
), dent
->d_name
, &stats
, AT_SYMLINK_NOFOLLOW
) != 0)
236 if ((stats
.st_mode
& mask
) != 0)
238 if (S_ISDIR(stats
.st_mode
)) {
239 _cleanup_closedir_
DIR *dir2
= NULL
;
241 dir2
= fdopendir(openat(dirfd(dir
), dent
->d_name
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
));
243 cleanup_dir(dir2
, mask
, depth
-1);
245 (void) unlinkat(dirfd(dir
), dent
->d_name
, AT_REMOVEDIR
);
247 (void) unlinkat(dirfd(dir
), dent
->d_name
, 0);
251 static void cleanup_db(void) {
252 _cleanup_closedir_
DIR *dir1
= NULL
, *dir2
= NULL
, *dir3
= NULL
, *dir4
= NULL
, *dir5
= NULL
;
254 (void) unlink("/run/udev/queue.bin");
256 dir1
= opendir("/run/udev/data");
258 cleanup_dir(dir1
, S_ISVTX
, 1);
260 dir2
= opendir("/run/udev/links");
262 cleanup_dir(dir2
, 0, 2);
264 dir3
= opendir("/run/udev/tags");
266 cleanup_dir(dir3
, 0, 2);
268 dir4
= opendir("/run/udev/static_node-tags");
270 cleanup_dir(dir4
, 0, 2);
272 dir5
= opendir("/run/udev/watch");
274 cleanup_dir(dir5
, 0, 1);
277 static int query_device(QueryType query
, sd_device
* device
) {
286 r
= sd_device_get_devname(device
, &node
);
288 return log_error_errno(r
, "No device node found: %m");
291 assert_se(node
= path_startswith(node
, "/dev/"));
292 printf("%s\n", node
);
296 case QUERY_SYMLINK
: {
297 const char *devlink
, *prefix
= "";
299 FOREACH_DEVICE_DEVLINK(device
, devlink
) {
301 assert_se(devlink
= path_startswith(devlink
, "/dev/"));
302 printf("%s%s", prefix
, devlink
);
312 r
= sd_device_get_devpath(device
, &devpath
);
314 return log_error_errno(r
, "Failed to get device path: %m");
316 printf("%s\n", devpath
);
320 case QUERY_PROPERTY
: {
321 const char *key
, *value
;
323 FOREACH_DEVICE_PROPERTY(device
, key
, value
)
325 printf("%s%s='%s'\n", strempty(arg_export_prefix
), key
, value
);
327 printf("%s=%s\n", key
, value
);
332 return print_record(device
);
335 assert_not_reached("unknown query type");
339 static int help(void) {
340 printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
341 "Query sysfs or the udev database.\n\n"
342 " -h --help Print this message\n"
343 " -V --version Print version of the program\n"
344 " -q --query=TYPE Query device information:\n"
345 " name Name of device node\n"
346 " symlink Pointing to node\n"
347 " path sysfs device path\n"
348 " property The device properties\n"
350 " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
351 " -n --name=NAME Node or symlink name used for query or attribute walk\n"
352 " -r --root Prepend dev directory to path names\n"
353 " -a --attribute-walk Print all key matches walking along the chain\n"
354 " of parent devices\n"
355 " -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
356 " -x --export Export key/value pairs\n"
357 " -P --export-prefix Export the key name with a prefix\n"
358 " -e --export-db Export the content of the udev database\n"
359 " -c --cleanup-db Clean up the udev database\n"
360 " -w --wait-for-initialization[=SECONDS]\n"
361 " Wait for device to be initialized\n"
362 , program_invocation_short_name
);
367 int info_main(int argc
, char *argv
[], void *userdata
) {
368 _cleanup_strv_free_
char **devices
= NULL
;
369 _cleanup_free_
char *name
= NULL
;
372 static const struct option options
[] = {
373 { "name", required_argument
, NULL
, 'n' },
374 { "path", required_argument
, NULL
, 'p' },
375 { "query", required_argument
, NULL
, 'q' },
376 { "attribute-walk", no_argument
, NULL
, 'a' },
377 { "cleanup-db", no_argument
, NULL
, 'c' },
378 { "export-db", no_argument
, NULL
, 'e' },
379 { "root", no_argument
, NULL
, 'r' },
380 { "device-id-of-file", required_argument
, NULL
, 'd' },
381 { "export", no_argument
, NULL
, 'x' },
382 { "export-prefix", required_argument
, NULL
, 'P' },
383 { "wait-for-initialization", optional_argument
, NULL
, 'w' },
384 { "version", no_argument
, NULL
, 'V' },
385 { "help", no_argument
, NULL
, 'h' },
389 ActionType action
= ACTION_QUERY
;
390 QueryType query
= QUERY_ALL
;
392 while ((c
= getopt_long(argc
, argv
, "aced:n:p:q:rxP:w::Vh", options
, NULL
)) >= 0)
396 const char *prefix
= c
== 'n' ? "/dev/" : "/sys/";
399 path
= path_join(path_startswith(optarg
, prefix
) ? NULL
: prefix
, optarg
);
403 r
= strv_consume(&devices
, path
);
410 action
= ACTION_QUERY
;
411 if (streq(optarg
, "property") || streq(optarg
, "env"))
412 query
= QUERY_PROPERTY
;
413 else if (streq(optarg
, "name"))
415 else if (streq(optarg
, "symlink"))
416 query
= QUERY_SYMLINK
;
417 else if (streq(optarg
, "path"))
419 else if (streq(optarg
, "all"))
422 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "unknown query type");
428 action
= ACTION_DEVICE_ID_FILE
;
429 r
= free_and_strdup(&name
, optarg
);
434 action
= ACTION_ATTRIBUTE_WALK
;
437 return export_devices();
446 arg_export_prefix
= optarg
;
450 r
= parse_sec(optarg
, &arg_wait_for_initialization_timeout
);
452 return log_error_errno(r
, "Failed to parse timeout value: %m");
454 arg_wait_for_initialization_timeout
= USEC_INFINITY
;
457 return print_version();
463 assert_not_reached("Unknown option");
466 if (action
== ACTION_DEVICE_ID_FILE
) {
468 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
469 "Positional arguments are not allowed with -d/--device-id-of-file.");
471 return stat_device(name
, arg_export
, arg_export_prefix
);
474 r
= strv_extend_strv(&devices
, argv
+ optind
, false);
476 return log_error_errno(r
, "Failed to build argument list: %m");
478 if (strv_isempty(devices
))
479 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
480 "A device name or path is required");
481 if (action
== ACTION_ATTRIBUTE_WALK
&& strv_length(devices
) > 1)
482 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
483 "Only one device may be specified with -a/--attribute-walk");
486 STRV_FOREACH(p
, devices
) {
487 _cleanup_(sd_device_unrefp
) sd_device
*device
= NULL
;
489 r
= find_device(*p
, NULL
, &device
);
491 return log_error_errno(r
, "Bad argument \"%s\", expected an absolute path in /dev/ or /sys or a unit name: %m", *p
);
493 return log_error_errno(r
, "Unknown device \"%s\": %m", *p
);
495 if (arg_wait_for_initialization_timeout
> 0) {
498 r
= device_wait_for_initialization(device
, NULL
, arg_wait_for_initialization_timeout
, &d
);
502 sd_device_unref(device
);
506 if (action
== ACTION_QUERY
)
507 r
= query_device(query
, device
);
508 else if (action
== ACTION_ATTRIBUTE_WALK
)
509 r
= print_device_chain(device
);
511 assert_not_reached("Unknown action");