1 /* SPDX-License-Identifier: GPL-2.0+ */
13 #include "dirent-util.h"
15 #include "string-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(void) {
160 _cleanup_(udev_enumerate_unrefp
) struct udev_enumerate
*udev_enumerate
;
161 struct udev_list_entry
*list_entry
;
163 udev_enumerate
= udev_enumerate_new(NULL
);
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(NULL
, 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(void) {
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 int 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
);
260 int info_main(int argc
, char *argv
[], void *userdata
) {
261 _cleanup_(udev_device_unrefp
) struct udev_device
*device
= NULL
;
264 const char *export_prefix
= NULL
;
265 char name
[UTIL_PATH_SIZE
];
266 struct udev_list_entry
*list_entry
;
269 static const struct option options
[] = {
270 { "name", required_argument
, NULL
, 'n' },
271 { "path", required_argument
, NULL
, 'p' },
272 { "query", required_argument
, NULL
, 'q' },
273 { "attribute-walk", no_argument
, NULL
, 'a' },
274 { "cleanup-db", no_argument
, NULL
, 'c' },
275 { "export-db", no_argument
, NULL
, 'e' },
276 { "root", no_argument
, NULL
, 'r' },
277 { "device-id-of-file", required_argument
, NULL
, 'd' },
278 { "export", no_argument
, NULL
, 'x' },
279 { "export-prefix", required_argument
, NULL
, 'P' },
280 { "version", no_argument
, NULL
, 'V' },
281 { "help", no_argument
, NULL
, 'h' },
287 ACTION_ATTRIBUTE_WALK
,
288 ACTION_DEVICE_ID_FILE
,
289 } action
= ACTION_QUERY
;
299 while ((c
= getopt_long(argc
, argv
, "aced:n:p:q:rxP:RVh", options
, NULL
)) >= 0)
303 log_error("device already specified");
307 device
= find_device(optarg
, "/dev/");
309 return log_error_errno(errno
, "device node not found: %m");
313 log_error("device already specified");
317 device
= find_device(optarg
, "/sys");
319 return log_error_errno(errno
, "syspath not found: %m");
322 action
= ACTION_QUERY
;
323 if (streq(optarg
, "property") || streq(optarg
, "env"))
324 query
= QUERY_PROPERTY
;
325 else if (streq(optarg
, "name"))
327 else if (streq(optarg
, "symlink"))
328 query
= QUERY_SYMLINK
;
329 else if (streq(optarg
, "path"))
331 else if (streq(optarg
, "all"))
334 log_error("unknown query type");
342 action
= ACTION_DEVICE_ID_FILE
;
343 strscpy(name
, sizeof(name
), optarg
);
346 action
= ACTION_ATTRIBUTE_WALK
;
349 return export_devices();
357 export_prefix
= optarg
;
360 return print_version();
366 assert_not_reached("Unknown option");
376 device
= find_device(argv
[optind
], NULL
);
378 log_error("Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.");
385 const char *node
= udev_device_get_devnode(device
);
388 return log_error_errno(errno
, "no device node found");
391 printf("%s\n", udev_device_get_devnode(device
));
394 udev_device_get_devnode(device
) + STRLEN("/dev/"));
398 list_entry
= udev_device_get_devlinks_list_entry(device
);
401 printf("%s", udev_list_entry_get_name(list_entry
));
404 udev_list_entry_get_name(list_entry
) + STRLEN("/dev/"));
405 list_entry
= udev_list_entry_get_next(list_entry
);
412 printf("%s\n", udev_device_get_devpath(device
));
415 list_entry
= udev_device_get_properties_list_entry(device
);
418 printf("%s%s='%s'\n", strempty(export_prefix
),
419 udev_list_entry_get_name(list_entry
),
420 udev_list_entry_get_value(list_entry
));
422 printf("%s=%s\n", udev_list_entry_get_name(list_entry
), udev_list_entry_get_value(list_entry
));
424 list_entry
= udev_list_entry_get_next(list_entry
);
428 print_record(device
);
431 assert_not_reached("unknown query type");
434 case ACTION_ATTRIBUTE_WALK
:
435 if (!device
&& argv
[optind
]) {
436 device
= find_device(argv
[optind
], NULL
);
438 log_error("Unknown device, absolute path in /dev/ or /sys expected.");
443 log_error("Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.");
446 print_device_chain(device
);
448 case ACTION_DEVICE_ID_FILE
:
449 r
= stat_device(name
, export
, export_prefix
);