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 /* Those are either displayed separately or should not be shown at all. */
48 return STR_IN_SET(name
,
58 typedef struct SysAttr
{
63 static int sysattr_compare(const SysAttr
*a
, const SysAttr
*b
) {
64 return strcmp(a
->name
, b
->name
);
67 static int print_all_attributes(sd_device
*device
, bool is_parent
) {
68 _cleanup_free_ SysAttr
*sysattrs
= NULL
;
69 size_t n_items
= 0, n_allocated
= 0;
70 const char *name
, *value
;
73 (void) sd_device_get_devpath(device
, &value
);
74 printf(" looking at %sdevice '%s':\n", is_parent
? "parent " : "", strempty(value
));
77 (void) sd_device_get_sysname(device
, &value
);
78 printf(" %s==\"%s\"\n", is_parent
? "KERNELS" : "KERNEL", strempty(value
));
81 (void) sd_device_get_subsystem(device
, &value
);
82 printf(" %s==\"%s\"\n", is_parent
? "SUBSYSTEMS" : "SUBSYSTEM", strempty(value
));
85 (void) sd_device_get_driver(device
, &value
);
86 printf(" %s==\"%s\"\n", is_parent
? "DRIVERS" : "DRIVER", strempty(value
));
88 FOREACH_DEVICE_SYSATTR(device
, name
) {
91 if (skip_attribute(name
))
94 if (sd_device_get_sysattr_value(device
, name
, &value
) < 0)
97 /* skip any values that look like a path */
101 /* skip nonprintable attributes */
103 while (len
> 0 && isprint((unsigned char) value
[len
-1]))
108 if (!GREEDY_REALLOC(sysattrs
, n_allocated
, n_items
+ 1))
111 sysattrs
[n_items
] = (SysAttr
) {
118 typesafe_qsort(sysattrs
, n_items
, sysattr_compare
);
120 for (size_t i
= 0; i
< n_items
; i
++)
121 printf(" %s{%s}==\"%s\"\n", is_parent
? "ATTRS" : "ATTR", sysattrs
[i
].name
, sysattrs
[i
].value
);
128 static int print_device_chain(sd_device
*device
) {
129 sd_device
*child
, *parent
;
133 "Udevadm info starts with the device specified by the devpath and then\n"
134 "walks up the chain of parent devices. It prints for every device\n"
135 "found, all possible attributes in the udev rules key format.\n"
136 "A rule to match, can be composed by the attributes of the device\n"
137 "and the attributes from one single parent device.\n"
140 r
= print_all_attributes(device
, false);
144 for (child
= device
; sd_device_get_parent(child
, &parent
) >= 0; child
= parent
) {
145 r
= print_all_attributes(parent
, true);
153 static int print_record(sd_device
*device
) {
154 const char *str
, *val
;
157 (void) sd_device_get_devpath(device
, &str
);
158 printf("P: %s\n", str
);
160 if (sd_device_get_devname(device
, &str
) >= 0) {
161 assert_se(val
= path_startswith(str
, "/dev/"));
162 printf("N: %s\n", val
);
165 if (device_get_devlink_priority(device
, &i
) >= 0)
166 printf("L: %i\n", i
);
168 FOREACH_DEVICE_DEVLINK(device
, str
) {
169 assert_se(val
= path_startswith(str
, "/dev/"));
170 printf("S: %s\n", val
);
173 FOREACH_DEVICE_PROPERTY(device
, str
, val
)
174 printf("E: %s=%s\n", str
, val
);
180 static int stat_device(const char *name
, bool export
, const char *prefix
) {
183 if (stat(name
, &statbuf
) != 0)
189 printf("%sMAJOR=%u\n"
191 prefix
, major(statbuf
.st_dev
),
192 prefix
, minor(statbuf
.st_dev
));
194 printf("%u:%u\n", major(statbuf
.st_dev
), minor(statbuf
.st_dev
));
198 static int export_devices(void) {
199 _cleanup_(sd_device_enumerator_unrefp
) sd_device_enumerator
*e
= NULL
;
203 r
= sd_device_enumerator_new(&e
);
207 r
= sd_device_enumerator_allow_uninitialized(e
);
209 return log_error_errno(r
, "Failed to set allowing uninitialized flag: %m");
211 r
= device_enumerator_scan_devices(e
);
213 return log_error_errno(r
, "Failed to scan devices: %m");
215 FOREACH_DEVICE_AND_SUBSYSTEM(e
, d
)
216 (void) print_record(d
);
221 static void cleanup_dir(DIR *dir
, mode_t mask
, int depth
) {
227 FOREACH_DIRENT_ALL(dent
, dir
, break) {
230 if (dent
->d_name
[0] == '.')
232 if (fstatat(dirfd(dir
), dent
->d_name
, &stats
, AT_SYMLINK_NOFOLLOW
) != 0)
234 if ((stats
.st_mode
& mask
) != 0)
236 if (S_ISDIR(stats
.st_mode
)) {
237 _cleanup_closedir_
DIR *dir2
= NULL
;
239 dir2
= fdopendir(openat(dirfd(dir
), dent
->d_name
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
));
241 cleanup_dir(dir2
, mask
, depth
-1);
243 (void) unlinkat(dirfd(dir
), dent
->d_name
, AT_REMOVEDIR
);
245 (void) unlinkat(dirfd(dir
), dent
->d_name
, 0);
249 static void cleanup_db(void) {
250 _cleanup_closedir_
DIR *dir1
= NULL
, *dir2
= NULL
, *dir3
= NULL
, *dir4
= NULL
, *dir5
= NULL
;
252 (void) unlink("/run/udev/queue.bin");
254 dir1
= opendir("/run/udev/data");
256 cleanup_dir(dir1
, S_ISVTX
, 1);
258 dir2
= opendir("/run/udev/links");
260 cleanup_dir(dir2
, 0, 2);
262 dir3
= opendir("/run/udev/tags");
264 cleanup_dir(dir3
, 0, 2);
266 dir4
= opendir("/run/udev/static_node-tags");
268 cleanup_dir(dir4
, 0, 2);
270 dir5
= opendir("/run/udev/watch");
272 cleanup_dir(dir5
, 0, 1);
275 static int query_device(QueryType query
, sd_device
* device
) {
284 r
= sd_device_get_devname(device
, &node
);
286 return log_error_errno(r
, "No device node found: %m");
289 assert_se(node
= path_startswith(node
, "/dev/"));
290 printf("%s\n", node
);
294 case QUERY_SYMLINK
: {
295 const char *devlink
, *prefix
= "";
297 FOREACH_DEVICE_DEVLINK(device
, devlink
) {
299 assert_se(devlink
= path_startswith(devlink
, "/dev/"));
300 printf("%s%s", prefix
, devlink
);
310 r
= sd_device_get_devpath(device
, &devpath
);
312 return log_error_errno(r
, "Failed to get device path: %m");
314 printf("%s\n", devpath
);
318 case QUERY_PROPERTY
: {
319 const char *key
, *value
;
321 FOREACH_DEVICE_PROPERTY(device
, key
, value
)
323 printf("%s%s='%s'\n", strempty(arg_export_prefix
), key
, value
);
325 printf("%s=%s\n", key
, value
);
330 return print_record(device
);
333 assert_not_reached("unknown query type");
337 static int help(void) {
338 printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
339 "Query sysfs or the udev database.\n\n"
340 " -h --help Print this message\n"
341 " -V --version Print version of the program\n"
342 " -q --query=TYPE Query device information:\n"
343 " name Name of device node\n"
344 " symlink Pointing to node\n"
345 " path sysfs device path\n"
346 " property The device properties\n"
348 " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
349 " -n --name=NAME Node or symlink name used for query or attribute walk\n"
350 " -r --root Prepend dev directory to path names\n"
351 " -a --attribute-walk Print all key matches walking along the chain\n"
352 " of parent devices\n"
353 " -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
354 " -x --export Export key/value pairs\n"
355 " -P --export-prefix Export the key name with a prefix\n"
356 " -e --export-db Export the content of the udev database\n"
357 " -c --cleanup-db Clean up the udev database\n"
358 " -w --wait-for-initialization[=SECONDS]\n"
359 " Wait for device to be initialized\n"
360 , program_invocation_short_name
);
365 int info_main(int argc
, char *argv
[], void *userdata
) {
366 _cleanup_strv_free_
char **devices
= NULL
;
367 _cleanup_free_
char *name
= NULL
;
370 static const struct option options
[] = {
371 { "name", required_argument
, NULL
, 'n' },
372 { "path", required_argument
, NULL
, 'p' },
373 { "query", required_argument
, NULL
, 'q' },
374 { "attribute-walk", no_argument
, NULL
, 'a' },
375 { "cleanup-db", no_argument
, NULL
, 'c' },
376 { "export-db", no_argument
, NULL
, 'e' },
377 { "root", no_argument
, NULL
, 'r' },
378 { "device-id-of-file", required_argument
, NULL
, 'd' },
379 { "export", no_argument
, NULL
, 'x' },
380 { "export-prefix", required_argument
, NULL
, 'P' },
381 { "wait-for-initialization", optional_argument
, NULL
, 'w' },
382 { "version", no_argument
, NULL
, 'V' },
383 { "help", no_argument
, NULL
, 'h' },
387 ActionType action
= ACTION_QUERY
;
388 QueryType query
= QUERY_ALL
;
390 while ((c
= getopt_long(argc
, argv
, "aced:n:p:q:rxP:w::Vh", options
, NULL
)) >= 0)
394 const char *prefix
= c
== 'n' ? "/dev/" : "/sys/";
397 path
= path_join(path_startswith(optarg
, prefix
) ? NULL
: prefix
, optarg
);
401 r
= strv_consume(&devices
, path
);
408 action
= ACTION_QUERY
;
409 if (streq(optarg
, "property") || streq(optarg
, "env"))
410 query
= QUERY_PROPERTY
;
411 else if (streq(optarg
, "name"))
413 else if (streq(optarg
, "symlink"))
414 query
= QUERY_SYMLINK
;
415 else if (streq(optarg
, "path"))
417 else if (streq(optarg
, "all"))
420 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "unknown query type");
426 action
= ACTION_DEVICE_ID_FILE
;
427 r
= free_and_strdup(&name
, optarg
);
432 action
= ACTION_ATTRIBUTE_WALK
;
435 return export_devices();
444 arg_export_prefix
= optarg
;
448 r
= parse_sec(optarg
, &arg_wait_for_initialization_timeout
);
450 return log_error_errno(r
, "Failed to parse timeout value: %m");
452 arg_wait_for_initialization_timeout
= USEC_INFINITY
;
455 return print_version();
461 assert_not_reached("Unknown option");
464 if (action
== ACTION_DEVICE_ID_FILE
) {
466 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
467 "Positional arguments are not allowed with -d/--device-id-of-file.");
469 return stat_device(name
, arg_export
, arg_export_prefix
);
472 r
= strv_extend_strv(&devices
, argv
+ optind
, false);
474 return log_error_errno(r
, "Failed to build argument list: %m");
476 if (strv_isempty(devices
))
477 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
478 "A device name or path is required");
479 if (action
== ACTION_ATTRIBUTE_WALK
&& strv_length(devices
) > 1)
480 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
481 "Only one device may be specified with -a/--attribute-walk");
484 STRV_FOREACH(p
, devices
) {
485 _cleanup_(sd_device_unrefp
) sd_device
*device
= NULL
;
487 r
= find_device(*p
, NULL
, &device
);
489 return log_error_errno(r
, "Bad argument \"%s\", expected an absolute path in /dev/ or /sys or a unit name: %m", *p
);
491 return log_error_errno(r
, "Unknown device \"%s\": %m", *p
);
493 if (arg_wait_for_initialization_timeout
> 0) {
496 r
= device_wait_for_initialization(device
, NULL
, arg_wait_for_initialization_timeout
, &d
);
500 sd_device_unref(device
);
504 if (action
== ACTION_QUERY
)
505 r
= query_device(query
, device
);
506 else if (action
== ACTION_ATTRIBUTE_WALK
)
507 r
= print_device_chain(device
);
509 assert_not_reached("Unknown action");