1 /* SPDX-License-Identifier: GPL-2.0+ */
13 #include "dirent-util.h"
15 #include "string-util.h"
16 #include "udev-util.h"
18 #include "udevadm-util.h"
20 static bool skip_attribute(const char *name
) {
21 static const char* const skip
[] = {
32 for (i
= 0; i
< ELEMENTSOF(skip
); i
++)
33 if (streq(name
, skip
[i
]))
38 static void print_all_attributes(struct udev_device
*device
, const char *key
) {
39 struct udev_list_entry
*sysattr
;
41 udev_list_entry_foreach(sysattr
, udev_device_get_sysattr_list_entry(device
)) {
46 name
= udev_list_entry_get_name(sysattr
);
47 if (skip_attribute(name
))
50 value
= udev_device_get_sysattr_value(device
, name
);
54 /* skip any values that look like a path */
58 /* skip nonprintable attributes */
60 while (len
> 0 && isprint(value
[len
-1]))
65 printf(" %s{%s}==\"%s\"\n", key
, name
, value
);
70 static int print_device_chain(struct udev_device
*device
) {
71 struct udev_device
*device_parent
;
75 "Udevadm info starts with the device specified by the devpath and then\n"
76 "walks up the chain of parent devices. It prints for every device\n"
77 "found, all possible attributes in the udev rules key format.\n"
78 "A rule to match, can be composed by the attributes of the device\n"
79 "and the attributes from one single parent device.\n"
82 printf(" looking at device '%s':\n", udev_device_get_devpath(device
));
83 printf(" KERNEL==\"%s\"\n", udev_device_get_sysname(device
));
84 str
= udev_device_get_subsystem(device
);
87 printf(" SUBSYSTEM==\"%s\"\n", str
);
88 str
= udev_device_get_driver(device
);
91 printf(" DRIVER==\"%s\"\n", str
);
92 print_all_attributes(device
, "ATTR");
94 device_parent
= device
;
96 device_parent
= udev_device_get_parent(device_parent
);
97 if (device_parent
== NULL
)
99 printf(" looking at parent device '%s':\n", udev_device_get_devpath(device_parent
));
100 printf(" KERNELS==\"%s\"\n", udev_device_get_sysname(device_parent
));
101 str
= udev_device_get_subsystem(device_parent
);
104 printf(" SUBSYSTEMS==\"%s\"\n", str
);
105 str
= udev_device_get_driver(device_parent
);
108 printf(" DRIVERS==\"%s\"\n", str
);
109 print_all_attributes(device_parent
, "ATTRS");
110 } while (device_parent
!= NULL
);
115 static void print_record(struct udev_device
*device
) {
118 struct udev_list_entry
*list_entry
;
120 printf("P: %s\n", udev_device_get_devpath(device
));
122 str
= udev_device_get_devnode(device
);
124 printf("N: %s\n", str
+ STRLEN("/dev/"));
126 i
= udev_device_get_devlink_priority(device
);
128 printf("L: %i\n", i
);
130 udev_list_entry_foreach(list_entry
, udev_device_get_devlinks_list_entry(device
))
132 udev_list_entry_get_name(list_entry
) + STRLEN("/dev/"));
134 udev_list_entry_foreach(list_entry
, udev_device_get_properties_list_entry(device
))
136 udev_list_entry_get_name(list_entry
),
137 udev_list_entry_get_value(list_entry
));
141 static int stat_device(const char *name
, bool export
, const char *prefix
) {
144 if (stat(name
, &statbuf
) != 0)
150 printf("%sMAJOR=%u\n"
152 prefix
, major(statbuf
.st_dev
),
153 prefix
, minor(statbuf
.st_dev
));
155 printf("%u:%u\n", major(statbuf
.st_dev
), minor(statbuf
.st_dev
));
159 static int export_devices(struct udev
*udev
) {
160 _cleanup_(udev_enumerate_unrefp
) struct udev_enumerate
*udev_enumerate
;
161 struct udev_list_entry
*list_entry
;
163 udev_enumerate
= udev_enumerate_new(udev
);
164 if (udev_enumerate
== NULL
)
167 udev_enumerate_scan_devices(udev_enumerate
);
168 udev_list_entry_foreach(list_entry
, udev_enumerate_get_list_entry(udev_enumerate
)) {
169 _cleanup_(udev_device_unrefp
) struct udev_device
*device
;
171 device
= udev_device_new_from_syspath(udev
, udev_list_entry_get_name(list_entry
));
173 print_record(device
);
179 static void cleanup_dir(DIR *dir
, mode_t mask
, int depth
) {
185 FOREACH_DIRENT_ALL(dent
, dir
, break) {
188 if (dent
->d_name
[0] == '.')
190 if (fstatat(dirfd(dir
), dent
->d_name
, &stats
, AT_SYMLINK_NOFOLLOW
) != 0)
192 if ((stats
.st_mode
& mask
) != 0)
194 if (S_ISDIR(stats
.st_mode
)) {
195 _cleanup_closedir_
DIR *dir2
;
197 dir2
= fdopendir(openat(dirfd(dir
), dent
->d_name
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
));
199 cleanup_dir(dir2
, mask
, depth
-1);
201 (void) unlinkat(dirfd(dir
), dent
->d_name
, AT_REMOVEDIR
);
203 (void) unlinkat(dirfd(dir
), dent
->d_name
, 0);
207 static void cleanup_db(struct udev
*udev
) {
208 _cleanup_closedir_
DIR *dir1
= NULL
, *dir2
= NULL
, *dir3
= NULL
, *dir4
= NULL
, *dir5
= NULL
;
210 (void) unlink("/run/udev/queue.bin");
212 dir1
= opendir("/run/udev/data");
214 cleanup_dir(dir1
, S_ISVTX
, 1);
216 dir2
= opendir("/run/udev/links");
218 cleanup_dir(dir2
, 0, 2);
220 dir3
= opendir("/run/udev/tags");
222 cleanup_dir(dir3
, 0, 2);
224 dir4
= opendir("/run/udev/static_node-tags");
226 cleanup_dir(dir4
, 0, 2);
228 dir5
= opendir("/run/udev/watch");
230 cleanup_dir(dir5
, 0, 1);
233 static void help(void) {
235 printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
236 "Query sysfs or the udev database.\n\n"
237 " -h --help Print this message\n"
238 " -V --version Print version of the program\n"
239 " -q --query=TYPE Query device information:\n"
240 " name Name of device node\n"
241 " symlink Pointing to node\n"
242 " path sysfs device path\n"
243 " property The device properties\n"
245 " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
246 " -n --name=NAME Node or symlink name used for query or attribute walk\n"
247 " -r --root Prepend dev directory to path names\n"
248 " -a --attribute-walk Print all key matches walking along the chain\n"
249 " of parent devices\n"
250 " -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
251 " -x --export Export key/value pairs\n"
252 " -P --export-prefix Export the key name with a prefix\n"
253 " -e --export-db Export the content of the udev database\n"
254 " -c --cleanup-db Clean up the udev database\n"
255 , program_invocation_short_name
);
258 static int uinfo(struct udev
*udev
, int argc
, char *argv
[]) {
259 _cleanup_(udev_device_unrefp
) struct udev_device
*device
= NULL
;
262 const char *export_prefix
= NULL
;
263 char name
[UTIL_PATH_SIZE
];
264 struct udev_list_entry
*list_entry
;
267 static const struct option options
[] = {
268 { "name", required_argument
, NULL
, 'n' },
269 { "path", required_argument
, NULL
, 'p' },
270 { "query", required_argument
, NULL
, 'q' },
271 { "attribute-walk", no_argument
, NULL
, 'a' },
272 { "cleanup-db", no_argument
, NULL
, 'c' },
273 { "export-db", no_argument
, NULL
, 'e' },
274 { "root", no_argument
, NULL
, 'r' },
275 { "device-id-of-file", required_argument
, NULL
, 'd' },
276 { "export", no_argument
, NULL
, 'x' },
277 { "export-prefix", required_argument
, NULL
, 'P' },
278 { "version", no_argument
, NULL
, 'V' },
279 { "help", no_argument
, NULL
, 'h' },
285 ACTION_ATTRIBUTE_WALK
,
286 ACTION_DEVICE_ID_FILE
,
287 } action
= ACTION_QUERY
;
297 while ((c
= getopt_long(argc
, argv
, "aced:n:p:q:rxP:RVh", options
, NULL
)) >= 0)
300 if (device
!= NULL
) {
301 fprintf(stderr
, "device already specified\n");
305 device
= find_device(udev
, optarg
, "/dev/");
306 if (device
== NULL
) {
307 fprintf(stderr
, "device node not found\n");
313 if (device
!= NULL
) {
314 fprintf(stderr
, "device already specified\n");
318 device
= find_device(udev
, optarg
, "/sys");
319 if (device
== NULL
) {
320 fprintf(stderr
, "syspath not found\n");
325 action
= ACTION_QUERY
;
326 if (streq(optarg
, "property") || streq(optarg
, "env"))
327 query
= QUERY_PROPERTY
;
328 else if (streq(optarg
, "name"))
330 else if (streq(optarg
, "symlink"))
331 query
= QUERY_SYMLINK
;
332 else if (streq(optarg
, "path"))
334 else if (streq(optarg
, "all"))
337 fprintf(stderr
, "unknown query type\n");
345 action
= ACTION_DEVICE_ID_FILE
;
346 strscpy(name
, sizeof(name
), optarg
);
349 action
= ACTION_ATTRIBUTE_WALK
;
352 if (export_devices(udev
) < 0)
362 export_prefix
= optarg
;
381 device
= find_device(udev
, argv
[optind
], NULL
);
383 fprintf(stderr
, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
390 const char *node
= udev_device_get_devnode(device
);
393 fprintf(stderr
, "no device node found\n");
398 printf("%s\n", udev_device_get_devnode(device
));
401 udev_device_get_devnode(device
) + STRLEN("/dev/"));
405 list_entry
= udev_device_get_devlinks_list_entry(device
);
406 while (list_entry
!= NULL
) {
408 printf("%s", udev_list_entry_get_name(list_entry
));
411 udev_list_entry_get_name(list_entry
) + STRLEN("/dev/"));
412 list_entry
= udev_list_entry_get_next(list_entry
);
413 if (list_entry
!= NULL
)
419 printf("%s\n", udev_device_get_devpath(device
));
422 list_entry
= udev_device_get_properties_list_entry(device
);
423 while (list_entry
!= NULL
) {
425 printf("%s%s='%s'\n", strempty(export_prefix
),
426 udev_list_entry_get_name(list_entry
),
427 udev_list_entry_get_value(list_entry
));
429 printf("%s=%s\n", udev_list_entry_get_name(list_entry
), udev_list_entry_get_value(list_entry
));
431 list_entry
= udev_list_entry_get_next(list_entry
);
435 print_record(device
);
438 assert_not_reached("unknown query type");
441 case ACTION_ATTRIBUTE_WALK
:
442 if (!device
&& argv
[optind
]) {
443 device
= find_device(udev
, argv
[optind
], NULL
);
445 fprintf(stderr
, "Unknown device, absolute path in /dev/ or /sys expected.\n");
450 fprintf(stderr
, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
453 print_device_chain(device
);
455 case ACTION_DEVICE_ID_FILE
:
456 if (stat_device(name
, export
, export_prefix
) != 0)
464 const struct udevadm_cmd udevadm_info
= {
467 .help
= "Query sysfs or the udev database",