1 /* SPDX-License-Identifier: GPL-2.0+ */
13 #include "dirent-util.h"
15 #include "string-util.h"
17 #include "udevadm-util.h"
19 static bool skip_attribute(const char *name
) {
20 static const char* const skip
[] = {
31 for (i
= 0; i
< ELEMENTSOF(skip
); i
++)
32 if (streq(name
, skip
[i
]))
37 static void print_all_attributes(struct udev_device
*device
, const char *key
) {
38 struct udev_list_entry
*sysattr
;
40 udev_list_entry_foreach(sysattr
, udev_device_get_sysattr_list_entry(device
)) {
45 name
= udev_list_entry_get_name(sysattr
);
46 if (skip_attribute(name
))
49 value
= udev_device_get_sysattr_value(device
, name
);
53 /* skip any values that look like a path */
57 /* skip nonprintable attributes */
59 while (len
> 0 && isprint(value
[len
-1]))
64 printf(" %s{%s}==\"%s\"\n", key
, name
, value
);
69 static int print_device_chain(struct udev_device
*device
) {
70 struct udev_device
*device_parent
;
74 "Udevadm info starts with the device specified by the devpath and then\n"
75 "walks up the chain of parent devices. It prints for every device\n"
76 "found, all possible attributes in the udev rules key format.\n"
77 "A rule to match, can be composed by the attributes of the device\n"
78 "and the attributes from one single parent device.\n"
81 printf(" looking at device '%s':\n", udev_device_get_devpath(device
));
82 printf(" KERNEL==\"%s\"\n", udev_device_get_sysname(device
));
83 str
= udev_device_get_subsystem(device
);
86 printf(" SUBSYSTEM==\"%s\"\n", str
);
87 str
= udev_device_get_driver(device
);
90 printf(" DRIVER==\"%s\"\n", str
);
91 print_all_attributes(device
, "ATTR");
93 device_parent
= device
;
95 device_parent
= udev_device_get_parent(device_parent
);
96 if (device_parent
== NULL
)
98 printf(" looking at parent device '%s':\n", udev_device_get_devpath(device_parent
));
99 printf(" KERNELS==\"%s\"\n", udev_device_get_sysname(device_parent
));
100 str
= udev_device_get_subsystem(device_parent
);
103 printf(" SUBSYSTEMS==\"%s\"\n", str
);
104 str
= udev_device_get_driver(device_parent
);
107 printf(" DRIVERS==\"%s\"\n", str
);
108 print_all_attributes(device_parent
, "ATTRS");
109 } while (device_parent
!= NULL
);
114 static void print_record(struct udev_device
*device
) {
117 struct udev_list_entry
*list_entry
;
119 printf("P: %s\n", udev_device_get_devpath(device
));
121 str
= udev_device_get_devnode(device
);
123 printf("N: %s\n", str
+ STRLEN("/dev/"));
125 i
= udev_device_get_devlink_priority(device
);
127 printf("L: %i\n", i
);
129 udev_list_entry_foreach(list_entry
, udev_device_get_devlinks_list_entry(device
))
131 udev_list_entry_get_name(list_entry
) + STRLEN("/dev/"));
133 udev_list_entry_foreach(list_entry
, udev_device_get_properties_list_entry(device
))
135 udev_list_entry_get_name(list_entry
),
136 udev_list_entry_get_value(list_entry
));
140 static int stat_device(const char *name
, bool export
, const char *prefix
) {
143 if (stat(name
, &statbuf
) != 0)
149 printf("%sMAJOR=%u\n"
151 prefix
, major(statbuf
.st_dev
),
152 prefix
, minor(statbuf
.st_dev
));
154 printf("%u:%u\n", major(statbuf
.st_dev
), minor(statbuf
.st_dev
));
158 static int export_devices(struct udev
*udev
) {
159 _cleanup_(udev_enumerate_unrefp
) struct udev_enumerate
*udev_enumerate
;
160 struct udev_list_entry
*list_entry
;
162 udev_enumerate
= udev_enumerate_new(udev
);
163 if (udev_enumerate
== NULL
)
166 udev_enumerate_scan_devices(udev_enumerate
);
167 udev_list_entry_foreach(list_entry
, udev_enumerate_get_list_entry(udev_enumerate
)) {
168 _cleanup_(udev_device_unrefp
) struct udev_device
*device
;
170 device
= udev_device_new_from_syspath(udev
, udev_list_entry_get_name(list_entry
));
172 print_record(device
);
178 static void cleanup_dir(DIR *dir
, mode_t mask
, int depth
) {
184 FOREACH_DIRENT_ALL(dent
, dir
, break) {
187 if (dent
->d_name
[0] == '.')
189 if (fstatat(dirfd(dir
), dent
->d_name
, &stats
, AT_SYMLINK_NOFOLLOW
) != 0)
191 if ((stats
.st_mode
& mask
) != 0)
193 if (S_ISDIR(stats
.st_mode
)) {
194 _cleanup_closedir_
DIR *dir2
;
196 dir2
= fdopendir(openat(dirfd(dir
), dent
->d_name
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
));
198 cleanup_dir(dir2
, mask
, depth
-1);
200 (void) unlinkat(dirfd(dir
), dent
->d_name
, AT_REMOVEDIR
);
202 (void) unlinkat(dirfd(dir
), dent
->d_name
, 0);
206 static void cleanup_db(struct udev
*udev
) {
207 _cleanup_closedir_
DIR *dir1
= NULL
, *dir2
= NULL
, *dir3
= NULL
, *dir4
= NULL
, *dir5
= NULL
;
209 (void) unlink("/run/udev/queue.bin");
211 dir1
= opendir("/run/udev/data");
213 cleanup_dir(dir1
, S_ISVTX
, 1);
215 dir2
= opendir("/run/udev/links");
217 cleanup_dir(dir2
, 0, 2);
219 dir3
= opendir("/run/udev/tags");
221 cleanup_dir(dir3
, 0, 2);
223 dir4
= opendir("/run/udev/static_node-tags");
225 cleanup_dir(dir4
, 0, 2);
227 dir5
= opendir("/run/udev/watch");
229 cleanup_dir(dir5
, 0, 1);
232 static void help(void) {
234 printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
235 "Query sysfs or the udev database.\n\n"
236 " -h --help Print this message\n"
237 " -V --version Print version of the program\n"
238 " -q --query=TYPE Query device information:\n"
239 " name Name of device node\n"
240 " symlink Pointing to node\n"
241 " path sysfs device path\n"
242 " property The device properties\n"
244 " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
245 " -n --name=NAME Node or symlink name used for query or attribute walk\n"
246 " -r --root Prepend dev directory to path names\n"
247 " -a --attribute-walk Print all key matches walking along the chain\n"
248 " of parent devices\n"
249 " -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
250 " -x --export Export key/value pairs\n"
251 " -P --export-prefix Export the key name with a prefix\n"
252 " -e --export-db Export the content of the udev database\n"
253 " -c --cleanup-db Clean up the udev database\n"
254 , program_invocation_short_name
);
257 static int uinfo(struct udev
*udev
, int argc
, char *argv
[]) {
258 _cleanup_(udev_device_unrefp
) struct udev_device
*device
= NULL
;
261 const char *export_prefix
= NULL
;
262 char name
[UTIL_PATH_SIZE
];
263 struct udev_list_entry
*list_entry
;
266 static const struct option options
[] = {
267 { "name", required_argument
, NULL
, 'n' },
268 { "path", required_argument
, NULL
, 'p' },
269 { "query", required_argument
, NULL
, 'q' },
270 { "attribute-walk", no_argument
, NULL
, 'a' },
271 { "cleanup-db", no_argument
, NULL
, 'c' },
272 { "export-db", no_argument
, NULL
, 'e' },
273 { "root", no_argument
, NULL
, 'r' },
274 { "device-id-of-file", required_argument
, NULL
, 'd' },
275 { "export", no_argument
, NULL
, 'x' },
276 { "export-prefix", required_argument
, NULL
, 'P' },
277 { "version", no_argument
, NULL
, 'V' },
278 { "help", no_argument
, NULL
, 'h' },
284 ACTION_ATTRIBUTE_WALK
,
285 ACTION_DEVICE_ID_FILE
,
286 } action
= ACTION_QUERY
;
296 while ((c
= getopt_long(argc
, argv
, "aced:n:p:q:rxP:RVh", options
, NULL
)) >= 0)
299 if (device
!= NULL
) {
300 fprintf(stderr
, "device already specified\n");
304 device
= find_device(udev
, optarg
, "/dev/");
305 if (device
== NULL
) {
306 fprintf(stderr
, "device node not found\n");
312 if (device
!= NULL
) {
313 fprintf(stderr
, "device already specified\n");
317 device
= find_device(udev
, optarg
, "/sys");
318 if (device
== NULL
) {
319 fprintf(stderr
, "syspath not found\n");
324 action
= ACTION_QUERY
;
325 if (streq(optarg
, "property") || streq(optarg
, "env"))
326 query
= QUERY_PROPERTY
;
327 else if (streq(optarg
, "name"))
329 else if (streq(optarg
, "symlink"))
330 query
= QUERY_SYMLINK
;
331 else if (streq(optarg
, "path"))
333 else if (streq(optarg
, "all"))
336 fprintf(stderr
, "unknown query type\n");
344 action
= ACTION_DEVICE_ID_FILE
;
345 strscpy(name
, sizeof(name
), optarg
);
348 action
= ACTION_ATTRIBUTE_WALK
;
351 if (export_devices(udev
) < 0)
361 export_prefix
= optarg
;
380 device
= find_device(udev
, argv
[optind
], NULL
);
382 fprintf(stderr
, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
389 const char *node
= udev_device_get_devnode(device
);
392 fprintf(stderr
, "no device node found\n");
397 printf("%s\n", udev_device_get_devnode(device
));
400 udev_device_get_devnode(device
) + STRLEN("/dev/"));
404 list_entry
= udev_device_get_devlinks_list_entry(device
);
405 while (list_entry
!= NULL
) {
407 printf("%s", udev_list_entry_get_name(list_entry
));
410 udev_list_entry_get_name(list_entry
) + STRLEN("/dev/"));
411 list_entry
= udev_list_entry_get_next(list_entry
);
412 if (list_entry
!= NULL
)
418 printf("%s\n", udev_device_get_devpath(device
));
421 list_entry
= udev_device_get_properties_list_entry(device
);
422 while (list_entry
!= NULL
) {
424 printf("%s%s='%s'\n", strempty(export_prefix
),
425 udev_list_entry_get_name(list_entry
),
426 udev_list_entry_get_value(list_entry
));
428 printf("%s=%s\n", udev_list_entry_get_name(list_entry
), udev_list_entry_get_value(list_entry
));
430 list_entry
= udev_list_entry_get_next(list_entry
);
434 print_record(device
);
437 assert_not_reached("unknown query type");
440 case ACTION_ATTRIBUTE_WALK
:
441 if (!device
&& argv
[optind
]) {
442 device
= find_device(udev
, argv
[optind
], NULL
);
444 fprintf(stderr
, "Unknown device, absolute path in /dev/ or /sys expected.\n");
449 fprintf(stderr
, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
452 print_device_chain(device
);
454 case ACTION_DEVICE_ID_FILE
:
455 if (stat_device(name
, export
, export_prefix
) != 0)
463 const struct udevadm_cmd udevadm_info
= {
466 .help
= "Query sysfs or the udev database",