]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/udevadm-info.c
Merge pull request #10135 from yuwata/log-skipped-tests
[thirdparty/systemd.git] / src / udev / udevadm-info.c
CommitLineData
e7145211 1/* SPDX-License-Identifier: GPL-2.0+ */
be9b51f6 2
034f35d7 3#include <ctype.h>
87171e46 4#include <errno.h>
e6c1a2bd 5#include <fcntl.h>
07630cea
LP
6#include <getopt.h>
7#include <stddef.h>
8#include <stdio.h>
9#include <string.h>
492e76c9 10#include <sys/stat.h>
07630cea 11#include <unistd.h>
be9b51f6 12
8fb3f009 13#include "dirent-util.h"
3ffd4af2 14#include "fd-util.h"
07630cea 15#include "string-util.h"
07630cea 16#include "udev.h"
3d05193e 17#include "udevadm.h"
d6170d27 18#include "udevadm-util.h"
be9b51f6 19
9ec6e95b 20static bool skip_attribute(const char *name) {
46512cd9 21 static const char* const skip[] = {
912541b0
KS
22 "uevent",
23 "dev",
24 "modalias",
25 "resource",
26 "driver",
27 "subsystem",
28 "module",
29 };
30 unsigned int i;
31
8fef0ff2 32 for (i = 0; i < ELEMENTSOF(skip); i++)
090be865 33 if (streq(name, skip[i]))
912541b0
KS
34 return true;
35 return false;
20bee04c
KS
36}
37
9ec6e95b 38static void print_all_attributes(struct udev_device *device, const char *key) {
912541b0
KS
39 struct udev_list_entry *sysattr;
40
41 udev_list_entry_foreach(sysattr, udev_device_get_sysattr_list_entry(device)) {
42 const char *name;
43 const char *value;
44 size_t len;
45
46 name = udev_list_entry_get_name(sysattr);
47 if (skip_attribute(name))
48 continue;
49
50 value = udev_device_get_sysattr_value(device, name);
51 if (value == NULL)
52 continue;
912541b0
KS
53
54 /* skip any values that look like a path */
55 if (value[0] == '/')
56 continue;
57
58 /* skip nonprintable attributes */
59 len = strlen(value);
60 while (len > 0 && isprint(value[len-1]))
61 len--;
baa30fbc 62 if (len > 0)
912541b0 63 continue;
912541b0
KS
64
65 printf(" %s{%s}==\"%s\"\n", key, name, value);
66 }
67 printf("\n");
be9b51f6
KS
68}
69
9ec6e95b 70static int print_device_chain(struct udev_device *device) {
912541b0
KS
71 struct udev_device *device_parent;
72 const char *str;
73
74 printf("\n"
75 "Udevadm info starts with the device specified by the devpath and then\n"
76 "walks up the chain of parent devices. It prints for every device\n"
77 "found, all possible attributes in the udev rules key format.\n"
78 "A rule to match, can be composed by the attributes of the device\n"
79 "and the attributes from one single parent device.\n"
80 "\n");
81
82 printf(" looking at device '%s':\n", udev_device_get_devpath(device));
83 printf(" KERNEL==\"%s\"\n", udev_device_get_sysname(device));
84 str = udev_device_get_subsystem(device);
85 if (str == NULL)
86 str = "";
87 printf(" SUBSYSTEM==\"%s\"\n", str);
88 str = udev_device_get_driver(device);
89 if (str == NULL)
90 str = "";
91 printf(" DRIVER==\"%s\"\n", str);
92 print_all_attributes(device, "ATTR");
93
94 device_parent = device;
95 do {
96 device_parent = udev_device_get_parent(device_parent);
97 if (device_parent == NULL)
98 break;
99 printf(" looking at parent device '%s':\n", udev_device_get_devpath(device_parent));
100 printf(" KERNELS==\"%s\"\n", udev_device_get_sysname(device_parent));
101 str = udev_device_get_subsystem(device_parent);
102 if (str == NULL)
103 str = "";
104 printf(" SUBSYSTEMS==\"%s\"\n", str);
105 str = udev_device_get_driver(device_parent);
106 if (str == NULL)
107 str = "";
108 printf(" DRIVERS==\"%s\"\n", str);
109 print_all_attributes(device_parent, "ATTRS");
110 } while (device_parent != NULL);
111
112 return 0;
1aa1e248
KS
113}
114
9ec6e95b 115static void print_record(struct udev_device *device) {
912541b0
KS
116 const char *str;
117 int i;
118 struct udev_list_entry *list_entry;
119
120 printf("P: %s\n", udev_device_get_devpath(device));
121
912541b0
KS
122 str = udev_device_get_devnode(device);
123 if (str != NULL)
fbd0b64f 124 printf("N: %s\n", str + STRLEN("/dev/"));
912541b0
KS
125
126 i = udev_device_get_devlink_priority(device);
127 if (i != 0)
128 printf("L: %i\n", i);
129
6ada823a 130 udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device))
fbd0b64f
LP
131 printf("S: %s\n",
132 udev_list_entry_get_name(list_entry) + STRLEN("/dev/"));
912541b0
KS
133
134 udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device))
135 printf("E: %s=%s\n",
136 udev_list_entry_get_name(list_entry),
137 udev_list_entry_get_value(list_entry));
138 printf("\n");
31de3a2b
KS
139}
140
9ec6e95b 141static int stat_device(const char *name, bool export, const char *prefix) {
912541b0
KS
142 struct stat statbuf;
143
144 if (stat(name, &statbuf) != 0)
755700bb 145 return -errno;
912541b0
KS
146
147 if (export) {
148 if (prefix == NULL)
149 prefix = "INFO_";
1fa2f38f
ZJS
150 printf("%sMAJOR=%u\n"
151 "%sMINOR=%u\n",
912541b0
KS
152 prefix, major(statbuf.st_dev),
153 prefix, minor(statbuf.st_dev));
154 } else
1fa2f38f 155 printf("%u:%u\n", major(statbuf.st_dev), minor(statbuf.st_dev));
912541b0 156 return 0;
f338bac8
KS
157}
158
2024ed61 159static int export_devices(void) {
8e766630 160 _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *udev_enumerate;
912541b0
KS
161 struct udev_list_entry *list_entry;
162
2024ed61 163 udev_enumerate = udev_enumerate_new(NULL);
912541b0 164 if (udev_enumerate == NULL)
755700bb
ZJS
165 return -ENOMEM;
166
912541b0
KS
167 udev_enumerate_scan_devices(udev_enumerate);
168 udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
8e766630 169 _cleanup_(udev_device_unrefp) struct udev_device *device;
912541b0 170
2024ed61 171 device = udev_device_new_from_syspath(NULL, udev_list_entry_get_name(list_entry));
755700bb 172 if (device != NULL)
912541b0 173 print_record(device);
912541b0 174 }
755700bb 175
912541b0 176 return 0;
bf7ad0ea
KS
177}
178
9ec6e95b 179static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
912541b0
KS
180 struct dirent *dent;
181
182 if (depth <= 0)
183 return;
184
8fb3f009 185 FOREACH_DIRENT_ALL(dent, dir, break) {
912541b0
KS
186 struct stat stats;
187
188 if (dent->d_name[0] == '.')
189 continue;
190 if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0)
191 continue;
192 if ((stats.st_mode & mask) != 0)
193 continue;
194 if (S_ISDIR(stats.st_mode)) {
200c7fa6 195 _cleanup_closedir_ DIR *dir2;
912541b0
KS
196
197 dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
200c7fa6 198 if (dir2 != NULL)
912541b0 199 cleanup_dir(dir2, mask, depth-1);
200c7fa6
LP
200
201 (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
202 } else
203 (void) unlinkat(dirfd(dir), dent->d_name, 0);
912541b0 204 }
9ead6627
KS
205}
206
2024ed61 207static void cleanup_db(void) {
755700bb 208 _cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL, *dir5 = NULL;
912541b0 209
755700bb 210 (void) unlink("/run/udev/queue.bin");
912541b0 211
755700bb
ZJS
212 dir1 = opendir("/run/udev/data");
213 if (dir1 != NULL)
214 cleanup_dir(dir1, S_ISVTX, 1);
912541b0 215
755700bb
ZJS
216 dir2 = opendir("/run/udev/links");
217 if (dir2 != NULL)
218 cleanup_dir(dir2, 0, 2);
912541b0 219
755700bb
ZJS
220 dir3 = opendir("/run/udev/tags");
221 if (dir3 != NULL)
222 cleanup_dir(dir3, 0, 2);
912541b0 223
755700bb
ZJS
224 dir4 = opendir("/run/udev/static_node-tags");
225 if (dir4 != NULL)
226 cleanup_dir(dir4, 0, 2);
84b6ad70 227
755700bb
ZJS
228 dir5 = opendir("/run/udev/watch");
229 if (dir5 != NULL)
230 cleanup_dir(dir5, 0, 1);
9ead6627
KS
231}
232
ee4a776d 233static int help(void) {
5ac0162c
LP
234
235 printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
236 "Query sysfs or the udev database.\n\n"
237 " -h --help Print this message\n"
73527992 238 " -V --version Print version of the program\n"
5ac0162c
LP
239 " -q --query=TYPE Query device information:\n"
240 " name Name of device node\n"
241 " symlink Pointing to node\n"
242 " path sysfs device path\n"
243 " property The device properties\n"
244 " all All values\n"
245 " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
246 " -n --name=NAME Node or symlink name used for query or attribute walk\n"
247 " -r --root Prepend dev directory to path names\n"
248 " -a --attribute-walk Print all key matches walking along the chain\n"
249 " of parent devices\n"
250 " -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
251 " -x --export Export key/value pairs\n"
252 " -P --export-prefix Export the key name with a prefix\n"
253 " -e --export-db Export the content of the udev database\n"
254 " -c --cleanup-db Clean up the udev database\n"
255 , program_invocation_short_name);
ee4a776d
YW
256
257 return 0;
5ac0162c
LP
258}
259
3d05193e 260int info_main(int argc, char *argv[], void *userdata) {
8e766630 261 _cleanup_(udev_device_unrefp) struct udev_device *device = NULL;
912541b0
KS
262 bool root = 0;
263 bool export = 0;
264 const char *export_prefix = NULL;
912541b0
KS
265 char name[UTIL_PATH_SIZE];
266 struct udev_list_entry *list_entry;
ee4a776d 267 int c, r;
912541b0
KS
268
269 static const struct option options[] = {
7643ac9a
ZJS
270 { "name", required_argument, NULL, 'n' },
271 { "path", required_argument, NULL, 'p' },
272 { "query", required_argument, NULL, 'q' },
273 { "attribute-walk", no_argument, NULL, 'a' },
274 { "cleanup-db", no_argument, NULL, 'c' },
275 { "export-db", no_argument, NULL, 'e' },
276 { "root", no_argument, NULL, 'r' },
912541b0 277 { "device-id-of-file", required_argument, NULL, 'd' },
7643ac9a
ZJS
278 { "export", no_argument, NULL, 'x' },
279 { "export-prefix", required_argument, NULL, 'P' },
280 { "version", no_argument, NULL, 'V' },
281 { "help", no_argument, NULL, 'h' },
912541b0
KS
282 {}
283 };
284
285 enum action_type {
912541b0
KS
286 ACTION_QUERY,
287 ACTION_ATTRIBUTE_WALK,
912541b0 288 ACTION_DEVICE_ID_FILE,
4f5d327a 289 } action = ACTION_QUERY;
912541b0
KS
290
291 enum query_type {
912541b0
KS
292 QUERY_NAME,
293 QUERY_PATH,
294 QUERY_SYMLINK,
295 QUERY_PROPERTY,
296 QUERY_ALL,
4f5d327a 297 } query = QUERY_ALL;
912541b0 298
7643ac9a
ZJS
299 while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL)) >= 0)
300 switch (c) {
ee4a776d
YW
301 case 'n':
302 if (device) {
303 log_error("device already specified");
304 return -EINVAL;
912541b0 305 }
4f5d327a 306
2024ed61 307 device = find_device(optarg, "/dev/");
ee4a776d
YW
308 if (!device)
309 return log_error_errno(errno, "device node not found: %m");
912541b0
KS
310 break;
311 case 'p':
ee4a776d
YW
312 if (device) {
313 log_error("device already specified");
314 return -EINVAL;
912541b0 315 }
4f5d327a 316
2024ed61 317 device = find_device(optarg, "/sys");
ee4a776d
YW
318 if (!device)
319 return log_error_errno(errno, "syspath not found: %m");
912541b0
KS
320 break;
321 case 'q':
322 action = ACTION_QUERY;
44433ebd 323 if (streq(optarg, "property") || streq(optarg, "env"))
912541b0 324 query = QUERY_PROPERTY;
44433ebd 325 else if (streq(optarg, "name"))
912541b0 326 query = QUERY_NAME;
44433ebd 327 else if (streq(optarg, "symlink"))
912541b0 328 query = QUERY_SYMLINK;
44433ebd 329 else if (streq(optarg, "path"))
912541b0 330 query = QUERY_PATH;
44433ebd 331 else if (streq(optarg, "all"))
912541b0 332 query = QUERY_ALL;
44433ebd 333 else {
ee4a776d
YW
334 log_error("unknown query type");
335 return -EINVAL;
912541b0
KS
336 }
337 break;
338 case 'r':
912541b0
KS
339 root = true;
340 break;
912541b0
KS
341 case 'd':
342 action = ACTION_DEVICE_ID_FILE;
d5a89d7d 343 strscpy(name, sizeof(name), optarg);
912541b0
KS
344 break;
345 case 'a':
346 action = ACTION_ATTRIBUTE_WALK;
347 break;
348 case 'e':
ee4a776d 349 return export_devices();
912541b0 350 case 'c':
2024ed61 351 cleanup_db();
44433ebd 352 return 0;
912541b0
KS
353 case 'x':
354 export = true;
355 break;
356 case 'P':
357 export_prefix = optarg;
358 break;
359 case 'V':
ee4a776d 360 return version();
912541b0 361 case 'h':
ee4a776d
YW
362 return help();
363 case '?':
364 return -EINVAL;
912541b0 365 default:
ee4a776d 366 assert_not_reached("Unknown option");
912541b0 367 }
912541b0
KS
368
369 switch (action) {
370 case ACTION_QUERY:
4f5d327a
KS
371 if (!device) {
372 if (!argv[optind]) {
5ac0162c 373 help();
ee4a776d 374 return -EINVAL;
4f5d327a 375 }
2024ed61 376 device = find_device(argv[optind], NULL);
4f5d327a 377 if (!device) {
ee4a776d
YW
378 log_error("Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.");
379 return -EINVAL;
4f5d327a 380 }
912541b0
KS
381 }
382
383 switch(query) {
384 case QUERY_NAME: {
385 const char *node = udev_device_get_devnode(device);
386
ee4a776d
YW
387 if (!node)
388 return log_error_errno(errno, "no device node found");
912541b0 389
6ada823a 390 if (root)
912541b0 391 printf("%s\n", udev_device_get_devnode(device));
6ada823a 392 else
fbd0b64f
LP
393 printf("%s\n",
394 udev_device_get_devnode(device) + STRLEN("/dev/"));
912541b0
KS
395 break;
396 }
397 case QUERY_SYMLINK:
398 list_entry = udev_device_get_devlinks_list_entry(device);
ee4a776d 399 while (list_entry) {
6ada823a 400 if (root)
912541b0 401 printf("%s", udev_list_entry_get_name(list_entry));
6ada823a 402 else
fbd0b64f
LP
403 printf("%s",
404 udev_list_entry_get_name(list_entry) + STRLEN("/dev/"));
912541b0 405 list_entry = udev_list_entry_get_next(list_entry);
ee4a776d 406 if (list_entry)
912541b0
KS
407 printf(" ");
408 }
409 printf("\n");
410 break;
411 case QUERY_PATH:
412 printf("%s\n", udev_device_get_devpath(device));
44433ebd 413 return 0;
912541b0
KS
414 case QUERY_PROPERTY:
415 list_entry = udev_device_get_properties_list_entry(device);
ee4a776d 416 while (list_entry) {
19a29798
ZJS
417 if (export)
418 printf("%s%s='%s'\n", strempty(export_prefix),
912541b0
KS
419 udev_list_entry_get_name(list_entry),
420 udev_list_entry_get_value(list_entry));
19a29798 421 else
912541b0 422 printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
19a29798 423
912541b0
KS
424 list_entry = udev_list_entry_get_next(list_entry);
425 }
426 break;
427 case QUERY_ALL:
428 print_record(device);
429 break;
430 default:
bdd13f6b 431 assert_not_reached("unknown query type");
912541b0
KS
432 }
433 break;
434 case ACTION_ATTRIBUTE_WALK:
4f5d327a 435 if (!device && argv[optind]) {
2024ed61 436 device = find_device(argv[optind], NULL);
4f5d327a 437 if (!device) {
ee4a776d
YW
438 log_error("Unknown device, absolute path in /dev/ or /sys expected.");
439 return -EINVAL;
4f5d327a
KS
440 }
441 }
442 if (!device) {
ee4a776d
YW
443 log_error("Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.");
444 return -EINVAL;
912541b0
KS
445 }
446 print_device_chain(device);
447 break;
448 case ACTION_DEVICE_ID_FILE:
ee4a776d
YW
449 r = stat_device(name, export, export_prefix);
450 if (r < 0)
451 return r;
912541b0 452 break;
912541b0 453 }
87171e46 454
44433ebd 455 return 0;
87171e46 456}