1 /* SPDX-License-Identifier: GPL-2.0+ */
13 #include "sd-device.h"
15 #include "device-enumerator-private.h"
16 #include "device-private.h"
17 #include "device-util.h"
18 #include "dirent-util.h"
20 #include "libudev-private.h"
21 #include "string-util.h"
23 #include "udevadm-util.h"
25 static bool skip_attribute(const char *name
) {
26 static const char* const skip
[] = {
37 for (i
= 0; i
< ELEMENTSOF(skip
); i
++)
38 if (streq(name
, skip
[i
]))
43 static void print_all_attributes(sd_device
*device
, const char *key
) {
44 const char *name
, *value
;
46 FOREACH_DEVICE_PROPERTY(device
, name
, value
) {
49 if (skip_attribute(name
))
52 /* skip any values that look like a path */
56 /* skip nonprintable attributes */
58 while (len
> 0 && isprint(value
[len
-1]))
63 printf(" %s{%s}==\"%s\"\n", key
, name
, value
);
68 static int print_device_chain(sd_device
*device
) {
69 sd_device
*child
, *parent
;
73 "Udevadm info starts with the device specified by the devpath and then\n"
74 "walks up the chain of parent devices. It prints for every device\n"
75 "found, all possible attributes in the udev rules key format.\n"
76 "A rule to match, can be composed by the attributes of the device\n"
77 "and the attributes from one single parent device.\n"
80 (void) sd_device_get_devpath(device
, &str
);
81 printf(" looking at device '%s':\n", str
);
82 (void) sd_device_get_sysname(device
, &str
);
83 printf(" KERNEL==\"%s\"\n", str
);
84 if (sd_device_get_subsystem(device
, &str
) < 0)
86 printf(" SUBSYSTEM==\"%s\"\n", str
);
87 if (sd_device_get_driver(device
, &str
) < 0)
89 printf(" DRIVER==\"%s\"\n", str
);
90 print_all_attributes(device
, "ATTR");
92 for (child
= device
; sd_device_get_parent(child
, &parent
) >= 0; child
= parent
) {
93 (void) sd_device_get_devpath(parent
, &str
);
94 printf(" looking at parent device '%s':\n", str
);
95 (void) sd_device_get_sysname(parent
, &str
);
96 printf(" KERNELS==\"%s\"\n", str
);
97 if (sd_device_get_subsystem(parent
, &str
) < 0)
99 printf(" SUBSYSTEMS==\"%s\"\n", str
);
100 if (sd_device_get_driver(parent
, &str
) < 0)
102 printf(" DRIVERS==\"%s\"\n", str
);
103 print_all_attributes(parent
, "ATTRS");
109 static void print_record(sd_device
*device
) {
110 const char *str
, *val
;
113 (void) sd_device_get_devpath(device
, &str
);
114 printf("P: %s\n", str
);
116 if (sd_device_get_devname(device
, &str
) >= 0)
117 printf("N: %s\n", str
+ STRLEN("/dev/"));
119 if (device_get_devlink_priority(device
, &i
) >= 0)
120 printf("L: %i\n", i
);
122 FOREACH_DEVICE_DEVLINK(device
, str
)
123 printf("S: %s\n", str
+ STRLEN("/dev/"));
125 FOREACH_DEVICE_PROPERTY(device
, str
, val
)
126 printf("E: %s=%s\n", str
, val
);
131 static int stat_device(const char *name
, bool export
, const char *prefix
) {
134 if (stat(name
, &statbuf
) != 0)
140 printf("%sMAJOR=%u\n"
142 prefix
, major(statbuf
.st_dev
),
143 prefix
, minor(statbuf
.st_dev
));
145 printf("%u:%u\n", major(statbuf
.st_dev
), minor(statbuf
.st_dev
));
149 static int export_devices(void) {
150 _cleanup_(sd_device_enumerator_unrefp
) sd_device_enumerator
*e
= NULL
;
154 r
= sd_device_enumerator_new(&e
);
158 r
= sd_device_enumerator_allow_uninitialized(e
);
162 r
= device_enumerator_scan_devices(e
);
166 FOREACH_DEVICE_AND_SUBSYSTEM(e
, d
)
172 static void cleanup_dir(DIR *dir
, mode_t mask
, int depth
) {
178 FOREACH_DIRENT_ALL(dent
, dir
, break) {
181 if (dent
->d_name
[0] == '.')
183 if (fstatat(dirfd(dir
), dent
->d_name
, &stats
, AT_SYMLINK_NOFOLLOW
) != 0)
185 if ((stats
.st_mode
& mask
) != 0)
187 if (S_ISDIR(stats
.st_mode
)) {
188 _cleanup_closedir_
DIR *dir2
= NULL
;
190 dir2
= fdopendir(openat(dirfd(dir
), dent
->d_name
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
));
192 cleanup_dir(dir2
, mask
, depth
-1);
194 (void) unlinkat(dirfd(dir
), dent
->d_name
, AT_REMOVEDIR
);
196 (void) unlinkat(dirfd(dir
), dent
->d_name
, 0);
200 static void cleanup_db(void) {
201 _cleanup_closedir_
DIR *dir1
= NULL
, *dir2
= NULL
, *dir3
= NULL
, *dir4
= NULL
, *dir5
= NULL
;
203 (void) unlink("/run/udev/queue.bin");
205 dir1
= opendir("/run/udev/data");
207 cleanup_dir(dir1
, S_ISVTX
, 1);
209 dir2
= opendir("/run/udev/links");
211 cleanup_dir(dir2
, 0, 2);
213 dir3
= opendir("/run/udev/tags");
215 cleanup_dir(dir3
, 0, 2);
217 dir4
= opendir("/run/udev/static_node-tags");
219 cleanup_dir(dir4
, 0, 2);
221 dir5
= opendir("/run/udev/watch");
223 cleanup_dir(dir5
, 0, 1);
226 static int help(void) {
228 printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
229 "Query sysfs or the udev database.\n\n"
230 " -h --help Print this message\n"
231 " -V --version Print version of the program\n"
232 " -q --query=TYPE Query device information:\n"
233 " name Name of device node\n"
234 " symlink Pointing to node\n"
235 " path sysfs device path\n"
236 " property The device properties\n"
238 " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
239 " -n --name=NAME Node or symlink name used for query or attribute walk\n"
240 " -r --root Prepend dev directory to path names\n"
241 " -a --attribute-walk Print all key matches walking along the chain\n"
242 " of parent devices\n"
243 " -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
244 " -x --export Export key/value pairs\n"
245 " -P --export-prefix Export the key name with a prefix\n"
246 " -e --export-db Export the content of the udev database\n"
247 " -c --cleanup-db Clean up the udev database\n"
248 , program_invocation_short_name
);
253 int info_main(int argc
, char *argv
[], void *userdata
) {
254 _cleanup_(sd_device_unrefp
) sd_device
*device
= NULL
;
257 const char *export_prefix
= NULL
;
258 char name
[UTIL_PATH_SIZE
];
261 static const struct option options
[] = {
262 { "name", required_argument
, NULL
, 'n' },
263 { "path", required_argument
, NULL
, 'p' },
264 { "query", required_argument
, NULL
, 'q' },
265 { "attribute-walk", no_argument
, NULL
, 'a' },
266 { "cleanup-db", no_argument
, NULL
, 'c' },
267 { "export-db", no_argument
, NULL
, 'e' },
268 { "root", no_argument
, NULL
, 'r' },
269 { "device-id-of-file", required_argument
, NULL
, 'd' },
270 { "export", no_argument
, NULL
, 'x' },
271 { "export-prefix", required_argument
, NULL
, 'P' },
272 { "version", no_argument
, NULL
, 'V' },
273 { "help", no_argument
, NULL
, 'h' },
279 ACTION_ATTRIBUTE_WALK
,
280 ACTION_DEVICE_ID_FILE
,
281 } action
= ACTION_QUERY
;
291 while ((c
= getopt_long(argc
, argv
, "aced:n:p:q:rxP:RVh", options
, NULL
)) >= 0)
295 log_error("device already specified");
299 r
= find_device(optarg
, "/dev/", &device
);
301 return log_error_errno(r
, "device node not found: %m");
305 log_error("device already specified");
309 r
= find_device(optarg
, "/sys", &device
);
311 return log_error_errno(r
, "syspath not found: %m");
314 action
= ACTION_QUERY
;
315 if (streq(optarg
, "property") || streq(optarg
, "env"))
316 query
= QUERY_PROPERTY
;
317 else if (streq(optarg
, "name"))
319 else if (streq(optarg
, "symlink"))
320 query
= QUERY_SYMLINK
;
321 else if (streq(optarg
, "path"))
323 else if (streq(optarg
, "all"))
326 log_error("unknown query type");
334 action
= ACTION_DEVICE_ID_FILE
;
335 strscpy(name
, sizeof(name
), optarg
);
338 action
= ACTION_ATTRIBUTE_WALK
;
341 return export_devices();
349 export_prefix
= optarg
;
352 return print_version();
358 assert_not_reached("Unknown option");
368 r
= find_device(argv
[optind
], NULL
, &device
);
370 return log_error_errno(r
, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected: %m");
377 r
= sd_device_get_devname(device
, &node
);
379 return log_error_errno(r
, "no device node found: %m");
382 printf("%s\n", node
);
384 printf("%s\n", node
+ STRLEN("/dev/"));
387 case QUERY_SYMLINK
: {
391 FOREACH_DEVICE_DEVLINK(device
, devlink
) {
395 printf("%s", devlink
);
397 printf("%s", devlink
+ STRLEN("/dev/"));
407 r
= sd_device_get_devpath(device
, &devpath
);
409 return log_error_errno(r
, "Failed to get device path: %m");
411 printf("%s\n", devpath
);
414 case QUERY_PROPERTY
: {
415 const char *key
, *value
;
417 FOREACH_DEVICE_PROPERTY(device
, key
, value
)
419 printf("%s%s='%s'\n", strempty(export_prefix
), key
, value
);
421 printf("%s=%s\n", key
, value
);
426 print_record(device
);
429 assert_not_reached("unknown query type");
432 case ACTION_ATTRIBUTE_WALK
:
433 if (!device
&& argv
[optind
]) {
434 r
= find_device(argv
[optind
], NULL
, &device
);
436 return log_error_errno(r
, "Unknown device, absolute path in /dev/ or /sys expected: %m");
439 log_error("Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.");
442 print_device_chain(device
);
444 case ACTION_DEVICE_ID_FILE
:
445 r
= stat_device(name
, export
, export_prefix
);