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_SYSATTR(device
, name
) {
67 if (skip_attribute(name
))
70 if (sd_device_get_sysattr_value(device
, name
, &value
) < 0)
73 /* skip any values that look like a path */
77 /* skip nonprintable attributes */
79 while (len
> 0 && isprint(value
[len
-1]))
84 printf(" %s{%s}==\"%s\"\n", key
, name
, value
);
89 static int print_device_chain(sd_device
*device
) {
90 sd_device
*child
, *parent
;
94 "Udevadm info starts with the device specified by the devpath and then\n"
95 "walks up the chain of parent devices. It prints for every device\n"
96 "found, all possible attributes in the udev rules key format.\n"
97 "A rule to match, can be composed by the attributes of the device\n"
98 "and the attributes from one single parent device.\n"
101 (void) sd_device_get_devpath(device
, &str
);
102 printf(" looking at device '%s':\n", str
);
103 (void) sd_device_get_sysname(device
, &str
);
104 printf(" KERNEL==\"%s\"\n", str
);
105 if (sd_device_get_subsystem(device
, &str
) < 0)
107 printf(" SUBSYSTEM==\"%s\"\n", str
);
108 if (sd_device_get_driver(device
, &str
) < 0)
110 printf(" DRIVER==\"%s\"\n", str
);
111 print_all_attributes(device
, "ATTR");
113 for (child
= device
; sd_device_get_parent(child
, &parent
) >= 0; child
= parent
) {
114 (void) sd_device_get_devpath(parent
, &str
);
115 printf(" looking at parent device '%s':\n", str
);
116 (void) sd_device_get_sysname(parent
, &str
);
117 printf(" KERNELS==\"%s\"\n", str
);
118 if (sd_device_get_subsystem(parent
, &str
) < 0)
120 printf(" SUBSYSTEMS==\"%s\"\n", str
);
121 if (sd_device_get_driver(parent
, &str
) < 0)
123 printf(" DRIVERS==\"%s\"\n", str
);
124 print_all_attributes(parent
, "ATTRS");
130 static int print_record(sd_device
*device
) {
131 const char *str
, *val
;
134 (void) sd_device_get_devpath(device
, &str
);
135 printf("P: %s\n", str
);
137 if (sd_device_get_devname(device
, &str
) >= 0) {
138 assert_se(val
= path_startswith(str
, "/dev/"));
139 printf("N: %s\n", val
);
142 if (device_get_devlink_priority(device
, &i
) >= 0)
143 printf("L: %i\n", i
);
145 FOREACH_DEVICE_DEVLINK(device
, str
) {
146 assert_se(val
= path_startswith(str
, "/dev/"));
147 printf("S: %s\n", val
);
150 FOREACH_DEVICE_PROPERTY(device
, str
, val
)
151 printf("E: %s=%s\n", str
, val
);
157 static int stat_device(const char *name
, bool export
, const char *prefix
) {
160 if (stat(name
, &statbuf
) != 0)
166 printf("%sMAJOR=%u\n"
168 prefix
, major(statbuf
.st_dev
),
169 prefix
, minor(statbuf
.st_dev
));
171 printf("%u:%u\n", major(statbuf
.st_dev
), minor(statbuf
.st_dev
));
175 static int export_devices(void) {
176 _cleanup_(sd_device_enumerator_unrefp
) sd_device_enumerator
*e
= NULL
;
180 r
= sd_device_enumerator_new(&e
);
184 r
= sd_device_enumerator_allow_uninitialized(e
);
188 r
= device_enumerator_scan_devices(e
);
192 FOREACH_DEVICE_AND_SUBSYSTEM(e
, d
)
198 static void cleanup_dir(DIR *dir
, mode_t mask
, int depth
) {
204 FOREACH_DIRENT_ALL(dent
, dir
, break) {
207 if (dent
->d_name
[0] == '.')
209 if (fstatat(dirfd(dir
), dent
->d_name
, &stats
, AT_SYMLINK_NOFOLLOW
) != 0)
211 if ((stats
.st_mode
& mask
) != 0)
213 if (S_ISDIR(stats
.st_mode
)) {
214 _cleanup_closedir_
DIR *dir2
= NULL
;
216 dir2
= fdopendir(openat(dirfd(dir
), dent
->d_name
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
));
218 cleanup_dir(dir2
, mask
, depth
-1);
220 (void) unlinkat(dirfd(dir
), dent
->d_name
, AT_REMOVEDIR
);
222 (void) unlinkat(dirfd(dir
), dent
->d_name
, 0);
226 static void cleanup_db(void) {
227 _cleanup_closedir_
DIR *dir1
= NULL
, *dir2
= NULL
, *dir3
= NULL
, *dir4
= NULL
, *dir5
= NULL
;
229 (void) unlink("/run/udev/queue.bin");
231 dir1
= opendir("/run/udev/data");
233 cleanup_dir(dir1
, S_ISVTX
, 1);
235 dir2
= opendir("/run/udev/links");
237 cleanup_dir(dir2
, 0, 2);
239 dir3
= opendir("/run/udev/tags");
241 cleanup_dir(dir3
, 0, 2);
243 dir4
= opendir("/run/udev/static_node-tags");
245 cleanup_dir(dir4
, 0, 2);
247 dir5
= opendir("/run/udev/watch");
249 cleanup_dir(dir5
, 0, 1);
252 static int query_device(QueryType query
, sd_device
* device
) {
261 r
= sd_device_get_devname(device
, &node
);
263 return log_error_errno(r
, "No device node found: %m");
266 assert_se(node
= path_startswith(node
, "/dev/"));
267 printf("%s\n", node
);
271 case QUERY_SYMLINK
: {
272 const char *devlink
, *prefix
= "";
274 FOREACH_DEVICE_DEVLINK(device
, devlink
) {
276 assert_se(devlink
= path_startswith(devlink
, "/dev/"));
277 printf("%s%s", prefix
, devlink
);
287 r
= sd_device_get_devpath(device
, &devpath
);
289 return log_error_errno(r
, "Failed to get device path: %m");
291 printf("%s\n", devpath
);
295 case QUERY_PROPERTY
: {
296 const char *key
, *value
;
298 FOREACH_DEVICE_PROPERTY(device
, key
, value
)
300 printf("%s%s='%s'\n", strempty(arg_export_prefix
), key
, value
);
302 printf("%s=%s\n", key
, value
);
307 return print_record(device
);
310 assert_not_reached("unknown query type");
314 static int help(void) {
315 printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
316 "Query sysfs or the udev database.\n\n"
317 " -h --help Print this message\n"
318 " -V --version Print version of the program\n"
319 " -q --query=TYPE Query device information:\n"
320 " name Name of device node\n"
321 " symlink Pointing to node\n"
322 " path sysfs device path\n"
323 " property The device properties\n"
325 " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
326 " -n --name=NAME Node or symlink name used for query or attribute walk\n"
327 " -r --root Prepend dev directory to path names\n"
328 " -a --attribute-walk Print all key matches walking along the chain\n"
329 " of parent devices\n"
330 " -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
331 " -x --export Export key/value pairs\n"
332 " -P --export-prefix Export the key name with a prefix\n"
333 " -e --export-db Export the content of the udev database\n"
334 " -c --cleanup-db Clean up the udev database\n"
335 , program_invocation_short_name
);
340 int info_main(int argc
, char *argv
[], void *userdata
) {
341 _cleanup_strv_free_
char **devices
= NULL
;
342 _cleanup_free_
char *name
= NULL
;
345 static const struct option options
[] = {
346 { "name", required_argument
, NULL
, 'n' },
347 { "path", required_argument
, NULL
, 'p' },
348 { "query", required_argument
, NULL
, 'q' },
349 { "attribute-walk", no_argument
, NULL
, 'a' },
350 { "cleanup-db", no_argument
, NULL
, 'c' },
351 { "export-db", no_argument
, NULL
, 'e' },
352 { "root", no_argument
, NULL
, 'r' },
353 { "device-id-of-file", required_argument
, NULL
, 'd' },
354 { "export", no_argument
, NULL
, 'x' },
355 { "export-prefix", required_argument
, NULL
, 'P' },
356 { "version", no_argument
, NULL
, 'V' },
357 { "help", no_argument
, NULL
, 'h' },
361 ActionType action
= ACTION_QUERY
;
362 QueryType query
= QUERY_ALL
;
364 while ((c
= getopt_long(argc
, argv
, "aced:n:p:q:rxP:RVh", options
, NULL
)) >= 0)
368 const char *prefix
= c
== 'n' ? "/dev/" : "/sys/";
371 path
= path_join(path_startswith(optarg
, prefix
) ? NULL
: prefix
, optarg
);
375 r
= strv_consume(&devices
, path
);
382 action
= ACTION_QUERY
;
383 if (streq(optarg
, "property") || streq(optarg
, "env"))
384 query
= QUERY_PROPERTY
;
385 else if (streq(optarg
, "name"))
387 else if (streq(optarg
, "symlink"))
388 query
= QUERY_SYMLINK
;
389 else if (streq(optarg
, "path"))
391 else if (streq(optarg
, "all"))
394 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "unknown query type");
400 action
= ACTION_DEVICE_ID_FILE
;
401 r
= free_and_strdup(&name
, optarg
);
406 action
= ACTION_ATTRIBUTE_WALK
;
409 return export_devices();
418 arg_export_prefix
= optarg
;
421 return print_version();
427 assert_not_reached("Unknown option");
430 if (action
== ACTION_DEVICE_ID_FILE
) {
432 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
433 "Positional arguments are not allowed with -d/--device-id-of-file.");
435 return stat_device(name
, arg_export
, arg_export_prefix
);
438 r
= strv_extend_strv(&devices
, argv
+ optind
, false);
440 return log_error_errno(r
, "Failed to build argument list: %m");
442 if (strv_isempty(devices
))
443 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
444 "A device name or path is required");
445 if (action
== ACTION_ATTRIBUTE_WALK
&& strv_length(devices
) > 1)
446 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
447 "Only one device may be specified with -a/--attribute-walk");
450 STRV_FOREACH(p
, devices
) {
451 _cleanup_(sd_device_unrefp
) sd_device
*device
= NULL
;
453 r
= find_device(*p
, NULL
, &device
);
455 return log_error_errno(r
, "Bad argument \"%s\", expected an absolute path in /dev/ or /sys or a unit name: %m", *p
);
457 return log_error_errno(r
, "Unknown device \"%s\": %m", *p
);
459 if (action
== ACTION_QUERY
)
460 r
= query_device(query
, device
);
461 else if (action
== ACTION_ATTRIBUTE_WALK
)
462 r
= print_device_chain(device
);
464 assert_not_reached("Unknown action");