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 "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
;
44 static bool skip_attribute(const char *name
) {
45 static const char* const skip
[] = {
55 return string_table_lookup(skip
, ELEMENTSOF(skip
), name
) >= 0;
58 static void print_all_attributes(sd_device
*device
, const char *key
) {
59 const char *name
, *value
;
61 FOREACH_DEVICE_SYSATTR(device
, name
) {
64 if (skip_attribute(name
))
67 if (sd_device_get_sysattr_value(device
, name
, &value
) < 0)
70 /* skip any values that look like a path */
74 /* skip nonprintable attributes */
76 while (len
> 0 && isprint(value
[len
-1]))
81 printf(" %s{%s}==\"%s\"\n", key
, name
, value
);
86 static int print_device_chain(sd_device
*device
) {
87 sd_device
*child
, *parent
;
91 "Udevadm info starts with the device specified by the devpath and then\n"
92 "walks up the chain of parent devices. It prints for every device\n"
93 "found, all possible attributes in the udev rules key format.\n"
94 "A rule to match, can be composed by the attributes of the device\n"
95 "and the attributes from one single parent device.\n"
98 (void) sd_device_get_devpath(device
, &str
);
99 printf(" looking at device '%s':\n", str
);
100 (void) sd_device_get_sysname(device
, &str
);
101 printf(" KERNEL==\"%s\"\n", str
);
102 if (sd_device_get_subsystem(device
, &str
) < 0)
104 printf(" SUBSYSTEM==\"%s\"\n", str
);
105 if (sd_device_get_driver(device
, &str
) < 0)
107 printf(" DRIVER==\"%s\"\n", str
);
108 print_all_attributes(device
, "ATTR");
110 for (child
= device
; sd_device_get_parent(child
, &parent
) >= 0; child
= parent
) {
111 (void) sd_device_get_devpath(parent
, &str
);
112 printf(" looking at parent device '%s':\n", str
);
113 (void) sd_device_get_sysname(parent
, &str
);
114 printf(" KERNELS==\"%s\"\n", str
);
115 if (sd_device_get_subsystem(parent
, &str
) < 0)
117 printf(" SUBSYSTEMS==\"%s\"\n", str
);
118 if (sd_device_get_driver(parent
, &str
) < 0)
120 printf(" DRIVERS==\"%s\"\n", str
);
121 print_all_attributes(parent
, "ATTRS");
127 static int print_record(sd_device
*device
) {
128 const char *str
, *val
;
131 (void) sd_device_get_devpath(device
, &str
);
132 printf("P: %s\n", str
);
134 if (sd_device_get_devname(device
, &str
) >= 0) {
135 assert_se(val
= path_startswith(str
, "/dev/"));
136 printf("N: %s\n", val
);
139 if (device_get_devlink_priority(device
, &i
) >= 0)
140 printf("L: %i\n", i
);
142 FOREACH_DEVICE_DEVLINK(device
, str
) {
143 assert_se(val
= path_startswith(str
, "/dev/"));
144 printf("S: %s\n", val
);
147 FOREACH_DEVICE_PROPERTY(device
, str
, val
)
148 printf("E: %s=%s\n", str
, val
);
154 static int stat_device(const char *name
, bool export
, const char *prefix
) {
157 if (stat(name
, &statbuf
) != 0)
163 printf("%sMAJOR=%u\n"
165 prefix
, major(statbuf
.st_dev
),
166 prefix
, minor(statbuf
.st_dev
));
168 printf("%u:%u\n", major(statbuf
.st_dev
), minor(statbuf
.st_dev
));
172 static int export_devices(void) {
173 _cleanup_(sd_device_enumerator_unrefp
) sd_device_enumerator
*e
= NULL
;
177 r
= sd_device_enumerator_new(&e
);
181 r
= sd_device_enumerator_allow_uninitialized(e
);
185 r
= device_enumerator_scan_devices(e
);
189 FOREACH_DEVICE_AND_SUBSYSTEM(e
, d
)
195 static void cleanup_dir(DIR *dir
, mode_t mask
, int depth
) {
201 FOREACH_DIRENT_ALL(dent
, dir
, break) {
204 if (dent
->d_name
[0] == '.')
206 if (fstatat(dirfd(dir
), dent
->d_name
, &stats
, AT_SYMLINK_NOFOLLOW
) != 0)
208 if ((stats
.st_mode
& mask
) != 0)
210 if (S_ISDIR(stats
.st_mode
)) {
211 _cleanup_closedir_
DIR *dir2
= NULL
;
213 dir2
= fdopendir(openat(dirfd(dir
), dent
->d_name
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
));
215 cleanup_dir(dir2
, mask
, depth
-1);
217 (void) unlinkat(dirfd(dir
), dent
->d_name
, AT_REMOVEDIR
);
219 (void) unlinkat(dirfd(dir
), dent
->d_name
, 0);
223 static void cleanup_db(void) {
224 _cleanup_closedir_
DIR *dir1
= NULL
, *dir2
= NULL
, *dir3
= NULL
, *dir4
= NULL
, *dir5
= NULL
;
226 (void) unlink("/run/udev/queue.bin");
228 dir1
= opendir("/run/udev/data");
230 cleanup_dir(dir1
, S_ISVTX
, 1);
232 dir2
= opendir("/run/udev/links");
234 cleanup_dir(dir2
, 0, 2);
236 dir3
= opendir("/run/udev/tags");
238 cleanup_dir(dir3
, 0, 2);
240 dir4
= opendir("/run/udev/static_node-tags");
242 cleanup_dir(dir4
, 0, 2);
244 dir5
= opendir("/run/udev/watch");
246 cleanup_dir(dir5
, 0, 1);
249 static int query_device(QueryType query
, sd_device
* device
) {
258 r
= sd_device_get_devname(device
, &node
);
260 return log_error_errno(r
, "No device node found: %m");
263 assert_se(node
= path_startswith(node
, "/dev/"));
264 printf("%s\n", node
);
268 case QUERY_SYMLINK
: {
269 const char *devlink
, *prefix
= "";
271 FOREACH_DEVICE_DEVLINK(device
, devlink
) {
273 assert_se(devlink
= path_startswith(devlink
, "/dev/"));
274 printf("%s%s", prefix
, devlink
);
284 r
= sd_device_get_devpath(device
, &devpath
);
286 return log_error_errno(r
, "Failed to get device path: %m");
288 printf("%s\n", devpath
);
292 case QUERY_PROPERTY
: {
293 const char *key
, *value
;
295 FOREACH_DEVICE_PROPERTY(device
, key
, value
)
297 printf("%s%s='%s'\n", strempty(arg_export_prefix
), key
, value
);
299 printf("%s=%s\n", key
, value
);
304 return print_record(device
);
307 assert_not_reached("unknown query type");
311 static int help(void) {
312 printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
313 "Query sysfs or the udev database.\n\n"
314 " -h --help Print this message\n"
315 " -V --version Print version of the program\n"
316 " -q --query=TYPE Query device information:\n"
317 " name Name of device node\n"
318 " symlink Pointing to node\n"
319 " path sysfs device path\n"
320 " property The device properties\n"
322 " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
323 " -n --name=NAME Node or symlink name used for query or attribute walk\n"
324 " -r --root Prepend dev directory to path names\n"
325 " -a --attribute-walk Print all key matches walking along the chain\n"
326 " of parent devices\n"
327 " -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
328 " -x --export Export key/value pairs\n"
329 " -P --export-prefix Export the key name with a prefix\n"
330 " -e --export-db Export the content of the udev database\n"
331 " -c --cleanup-db Clean up the udev database\n"
332 , program_invocation_short_name
);
337 int info_main(int argc
, char *argv
[], void *userdata
) {
338 _cleanup_strv_free_
char **devices
= NULL
;
339 _cleanup_free_
char *name
= NULL
;
342 static const struct option options
[] = {
343 { "name", required_argument
, NULL
, 'n' },
344 { "path", required_argument
, NULL
, 'p' },
345 { "query", required_argument
, NULL
, 'q' },
346 { "attribute-walk", no_argument
, NULL
, 'a' },
347 { "cleanup-db", no_argument
, NULL
, 'c' },
348 { "export-db", no_argument
, NULL
, 'e' },
349 { "root", no_argument
, NULL
, 'r' },
350 { "device-id-of-file", required_argument
, NULL
, 'd' },
351 { "export", no_argument
, NULL
, 'x' },
352 { "export-prefix", required_argument
, NULL
, 'P' },
353 { "version", no_argument
, NULL
, 'V' },
354 { "help", no_argument
, NULL
, 'h' },
358 ActionType action
= ACTION_QUERY
;
359 QueryType query
= QUERY_ALL
;
361 while ((c
= getopt_long(argc
, argv
, "aced:n:p:q:rxP:RVh", options
, NULL
)) >= 0)
365 const char *prefix
= c
== 'n' ? "/dev/" : "/sys/";
368 path
= path_join(path_startswith(optarg
, prefix
) ? NULL
: prefix
, optarg
);
372 r
= strv_consume(&devices
, path
);
379 action
= ACTION_QUERY
;
380 if (streq(optarg
, "property") || streq(optarg
, "env"))
381 query
= QUERY_PROPERTY
;
382 else if (streq(optarg
, "name"))
384 else if (streq(optarg
, "symlink"))
385 query
= QUERY_SYMLINK
;
386 else if (streq(optarg
, "path"))
388 else if (streq(optarg
, "all"))
391 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "unknown query type");
397 action
= ACTION_DEVICE_ID_FILE
;
398 r
= free_and_strdup(&name
, optarg
);
403 action
= ACTION_ATTRIBUTE_WALK
;
406 return export_devices();
415 arg_export_prefix
= optarg
;
418 return print_version();
424 assert_not_reached("Unknown option");
427 if (action
== ACTION_DEVICE_ID_FILE
) {
429 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
430 "Positional arguments are not allowed with -d/--device-id-of-file.");
432 return stat_device(name
, arg_export
, arg_export_prefix
);
435 r
= strv_extend_strv(&devices
, argv
+ optind
, false);
437 return log_error_errno(r
, "Failed to build argument list: %m");
439 if (strv_isempty(devices
))
440 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
441 "A device name or path is required");
442 if (action
== ACTION_ATTRIBUTE_WALK
&& strv_length(devices
) > 1)
443 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
444 "Only one device may be specified with -a/--attribute-walk");
447 STRV_FOREACH(p
, devices
) {
448 _cleanup_(sd_device_unrefp
) sd_device
*device
= NULL
;
450 r
= find_device(*p
, NULL
, &device
);
452 return log_error_errno(r
, "Bad argument \"%s\", expected an absolute path in /dev/ or /sys or a unit name: %m", *p
);
454 return log_error_errno(r
, "Unknown device \"%s\": %m", *p
);
456 if (action
== ACTION_QUERY
)
457 r
= query_device(query
, device
);
458 else if (action
== ACTION_ATTRIBUTE_WALK
)
459 r
= print_device_chain(device
);
461 assert_not_reached("Unknown action");