]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/udevadm-info.c
udevadm: info: also show parent devices by --tree
[thirdparty/systemd.git] / src / udev / udevadm-info.c
CommitLineData
f13467ec 1/* SPDX-License-Identifier: GPL-2.0-or-later */
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>
492e76c9 9#include <sys/stat.h>
07630cea 10#include <unistd.h>
be9b51f6 11
13aca847
YW
12#include "sd-device.h"
13
c4abe719 14#include "alloc-util.h"
13aca847
YW
15#include "device-enumerator-private.h"
16#include "device-private.h"
17#include "device-util.h"
8fb3f009 18#include "dirent-util.h"
a24e3938 19#include "errno-util.h"
3ffd4af2 20#include "fd-util.h"
7b6d1683 21#include "fileio.h"
9117d94b
LP
22#include "glyph-util.h"
23#include "pager.h"
21df1465 24#include "sort-util.h"
6c1482b2 25#include "static-destruct.h"
0f4b93c4 26#include "string-table.h"
07630cea 27#include "string-util.h"
13005c8f 28#include "terminal-util.h"
ae760f4b 29#include "udev-util.h"
0f4b93c4 30#include "udevadm.h"
13005c8f 31#include "udevadm-util.h"
be9b51f6 32
668e7c0c
ZJS
33typedef enum ActionType {
34 ACTION_QUERY,
35 ACTION_ATTRIBUTE_WALK,
36 ACTION_DEVICE_ID_FILE,
9117d94b 37 ACTION_TREE,
668e7c0c
ZJS
38} ActionType;
39
40typedef enum QueryType {
41 QUERY_NAME,
42 QUERY_PATH,
43 QUERY_SYMLINK,
44 QUERY_PROPERTY,
45 QUERY_ALL,
46} QueryType;
47
6c1482b2 48static char **arg_properties = NULL;
668e7c0c
ZJS
49static bool arg_root = false;
50static bool arg_export = false;
6c1482b2 51static bool arg_value = false;
668e7c0c 52static const char *arg_export_prefix = NULL;
ae760f4b 53static usec_t arg_wait_for_initialization_timeout = 0;
668e7c0c 54
9117d94b
LP
55/* Put a limit on --tree descent level to not exhaust our stack */
56#define TREE_DEPTH_MAX 64
57
9ec6e95b 58static bool skip_attribute(const char *name) {
4881a0d2
YW
59 assert(name);
60
5992f362
ZJS
61 /* Those are either displayed separately or should not be shown at all. */
62 return STR_IN_SET(name,
63 "uevent",
64 "dev",
65 "modalias",
66 "resource",
67 "driver",
68 "subsystem",
69 "module");
20bee04c
KS
70}
71
21df1465
YW
72typedef struct SysAttr {
73 const char *name;
74 const char *value;
75} SysAttr;
76
6c1482b2
FS
77STATIC_DESTRUCTOR_REGISTER(arg_properties, strv_freep);
78
21df1465 79static int sysattr_compare(const SysAttr *a, const SysAttr *b) {
4881a0d2
YW
80 assert(a);
81 assert(b);
82
21df1465
YW
83 return strcmp(a->name, b->name);
84}
85
86static int print_all_attributes(sd_device *device, bool is_parent) {
87 _cleanup_free_ SysAttr *sysattrs = NULL;
13aca847 88 const char *name, *value;
319a4f4b 89 size_t n_items = 0;
3a90bef5 90 int r;
912541b0 91
4881a0d2
YW
92 assert(device);
93
21df1465
YW
94 value = NULL;
95 (void) sd_device_get_devpath(device, &value);
96 printf(" looking at %sdevice '%s':\n", is_parent ? "parent " : "", strempty(value));
97
98 value = NULL;
99 (void) sd_device_get_sysname(device, &value);
100 printf(" %s==\"%s\"\n", is_parent ? "KERNELS" : "KERNEL", strempty(value));
101
102 value = NULL;
103 (void) sd_device_get_subsystem(device, &value);
104 printf(" %s==\"%s\"\n", is_parent ? "SUBSYSTEMS" : "SUBSYSTEM", strempty(value));
105
106 value = NULL;
107 (void) sd_device_get_driver(device, &value);
108 printf(" %s==\"%s\"\n", is_parent ? "DRIVERS" : "DRIVER", strempty(value));
109
6d6308f6 110 FOREACH_DEVICE_SYSATTR(device, name) {
912541b0
KS
111 size_t len;
112
912541b0
KS
113 if (skip_attribute(name))
114 continue;
115
3a90bef5
YW
116 r = sd_device_get_sysattr_value(device, name, &value);
117 if (r >= 0) {
118 /* skip any values that look like a path */
119 if (value[0] == '/')
120 continue;
121
122 /* skip nonprintable attributes */
123 len = strlen(value);
124 while (len > 0 && isprint((unsigned char) value[len-1]))
125 len--;
126 if (len > 0)
127 continue;
128
a24e3938
LP
129 } else if (ERRNO_IS_PRIVILEGE(r))
130 value = "(not readable)";
3a90bef5 131 else
912541b0 132 continue;
912541b0 133
319a4f4b 134 if (!GREEDY_REALLOC(sysattrs, n_items + 1))
21df1465
YW
135 return log_oom();
136
137 sysattrs[n_items] = (SysAttr) {
138 .name = name,
139 .value = value,
140 };
141 n_items++;
912541b0 142 }
21df1465
YW
143
144 typesafe_qsort(sysattrs, n_items, sysattr_compare);
145
146 for (size_t i = 0; i < n_items; i++)
147 printf(" %s{%s}==\"%s\"\n", is_parent ? "ATTRS" : "ATTR", sysattrs[i].name, sysattrs[i].value);
148
d539f791 149 puts("");
21df1465
YW
150
151 return 0;
be9b51f6
KS
152}
153
13aca847
YW
154static int print_device_chain(sd_device *device) {
155 sd_device *child, *parent;
21df1465 156 int r;
912541b0 157
4881a0d2
YW
158 assert(device);
159
912541b0
KS
160 printf("\n"
161 "Udevadm info starts with the device specified by the devpath and then\n"
162 "walks up the chain of parent devices. It prints for every device\n"
163 "found, all possible attributes in the udev rules key format.\n"
164 "A rule to match, can be composed by the attributes of the device\n"
165 "and the attributes from one single parent device.\n"
166 "\n");
167
21df1465
YW
168 r = print_all_attributes(device, false);
169 if (r < 0)
170 return r;
912541b0 171
13aca847 172 for (child = device; sd_device_get_parent(child, &parent) >= 0; child = parent) {
21df1465
YW
173 r = print_all_attributes(parent, true);
174 if (r < 0)
175 return r;
13aca847 176 }
912541b0
KS
177
178 return 0;
1aa1e248
KS
179}
180
9117d94b 181static int print_record(sd_device *device, const char *prefix) {
a0e90259
LP
182 const char *str, *val, *subsys;
183 dev_t devnum;
184 uint64_t q;
185 int i, ifi;
912541b0 186
4881a0d2
YW
187 assert(device);
188
9117d94b
LP
189 prefix = strempty(prefix);
190
a0e90259
LP
191 /* We don't show syspath here, because it's identical to devpath (modulo the "/sys" prefix).
192 *
193 * We don't show action/seqnum here because that only makes sense for records synthesized from
194 * uevents, not for those synthesized from database entries.
195 *
196 * We don't show sysattrs here, because they can be expensive and potentially issue expensive driver
13005c8f
LP
197 * IO.
198 *
199 * Coloring: let's be conservative with coloring. Let's use it to group related fields. Right now:
200 *
201 * • white for fields that give the device a name
202 * • green for fields that categorize the device into subsystem/devtype and similar
203 * • cyan for fields about associated device nodes/symlinks/network interfaces and such
204 * • magenta for block device diskseq
205 * • yellow for driver info
206 * • no color for regular properties */
a0e90259
LP
207
208 assert_se(sd_device_get_devpath(device, &str) >= 0);
9117d94b 209 printf("%sP: %s%s%s\n", prefix, ansi_highlight_white(), str, ansi_normal());
912541b0 210
a0e90259 211 if (sd_device_get_sysname(device, &str) >= 0)
9117d94b 212 printf("%sM: %s%s%s\n", prefix, ansi_highlight_white(), str, ansi_normal());
a0e90259
LP
213
214 if (sd_device_get_sysnum(device, &str) >= 0)
9117d94b 215 printf("%sR: %s%s%s\n", prefix, ansi_highlight_white(), str, ansi_normal());
a0e90259
LP
216
217 if (sd_device_get_subsystem(device, &subsys) >= 0)
9117d94b 218 printf("%sU: %s%s%s\n", prefix, ansi_highlight_green(), subsys, ansi_normal());
a0e90259
LP
219
220 if (sd_device_get_devtype(device, &str) >= 0)
9117d94b 221 printf("%sT: %s%s%s\n", prefix, ansi_highlight_green(), str, ansi_normal());
a0e90259
LP
222
223 if (sd_device_get_devnum(device, &devnum) >= 0)
9117d94b
LP
224 printf("%sD: %s%c %u:%u%s\n",
225 prefix,
13005c8f
LP
226 ansi_highlight_cyan(),
227 streq_ptr(subsys, "block") ? 'b' : 'c', major(devnum), minor(devnum),
228 ansi_normal());
a0e90259
LP
229
230 if (sd_device_get_ifindex(device, &ifi) >= 0)
9117d94b 231 printf("%sI: %s%i%s\n", prefix, ansi_highlight_cyan(), ifi, ansi_normal());
a0e90259 232
d539f791
ZJS
233 if (sd_device_get_devname(device, &str) >= 0) {
234 assert_se(val = path_startswith(str, "/dev/"));
9117d94b 235 printf("%sN: %s%s%s\n", prefix, ansi_highlight_cyan(), val, ansi_normal());
912541b0 236
a0e90259 237 if (device_get_devlink_priority(device, &i) >= 0)
9117d94b 238 printf("%sL: %s%i%s\n", prefix, ansi_highlight_cyan(), i, ansi_normal());
912541b0 239
a0e90259
LP
240 FOREACH_DEVICE_DEVLINK(device, str) {
241 assert_se(val = path_startswith(str, "/dev/"));
9117d94b 242 printf("%sS: %s%s%s\n", prefix, ansi_highlight_cyan(), val, ansi_normal());
a0e90259 243 }
d539f791 244 }
13aca847 245
a0e90259 246 if (sd_device_get_diskseq(device, &q) >= 0)
9117d94b 247 printf("%sQ: %s%" PRIu64 "%s\n", prefix, ansi_highlight_magenta(), q, ansi_normal());
a0e90259
LP
248
249 if (sd_device_get_driver(device, &str) >= 0)
9117d94b 250 printf("%sV: %s%s%s\n", prefix, ansi_highlight_yellow4(), str, ansi_normal());
a0e90259 251
13aca847 252 FOREACH_DEVICE_PROPERTY(device, str, val)
9117d94b 253 printf("%sE: %s=%s\n", prefix, str, val);
912541b0 254
9117d94b
LP
255 if (isempty(prefix))
256 puts("");
668e7c0c 257 return 0;
31de3a2b
KS
258}
259
9ec6e95b 260static int stat_device(const char *name, bool export, const char *prefix) {
912541b0
KS
261 struct stat statbuf;
262
4881a0d2
YW
263 assert(name);
264
912541b0 265 if (stat(name, &statbuf) != 0)
755700bb 266 return -errno;
912541b0
KS
267
268 if (export) {
13aca847 269 if (!prefix)
912541b0 270 prefix = "INFO_";
1fa2f38f
ZJS
271 printf("%sMAJOR=%u\n"
272 "%sMINOR=%u\n",
912541b0
KS
273 prefix, major(statbuf.st_dev),
274 prefix, minor(statbuf.st_dev));
275 } else
1fa2f38f 276 printf("%u:%u\n", major(statbuf.st_dev), minor(statbuf.st_dev));
912541b0 277 return 0;
f338bac8
KS
278}
279
2024ed61 280static int export_devices(void) {
13aca847
YW
281 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
282 sd_device *d;
283 int r;
912541b0 284
13aca847
YW
285 r = sd_device_enumerator_new(&e);
286 if (r < 0)
df5a4889 287 return log_oom();
755700bb 288
13aca847
YW
289 r = sd_device_enumerator_allow_uninitialized(e);
290 if (r < 0)
df5a4889 291 return log_error_errno(r, "Failed to set allowing uninitialized flag: %m");
912541b0 292
13aca847
YW
293 r = device_enumerator_scan_devices(e);
294 if (r < 0)
df5a4889 295 return log_error_errno(r, "Failed to scan devices: %m");
13aca847
YW
296
297 FOREACH_DEVICE_AND_SUBSYSTEM(e, d)
9117d94b 298 (void) print_record(d, NULL);
755700bb 299
912541b0 300 return 0;
bf7ad0ea
KS
301}
302
9ec6e95b 303static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
4881a0d2
YW
304 assert(dir);
305
912541b0
KS
306 if (depth <= 0)
307 return;
308
8fb3f009 309 FOREACH_DIRENT_ALL(dent, dir, break) {
912541b0
KS
310 struct stat stats;
311
28d6e854 312 if (dot_or_dot_dot(dent->d_name))
912541b0 313 continue;
28d6e854 314 if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) < 0)
912541b0
KS
315 continue;
316 if ((stats.st_mode & mask) != 0)
317 continue;
318 if (S_ISDIR(stats.st_mode)) {
7b6d1683 319 _cleanup_closedir_ DIR *subdir = NULL;
912541b0 320
7b6d1683
LP
321 subdir = xopendirat(dirfd(dir), dent->d_name, O_NOFOLLOW);
322 if (!subdir)
323 log_debug_errno(errno, "Failed to open subdirectory '%s', ignoring: %m", dent->d_name);
324 else
325 cleanup_dir(subdir, mask, depth-1);
200c7fa6
LP
326
327 (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
328 } else
329 (void) unlinkat(dirfd(dir), dent->d_name, 0);
912541b0 330 }
9ead6627
KS
331}
332
7ec62414
MW
333/*
334 * Assume that dir is a directory with file names matching udev data base
335 * entries for devices in /run/udev/data (such as "b8:16"), and removes
336 * all files except those that haven't been deleted in /run/udev/data
337 * (i.e. they were skipped during db cleanup because of the db_persist flag).
7ec62414 338 */
636ab001 339static void cleanup_dir_after_db_cleanup(DIR *dir, DIR *datadir) {
9e0bd1d6
YW
340 assert(dir);
341 assert(datadir);
7ec62414
MW
342
343 FOREACH_DIRENT_ALL(dent, dir, break) {
7ec62414
MW
344 if (dot_or_dot_dot(dent->d_name))
345 continue;
636ab001
YW
346
347 if (faccessat(dirfd(datadir), dent->d_name, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
348 /* The corresponding udev database file still exists.
349 * Assuming the parsistent flag is set for the database. */
7ec62414 350 continue;
7ec62414 351
636ab001 352 (void) unlinkat(dirfd(dir), dent->d_name, 0);
7ec62414 353 }
7ec62414
MW
354}
355
356static void cleanup_dirs_after_db_cleanup(DIR *dir, DIR *datadir) {
9e0bd1d6
YW
357 assert(dir);
358 assert(datadir);
7ec62414
MW
359
360 FOREACH_DIRENT_ALL(dent, dir, break) {
361 struct stat stats;
362
363 if (dot_or_dot_dot(dent->d_name))
364 continue;
365 if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) < 0)
366 continue;
367 if (S_ISDIR(stats.st_mode)) {
7b6d1683 368 _cleanup_closedir_ DIR *subdir = NULL;
7ec62414 369
7b6d1683
LP
370 subdir = xopendirat(dirfd(dir), dent->d_name, O_NOFOLLOW);
371 if (!subdir)
372 log_debug_errno(errno, "Failed to open subdirectory '%s', ignoring: %m", dent->d_name);
373 else
374 cleanup_dir_after_db_cleanup(subdir, datadir);
636ab001
YW
375
376 (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
7ec62414
MW
377 } else
378 (void) unlinkat(dirfd(dir), dent->d_name, 0);
379 }
380}
381
2024ed61 382static void cleanup_db(void) {
bd979801 383 _cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL;
912541b0 384
755700bb 385 dir1 = opendir("/run/udev/data");
13aca847 386 if (dir1)
755700bb 387 cleanup_dir(dir1, S_ISVTX, 1);
912541b0 388
755700bb 389 dir2 = opendir("/run/udev/links");
13aca847 390 if (dir2)
7ec62414 391 cleanup_dirs_after_db_cleanup(dir2, dir1);
912541b0 392
755700bb 393 dir3 = opendir("/run/udev/tags");
13aca847 394 if (dir3)
7ec62414 395 cleanup_dirs_after_db_cleanup(dir3, dir1);
912541b0 396
755700bb 397 dir4 = opendir("/run/udev/static_node-tags");
13aca847 398 if (dir4)
755700bb 399 cleanup_dir(dir4, 0, 2);
84b6ad70 400
bd979801
YW
401 /* Do not remove /run/udev/watch. It will be handled by udevd well on restart.
402 * And should not be removed by external program when udevd is running. */
9ead6627
KS
403}
404
668e7c0c
ZJS
405static int query_device(QueryType query, sd_device* device) {
406 int r;
407
408 assert(device);
409
79893116 410 switch (query) {
668e7c0c
ZJS
411 case QUERY_NAME: {
412 const char *node;
413
414 r = sd_device_get_devname(device, &node);
415 if (r < 0)
416 return log_error_errno(r, "No device node found: %m");
417
d539f791
ZJS
418 if (!arg_root)
419 assert_se(node = path_startswith(node, "/dev/"));
420 printf("%s\n", node);
668e7c0c
ZJS
421 return 0;
422 }
423
424 case QUERY_SYMLINK: {
d539f791 425 const char *devlink, *prefix = "";
668e7c0c
ZJS
426
427 FOREACH_DEVICE_DEVLINK(device, devlink) {
d539f791
ZJS
428 if (!arg_root)
429 assert_se(devlink = path_startswith(devlink, "/dev/"));
430 printf("%s%s", prefix, devlink);
431 prefix = " ";
668e7c0c 432 }
d539f791 433 puts("");
668e7c0c
ZJS
434 return 0;
435 }
436
437 case QUERY_PATH: {
438 const char *devpath;
5ac0162c 439
668e7c0c
ZJS
440 r = sd_device_get_devpath(device, &devpath);
441 if (r < 0)
442 return log_error_errno(r, "Failed to get device path: %m");
443
444 printf("%s\n", devpath);
445 return 0;
446 }
447
448 case QUERY_PROPERTY: {
449 const char *key, *value;
450
6c1482b2
FS
451 FOREACH_DEVICE_PROPERTY(device, key, value) {
452 if (arg_properties && !strv_contains(arg_properties, key))
453 continue;
454
668e7c0c
ZJS
455 if (arg_export)
456 printf("%s%s='%s'\n", strempty(arg_export_prefix), key, value);
6c1482b2
FS
457 else if (arg_value)
458 printf("%s\n", value);
668e7c0c
ZJS
459 else
460 printf("%s=%s\n", key, value);
6c1482b2
FS
461 }
462
668e7c0c
ZJS
463 return 0;
464 }
465
466 case QUERY_ALL:
9117d94b 467 return print_record(device, NULL);
668e7c0c 468
4881a0d2
YW
469 default:
470 assert_not_reached();
471 }
668e7c0c
ZJS
472}
473
474static int help(void) {
5ac0162c
LP
475 printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
476 "Query sysfs or the udev database.\n\n"
477 " -h --help Print this message\n"
73527992 478 " -V --version Print version of the program\n"
5ac0162c
LP
479 " -q --query=TYPE Query device information:\n"
480 " name Name of device node\n"
481 " symlink Pointing to node\n"
482 " path sysfs device path\n"
483 " property The device properties\n"
484 " all All values\n"
6c1482b2
FS
485 " --property=NAME Show only properties by this name\n"
486 " --value When showing properties, print only their values\n"
5ac0162c
LP
487 " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
488 " -n --name=NAME Node or symlink name used for query or attribute walk\n"
489 " -r --root Prepend dev directory to path names\n"
490 " -a --attribute-walk Print all key matches walking along the chain\n"
491 " of parent devices\n"
9117d94b 492 " -t --tree Show tree of devices\n"
5ac0162c
LP
493 " -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
494 " -x --export Export key/value pairs\n"
495 " -P --export-prefix Export the key name with a prefix\n"
496 " -e --export-db Export the content of the udev database\n"
497 " -c --cleanup-db Clean up the udev database\n"
ae760f4b 498 " -w --wait-for-initialization[=SECONDS]\n"
bc556335
DDM
499 " Wait for device to be initialized\n",
500 program_invocation_short_name);
ee4a776d
YW
501
502 return 0;
5ac0162c
LP
503}
504
9117d94b
LP
505static int draw_tree(
506 sd_device *parent,
507 sd_device *const array[], size_t n,
508 const char *prefix,
509 unsigned level);
510
511static int output_tree_device(
512 sd_device *device,
513 const char *str,
514 const char *prefix,
515 bool more,
516 sd_device *const array[], size_t n,
517 unsigned level) {
518
519 _cleanup_free_ char *subprefix = NULL, *subsubprefix = NULL;
520
521 assert(device);
522 assert(str);
523
524 prefix = strempty(prefix);
525
526 printf("%s%s%s\n", prefix, special_glyph(more ? SPECIAL_GLYPH_TREE_BRANCH : SPECIAL_GLYPH_TREE_RIGHT), str);
527
528 subprefix = strjoin(prefix, special_glyph(more ? SPECIAL_GLYPH_TREE_VERTICAL : SPECIAL_GLYPH_TREE_SPACE));
529 if (!subprefix)
530 return log_oom();
531
532 subsubprefix = strjoin(subprefix, special_glyph(SPECIAL_GLYPH_VERTICAL_DOTTED), " ");
533 if (!subsubprefix)
534 return log_oom();
535
536 (void) print_record(device, subsubprefix);
537
538 return draw_tree(device, array, n, subprefix, level + 1);
539}
540
541static int draw_tree(
542 sd_device *parent,
543 sd_device *const array[], size_t n,
544 const char *prefix,
545 unsigned level) {
546
547 const char *parent_path;
548 size_t i = 0;
549 int r;
550
551 if (n == 0)
552 return 0;
553
554 assert(array);
555
556 if (parent) {
557 r = sd_device_get_devpath(parent, &parent_path);
558 if (r < 0)
559 return log_error_errno(r, "Failed to get sysfs path of parent device: %m");
560 } else
561 parent_path = NULL;
562
563 if (level > TREE_DEPTH_MAX) {
564 log_warning("Eliding tree below '%s', too deep.", strna(parent_path));
565 return 0;
566 }
567
568 while (i < n) {
569 sd_device *device = array[i];
570 const char *device_path, *str;
571 bool more = false;
572 size_t j;
573
574 r = sd_device_get_devpath(device, &device_path);
575 if (r < 0)
576 return log_error_errno(r, "Failed to get sysfs path of enumerated device: %m");
577
578 /* Scan through the subsequent devices looking children of the device we are looking at. */
579 for (j = i + 1; j < n; j++) {
580 sd_device *next = array[j];
581 const char *next_path;
582
583 r = sd_device_get_devpath(next, &next_path);
584 if (r < 0)
585 return log_error_errno(r, "Failed to get sysfs of child device: %m");
586
587 if (!path_startswith(next_path, device_path)) {
588 more = !parent_path || path_startswith(next_path, parent_path);
589 break;
590 }
591 }
592
593 /* Determine the string to display for this node. If we are at the top of the tree, the full
594 * device path so far, otherwise just the part suffixing the parent's device path. */
595 str = parent ? ASSERT_PTR(path_startswith(device_path, parent_path)) : device_path;
596
597 r = output_tree_device(device, str, prefix, more, array + i + 1, j - i - 1, level);
598 if (r < 0)
599 return r;
600
601 i = j;
602 }
603
604 return 0;
605}
606
607static int print_tree(sd_device* below) {
608 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
609 const char *below_path;
610 sd_device **array;
611 size_t n = 0;
612 int r;
613
614 if (below) {
615 r = sd_device_get_devpath(below, &below_path);
616 if (r < 0)
617 return log_error_errno(r, "Failed to get sysfs path of device: %m");
618
619 } else
620 below_path = NULL;
621
622 r = sd_device_enumerator_new(&e);
623 if (r < 0)
624 return log_error_errno(r, "Failed to allocate device enumerator: %m");
625
626 if (below) {
627 r = sd_device_enumerator_add_match_parent(e, below);
628 if (r < 0)
629 return log_error_errno(r, "Failed to install parent enumerator match: %m");
630 }
631
632 r = sd_device_enumerator_allow_uninitialized(e);
633 if (r < 0)
634 return log_error_errno(r, "Failed to enable enumeration of uninitialized devices: %m");
635
636 r = device_enumerator_scan_devices_and_subsystems(e);
637 if (r < 0)
638 return log_error_errno(r, "Failed to scan for devices and subsystems: %m");
639
bd4297e7
YW
640 if (below) {
641 /* This must be called after device_enumerator_scan_devices_and_subsystems(). */
642 r = device_enumerator_add_parent_devices(e, below);
643 if (r < 0)
644 return log_error_errno(r, "Failed to add parent devices: %m");
645 }
646
9117d94b
LP
647 assert_se(array = device_enumerator_get_devices(e, &n));
648
649 if (n == 0) {
650 log_info("No items.");
651 return 0;
652 }
653
654 r = draw_tree(NULL, array, n, NULL, 0);
655 if (r < 0)
656 return r;
657
658 printf("\n%zu items shown.\n", n);
659 return 0;
660}
661
3d05193e 662int info_main(int argc, char *argv[], void *userdata) {
3c79311a 663 _cleanup_strv_free_ char **devices = NULL;
c4abe719 664 _cleanup_free_ char *name = NULL;
7ba77d8f 665 int c, r, ret;
912541b0 666
6c1482b2
FS
667 enum {
668 ARG_PROPERTY = 0x100,
669 ARG_VALUE,
670 };
671
912541b0 672 static const struct option options[] = {
6c1482b2 673 { "attribute-walk", no_argument, NULL, 'a' },
9117d94b 674 { "tree", no_argument, NULL, 't' },
6c1482b2
FS
675 { "cleanup-db", no_argument, NULL, 'c' },
676 { "device-id-of-file", required_argument, NULL, 'd' },
677 { "export", no_argument, NULL, 'x' },
678 { "export-db", no_argument, NULL, 'e' },
679 { "export-prefix", required_argument, NULL, 'P' },
680 { "help", no_argument, NULL, 'h' },
681 { "name", required_argument, NULL, 'n' },
682 { "path", required_argument, NULL, 'p' },
683 { "property", required_argument, NULL, ARG_PROPERTY },
684 { "query", required_argument, NULL, 'q' },
685 { "root", no_argument, NULL, 'r' },
686 { "value", no_argument, NULL, ARG_VALUE },
687 { "version", no_argument, NULL, 'V' },
688 { "wait-for-initialization", optional_argument, NULL, 'w' },
912541b0
KS
689 {}
690 };
691
668e7c0c
ZJS
692 ActionType action = ACTION_QUERY;
693 QueryType query = QUERY_ALL;
912541b0 694
9117d94b 695 while ((c = getopt_long(argc, argv, "atced:n:p:q:rxP:w::Vh", options, NULL)) >= 0)
7643ac9a 696 switch (c) {
6c1482b2
FS
697 case ARG_PROPERTY:
698 /* Make sure that if the empty property list was specified, we won't show any
699 properties. */
700 if (isempty(optarg) && !arg_properties) {
701 arg_properties = new0(char*, 1);
702 if (!arg_properties)
703 return log_oom();
704 } else {
705 r = strv_split_and_extend(&arg_properties, optarg, ",", true);
706 if (r < 0)
707 return log_oom();
708 }
709 break;
710 case ARG_VALUE:
711 arg_value = true;
712 break;
ee4a776d 713 case 'n':
3c79311a
ZJS
714 case 'p': {
715 const char *prefix = c == 'n' ? "/dev/" : "/sys/";
716 char *path;
4f5d327a 717
3c79311a
ZJS
718 path = path_join(path_startswith(optarg, prefix) ? NULL : prefix, optarg);
719 if (!path)
720 return log_oom();
4f5d327a 721
3c79311a 722 r = strv_consume(&devices, path);
13aca847 723 if (r < 0)
3c79311a 724 return log_oom();
912541b0 725 break;
3c79311a
ZJS
726 }
727
912541b0
KS
728 case 'q':
729 action = ACTION_QUERY;
44433ebd 730 if (streq(optarg, "property") || streq(optarg, "env"))
912541b0 731 query = QUERY_PROPERTY;
44433ebd 732 else if (streq(optarg, "name"))
912541b0 733 query = QUERY_NAME;
44433ebd 734 else if (streq(optarg, "symlink"))
912541b0 735 query = QUERY_SYMLINK;
44433ebd 736 else if (streq(optarg, "path"))
912541b0 737 query = QUERY_PATH;
44433ebd 738 else if (streq(optarg, "all"))
912541b0 739 query = QUERY_ALL;
47c8fcbe
YW
740 else
741 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "unknown query type");
912541b0
KS
742 break;
743 case 'r':
668e7c0c 744 arg_root = true;
912541b0 745 break;
912541b0
KS
746 case 'd':
747 action = ACTION_DEVICE_ID_FILE;
1cb7d29d
YW
748 r = free_and_strdup(&name, optarg);
749 if (r < 0)
c4abe719 750 return log_oom();
912541b0
KS
751 break;
752 case 'a':
753 action = ACTION_ATTRIBUTE_WALK;
754 break;
9117d94b
LP
755 case 't':
756 action = ACTION_TREE;
757 break;
912541b0 758 case 'e':
ee4a776d 759 return export_devices();
912541b0 760 case 'c':
2024ed61 761 cleanup_db();
44433ebd 762 return 0;
912541b0 763 case 'x':
668e7c0c 764 arg_export = true;
912541b0
KS
765 break;
766 case 'P':
2277e845 767 arg_export = true;
668e7c0c 768 arg_export_prefix = optarg;
912541b0 769 break;
ae760f4b
YW
770 case 'w':
771 if (optarg) {
772 r = parse_sec(optarg, &arg_wait_for_initialization_timeout);
773 if (r < 0)
774 return log_error_errno(r, "Failed to parse timeout value: %m");
775 } else
776 arg_wait_for_initialization_timeout = USEC_INFINITY;
777 break;
912541b0 778 case 'V':
51b006e1 779 return print_version();
912541b0 780 case 'h':
ee4a776d
YW
781 return help();
782 case '?':
783 return -EINVAL;
912541b0 784 default:
04499a70 785 assert_not_reached();
912541b0 786 }
912541b0 787
3c79311a
ZJS
788 if (action == ACTION_DEVICE_ID_FILE) {
789 if (argv[optind])
668e7c0c 790 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
3c79311a
ZJS
791 "Positional arguments are not allowed with -d/--device-id-of-file.");
792 assert(name);
793 return stat_device(name, arg_export, arg_export_prefix);
668e7c0c 794 }
13aca847 795
3c79311a
ZJS
796 r = strv_extend_strv(&devices, argv + optind, false);
797 if (r < 0)
798 return log_error_errno(r, "Failed to build argument list: %m");
19a29798 799
9117d94b 800 if (action != ACTION_TREE && strv_isempty(devices))
3c79311a
ZJS
801 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
802 "A device name or path is required");
9117d94b 803 if (IN_SET(action, ACTION_ATTRIBUTE_WALK, ACTION_TREE) && strv_length(devices) > 1)
3c79311a 804 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
9117d94b 805 "Only one device may be specified with -a/--attribute-walk and -t/--tree");
668e7c0c 806
6c1482b2
FS
807 if (arg_export && arg_value)
808 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
809 "-x/--export or -P/--export-prefix cannot be used with --value");
810
9117d94b
LP
811 if (strv_isempty(devices)) {
812 assert(action == ACTION_TREE);
813 pager_open(0);
814 return print_tree(NULL);
815 }
816
7ba77d8f 817 ret = 0;
3c79311a
ZJS
818 STRV_FOREACH(p, devices) {
819 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
820
821 r = find_device(*p, NULL, &device);
7ba77d8f
LP
822 if (r < 0) {
823 if (r == -EINVAL)
824 log_error_errno(r, "Bad argument \"%s\", expected an absolute path in /dev/ or /sys/ or a unit name: %m", *p);
825 else
826 log_error_errno(r, "Unknown device \"%s\": %m", *p);
827
828 if (ret == 0)
829 ret = r;
830 continue;
831 }
3c79311a 832
ae760f4b 833 if (arg_wait_for_initialization_timeout > 0) {
7a555216
YW
834 sd_device *d;
835
9e3d9067
LP
836 r = device_wait_for_initialization(
837 device,
838 NULL,
839 usec_add(now(CLOCK_MONOTONIC), arg_wait_for_initialization_timeout),
840 &d);
ae760f4b
YW
841 if (r < 0)
842 return r;
7a555216
YW
843
844 sd_device_unref(device);
845 device = d;
ae760f4b
YW
846 }
847
3c79311a
ZJS
848 if (action == ACTION_QUERY)
849 r = query_device(query, device);
850 else if (action == ACTION_ATTRIBUTE_WALK)
851 r = print_device_chain(device);
9117d94b
LP
852 else if (action == ACTION_TREE)
853 r = print_tree(device);
3c79311a 854 else
04499a70 855 assert_not_reached();
3c79311a
ZJS
856 if (r < 0)
857 return r;
912541b0 858 }
87171e46 859
7ba77d8f 860 return ret;
87171e46 861}