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