1 /* SPDX-License-Identifier: GPL-2.0+ */
13 #include "sd-device.h"
15 #include "alloc-util.h"
16 #include "device-enumerator-private.h"
17 #include "device-private.h"
18 #include "device-util.h"
19 #include "dirent-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 static void print_all_attributes(sd_device
*device
, const char *key
) {
61 const char *name
, *value
;
63 FOREACH_DEVICE_SYSATTR(device
, name
) {
66 if (skip_attribute(name
))
69 if (sd_device_get_sysattr_value(device
, name
, &value
) < 0)
72 /* skip any values that look like a path */
76 /* skip nonprintable attributes */
78 while (len
> 0 && isprint(value
[len
-1]))
83 printf(" %s{%s}==\"%s\"\n", key
, name
, value
);
88 static int print_device_chain(sd_device
*device
) {
89 sd_device
*child
, *parent
;
93 "Udevadm info starts with the device specified by the devpath and then\n"
94 "walks up the chain of parent devices. It prints for every device\n"
95 "found, all possible attributes in the udev rules key format.\n"
96 "A rule to match, can be composed by the attributes of the device\n"
97 "and the attributes from one single parent device.\n"
100 (void) sd_device_get_devpath(device
, &str
);
101 printf(" looking at device '%s':\n", str
);
102 (void) sd_device_get_sysname(device
, &str
);
103 printf(" KERNEL==\"%s\"\n", str
);
104 if (sd_device_get_subsystem(device
, &str
) < 0)
106 printf(" SUBSYSTEM==\"%s\"\n", str
);
107 if (sd_device_get_driver(device
, &str
) < 0)
109 printf(" DRIVER==\"%s\"\n", str
);
110 print_all_attributes(device
, "ATTR");
112 for (child
= device
; sd_device_get_parent(child
, &parent
) >= 0; child
= parent
) {
113 (void) sd_device_get_devpath(parent
, &str
);
114 printf(" looking at parent device '%s':\n", str
);
115 (void) sd_device_get_sysname(parent
, &str
);
116 printf(" KERNELS==\"%s\"\n", str
);
117 if (sd_device_get_subsystem(parent
, &str
) < 0)
119 printf(" SUBSYSTEMS==\"%s\"\n", str
);
120 if (sd_device_get_driver(parent
, &str
) < 0)
122 printf(" DRIVERS==\"%s\"\n", str
);
123 print_all_attributes(parent
, "ATTRS");
129 static int print_record(sd_device
*device
) {
130 const char *str
, *val
;
133 (void) sd_device_get_devpath(device
, &str
);
134 printf("P: %s\n", str
);
136 if (sd_device_get_devname(device
, &str
) >= 0) {
137 assert_se(val
= path_startswith(str
, "/dev/"));
138 printf("N: %s\n", val
);
141 if (device_get_devlink_priority(device
, &i
) >= 0)
142 printf("L: %i\n", i
);
144 FOREACH_DEVICE_DEVLINK(device
, str
) {
145 assert_se(val
= path_startswith(str
, "/dev/"));
146 printf("S: %s\n", val
);
149 FOREACH_DEVICE_PROPERTY(device
, str
, val
)
150 printf("E: %s=%s\n", str
, val
);
156 static int stat_device(const char *name
, bool export
, const char *prefix
) {
159 if (stat(name
, &statbuf
) != 0)
165 printf("%sMAJOR=%u\n"
167 prefix
, major(statbuf
.st_dev
),
168 prefix
, minor(statbuf
.st_dev
));
170 printf("%u:%u\n", major(statbuf
.st_dev
), minor(statbuf
.st_dev
));
174 static int export_devices(void) {
175 _cleanup_(sd_device_enumerator_unrefp
) sd_device_enumerator
*e
= NULL
;
179 r
= sd_device_enumerator_new(&e
);
183 r
= sd_device_enumerator_allow_uninitialized(e
);
187 r
= device_enumerator_scan_devices(e
);
191 FOREACH_DEVICE_AND_SUBSYSTEM(e
, d
)
197 static void cleanup_dir(DIR *dir
, mode_t mask
, int depth
) {
203 FOREACH_DIRENT_ALL(dent
, dir
, break) {
206 if (dent
->d_name
[0] == '.')
208 if (fstatat(dirfd(dir
), dent
->d_name
, &stats
, AT_SYMLINK_NOFOLLOW
) != 0)
210 if ((stats
.st_mode
& mask
) != 0)
212 if (S_ISDIR(stats
.st_mode
)) {
213 _cleanup_closedir_
DIR *dir2
= NULL
;
215 dir2
= fdopendir(openat(dirfd(dir
), dent
->d_name
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
));
217 cleanup_dir(dir2
, mask
, depth
-1);
219 (void) unlinkat(dirfd(dir
), dent
->d_name
, AT_REMOVEDIR
);
221 (void) unlinkat(dirfd(dir
), dent
->d_name
, 0);
225 static void cleanup_db(void) {
226 _cleanup_closedir_
DIR *dir1
= NULL
, *dir2
= NULL
, *dir3
= NULL
, *dir4
= NULL
, *dir5
= NULL
;
228 (void) unlink("/run/udev/queue.bin");
230 dir1
= opendir("/run/udev/data");
232 cleanup_dir(dir1
, S_ISVTX
, 1);
234 dir2
= opendir("/run/udev/links");
236 cleanup_dir(dir2
, 0, 2);
238 dir3
= opendir("/run/udev/tags");
240 cleanup_dir(dir3
, 0, 2);
242 dir4
= opendir("/run/udev/static_node-tags");
244 cleanup_dir(dir4
, 0, 2);
246 dir5
= opendir("/run/udev/watch");
248 cleanup_dir(dir5
, 0, 1);
251 static int query_device(QueryType query
, sd_device
* device
) {
260 r
= sd_device_get_devname(device
, &node
);
262 return log_error_errno(r
, "No device node found: %m");
265 assert_se(node
= path_startswith(node
, "/dev/"));
266 printf("%s\n", node
);
270 case QUERY_SYMLINK
: {
271 const char *devlink
, *prefix
= "";
273 FOREACH_DEVICE_DEVLINK(device
, devlink
) {
275 assert_se(devlink
= path_startswith(devlink
, "/dev/"));
276 printf("%s%s", prefix
, devlink
);
286 r
= sd_device_get_devpath(device
, &devpath
);
288 return log_error_errno(r
, "Failed to get device path: %m");
290 printf("%s\n", devpath
);
294 case QUERY_PROPERTY
: {
295 const char *key
, *value
;
297 FOREACH_DEVICE_PROPERTY(device
, key
, value
)
299 printf("%s%s='%s'\n", strempty(arg_export_prefix
), key
, value
);
301 printf("%s=%s\n", key
, value
);
306 return print_record(device
);
309 assert_not_reached("unknown query type");
313 static int help(void) {
314 printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
315 "Query sysfs or the udev database.\n\n"
316 " -h --help Print this message\n"
317 " -V --version Print version of the program\n"
318 " -q --query=TYPE Query device information:\n"
319 " name Name of device node\n"
320 " symlink Pointing to node\n"
321 " path sysfs device path\n"
322 " property The device properties\n"
324 " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
325 " -n --name=NAME Node or symlink name used for query or attribute walk\n"
326 " -r --root Prepend dev directory to path names\n"
327 " -a --attribute-walk Print all key matches walking along the chain\n"
328 " of parent devices\n"
329 " -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
330 " -x --export Export key/value pairs\n"
331 " -P --export-prefix Export the key name with a prefix\n"
332 " -e --export-db Export the content of the udev database\n"
333 " -c --cleanup-db Clean up the udev database\n"
334 " -w --wait-for-initialization[=SECONDS]\n"
335 " Wait for device to be initialized\n"
336 , program_invocation_short_name
);
341 int info_main(int argc
, char *argv
[], void *userdata
) {
342 _cleanup_strv_free_
char **devices
= NULL
;
343 _cleanup_free_
char *name
= NULL
;
346 static const struct option options
[] = {
347 { "name", required_argument
, NULL
, 'n' },
348 { "path", required_argument
, NULL
, 'p' },
349 { "query", required_argument
, NULL
, 'q' },
350 { "attribute-walk", no_argument
, NULL
, 'a' },
351 { "cleanup-db", no_argument
, NULL
, 'c' },
352 { "export-db", no_argument
, NULL
, 'e' },
353 { "root", no_argument
, NULL
, 'r' },
354 { "device-id-of-file", required_argument
, NULL
, 'd' },
355 { "export", no_argument
, NULL
, 'x' },
356 { "export-prefix", required_argument
, NULL
, 'P' },
357 { "wait-for-initialization", optional_argument
, NULL
, 'w' },
358 { "version", no_argument
, NULL
, 'V' },
359 { "help", no_argument
, NULL
, 'h' },
363 ActionType action
= ACTION_QUERY
;
364 QueryType query
= QUERY_ALL
;
366 while ((c
= getopt_long(argc
, argv
, "aced:n:p:q:rxP:w::Vh", options
, NULL
)) >= 0)
370 const char *prefix
= c
== 'n' ? "/dev/" : "/sys/";
373 path
= path_join(path_startswith(optarg
, prefix
) ? NULL
: prefix
, optarg
);
377 r
= strv_consume(&devices
, path
);
384 action
= ACTION_QUERY
;
385 if (streq(optarg
, "property") || streq(optarg
, "env"))
386 query
= QUERY_PROPERTY
;
387 else if (streq(optarg
, "name"))
389 else if (streq(optarg
, "symlink"))
390 query
= QUERY_SYMLINK
;
391 else if (streq(optarg
, "path"))
393 else if (streq(optarg
, "all"))
396 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "unknown query type");
402 action
= ACTION_DEVICE_ID_FILE
;
403 r
= free_and_strdup(&name
, optarg
);
408 action
= ACTION_ATTRIBUTE_WALK
;
411 return export_devices();
420 arg_export_prefix
= optarg
;
424 r
= parse_sec(optarg
, &arg_wait_for_initialization_timeout
);
426 return log_error_errno(r
, "Failed to parse timeout value: %m");
428 arg_wait_for_initialization_timeout
= USEC_INFINITY
;
431 return print_version();
437 assert_not_reached("Unknown option");
440 if (action
== ACTION_DEVICE_ID_FILE
) {
442 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
443 "Positional arguments are not allowed with -d/--device-id-of-file.");
445 return stat_device(name
, arg_export
, arg_export_prefix
);
448 r
= strv_extend_strv(&devices
, argv
+ optind
, false);
450 return log_error_errno(r
, "Failed to build argument list: %m");
452 if (strv_isempty(devices
))
453 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
454 "A device name or path is required");
455 if (action
== ACTION_ATTRIBUTE_WALK
&& strv_length(devices
) > 1)
456 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
457 "Only one device may be specified with -a/--attribute-walk");
460 STRV_FOREACH(p
, devices
) {
461 _cleanup_(sd_device_unrefp
) sd_device
*device
= NULL
;
463 r
= find_device(*p
, NULL
, &device
);
465 return log_error_errno(r
, "Bad argument \"%s\", expected an absolute path in /dev/ or /sys or a unit name: %m", *p
);
467 return log_error_errno(r
, "Unknown device \"%s\": %m", *p
);
469 if (arg_wait_for_initialization_timeout
> 0) {
472 r
= device_wait_for_initialization(device
, NULL
, arg_wait_for_initialization_timeout
, &d
);
476 sd_device_unref(device
);
480 if (action
== ACTION_QUERY
)
481 r
= query_device(query
, device
);
482 else if (action
== ACTION_ATTRIBUTE_WALK
)
483 r
= print_device_chain(device
);
485 assert_not_reached("Unknown action");