]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udevadm-info.c
e3bc4c4858bf8f5cd7eb150bd44026ca70796094
[thirdparty/systemd.git] / src / udev / udevadm-info.c
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2
3 #include <ctype.h>
4 #include <fcntl.h>
5 #include <getopt.h>
6 #include <stdio.h>
7 #include <sys/stat.h>
8 #include <sys/sysmacros.h>
9 #include <unistd.h>
10
11 #include "sd-device.h"
12 #include "sd-json.h"
13
14 #include "alloc-util.h"
15 #include "ansi-color.h"
16 #include "device-enumerator-private.h"
17 #include "device-private.h"
18 #include "device-util.h"
19 #include "dirent-util.h"
20 #include "errno-util.h"
21 #include "fd-util.h"
22 #include "fileio.h"
23 #include "glyph-util.h"
24 #include "pager.h"
25 #include "parse-argument.h"
26 #include "sort-util.h"
27 #include "static-destruct.h"
28 #include "string-table.h"
29 #include "string-util.h"
30 #include "strv.h"
31 #include "time-util.h"
32 #include "udev-util.h"
33 #include "udevadm.h"
34 #include "udevadm-util.h"
35
36 typedef enum ActionType {
37 ACTION_QUERY,
38 ACTION_ATTRIBUTE_WALK,
39 ACTION_DEVICE_ID_FILE,
40 ACTION_TREE,
41 ACTION_EXPORT,
42 ACTION_CLEANUP_DB,
43 } ActionType;
44
45 typedef enum QueryType {
46 QUERY_NAME,
47 QUERY_PATH,
48 QUERY_SYMLINK,
49 QUERY_PROPERTY,
50 QUERY_ALL,
51 _QUERY_TYPE_MAX,
52 _QUERY_TYPE_INVALID = -EINVAL,
53 } QueryType;
54
55 static char **arg_properties = NULL;
56 static bool arg_root = false;
57 static bool arg_export = false;
58 static bool arg_value = false;
59 static const char *arg_export_prefix = NULL;
60 static usec_t arg_wait_for_initialization_timeout = 0;
61 static PagerFlags arg_pager_flags = 0;
62 static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
63 static ActionType arg_action_type = ACTION_QUERY;
64 static QueryType arg_query = QUERY_ALL;
65 static char **arg_devices = NULL;
66 static char *arg_name = NULL;
67 static char **arg_attr_match = NULL;
68 static char **arg_attr_nomatch = NULL;
69 static char **arg_name_match = NULL;
70 static char **arg_parent_match = NULL;
71 static char **arg_property_match = NULL;
72 static char **arg_subsystem_match = NULL;
73 static char **arg_subsystem_nomatch = NULL;
74 static char **arg_sysname_match = NULL;
75 static char **arg_tag_match = NULL;
76 static int arg_initialized_match = -1;
77
78 STATIC_DESTRUCTOR_REGISTER(arg_properties, strv_freep);
79 STATIC_DESTRUCTOR_REGISTER(arg_devices, strv_freep);
80 STATIC_DESTRUCTOR_REGISTER(arg_name, freep);
81 STATIC_DESTRUCTOR_REGISTER(arg_attr_match, strv_freep);
82 STATIC_DESTRUCTOR_REGISTER(arg_attr_nomatch, strv_freep);
83 STATIC_DESTRUCTOR_REGISTER(arg_name_match, strv_freep);
84 STATIC_DESTRUCTOR_REGISTER(arg_parent_match, strv_freep);
85 STATIC_DESTRUCTOR_REGISTER(arg_property_match, strv_freep);
86 STATIC_DESTRUCTOR_REGISTER(arg_subsystem_match, strv_freep);
87 STATIC_DESTRUCTOR_REGISTER(arg_subsystem_nomatch, strv_freep);
88 STATIC_DESTRUCTOR_REGISTER(arg_sysname_match, strv_freep);
89 STATIC_DESTRUCTOR_REGISTER(arg_tag_match, strv_freep);
90
91 /* Put a limit on --tree descent level to not exhaust our stack */
92 #define TREE_DEPTH_MAX 64
93
94 static const char *query_type_table[_QUERY_TYPE_MAX] = {
95 [QUERY_NAME] = "name",
96 [QUERY_PATH] = "path",
97 [QUERY_SYMLINK] = "symlink",
98 [QUERY_PROPERTY] = "property",
99 [QUERY_ALL] = "all",
100 };
101
102 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(query_type, QueryType);
103
104 static bool skip_attribute(const char *name) {
105 assert(name);
106
107 /* Those are either displayed separately or should not be shown at all. */
108 return STR_IN_SET(name,
109 "uevent",
110 "dev",
111 "modalias",
112 "resource",
113 "driver",
114 "subsystem",
115 "module");
116 }
117
118 typedef struct SysAttr {
119 const char *name;
120 const char *value;
121 } SysAttr;
122
123 static int sysattr_compare(const SysAttr *a, const SysAttr *b) {
124 assert(a);
125 assert(b);
126
127 return strcmp(a->name, b->name);
128 }
129
130 static int print_all_attributes(sd_device *device, bool is_parent) {
131 _cleanup_free_ SysAttr *sysattrs = NULL;
132 const char *value;
133 size_t n_items = 0;
134 int r;
135
136 assert(device);
137
138 if (is_parent)
139 puts("");
140
141 value = NULL;
142 (void) sd_device_get_devpath(device, &value);
143 printf(" looking at %sdevice '%s':\n", is_parent ? "parent " : "", strempty(value));
144
145 value = NULL;
146 (void) sd_device_get_sysname(device, &value);
147 printf(" %s==\"%s\"\n", is_parent ? "KERNELS" : "KERNEL", strempty(value));
148
149 value = NULL;
150 (void) sd_device_get_subsystem(device, &value);
151 printf(" %s==\"%s\"\n", is_parent ? "SUBSYSTEMS" : "SUBSYSTEM", strempty(value));
152
153 value = NULL;
154 (void) sd_device_get_driver(device, &value);
155 printf(" %s==\"%s\"\n", is_parent ? "DRIVERS" : "DRIVER", strempty(value));
156
157 FOREACH_DEVICE_SYSATTR(device, name) {
158 size_t len;
159
160 if (skip_attribute(name))
161 continue;
162
163 r = sd_device_get_sysattr_value(device, name, &value);
164 if (r >= 0) {
165 /* skip any values that look like a path */
166 if (value[0] == '/')
167 continue;
168
169 /* skip nonprintable attributes */
170 len = strlen(value);
171 while (len > 0 && isprint((unsigned char) value[len-1]))
172 len--;
173 if (len > 0)
174 continue;
175
176 } else if (ERRNO_IS_PRIVILEGE(r))
177 value = "(not readable)";
178 else
179 continue;
180
181 if (!GREEDY_REALLOC(sysattrs, n_items + 1))
182 return log_oom();
183
184 sysattrs[n_items] = (SysAttr) {
185 .name = name,
186 .value = value,
187 };
188 n_items++;
189 }
190
191 typesafe_qsort(sysattrs, n_items, sysattr_compare);
192
193 FOREACH_ARRAY(i, sysattrs, n_items)
194 printf(" %s{%s}==\"%s\"\n", is_parent ? "ATTRS" : "ATTR", i->name, i->value);
195
196 return 0;
197 }
198
199 static int print_device_chain(sd_device *device) {
200 sd_device *child, *parent;
201 int r;
202
203 assert(device);
204
205 printf("\n"
206 "Udevadm info starts with the device specified by the devpath and then\n"
207 "walks up the chain of parent devices. It prints for every device\n"
208 "found, all possible attributes in the udev rules key format.\n"
209 "A rule to match, can be composed by the attributes of the device\n"
210 "and the attributes from one single parent device.\n"
211 "\n");
212
213 r = print_all_attributes(device, /* is_parent = */ false);
214 if (r < 0)
215 return r;
216
217 for (child = device; sd_device_get_parent(child, &parent) >= 0; child = parent) {
218 r = print_all_attributes(parent, /* is_parent = */ true);
219 if (r < 0)
220 return r;
221 }
222
223 return 0;
224 }
225
226 static int print_all_attributes_in_json(sd_device *device, bool is_parent) {
227 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *w = NULL;
228 _cleanup_free_ SysAttr *sysattrs = NULL;
229 const char *value;
230 size_t n_items = 0;
231 int r;
232
233 assert(device);
234
235 value = NULL;
236 (void) sd_device_get_devpath(device, &value);
237 r = sd_json_variant_set_field_string(&v, "DEVPATH", value);
238 if (r < 0)
239 return r;
240
241 value = NULL;
242 (void) sd_device_get_sysname(device, &value);
243 r = sd_json_variant_set_field_string(&v, is_parent ? "KERNELS" : "KERNEL", value);
244 if (r < 0)
245 return r;
246
247 value = NULL;
248 (void) sd_device_get_subsystem(device, &value);
249 r = sd_json_variant_set_field_string(&v, is_parent ? "SUBSYSTEMS" : "SUBSYSTEM", value);
250 if (r < 0)
251 return r;
252
253 value = NULL;
254 (void) sd_device_get_driver(device, &value);
255 r = sd_json_variant_set_field_string(&v, is_parent ? "DRIVERS" : "DRIVER", value);
256 if (r < 0)
257 return r;
258
259 FOREACH_DEVICE_SYSATTR(device, name) {
260 size_t len;
261
262 if (skip_attribute(name))
263 continue;
264
265 r = sd_device_get_sysattr_value(device, name, &value);
266 if (r >= 0) {
267 /* skip any values that look like a path */
268 if (value[0] == '/')
269 continue;
270
271 /* skip nonprintable attributes */
272 len = strlen(value);
273 while (len > 0 && isprint((unsigned char) value[len-1]))
274 len--;
275 if (len > 0)
276 continue;
277
278 } else if (ERRNO_IS_PRIVILEGE(r))
279 value = "(not readable)";
280 else
281 continue;
282
283 if (!GREEDY_REALLOC(sysattrs, n_items + 1))
284 return log_oom();
285
286 sysattrs[n_items] = (SysAttr) {
287 .name = name,
288 .value = value,
289 };
290 n_items++;
291 }
292
293 typesafe_qsort(sysattrs, n_items, sysattr_compare);
294
295 FOREACH_ARRAY(i, sysattrs, n_items) {
296 r = sd_json_variant_set_field_string(&w, i->name, i->value);
297 if (r < 0)
298 return r;
299 }
300
301 r = sd_json_variant_set_field(&v, is_parent ? "ATTRS" : "ATTR", w);
302 if (r < 0)
303 return r;
304
305 return sd_json_variant_dump(v, arg_json_format_flags, stdout, NULL);
306 }
307
308 static int print_device_chain_in_json(sd_device *device) {
309 sd_device *child, *parent;
310 int r;
311
312 assert(device);
313
314 arg_json_format_flags |=SD_JSON_FORMAT_SEQ;
315
316 r = print_all_attributes_in_json(device, /* is_parent = */ false);
317 if (r < 0)
318 return r;
319
320 for (child = device; sd_device_get_parent(child, &parent) >= 0; child = parent) {
321 r = print_all_attributes_in_json(parent, /* is_parent = */ true);
322 if (r < 0)
323 return r;
324 }
325
326 return 0;
327 }
328
329 static int print_record(sd_device *device, const char *prefix) {
330 const char *str, *subsys;
331 dev_t devnum;
332 uint64_t q;
333 int i, ifi;
334
335 assert(device);
336
337 prefix = strempty(prefix);
338
339 /* We don't show syspath here, because it's identical to devpath (modulo the "/sys" prefix).
340 *
341 * We don't show action/seqnum here because that only makes sense for records synthesized from
342 * uevents, not for those synthesized from database entries.
343 *
344 * We don't show sysattrs here, because they can be expensive and potentially issue expensive driver
345 * IO.
346 *
347 * Coloring: let's be conservative with coloring. Let's use it to group related fields. Right now:
348 *
349 * • white for fields that give the device a name
350 * • green for fields that categorize the device into subsystem/devtype and similar
351 * • cyan for fields about associated device nodes/symlinks/network interfaces and such
352 * • magenta for block device diskseq
353 * • yellow for driver info
354 * • no color for regular properties */
355
356 assert_se(sd_device_get_devpath(device, &str) >= 0);
357 printf("%sP: %s%s%s\n", prefix, ansi_highlight_white(), str, ansi_normal());
358
359 if (sd_device_get_sysname(device, &str) >= 0)
360 printf("%sM: %s%s%s\n", prefix, ansi_highlight_white(), str, ansi_normal());
361
362 if (sd_device_get_sysnum(device, &str) >= 0)
363 printf("%sR: %s%s%s\n", prefix, ansi_highlight_white(), str, ansi_normal());
364
365 if (sd_device_get_device_id(device, &str) >= 0)
366 printf("%sJ: %s%s%s\n", prefix, ansi_highlight_white(), str, ansi_normal());
367
368 if (sd_device_get_subsystem(device, &subsys) >= 0)
369 printf("%sU: %s%s%s\n", prefix, ansi_highlight_green(), subsys, ansi_normal());
370
371 if (sd_device_get_driver_subsystem(device, &str) >= 0)
372 printf("%sB: %s%s%s\n", prefix, ansi_highlight_green(), str, ansi_normal());
373
374 if (sd_device_get_devtype(device, &str) >= 0)
375 printf("%sT: %s%s%s\n", prefix, ansi_highlight_green(), str, ansi_normal());
376
377 if (sd_device_get_devnum(device, &devnum) >= 0)
378 printf("%sD: %s%c %u:%u%s\n",
379 prefix,
380 ansi_highlight_cyan(),
381 streq_ptr(subsys, "block") ? 'b' : 'c', major(devnum), minor(devnum),
382 ansi_normal());
383
384 if (sd_device_get_ifindex(device, &ifi) >= 0)
385 printf("%sI: %s%i%s\n", prefix, ansi_highlight_cyan(), ifi, ansi_normal());
386
387 if (sd_device_get_devname(device, &str) >= 0) {
388 const char *val;
389
390 assert_se(val = path_startswith(str, "/dev/"));
391 printf("%sN: %s%s%s\n", prefix, ansi_highlight_cyan(), val, ansi_normal());
392
393 if (device_get_devlink_priority(device, &i) >= 0)
394 printf("%sL: %s%i%s\n", prefix, ansi_highlight_cyan(), i, ansi_normal());
395
396 FOREACH_DEVICE_DEVLINK(device, link) {
397 assert_se(val = path_startswith(link, "/dev/"));
398 printf("%sS: %s%s%s\n", prefix, ansi_highlight_cyan(), val, ansi_normal());
399 }
400 }
401
402 if (sd_device_get_diskseq(device, &q) >= 0)
403 printf("%sQ: %s%" PRIu64 "%s\n", prefix, ansi_highlight_magenta(), q, ansi_normal());
404
405 if (sd_device_get_driver(device, &str) >= 0)
406 printf("%sV: %s%s%s\n", prefix, ansi_highlight_yellow4(), str, ansi_normal());
407
408 FOREACH_DEVICE_PROPERTY(device, key, val)
409 printf("%sE: %s=%s\n", prefix, key, val);
410
411 if (isempty(prefix))
412 puts("");
413 return 0;
414 }
415
416 static int record_to_json(sd_device *device, sd_json_variant **ret) {
417 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
418 const char *str;
419 int r;
420
421 assert(device);
422 assert(ret);
423
424 /* We don't show any shorthand fields here as done in print_record() except for SYSNAME, SYSNUM,
425 * DRIVER_SUBSYSTEM, and DEVICE_ID, as all the other ones have a matching property which will already
426 * be included. */
427
428 if (sd_device_get_sysname(device, &str) >= 0) {
429 r = sd_json_variant_set_field_string(&v, "SYSNAME", str);
430 if (r < 0)
431 return r;
432 }
433
434 if (sd_device_get_sysnum(device, &str) >= 0) {
435 r = sd_json_variant_set_field_string(&v, "SYSNUM", str);
436 if (r < 0)
437 return r;
438 }
439
440 if (sd_device_get_driver_subsystem(device, &str) >= 0) {
441 r = sd_json_variant_set_field_string(&v, "DRIVER_SUBSYSTEM", str);
442 if (r < 0)
443 return r;
444 }
445
446 if (sd_device_get_device_id(device, &str) >= 0) {
447 r = sd_json_variant_set_field_string(&v, "DEVICE_ID", str);
448 if (r < 0)
449 return r;
450 }
451
452 FOREACH_DEVICE_PROPERTY(device, key, val) {
453 r = sd_json_variant_set_field_string(&v, key, val);
454 if (r < 0)
455 return r;
456 }
457
458 *ret = TAKE_PTR(v);
459 return 0;
460 }
461
462 static int stat_device(void) {
463 struct stat statbuf;
464
465 assert(arg_name);
466
467 if (stat(arg_name, &statbuf) != 0)
468 return -errno;
469
470 if (arg_export) {
471 const char *prefix = arg_export_prefix ?: "INFO_";
472 printf("%sMAJOR=%u\n"
473 "%sMINOR=%u\n",
474 prefix, major(statbuf.st_dev),
475 prefix, minor(statbuf.st_dev));
476 } else
477 printf("%u:%u\n", major(statbuf.st_dev), minor(statbuf.st_dev));
478 return 0;
479 }
480
481 static int add_match_parent(sd_device_enumerator *e, const char *s, const char *prefix) {
482 _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
483 int r;
484
485 assert(e);
486 assert(s);
487
488 r = find_device(s, prefix, &dev);
489 if (r < 0)
490 return log_error_errno(r, "Failed to open the device '%s': %m", s);
491
492 r = device_enumerator_add_match_parent_incremental(e, dev);
493 if (r < 0)
494 return log_error_errno(r, "Failed to add parent match '%s': %m", s);
495
496 return 0;
497 }
498
499 static int setup_matches(sd_device_enumerator *e) {
500 int r;
501
502 assert(e);
503
504 STRV_FOREACH(n, arg_name_match) {
505 r = add_match_parent(e, *n, "/dev");
506 if (r < 0)
507 return r;
508 }
509
510 STRV_FOREACH(p, arg_parent_match) {
511 r = add_match_parent(e, *p, "/sys");
512 if (r < 0)
513 return r;
514 }
515
516 STRV_FOREACH(s, arg_subsystem_match) {
517 r = sd_device_enumerator_add_match_subsystem(e, *s, /* match= */ true);
518 if (r < 0)
519 return log_error_errno(r, "Failed to add subsystem match '%s': %m", *s);
520 }
521
522 STRV_FOREACH(s, arg_subsystem_nomatch) {
523 r = sd_device_enumerator_add_match_subsystem(e, *s, /* match= */ false);
524 if (r < 0)
525 return log_error_errno(r, "Failed to add negative subsystem match '%s': %m", *s);
526 }
527
528 STRV_FOREACH(a, arg_attr_match) {
529 _cleanup_free_ char *k = NULL, *v = NULL;
530
531 r = parse_key_value_argument(*a, /* require_value= */ true, &k, &v);
532 if (r < 0)
533 return r;
534
535 r = sd_device_enumerator_add_match_sysattr(e, k, v, /* match= */ true);
536 if (r < 0)
537 return log_error_errno(r, "Failed to add sysattr match '%s=%s': %m", k, v);
538 }
539
540 STRV_FOREACH(a, arg_attr_nomatch) {
541 _cleanup_free_ char *k = NULL, *v = NULL;
542
543 r = parse_key_value_argument(*a, /* require_value= */ true, &k, &v);
544 if (r < 0)
545 return r;
546
547 r = sd_device_enumerator_add_match_sysattr(e, k, v, /* match= */ false);
548 if (r < 0)
549 return log_error_errno(r, "Failed to add negative sysattr match '%s=%s': %m", k, v);
550 }
551
552 STRV_FOREACH(p, arg_property_match) {
553 _cleanup_free_ char *k = NULL, *v = NULL;
554
555 r = parse_key_value_argument(*p, /* require_value= */ true, &k, &v);
556 if (r < 0)
557 return r;
558
559 r = sd_device_enumerator_add_match_property_required(e, k, v);
560 if (r < 0)
561 return log_error_errno(r, "Failed to add property match '%s=%s': %m", k, v);
562 }
563
564 STRV_FOREACH(t, arg_tag_match) {
565 r = sd_device_enumerator_add_match_tag(e, *t);
566 if (r < 0)
567 return log_error_errno(r, "Failed to add tag match '%s': %m", *t);
568 }
569
570 STRV_FOREACH(s, arg_sysname_match) {
571 r = sd_device_enumerator_add_match_sysname(e, *s);
572 if (r < 0)
573 return log_error_errno(r, "Failed to add sysname match '%s': %m", *s);
574 }
575
576 if (arg_initialized_match != -1) {
577 r = device_enumerator_add_match_is_initialized(e, arg_initialized_match);
578 if (r < 0)
579 return log_error_errno(r, "Failed to set initialized filter: %m");
580 }
581
582 return 0;
583 }
584
585 static int export_devices(void) {
586 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
587 sd_device *d;
588 int r;
589
590 r = sd_device_enumerator_new(&e);
591 if (r < 0)
592 return log_error_errno(r, "Failed to create device enumerator: %m");
593
594 r = sd_device_enumerator_allow_uninitialized(e);
595 if (r < 0)
596 return log_error_errno(r, "Failed to allow uninitialized devices: %m");
597
598 r = setup_matches(e);
599 if (r < 0)
600 return r;
601
602 r = device_enumerator_scan_devices(e);
603 if (r < 0)
604 return log_error_errno(r, "Failed to scan devices: %m");
605
606 FOREACH_DEVICE_AND_SUBSYSTEM(e, d)
607 if (sd_json_format_enabled(arg_json_format_flags)) {
608 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
609
610 r = record_to_json(d, &v);
611 if (r < 0)
612 return r;
613
614 (void) sd_json_variant_dump(v, arg_json_format_flags, stdout, NULL);
615 } else
616 (void) print_record(d, NULL);
617
618 return 0;
619 }
620
621 static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
622 assert(dir);
623
624 if (depth <= 0)
625 return;
626
627 FOREACH_DIRENT_ALL(dent, dir, break) {
628 struct stat stats;
629
630 if (dot_or_dot_dot(dent->d_name))
631 continue;
632 if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) < 0)
633 continue;
634 if ((stats.st_mode & mask) != 0)
635 continue;
636 if (S_ISDIR(stats.st_mode)) {
637 _cleanup_closedir_ DIR *subdir = NULL;
638
639 subdir = xopendirat(dirfd(dir), dent->d_name, O_NOFOLLOW);
640 if (!subdir)
641 log_debug_errno(errno, "Failed to open subdirectory '%s', ignoring: %m", dent->d_name);
642 else
643 cleanup_dir(subdir, mask, depth-1);
644
645 (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
646 } else
647 (void) unlinkat(dirfd(dir), dent->d_name, 0);
648 }
649 }
650
651 /*
652 * Assume that dir is a directory with file names matching udev data base
653 * entries for devices in /run/udev/data (such as "b8:16"), and removes
654 * all files except those that haven't been deleted in /run/udev/data
655 * (i.e. they were skipped during db cleanup because of the db_persist flag).
656 */
657 static void cleanup_dir_after_db_cleanup(DIR *dir, DIR *datadir) {
658 assert(dir);
659 assert(datadir);
660
661 FOREACH_DIRENT_ALL(dent, dir, break) {
662 if (dot_or_dot_dot(dent->d_name))
663 continue;
664
665 if (faccessat(dirfd(datadir), dent->d_name, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
666 /* The corresponding udev database file still exists.
667 * Assuming the persistent flag is set for the database. */
668 continue;
669
670 (void) unlinkat(dirfd(dir), dent->d_name, 0);
671 }
672 }
673
674 static void cleanup_dirs_after_db_cleanup(DIR *dir, DIR *datadir) {
675 assert(dir);
676 assert(datadir);
677
678 FOREACH_DIRENT_ALL(dent, dir, break) {
679 struct stat stats;
680
681 if (dot_or_dot_dot(dent->d_name))
682 continue;
683 if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) < 0)
684 continue;
685 if (S_ISDIR(stats.st_mode)) {
686 _cleanup_closedir_ DIR *subdir = NULL;
687
688 subdir = xopendirat(dirfd(dir), dent->d_name, O_NOFOLLOW);
689 if (!subdir)
690 log_debug_errno(errno, "Failed to open subdirectory '%s', ignoring: %m", dent->d_name);
691 else
692 cleanup_dir_after_db_cleanup(subdir, datadir);
693
694 (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
695 } else
696 (void) unlinkat(dirfd(dir), dent->d_name, 0);
697 }
698 }
699
700 static int cleanup_db(void) {
701 _cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL;
702
703 dir1 = opendir("/run/udev/data");
704 if (dir1)
705 cleanup_dir(dir1, S_ISVTX, 1);
706
707 dir2 = opendir("/run/udev/links");
708 if (dir2)
709 cleanup_dirs_after_db_cleanup(dir2, dir1);
710
711 dir3 = opendir("/run/udev/tags");
712 if (dir3)
713 cleanup_dirs_after_db_cleanup(dir3, dir1);
714
715 dir4 = opendir("/run/udev/static_node-tags");
716 if (dir4)
717 cleanup_dir(dir4, 0, 2);
718
719 /* Do not remove /run/udev/watch. It will be handled by udevd well on restart.
720 * And should not be removed by external program when udevd is running. */
721
722 return 0;
723 }
724
725 static int query_device(QueryType query, sd_device* device) {
726 int r;
727
728 assert(device);
729
730 switch (query) {
731 case QUERY_NAME: {
732 const char *node;
733
734 r = sd_device_get_devname(device, &node);
735 if (r < 0)
736 return log_error_errno(r, "No device node found: %m");
737
738 if (!arg_root)
739 assert_se(node = path_startswith(node, "/dev/"));
740 printf("%s\n", node);
741 return 0;
742 }
743
744 case QUERY_SYMLINK: {
745 const char *prefix = "";
746
747 FOREACH_DEVICE_DEVLINK(device, devlink) {
748 if (!arg_root)
749 assert_se(devlink = path_startswith(devlink, "/dev/"));
750 printf("%s%s", prefix, devlink);
751 prefix = " ";
752 }
753 puts("");
754 return 0;
755 }
756
757 case QUERY_PATH: {
758 const char *devpath;
759
760 r = sd_device_get_devpath(device, &devpath);
761 if (r < 0)
762 return log_error_errno(r, "Failed to get device path: %m");
763
764 printf("%s\n", devpath);
765 return 0;
766 }
767
768 case QUERY_PROPERTY:
769 FOREACH_DEVICE_PROPERTY(device, key, value) {
770 if (arg_properties && !strv_contains(arg_properties, key))
771 continue;
772
773 if (arg_export)
774 printf("%s%s='%s'\n", strempty(arg_export_prefix), key, value);
775 else if (arg_value)
776 printf("%s\n", value);
777 else
778 printf("%s=%s\n", key, value);
779 }
780
781 return 0;
782
783 case QUERY_ALL:
784 if (sd_json_format_enabled(arg_json_format_flags)) {
785 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
786
787 r = record_to_json(device, &v);
788 if (r < 0)
789 return r;
790
791 (void) sd_json_variant_dump(v, arg_json_format_flags, stdout, NULL);
792 } else
793 return print_record(device, NULL);
794
795 return 0;
796
797 default:
798 assert_not_reached();
799 }
800 }
801
802 static int help(void) {
803 printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
804 "Query sysfs or the udev database.\n\n"
805 " -h --help Print this message\n"
806 " -V --version Print version of the program\n"
807 " -q --query=TYPE Query device information:\n"
808 " name Name of device node\n"
809 " symlink Pointing to node\n"
810 " path sysfs device path\n"
811 " property The device properties\n"
812 " all All values\n"
813 " --property=NAME Show only properties by this name\n"
814 " --value When showing properties, print only their values\n"
815 " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
816 " -n --name=NAME Node or symlink name used for query or attribute walk\n"
817 " -r --root Prepend dev directory to path names\n"
818 " -a --attribute-walk Print all key matches walking along the chain\n"
819 " of parent devices\n"
820 " -t --tree Show tree of devices\n"
821 " -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
822 " -x --export Export key/value pairs\n"
823 " -P --export-prefix Export the key name with a prefix\n"
824 " -e --export-db Export the content of the udev database\n"
825 " -c --cleanup-db Clean up the udev database\n"
826 " -w --wait-for-initialization[=SECONDS]\n"
827 " Wait for device to be initialized\n"
828 " --no-pager Do not pipe output into a pager\n"
829 " --json=pretty|short|off Generate JSON output\n"
830 " --subsystem-match=SUBSYSTEM\n"
831 " Query devices matching a subsystem\n"
832 " --subsystem-nomatch=SUBSYSTEM\n"
833 " Query devices not matching a subsystem\n"
834 " --attr-match=FILE[=VALUE]\n"
835 " Query devices that match an attribute\n"
836 " --attr-nomatch=FILE[=VALUE]\n"
837 " Query devices that do not match an attribute\n"
838 " --property-match=KEY=VALUE\n"
839 " Query devices with matching properties\n"
840 " --tag-match=TAG Query devices with a matching tag\n"
841 " --sysname-match=NAME Query devices with this /sys path\n"
842 " --name-match=NAME Query devices with this /dev name\n"
843 " --parent-match=NAME Query devices with this parent device\n"
844 " --initialized-match Query devices that are already initialized\n"
845 " --initialized-nomatch Query devices that are not initialized yet\n",
846 program_invocation_short_name);
847
848 return 0;
849 }
850
851 static int draw_tree(
852 sd_device *parent,
853 sd_device *const array[], size_t n,
854 const char *prefix,
855 unsigned level);
856
857 static int output_tree_device(
858 sd_device *device,
859 const char *str,
860 const char *prefix,
861 bool more,
862 sd_device *const array[], size_t n,
863 unsigned level) {
864
865 _cleanup_free_ char *subprefix = NULL, *subsubprefix = NULL;
866
867 assert(device);
868 assert(str);
869
870 prefix = strempty(prefix);
871
872 printf("%s%s%s\n", prefix, glyph(more ? GLYPH_TREE_BRANCH : GLYPH_TREE_RIGHT), str);
873
874 subprefix = strjoin(prefix, glyph(more ? GLYPH_TREE_VERTICAL : GLYPH_TREE_SPACE));
875 if (!subprefix)
876 return log_oom();
877
878 subsubprefix = strjoin(subprefix, glyph(GLYPH_VERTICAL_DOTTED), " ");
879 if (!subsubprefix)
880 return log_oom();
881
882 (void) print_record(device, subsubprefix);
883
884 return draw_tree(device, array, n, subprefix, level + 1);
885 }
886
887 static int draw_tree(
888 sd_device *parent,
889 sd_device *const array[], size_t n,
890 const char *prefix,
891 unsigned level) {
892
893 const char *parent_path;
894 size_t i = 0;
895 int r;
896
897 if (n == 0)
898 return 0;
899
900 assert(array);
901
902 if (parent) {
903 r = sd_device_get_devpath(parent, &parent_path);
904 if (r < 0)
905 return log_error_errno(r, "Failed to get sysfs path of parent device: %m");
906 } else
907 parent_path = NULL;
908
909 if (level > TREE_DEPTH_MAX) {
910 log_warning("Eliding tree below '%s', too deep.", strna(parent_path));
911 return 0;
912 }
913
914 while (i < n) {
915 sd_device *device = array[i];
916 const char *device_path, *str;
917 bool more = false;
918 size_t j;
919
920 r = sd_device_get_devpath(device, &device_path);
921 if (r < 0)
922 return log_error_errno(r, "Failed to get sysfs path of enumerated device: %m");
923
924 /* Scan through the subsequent devices looking children of the device we are looking at. */
925 for (j = i + 1; j < n; j++) {
926 sd_device *next = array[j];
927 const char *next_path;
928
929 r = sd_device_get_devpath(next, &next_path);
930 if (r < 0)
931 return log_error_errno(r, "Failed to get sysfs of child device: %m");
932
933 if (!path_startswith(next_path, device_path)) {
934 more = !parent_path || path_startswith(next_path, parent_path);
935 break;
936 }
937 }
938
939 /* Determine the string to display for this node. If we are at the top of the tree, the full
940 * device path so far, otherwise just the part suffixing the parent's device path. */
941 str = parent ? ASSERT_PTR(path_startswith(device_path, parent_path)) : device_path;
942
943 r = output_tree_device(device, str, prefix, more, array + i + 1, j - i - 1, level);
944 if (r < 0)
945 return r;
946
947 i = j;
948 }
949
950 return 0;
951 }
952
953 static int print_tree(sd_device* below) {
954 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
955 const char *below_path;
956 sd_device **array;
957 size_t n = 0;
958 int r;
959
960 if (below) {
961 r = sd_device_get_devpath(below, &below_path);
962 if (r < 0)
963 return log_error_errno(r, "Failed to get sysfs path of device: %m");
964
965 } else
966 below_path = NULL;
967
968 r = sd_device_enumerator_new(&e);
969 if (r < 0)
970 return log_error_errno(r, "Failed to allocate device enumerator: %m");
971
972 if (below) {
973 r = sd_device_enumerator_add_match_parent(e, below);
974 if (r < 0)
975 return log_error_errno(r, "Failed to install parent enumerator match: %m");
976 }
977
978 r = sd_device_enumerator_allow_uninitialized(e);
979 if (r < 0)
980 return log_error_errno(r, "Failed to enable enumeration of uninitialized devices: %m");
981
982 r = device_enumerator_scan_devices_and_subsystems(e);
983 if (r < 0)
984 return log_error_errno(r, "Failed to scan for devices and subsystems: %m");
985
986 if (below) {
987 /* This must be called after device_enumerator_scan_devices_and_subsystems(). */
988 r = device_enumerator_add_parent_devices(e, below);
989 if (r < 0)
990 return log_error_errno(r, "Failed to add parent devices: %m");
991 }
992
993 assert_se(array = device_enumerator_get_devices(e, &n));
994
995 if (n == 0) {
996 log_info("No items.");
997 return 0;
998 }
999
1000 r = draw_tree(NULL, array, n, NULL, 0);
1001 if (r < 0)
1002 return r;
1003
1004 printf("\n%zu items shown.\n", n);
1005 return 0;
1006 }
1007
1008 static int parse_argv(int argc, char *argv[]) {
1009
1010 enum {
1011 ARG_PROPERTY = 0x100,
1012 ARG_VALUE,
1013 ARG_NO_PAGER,
1014 ARG_JSON,
1015 ARG_SUBSYSTEM_MATCH,
1016 ARG_SUBSYSTEM_NOMATCH,
1017 ARG_ATTR_MATCH,
1018 ARG_ATTR_NOMATCH,
1019 ARG_PROPERTY_MATCH,
1020 ARG_TAG_MATCH,
1021 ARG_SYSNAME_MATCH,
1022 ARG_NAME_MATCH,
1023 ARG_PARENT_MATCH,
1024 ARG_INITIALIZED_MATCH,
1025 ARG_INITIALIZED_NOMATCH,
1026 };
1027
1028 static const struct option options[] = {
1029 { "attribute-walk", no_argument, NULL, 'a' },
1030 { "tree", no_argument, NULL, 't' },
1031 { "cleanup-db", no_argument, NULL, 'c' },
1032 { "device-id-of-file", required_argument, NULL, 'd' },
1033 { "export", no_argument, NULL, 'x' },
1034 { "export-db", no_argument, NULL, 'e' },
1035 { "export-prefix", required_argument, NULL, 'P' },
1036 { "help", no_argument, NULL, 'h' },
1037 { "name", required_argument, NULL, 'n' },
1038 { "path", required_argument, NULL, 'p' },
1039 { "property", required_argument, NULL, ARG_PROPERTY },
1040 { "query", required_argument, NULL, 'q' },
1041 { "root", no_argument, NULL, 'r' },
1042 { "value", no_argument, NULL, ARG_VALUE },
1043 { "version", no_argument, NULL, 'V' },
1044 { "wait-for-initialization", optional_argument, NULL, 'w' },
1045 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1046 { "json", required_argument, NULL, ARG_JSON },
1047 { "subsystem-match", required_argument, NULL, ARG_SUBSYSTEM_MATCH },
1048 { "subsystem-nomatch", required_argument, NULL, ARG_SUBSYSTEM_NOMATCH },
1049 { "attr-match", required_argument, NULL, ARG_ATTR_MATCH },
1050 { "attr-nomatch", required_argument, NULL, ARG_ATTR_NOMATCH },
1051 { "property-match", required_argument, NULL, ARG_PROPERTY_MATCH },
1052 { "tag-match", required_argument, NULL, ARG_TAG_MATCH },
1053 { "sysname-match", required_argument, NULL, ARG_SYSNAME_MATCH },
1054 { "name-match", required_argument, NULL, ARG_NAME_MATCH },
1055 { "parent-match", required_argument, NULL, ARG_PARENT_MATCH },
1056 { "initialized-match", no_argument, NULL, ARG_INITIALIZED_MATCH },
1057 { "initialized-nomatch", no_argument, NULL, ARG_INITIALIZED_NOMATCH },
1058 {}
1059 };
1060
1061 int c, r;
1062
1063 assert(argc >= 0);
1064 assert(argv);
1065
1066 while ((c = getopt_long(argc, argv, "atced:n:p:q:rxP:w::Vh", options, NULL)) >= 0)
1067 switch (c) {
1068
1069 case ARG_PROPERTY:
1070 /* Make sure that if the empty property list was specified, we won't show any
1071 properties. */
1072 if (isempty(optarg) && !arg_properties) {
1073 arg_properties = new0(char*, 1);
1074 if (!arg_properties)
1075 return log_oom();
1076 } else {
1077 r = strv_split_and_extend(&arg_properties, optarg, ",", true);
1078 if (r < 0)
1079 return log_oom();
1080 }
1081 break;
1082
1083 case ARG_VALUE:
1084 arg_value = true;
1085 break;
1086
1087 case 'n':
1088 case 'p': {
1089 const char *prefix = c == 'n' ? "/dev/" : "/sys/";
1090 char *path;
1091
1092 path = path_join(path_startswith(optarg, prefix) ? NULL : prefix, optarg);
1093 if (!path)
1094 return log_oom();
1095
1096 r = strv_consume(&arg_devices, path);
1097 if (r < 0)
1098 return log_oom();
1099 break;
1100 }
1101
1102 case 'q':
1103 arg_query = query_type_from_string(optarg);
1104 if (arg_query < 0) {
1105 if (streq(optarg, "env")) /* deprecated */
1106 arg_query = QUERY_PROPERTY;
1107 else
1108 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown query type '%s'", optarg);
1109 }
1110 break;
1111
1112 case 'r':
1113 arg_root = true;
1114 break;
1115
1116 case 'd':
1117 arg_action_type = ACTION_DEVICE_ID_FILE;
1118 r = free_and_strdup(&arg_name, optarg);
1119 if (r < 0)
1120 return log_oom();
1121 break;
1122
1123 case 'a':
1124 arg_action_type = ACTION_ATTRIBUTE_WALK;
1125 break;
1126
1127 case 't':
1128 arg_action_type = ACTION_TREE;
1129 break;
1130
1131 case 'e':
1132 arg_action_type = ACTION_EXPORT;
1133 break;
1134
1135 case 'c':
1136 arg_action_type = ACTION_CLEANUP_DB;
1137 break;
1138
1139 case 'x':
1140 arg_export = true;
1141 break;
1142
1143 case 'P':
1144 arg_export = true;
1145 arg_export_prefix = optarg;
1146 break;
1147
1148 case 'w':
1149 if (optarg) {
1150 r = parse_sec(optarg, &arg_wait_for_initialization_timeout);
1151 if (r < 0)
1152 return log_error_errno(r, "Failed to parse timeout value: %m");
1153 } else
1154 arg_wait_for_initialization_timeout = USEC_INFINITY;
1155 break;
1156
1157 case 'V':
1158 return print_version();
1159
1160 case 'h':
1161 return help();
1162
1163 case ARG_NO_PAGER:
1164 arg_pager_flags |= PAGER_DISABLE;
1165 break;
1166
1167 case ARG_JSON:
1168 r = parse_json_argument(optarg, &arg_json_format_flags);
1169 if (r <= 0)
1170 return r;
1171 break;
1172
1173 case ARG_SUBSYSTEM_MATCH:
1174 r = strv_extend(&arg_subsystem_match, optarg);
1175 if (r < 0)
1176 return log_oom();
1177 break;
1178
1179 case ARG_SUBSYSTEM_NOMATCH:
1180 r = strv_extend(&arg_subsystem_nomatch, optarg);
1181 if (r < 0)
1182 return log_oom();
1183 break;
1184
1185 case ARG_ATTR_MATCH:
1186 if (!strchr(optarg, '='))
1187 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1188 "Expected <ATTR>=<value> instead of '%s'", optarg);
1189
1190 r = strv_extend(&arg_attr_match, optarg);
1191 if (r < 0)
1192 return log_oom();
1193 break;
1194
1195 case ARG_ATTR_NOMATCH:
1196 if (!strchr(optarg, '='))
1197 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1198 "Expected <ATTR>=<value> instead of '%s'", optarg);
1199
1200 r = strv_extend(&arg_attr_nomatch, optarg);
1201 if (r < 0)
1202 return log_oom();
1203 break;
1204
1205 case ARG_PROPERTY_MATCH:
1206 if (!strchr(optarg, '='))
1207 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1208 "Expected <PROPERTY>=<value> instead of '%s'", optarg);
1209
1210 r = strv_extend(&arg_property_match, optarg);
1211 if (r < 0)
1212 return log_oom();
1213 break;
1214
1215 case ARG_TAG_MATCH:
1216 r = strv_extend(&arg_tag_match, optarg);
1217 if (r < 0)
1218 return log_oom();
1219 break;
1220
1221 case ARG_SYSNAME_MATCH:
1222 r = strv_extend(&arg_sysname_match, optarg);
1223 if (r < 0)
1224 return log_oom();
1225 break;
1226
1227 case ARG_NAME_MATCH:
1228 r = strv_extend(&arg_name_match, optarg);
1229 if (r < 0)
1230 return log_oom();
1231 break;
1232
1233 case ARG_PARENT_MATCH:
1234 r = strv_extend(&arg_parent_match, optarg);
1235 if (r < 0)
1236 return log_oom();
1237 break;
1238
1239 case ARG_INITIALIZED_MATCH:
1240 arg_initialized_match = MATCH_INITIALIZED_YES;
1241 break;
1242
1243 case ARG_INITIALIZED_NOMATCH:
1244 arg_initialized_match = MATCH_INITIALIZED_NO;
1245 break;
1246
1247 case '?':
1248 return -EINVAL;
1249
1250 default:
1251 assert_not_reached();
1252 }
1253
1254 r = strv_extend_strv(&arg_devices, argv + optind, /* filter_duplicates= */ false);
1255 if (r < 0)
1256 return log_error_errno(r, "Failed to build argument list: %m");
1257
1258 if (IN_SET(arg_action_type, ACTION_DEVICE_ID_FILE, ACTION_CLEANUP_DB) && !strv_isempty(arg_devices))
1259 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1260 "Devices are not allowed with -d/--device-id-of-file and -c/--cleanup-db.");
1261
1262 if (!IN_SET(arg_action_type, ACTION_DEVICE_ID_FILE, ACTION_CLEANUP_DB, ACTION_EXPORT, ACTION_TREE) &&
1263 strv_isempty(arg_devices))
1264 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1265 "A device name or path is required");
1266
1267 if (IN_SET(arg_action_type, ACTION_ATTRIBUTE_WALK, ACTION_TREE) && strv_length(arg_devices) > 1)
1268 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1269 "Only one device may be specified with -a/--attribute-walk and -t/--tree");
1270
1271 if (arg_export && arg_value)
1272 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1273 "-x/--export or -P/--export-prefix cannot be used with --value");
1274
1275 return 1;
1276 }
1277
1278 int info_main(int argc, char *argv[], void *userdata) {
1279 int r;
1280
1281 r = parse_argv(argc, argv);
1282 if (r <= 0)
1283 return r;
1284
1285 if (arg_action_type == ACTION_CLEANUP_DB)
1286 return cleanup_db();
1287
1288 if (arg_action_type == ACTION_DEVICE_ID_FILE)
1289 return stat_device();
1290
1291 pager_open(arg_pager_flags);
1292
1293 if (arg_action_type == ACTION_EXPORT)
1294 return export_devices();
1295
1296 if (strv_isempty(arg_devices)) {
1297 assert(arg_action_type == ACTION_TREE);
1298 return print_tree(NULL);
1299 }
1300
1301 int ret = 0;
1302 STRV_FOREACH(p, arg_devices) {
1303 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
1304
1305 r = find_device(*p, /* prefix = */ NULL, &device);
1306 if (r < 0) {
1307 if (r == -EINVAL)
1308 log_error_errno(r, "Bad argument \"%s\", expected an absolute path in /dev/ or /sys/, device ID, or a unit name: %m", *p);
1309 else
1310 log_error_errno(r, "Unknown device \"%s\": %m", *p);
1311
1312 RET_GATHER(ret, r);
1313 continue;
1314 }
1315
1316 if (arg_wait_for_initialization_timeout > 0) {
1317 sd_device *d;
1318
1319 r = device_wait_for_initialization(
1320 device,
1321 NULL,
1322 arg_wait_for_initialization_timeout,
1323 &d);
1324 if (r < 0)
1325 return r;
1326
1327 sd_device_unref(device);
1328 device = d;
1329 }
1330
1331 if (arg_action_type == ACTION_QUERY)
1332 r = query_device(arg_query, device);
1333 else if (arg_action_type == ACTION_ATTRIBUTE_WALK) {
1334 if (sd_json_format_enabled(arg_json_format_flags))
1335 r = print_device_chain_in_json(device);
1336 else
1337 r = print_device_chain(device);
1338 } else if (arg_action_type == ACTION_TREE)
1339 r = print_tree(device);
1340 else
1341 assert_not_reached();
1342 if (r < 0)
1343 return r;
1344 }
1345
1346 return ret;
1347 }