1 /* SPDX-License-Identifier: GPL-2.0-or-later */
12 #include "sd-device.h"
14 #include "alloc-util.h"
15 #include "device-enumerator-private.h"
16 #include "device-private.h"
17 #include "device-util.h"
18 #include "dirent-util.h"
20 #include "sort-util.h"
21 #include "static-destruct.h"
22 #include "string-table.h"
23 #include "string-util.h"
24 #include "udev-util.h"
25 #include "udevadm-util.h"
28 typedef enum ActionType
{
30 ACTION_ATTRIBUTE_WALK
,
31 ACTION_DEVICE_ID_FILE
,
34 typedef enum QueryType
{
42 static char **arg_properties
= NULL
;
43 static bool arg_root
= false;
44 static bool arg_export
= false;
45 static bool arg_value
= false;
46 static const char *arg_export_prefix
= NULL
;
47 static usec_t arg_wait_for_initialization_timeout
= 0;
49 static bool skip_attribute(const char *name
) {
50 /* Those are either displayed separately or should not be shown at all. */
51 return STR_IN_SET(name
,
61 typedef struct SysAttr
{
66 STATIC_DESTRUCTOR_REGISTER(arg_properties
, strv_freep
);
68 static int sysattr_compare(const SysAttr
*a
, const SysAttr
*b
) {
69 return strcmp(a
->name
, b
->name
);
72 static int print_all_attributes(sd_device
*device
, bool is_parent
) {
73 _cleanup_free_ SysAttr
*sysattrs
= NULL
;
74 const char *name
, *value
;
78 (void) sd_device_get_devpath(device
, &value
);
79 printf(" looking at %sdevice '%s':\n", is_parent
? "parent " : "", strempty(value
));
82 (void) sd_device_get_sysname(device
, &value
);
83 printf(" %s==\"%s\"\n", is_parent
? "KERNELS" : "KERNEL", strempty(value
));
86 (void) sd_device_get_subsystem(device
, &value
);
87 printf(" %s==\"%s\"\n", is_parent
? "SUBSYSTEMS" : "SUBSYSTEM", strempty(value
));
90 (void) sd_device_get_driver(device
, &value
);
91 printf(" %s==\"%s\"\n", is_parent
? "DRIVERS" : "DRIVER", strempty(value
));
93 FOREACH_DEVICE_SYSATTR(device
, name
) {
96 if (skip_attribute(name
))
99 if (sd_device_get_sysattr_value(device
, name
, &value
) < 0)
102 /* skip any values that look like a path */
106 /* skip nonprintable attributes */
108 while (len
> 0 && isprint((unsigned char) value
[len
-1]))
113 if (!GREEDY_REALLOC(sysattrs
, n_items
+ 1))
116 sysattrs
[n_items
] = (SysAttr
) {
123 typesafe_qsort(sysattrs
, n_items
, sysattr_compare
);
125 for (size_t i
= 0; i
< n_items
; i
++)
126 printf(" %s{%s}==\"%s\"\n", is_parent
? "ATTRS" : "ATTR", sysattrs
[i
].name
, sysattrs
[i
].value
);
133 static int print_device_chain(sd_device
*device
) {
134 sd_device
*child
, *parent
;
138 "Udevadm info starts with the device specified by the devpath and then\n"
139 "walks up the chain of parent devices. It prints for every device\n"
140 "found, all possible attributes in the udev rules key format.\n"
141 "A rule to match, can be composed by the attributes of the device\n"
142 "and the attributes from one single parent device.\n"
145 r
= print_all_attributes(device
, false);
149 for (child
= device
; sd_device_get_parent(child
, &parent
) >= 0; child
= parent
) {
150 r
= print_all_attributes(parent
, true);
158 static int print_record(sd_device
*device
) {
159 const char *str
, *val
;
162 (void) sd_device_get_devpath(device
, &str
);
163 printf("P: %s\n", str
);
165 if (sd_device_get_devname(device
, &str
) >= 0) {
166 assert_se(val
= path_startswith(str
, "/dev/"));
167 printf("N: %s\n", val
);
170 if (device_get_devlink_priority(device
, &i
) >= 0)
171 printf("L: %i\n", i
);
173 FOREACH_DEVICE_DEVLINK(device
, str
) {
174 assert_se(val
= path_startswith(str
, "/dev/"));
175 printf("S: %s\n", val
);
178 FOREACH_DEVICE_PROPERTY(device
, str
, val
)
179 printf("E: %s=%s\n", str
, val
);
185 static int stat_device(const char *name
, bool export
, const char *prefix
) {
188 if (stat(name
, &statbuf
) != 0)
194 printf("%sMAJOR=%u\n"
196 prefix
, major(statbuf
.st_dev
),
197 prefix
, minor(statbuf
.st_dev
));
199 printf("%u:%u\n", major(statbuf
.st_dev
), minor(statbuf
.st_dev
));
203 static int export_devices(void) {
204 _cleanup_(sd_device_enumerator_unrefp
) sd_device_enumerator
*e
= NULL
;
208 r
= sd_device_enumerator_new(&e
);
212 r
= sd_device_enumerator_allow_uninitialized(e
);
214 return log_error_errno(r
, "Failed to set allowing uninitialized flag: %m");
216 r
= device_enumerator_scan_devices(e
);
218 return log_error_errno(r
, "Failed to scan devices: %m");
220 FOREACH_DEVICE_AND_SUBSYSTEM(e
, d
)
221 (void) print_record(d
);
226 static void cleanup_dir(DIR *dir
, mode_t mask
, int depth
) {
232 FOREACH_DIRENT_ALL(dent
, dir
, break) {
235 if (dent
->d_name
[0] == '.')
237 if (fstatat(dirfd(dir
), dent
->d_name
, &stats
, AT_SYMLINK_NOFOLLOW
) != 0)
239 if ((stats
.st_mode
& mask
) != 0)
241 if (S_ISDIR(stats
.st_mode
)) {
242 _cleanup_closedir_
DIR *dir2
= NULL
;
244 dir2
= fdopendir(openat(dirfd(dir
), dent
->d_name
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
));
246 cleanup_dir(dir2
, mask
, depth
-1);
248 (void) unlinkat(dirfd(dir
), dent
->d_name
, AT_REMOVEDIR
);
250 (void) unlinkat(dirfd(dir
), dent
->d_name
, 0);
254 static void cleanup_db(void) {
255 _cleanup_closedir_
DIR *dir1
= NULL
, *dir2
= NULL
, *dir3
= NULL
, *dir4
= NULL
, *dir5
= NULL
;
257 dir1
= opendir("/run/udev/data");
259 cleanup_dir(dir1
, S_ISVTX
, 1);
261 dir2
= opendir("/run/udev/links");
263 cleanup_dir(dir2
, 0, 2);
265 dir3
= opendir("/run/udev/tags");
267 cleanup_dir(dir3
, 0, 2);
269 dir4
= opendir("/run/udev/static_node-tags");
271 cleanup_dir(dir4
, 0, 2);
273 dir5
= opendir("/run/udev/watch");
275 cleanup_dir(dir5
, 0, 1);
278 static int query_device(QueryType query
, sd_device
* device
) {
287 r
= sd_device_get_devname(device
, &node
);
289 return log_error_errno(r
, "No device node found: %m");
292 assert_se(node
= path_startswith(node
, "/dev/"));
293 printf("%s\n", node
);
297 case QUERY_SYMLINK
: {
298 const char *devlink
, *prefix
= "";
300 FOREACH_DEVICE_DEVLINK(device
, devlink
) {
302 assert_se(devlink
= path_startswith(devlink
, "/dev/"));
303 printf("%s%s", prefix
, devlink
);
313 r
= sd_device_get_devpath(device
, &devpath
);
315 return log_error_errno(r
, "Failed to get device path: %m");
317 printf("%s\n", devpath
);
321 case QUERY_PROPERTY
: {
322 const char *key
, *value
;
324 FOREACH_DEVICE_PROPERTY(device
, key
, value
) {
325 if (arg_properties
&& !strv_contains(arg_properties
, key
))
329 printf("%s%s='%s'\n", strempty(arg_export_prefix
), key
, value
);
331 printf("%s\n", value
);
333 printf("%s=%s\n", key
, value
);
340 return print_record(device
);
343 assert_not_reached();
347 static int help(void) {
348 printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
349 "Query sysfs or the udev database.\n\n"
350 " -h --help Print this message\n"
351 " -V --version Print version of the program\n"
352 " -q --query=TYPE Query device information:\n"
353 " name Name of device node\n"
354 " symlink Pointing to node\n"
355 " path sysfs device path\n"
356 " property The device properties\n"
358 " --property=NAME Show only properties by this name\n"
359 " --value When showing properties, print only their values\n"
360 " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
361 " -n --name=NAME Node or symlink name used for query or attribute walk\n"
362 " -r --root Prepend dev directory to path names\n"
363 " -a --attribute-walk Print all key matches walking along the chain\n"
364 " of parent devices\n"
365 " -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
366 " -x --export Export key/value pairs\n"
367 " -P --export-prefix Export the key name with a prefix\n"
368 " -e --export-db Export the content of the udev database\n"
369 " -c --cleanup-db Clean up the udev database\n"
370 " -w --wait-for-initialization[=SECONDS]\n"
371 " Wait for device to be initialized\n",
372 program_invocation_short_name
);
377 int info_main(int argc
, char *argv
[], void *userdata
) {
378 _cleanup_strv_free_
char **devices
= NULL
;
379 _cleanup_free_
char *name
= NULL
;
383 ARG_PROPERTY
= 0x100,
387 static const struct option options
[] = {
388 { "attribute-walk", no_argument
, NULL
, 'a' },
389 { "cleanup-db", no_argument
, NULL
, 'c' },
390 { "device-id-of-file", required_argument
, NULL
, 'd' },
391 { "export", no_argument
, NULL
, 'x' },
392 { "export-db", no_argument
, NULL
, 'e' },
393 { "export-prefix", required_argument
, NULL
, 'P' },
394 { "help", no_argument
, NULL
, 'h' },
395 { "name", required_argument
, NULL
, 'n' },
396 { "path", required_argument
, NULL
, 'p' },
397 { "property", required_argument
, NULL
, ARG_PROPERTY
},
398 { "query", required_argument
, NULL
, 'q' },
399 { "root", no_argument
, NULL
, 'r' },
400 { "value", no_argument
, NULL
, ARG_VALUE
},
401 { "version", no_argument
, NULL
, 'V' },
402 { "wait-for-initialization", optional_argument
, NULL
, 'w' },
406 ActionType action
= ACTION_QUERY
;
407 QueryType query
= QUERY_ALL
;
409 while ((c
= getopt_long(argc
, argv
, "aced:n:p:q:rxP:w::Vh", options
, NULL
)) >= 0)
412 /* Make sure that if the empty property list was specified, we won't show any
414 if (isempty(optarg
) && !arg_properties
) {
415 arg_properties
= new0(char*, 1);
419 r
= strv_split_and_extend(&arg_properties
, optarg
, ",", true);
429 const char *prefix
= c
== 'n' ? "/dev/" : "/sys/";
432 path
= path_join(path_startswith(optarg
, prefix
) ? NULL
: prefix
, optarg
);
436 r
= strv_consume(&devices
, path
);
443 action
= ACTION_QUERY
;
444 if (streq(optarg
, "property") || streq(optarg
, "env"))
445 query
= QUERY_PROPERTY
;
446 else if (streq(optarg
, "name"))
448 else if (streq(optarg
, "symlink"))
449 query
= QUERY_SYMLINK
;
450 else if (streq(optarg
, "path"))
452 else if (streq(optarg
, "all"))
455 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "unknown query type");
461 action
= ACTION_DEVICE_ID_FILE
;
462 r
= free_and_strdup(&name
, optarg
);
467 action
= ACTION_ATTRIBUTE_WALK
;
470 return export_devices();
479 arg_export_prefix
= optarg
;
483 r
= parse_sec(optarg
, &arg_wait_for_initialization_timeout
);
485 return log_error_errno(r
, "Failed to parse timeout value: %m");
487 arg_wait_for_initialization_timeout
= USEC_INFINITY
;
490 return print_version();
496 assert_not_reached();
499 if (action
== ACTION_DEVICE_ID_FILE
) {
501 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
502 "Positional arguments are not allowed with -d/--device-id-of-file.");
504 return stat_device(name
, arg_export
, arg_export_prefix
);
507 r
= strv_extend_strv(&devices
, argv
+ optind
, false);
509 return log_error_errno(r
, "Failed to build argument list: %m");
511 if (strv_isempty(devices
))
512 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
513 "A device name or path is required");
514 if (action
== ACTION_ATTRIBUTE_WALK
&& strv_length(devices
) > 1)
515 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
516 "Only one device may be specified with -a/--attribute-walk");
518 if (arg_export
&& arg_value
)
519 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
520 "-x/--export or -P/--export-prefix cannot be used with --value");
523 STRV_FOREACH(p
, devices
) {
524 _cleanup_(sd_device_unrefp
) sd_device
*device
= NULL
;
526 r
= find_device(*p
, NULL
, &device
);
528 return log_error_errno(r
, "Bad argument \"%s\", expected an absolute path in /dev/ or /sys or a unit name: %m", *p
);
530 return log_error_errno(r
, "Unknown device \"%s\": %m", *p
);
532 if (arg_wait_for_initialization_timeout
> 0) {
535 r
= device_wait_for_initialization(
538 usec_add(now(CLOCK_MONOTONIC
), arg_wait_for_initialization_timeout
),
543 sd_device_unref(device
);
547 if (action
== ACTION_QUERY
)
548 r
= query_device(query
, device
);
549 else if (action
== ACTION_ATTRIBUTE_WALK
)
550 r
= print_device_chain(device
);
552 assert_not_reached();