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 "string-table.h"
21 #include "string-util.h"
22 #include "udev-util.h"
23 #include "udevadm-util.h"
26 typedef enum ActionType
{
28 ACTION_ATTRIBUTE_WALK
,
29 ACTION_DEVICE_ID_FILE
,
32 typedef enum QueryType
{
40 static bool arg_root
= false;
41 static bool arg_export
= false;
42 static const char *arg_export_prefix
= NULL
;
43 static usec_t arg_wait_for_initialization_timeout
= 0;
45 static bool skip_attribute(const char *name
) {
46 static const char* const skip
[] = {
56 return string_table_lookup(skip
, ELEMENTSOF(skip
), name
) >= 0;
59 static void print_all_attributes(sd_device
*device
, const char *key
) {
60 const char *name
, *value
;
62 FOREACH_DEVICE_SYSATTR(device
, name
) {
65 if (skip_attribute(name
))
68 if (sd_device_get_sysattr_value(device
, name
, &value
) < 0)
71 /* skip any values that look like a path */
75 /* skip nonprintable attributes */
77 while (len
> 0 && isprint(value
[len
-1]))
82 printf(" %s{%s}==\"%s\"\n", key
, name
, value
);
87 static int print_device_chain(sd_device
*device
) {
88 sd_device
*child
, *parent
;
92 "Udevadm info starts with the device specified by the devpath and then\n"
93 "walks up the chain of parent devices. It prints for every device\n"
94 "found, all possible attributes in the udev rules key format.\n"
95 "A rule to match, can be composed by the attributes of the device\n"
96 "and the attributes from one single parent device.\n"
99 (void) sd_device_get_devpath(device
, &str
);
100 printf(" looking at device '%s':\n", str
);
101 (void) sd_device_get_sysname(device
, &str
);
102 printf(" KERNEL==\"%s\"\n", str
);
103 if (sd_device_get_subsystem(device
, &str
) < 0)
105 printf(" SUBSYSTEM==\"%s\"\n", str
);
106 if (sd_device_get_driver(device
, &str
) < 0)
108 printf(" DRIVER==\"%s\"\n", str
);
109 print_all_attributes(device
, "ATTR");
111 for (child
= device
; sd_device_get_parent(child
, &parent
) >= 0; child
= parent
) {
112 (void) sd_device_get_devpath(parent
, &str
);
113 printf(" looking at parent device '%s':\n", str
);
114 (void) sd_device_get_sysname(parent
, &str
);
115 printf(" KERNELS==\"%s\"\n", str
);
116 if (sd_device_get_subsystem(parent
, &str
) < 0)
118 printf(" SUBSYSTEMS==\"%s\"\n", str
);
119 if (sd_device_get_driver(parent
, &str
) < 0)
121 printf(" DRIVERS==\"%s\"\n", str
);
122 print_all_attributes(parent
, "ATTRS");
128 static int print_record(sd_device
*device
) {
129 const char *str
, *val
;
132 (void) sd_device_get_devpath(device
, &str
);
133 printf("P: %s\n", str
);
135 if (sd_device_get_devname(device
, &str
) >= 0) {
136 assert_se(val
= path_startswith(str
, "/dev/"));
137 printf("N: %s\n", val
);
140 if (device_get_devlink_priority(device
, &i
) >= 0)
141 printf("L: %i\n", i
);
143 FOREACH_DEVICE_DEVLINK(device
, str
) {
144 assert_se(val
= path_startswith(str
, "/dev/"));
145 printf("S: %s\n", val
);
148 FOREACH_DEVICE_PROPERTY(device
, str
, val
)
149 printf("E: %s=%s\n", str
, val
);
155 static int stat_device(const char *name
, bool export
, const char *prefix
) {
158 if (stat(name
, &statbuf
) != 0)
164 printf("%sMAJOR=%u\n"
166 prefix
, major(statbuf
.st_dev
),
167 prefix
, minor(statbuf
.st_dev
));
169 printf("%u:%u\n", major(statbuf
.st_dev
), minor(statbuf
.st_dev
));
173 static int export_devices(void) {
174 _cleanup_(sd_device_enumerator_unrefp
) sd_device_enumerator
*e
= NULL
;
178 r
= sd_device_enumerator_new(&e
);
182 r
= sd_device_enumerator_allow_uninitialized(e
);
184 return log_error_errno(r
, "Failed to set allowing uninitialized flag: %m");
186 r
= device_enumerator_scan_devices(e
);
188 return log_error_errno(r
, "Failed to scan devices: %m");
190 FOREACH_DEVICE_AND_SUBSYSTEM(e
, d
)
191 (void) print_record(d
);
196 static void cleanup_dir(DIR *dir
, mode_t mask
, int depth
) {
202 FOREACH_DIRENT_ALL(dent
, dir
, break) {
205 if (dent
->d_name
[0] == '.')
207 if (fstatat(dirfd(dir
), dent
->d_name
, &stats
, AT_SYMLINK_NOFOLLOW
) != 0)
209 if ((stats
.st_mode
& mask
) != 0)
211 if (S_ISDIR(stats
.st_mode
)) {
212 _cleanup_closedir_
DIR *dir2
= NULL
;
214 dir2
= fdopendir(openat(dirfd(dir
), dent
->d_name
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
));
216 cleanup_dir(dir2
, mask
, depth
-1);
218 (void) unlinkat(dirfd(dir
), dent
->d_name
, AT_REMOVEDIR
);
220 (void) unlinkat(dirfd(dir
), dent
->d_name
, 0);
224 static void cleanup_db(void) {
225 _cleanup_closedir_
DIR *dir1
= NULL
, *dir2
= NULL
, *dir3
= NULL
, *dir4
= NULL
, *dir5
= NULL
;
227 (void) unlink("/run/udev/queue.bin");
229 dir1
= opendir("/run/udev/data");
231 cleanup_dir(dir1
, S_ISVTX
, 1);
233 dir2
= opendir("/run/udev/links");
235 cleanup_dir(dir2
, 0, 2);
237 dir3
= opendir("/run/udev/tags");
239 cleanup_dir(dir3
, 0, 2);
241 dir4
= opendir("/run/udev/static_node-tags");
243 cleanup_dir(dir4
, 0, 2);
245 dir5
= opendir("/run/udev/watch");
247 cleanup_dir(dir5
, 0, 1);
250 static int query_device(QueryType query
, sd_device
* device
) {
259 r
= sd_device_get_devname(device
, &node
);
261 return log_error_errno(r
, "No device node found: %m");
264 assert_se(node
= path_startswith(node
, "/dev/"));
265 printf("%s\n", node
);
269 case QUERY_SYMLINK
: {
270 const char *devlink
, *prefix
= "";
272 FOREACH_DEVICE_DEVLINK(device
, devlink
) {
274 assert_se(devlink
= path_startswith(devlink
, "/dev/"));
275 printf("%s%s", prefix
, devlink
);
285 r
= sd_device_get_devpath(device
, &devpath
);
287 return log_error_errno(r
, "Failed to get device path: %m");
289 printf("%s\n", devpath
);
293 case QUERY_PROPERTY
: {
294 const char *key
, *value
;
296 FOREACH_DEVICE_PROPERTY(device
, key
, value
)
298 printf("%s%s='%s'\n", strempty(arg_export_prefix
), key
, value
);
300 printf("%s=%s\n", key
, value
);
305 return print_record(device
);
308 assert_not_reached("unknown query type");
312 static int help(void) {
313 printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
314 "Query sysfs or the udev database.\n\n"
315 " -h --help Print this message\n"
316 " -V --version Print version of the program\n"
317 " -q --query=TYPE Query device information:\n"
318 " name Name of device node\n"
319 " symlink Pointing to node\n"
320 " path sysfs device path\n"
321 " property The device properties\n"
323 " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
324 " -n --name=NAME Node or symlink name used for query or attribute walk\n"
325 " -r --root Prepend dev directory to path names\n"
326 " -a --attribute-walk Print all key matches walking along the chain\n"
327 " of parent devices\n"
328 " -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
329 " -x --export Export key/value pairs\n"
330 " -P --export-prefix Export the key name with a prefix\n"
331 " -e --export-db Export the content of the udev database\n"
332 " -c --cleanup-db Clean up the udev database\n"
333 " -w --wait-for-initialization[=SECONDS]\n"
334 " Wait for device to be initialized\n"
335 , program_invocation_short_name
);
340 int info_main(int argc
, char *argv
[], void *userdata
) {
341 _cleanup_strv_free_
char **devices
= NULL
;
342 _cleanup_free_
char *name
= NULL
;
345 static const struct option options
[] = {
346 { "name", required_argument
, NULL
, 'n' },
347 { "path", required_argument
, NULL
, 'p' },
348 { "query", required_argument
, NULL
, 'q' },
349 { "attribute-walk", no_argument
, NULL
, 'a' },
350 { "cleanup-db", no_argument
, NULL
, 'c' },
351 { "export-db", no_argument
, NULL
, 'e' },
352 { "root", no_argument
, NULL
, 'r' },
353 { "device-id-of-file", required_argument
, NULL
, 'd' },
354 { "export", no_argument
, NULL
, 'x' },
355 { "export-prefix", required_argument
, NULL
, 'P' },
356 { "wait-for-initialization", optional_argument
, NULL
, 'w' },
357 { "version", no_argument
, NULL
, 'V' },
358 { "help", no_argument
, NULL
, 'h' },
362 ActionType action
= ACTION_QUERY
;
363 QueryType query
= QUERY_ALL
;
365 while ((c
= getopt_long(argc
, argv
, "aced:n:p:q:rxP:w::Vh", options
, NULL
)) >= 0)
369 const char *prefix
= c
== 'n' ? "/dev/" : "/sys/";
372 path
= path_join(path_startswith(optarg
, prefix
) ? NULL
: prefix
, optarg
);
376 r
= strv_consume(&devices
, path
);
383 action
= ACTION_QUERY
;
384 if (streq(optarg
, "property") || streq(optarg
, "env"))
385 query
= QUERY_PROPERTY
;
386 else if (streq(optarg
, "name"))
388 else if (streq(optarg
, "symlink"))
389 query
= QUERY_SYMLINK
;
390 else if (streq(optarg
, "path"))
392 else if (streq(optarg
, "all"))
395 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "unknown query type");
401 action
= ACTION_DEVICE_ID_FILE
;
402 r
= free_and_strdup(&name
, optarg
);
407 action
= ACTION_ATTRIBUTE_WALK
;
410 return export_devices();
419 arg_export_prefix
= optarg
;
423 r
= parse_sec(optarg
, &arg_wait_for_initialization_timeout
);
425 return log_error_errno(r
, "Failed to parse timeout value: %m");
427 arg_wait_for_initialization_timeout
= USEC_INFINITY
;
430 return print_version();
436 assert_not_reached("Unknown option");
439 if (action
== ACTION_DEVICE_ID_FILE
) {
441 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
442 "Positional arguments are not allowed with -d/--device-id-of-file.");
444 return stat_device(name
, arg_export
, arg_export_prefix
);
447 r
= strv_extend_strv(&devices
, argv
+ optind
, false);
449 return log_error_errno(r
, "Failed to build argument list: %m");
451 if (strv_isempty(devices
))
452 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
453 "A device name or path is required");
454 if (action
== ACTION_ATTRIBUTE_WALK
&& strv_length(devices
) > 1)
455 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
456 "Only one device may be specified with -a/--attribute-walk");
459 STRV_FOREACH(p
, devices
) {
460 _cleanup_(sd_device_unrefp
) sd_device
*device
= NULL
;
462 r
= find_device(*p
, NULL
, &device
);
464 return log_error_errno(r
, "Bad argument \"%s\", expected an absolute path in /dev/ or /sys or a unit name: %m", *p
);
466 return log_error_errno(r
, "Unknown device \"%s\": %m", *p
);
468 if (arg_wait_for_initialization_timeout
> 0) {
471 r
= device_wait_for_initialization(device
, NULL
, arg_wait_for_initialization_timeout
, &d
);
475 sd_device_unref(device
);
479 if (action
== ACTION_QUERY
)
480 r
= query_device(query
, device
);
481 else if (action
== ACTION_ATTRIBUTE_WALK
)
482 r
= print_device_chain(device
);
484 assert_not_reached("Unknown action");