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 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
;
255 bool root
= false, export
= false;
256 _cleanup_free_
char *name
= NULL
;
257 const char *export_prefix
= NULL
;
260 static const struct option options
[] = {
261 { "name", required_argument
, NULL
, 'n' },
262 { "path", required_argument
, NULL
, 'p' },
263 { "query", required_argument
, NULL
, 'q' },
264 { "attribute-walk", no_argument
, NULL
, 'a' },
265 { "cleanup-db", no_argument
, NULL
, 'c' },
266 { "export-db", no_argument
, NULL
, 'e' },
267 { "root", no_argument
, NULL
, 'r' },
268 { "device-id-of-file", required_argument
, NULL
, 'd' },
269 { "export", no_argument
, NULL
, 'x' },
270 { "export-prefix", required_argument
, NULL
, 'P' },
271 { "version", no_argument
, NULL
, 'V' },
272 { "help", no_argument
, NULL
, 'h' },
278 ACTION_ATTRIBUTE_WALK
,
279 ACTION_DEVICE_ID_FILE
,
280 } action
= ACTION_QUERY
;
290 while ((c
= getopt_long(argc
, argv
, "aced:n:p:q:rxP:RVh", options
, NULL
)) >= 0)
294 log_error("device already specified");
298 r
= find_device(optarg
, "/dev/", &device
);
300 return log_error_errno(r
, "device node not found: %m");
304 log_error("device already specified");
308 r
= find_device(optarg
, "/sys", &device
);
310 return log_error_errno(r
, "syspath not found: %m");
313 action
= ACTION_QUERY
;
314 if (streq(optarg
, "property") || streq(optarg
, "env"))
315 query
= QUERY_PROPERTY
;
316 else if (streq(optarg
, "name"))
318 else if (streq(optarg
, "symlink"))
319 query
= QUERY_SYMLINK
;
320 else if (streq(optarg
, "path"))
322 else if (streq(optarg
, "all"))
325 log_error("unknown query type");
333 action
= ACTION_DEVICE_ID_FILE
;
334 r
= free_and_strdup(&name
, optarg
);
339 action
= ACTION_ATTRIBUTE_WALK
;
342 return export_devices();
350 export_prefix
= optarg
;
353 return print_version();
359 assert_not_reached("Unknown option");
369 r
= find_device(argv
[optind
], NULL
, &device
);
371 return log_error_errno(r
, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected: %m");
378 r
= sd_device_get_devname(device
, &node
);
380 return log_error_errno(r
, "no device node found: %m");
383 printf("%s\n", node
);
385 printf("%s\n", node
+ STRLEN("/dev/"));
388 case QUERY_SYMLINK
: {
392 FOREACH_DEVICE_DEVLINK(device
, devlink
) {
396 printf("%s", devlink
);
398 printf("%s", devlink
+ STRLEN("/dev/"));
408 r
= sd_device_get_devpath(device
, &devpath
);
410 return log_error_errno(r
, "Failed to get device path: %m");
412 printf("%s\n", devpath
);
415 case QUERY_PROPERTY
: {
416 const char *key
, *value
;
418 FOREACH_DEVICE_PROPERTY(device
, key
, value
)
420 printf("%s%s='%s'\n", strempty(export_prefix
), key
, value
);
422 printf("%s=%s\n", key
, value
);
427 print_record(device
);
430 assert_not_reached("unknown query type");
433 case ACTION_ATTRIBUTE_WALK
:
434 if (!device
&& argv
[optind
]) {
435 r
= find_device(argv
[optind
], NULL
, &device
);
437 return log_error_errno(r
, "Unknown device, absolute path in /dev/ or /sys expected: %m");
440 log_error("Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.");
443 print_device_chain(device
);
445 case ACTION_DEVICE_ID_FILE
:
446 r
= stat_device(name
, export
, export_prefix
);