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-util.h"
23 #include "udevadm-util.h"
25 typedef enum ActionType
{
27 ACTION_ATTRIBUTE_WALK
,
28 ACTION_DEVICE_ID_FILE
,
31 typedef enum QueryType
{
39 static bool arg_root
= false;
40 static bool arg_export
= false;
41 static const char *arg_export_prefix
= NULL
;
43 static bool skip_attribute(const char *name
) {
44 static const char* const skip
[] = {
55 for (i
= 0; i
< ELEMENTSOF(skip
); i
++)
56 if (streq(name
, skip
[i
]))
61 static void print_all_attributes(sd_device
*device
, const char *key
) {
62 const char *name
, *value
;
64 FOREACH_DEVICE_PROPERTY(device
, name
, value
) {
67 if (skip_attribute(name
))
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 printf("N: %s\n", str
+ STRLEN("/dev/"));
137 if (device_get_devlink_priority(device
, &i
) >= 0)
138 printf("L: %i\n", i
);
140 FOREACH_DEVICE_DEVLINK(device
, str
)
141 printf("S: %s\n", str
+ STRLEN("/dev/"));
143 FOREACH_DEVICE_PROPERTY(device
, str
, val
)
144 printf("E: %s=%s\n", str
, val
);
150 static int stat_device(const char *name
, bool export
, const char *prefix
) {
153 if (stat(name
, &statbuf
) != 0)
159 printf("%sMAJOR=%u\n"
161 prefix
, major(statbuf
.st_dev
),
162 prefix
, minor(statbuf
.st_dev
));
164 printf("%u:%u\n", major(statbuf
.st_dev
), minor(statbuf
.st_dev
));
168 static int export_devices(void) {
169 _cleanup_(sd_device_enumerator_unrefp
) sd_device_enumerator
*e
= NULL
;
173 r
= sd_device_enumerator_new(&e
);
177 r
= sd_device_enumerator_allow_uninitialized(e
);
181 r
= device_enumerator_scan_devices(e
);
185 FOREACH_DEVICE_AND_SUBSYSTEM(e
, d
)
191 static void cleanup_dir(DIR *dir
, mode_t mask
, int depth
) {
197 FOREACH_DIRENT_ALL(dent
, dir
, break) {
200 if (dent
->d_name
[0] == '.')
202 if (fstatat(dirfd(dir
), dent
->d_name
, &stats
, AT_SYMLINK_NOFOLLOW
) != 0)
204 if ((stats
.st_mode
& mask
) != 0)
206 if (S_ISDIR(stats
.st_mode
)) {
207 _cleanup_closedir_
DIR *dir2
= NULL
;
209 dir2
= fdopendir(openat(dirfd(dir
), dent
->d_name
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
));
211 cleanup_dir(dir2
, mask
, depth
-1);
213 (void) unlinkat(dirfd(dir
), dent
->d_name
, AT_REMOVEDIR
);
215 (void) unlinkat(dirfd(dir
), dent
->d_name
, 0);
219 static void cleanup_db(void) {
220 _cleanup_closedir_
DIR *dir1
= NULL
, *dir2
= NULL
, *dir3
= NULL
, *dir4
= NULL
, *dir5
= NULL
;
222 (void) unlink("/run/udev/queue.bin");
224 dir1
= opendir("/run/udev/data");
226 cleanup_dir(dir1
, S_ISVTX
, 1);
228 dir2
= opendir("/run/udev/links");
230 cleanup_dir(dir2
, 0, 2);
232 dir3
= opendir("/run/udev/tags");
234 cleanup_dir(dir3
, 0, 2);
236 dir4
= opendir("/run/udev/static_node-tags");
238 cleanup_dir(dir4
, 0, 2);
240 dir5
= opendir("/run/udev/watch");
242 cleanup_dir(dir5
, 0, 1);
245 static int query_device(QueryType query
, sd_device
* device
) {
254 r
= sd_device_get_devname(device
, &node
);
256 return log_error_errno(r
, "No device node found: %m");
259 printf("%s\n", node
);
261 printf("%s\n", node
+ STRLEN("/dev/"));
265 case QUERY_SYMLINK
: {
269 FOREACH_DEVICE_DEVLINK(device
, devlink
) {
273 printf("%s", devlink
);
275 printf("%s", devlink
+ STRLEN("/dev/"));
286 r
= sd_device_get_devpath(device
, &devpath
);
288 return log_error_errno(r
, "Failed to get device path: %m");
290 printf("%s\n", devpath
);
294 case QUERY_PROPERTY
: {
295 const char *key
, *value
;
297 FOREACH_DEVICE_PROPERTY(device
, key
, value
)
299 printf("%s%s='%s'\n", strempty(arg_export_prefix
), key
, value
);
301 printf("%s=%s\n", key
, value
);
306 return print_record(device
);
309 assert_not_reached("unknown query type");
313 static int help(void) {
314 printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
315 "Query sysfs or the udev database.\n\n"
316 " -h --help Print this message\n"
317 " -V --version Print version of the program\n"
318 " -q --query=TYPE Query device information:\n"
319 " name Name of device node\n"
320 " symlink Pointing to node\n"
321 " path sysfs device path\n"
322 " property The device properties\n"
324 " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
325 " -n --name=NAME Node or symlink name used for query or attribute walk\n"
326 " -r --root Prepend dev directory to path names\n"
327 " -a --attribute-walk Print all key matches walking along the chain\n"
328 " of parent devices\n"
329 " -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
330 " -x --export Export key/value pairs\n"
331 " -P --export-prefix Export the key name with a prefix\n"
332 " -e --export-db Export the content of the udev database\n"
333 " -c --cleanup-db Clean up the udev database\n"
334 , program_invocation_short_name
);
339 int info_main(int argc
, char *argv
[], void *userdata
) {
340 _cleanup_(sd_device_unrefp
) sd_device
*device
= NULL
;
341 _cleanup_free_
char *name
= NULL
;
344 static const struct option options
[] = {
345 { "name", required_argument
, NULL
, 'n' },
346 { "path", required_argument
, NULL
, 'p' },
347 { "query", required_argument
, NULL
, 'q' },
348 { "attribute-walk", no_argument
, NULL
, 'a' },
349 { "cleanup-db", no_argument
, NULL
, 'c' },
350 { "export-db", no_argument
, NULL
, 'e' },
351 { "root", no_argument
, NULL
, 'r' },
352 { "device-id-of-file", required_argument
, NULL
, 'd' },
353 { "export", no_argument
, NULL
, 'x' },
354 { "export-prefix", required_argument
, NULL
, 'P' },
355 { "version", no_argument
, NULL
, 'V' },
356 { "help", no_argument
, NULL
, 'h' },
360 ActionType action
= ACTION_QUERY
;
361 QueryType query
= QUERY_ALL
;
363 while ((c
= getopt_long(argc
, argv
, "aced:n:p:q:rxP:RVh", options
, NULL
)) >= 0)
367 log_error("device already specified");
371 r
= find_device(optarg
, "/dev/", &device
);
373 return log_error_errno(r
, "device node not found: %m");
377 log_error("device already specified");
381 r
= find_device(optarg
, "/sys", &device
);
383 return log_error_errno(r
, "syspath not found: %m");
386 action
= ACTION_QUERY
;
387 if (streq(optarg
, "property") || streq(optarg
, "env"))
388 query
= QUERY_PROPERTY
;
389 else if (streq(optarg
, "name"))
391 else if (streq(optarg
, "symlink"))
392 query
= QUERY_SYMLINK
;
393 else if (streq(optarg
, "path"))
395 else if (streq(optarg
, "all"))
398 log_error("unknown query type");
406 action
= ACTION_DEVICE_ID_FILE
;
407 r
= free_and_strdup(&name
, optarg
);
412 action
= ACTION_ATTRIBUTE_WALK
;
415 return export_devices();
423 arg_export_prefix
= optarg
;
426 return print_version();
432 assert_not_reached("Unknown option");
435 if (IN_SET(action
, ACTION_QUERY
, ACTION_ATTRIBUTE_WALK
) &&
437 /* A device argument is required. It may be an option or a positional arg. */
440 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
441 "A device name or path is required");
443 r
= find_device(argv
[optind
], NULL
, &device
);
445 return log_error_errno(r
, "Bad argument \"%s\", an absolute path in /dev/ or /sys expected: %m",
448 return log_error_errno(r
, "Unknown device \"%s\": %m", argv
[optind
]);
454 return query_device(query
, device
);
456 case ACTION_ATTRIBUTE_WALK
:
458 return print_device_chain(device
);
460 case ACTION_DEVICE_ID_FILE
:
462 return stat_device(name
, arg_export
, arg_export_prefix
);
465 assert_not_reached("Unknown action");