]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/udevadm-info.c
io.systemd.Unit.List fix context/runtime split (#38172)
[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>
e6c1a2bd 4#include <fcntl.h>
07630cea 5#include <getopt.h>
07630cea 6#include <stdio.h>
492e76c9 7#include <sys/stat.h>
dc59a151 8#include <sys/sysmacros.h>
07630cea 9#include <unistd.h>
be9b51f6 10
13aca847 11#include "sd-device.h"
309a747f 12#include "sd-json.h"
13aca847 13
c4abe719 14#include "alloc-util.h"
b7120388 15#include "ansi-color.h"
13aca847
YW
16#include "device-enumerator-private.h"
17#include "device-private.h"
18#include "device-util.h"
8fb3f009 19#include "dirent-util.h"
a24e3938 20#include "errno-util.h"
3ffd4af2 21#include "fd-util.h"
7b6d1683 22#include "fileio.h"
9117d94b
LP
23#include "glyph-util.h"
24#include "pager.h"
d6e5f170 25#include "parse-argument.h"
21df1465 26#include "sort-util.h"
6c1482b2 27#include "static-destruct.h"
0f4b93c4 28#include "string-table.h"
07630cea 29#include "string-util.h"
e548ca38 30#include "strv.h"
dc59a151 31#include "time-util.h"
ae760f4b 32#include "udev-util.h"
0f4b93c4 33#include "udevadm.h"
13005c8f 34#include "udevadm-util.h"
be9b51f6 35
668e7c0c
ZJS
36typedef enum ActionType {
37 ACTION_QUERY,
38 ACTION_ATTRIBUTE_WALK,
39 ACTION_DEVICE_ID_FILE,
9117d94b 40 ACTION_TREE,
d6e5f170 41 ACTION_EXPORT,
d89b3004 42 ACTION_CLEANUP_DB,
668e7c0c
ZJS
43} ActionType;
44
45typedef enum QueryType {
46 QUERY_NAME,
47 QUERY_PATH,
48 QUERY_SYMLINK,
49 QUERY_PROPERTY,
50 QUERY_ALL,
6cdc63d1
DT
51 _QUERY_TYPE_MAX,
52 _QUERY_TYPE_INVALID = -EINVAL,
668e7c0c
ZJS
53} QueryType;
54
6c1482b2 55static char **arg_properties = NULL;
668e7c0c
ZJS
56static bool arg_root = false;
57static bool arg_export = false;
6c1482b2 58static bool arg_value = false;
668e7c0c 59static const char *arg_export_prefix = NULL;
ae760f4b 60static usec_t arg_wait_for_initialization_timeout = 0;
5b4d7320 61static PagerFlags arg_pager_flags = 0;
309a747f 62static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
d89b3004
DT
63static ActionType arg_action_type = ACTION_QUERY;
64static QueryType arg_query = QUERY_ALL;
65static char **arg_devices = NULL;
66static char *arg_name = NULL;
67static char **arg_attr_match = NULL;
68static char **arg_attr_nomatch = NULL;
69static char **arg_name_match = NULL;
70static char **arg_parent_match = NULL;
71static char **arg_property_match = NULL;
72static char **arg_subsystem_match = NULL;
73static char **arg_subsystem_nomatch = NULL;
74static char **arg_sysname_match = NULL;
75static char **arg_tag_match = NULL;
76static int arg_initialized_match = -1;
668e7c0c 77
cb3f6c9e 78STATIC_DESTRUCTOR_REGISTER(arg_properties, strv_freep);
d89b3004
DT
79STATIC_DESTRUCTOR_REGISTER(arg_devices, strv_freep);
80STATIC_DESTRUCTOR_REGISTER(arg_name, freep);
81STATIC_DESTRUCTOR_REGISTER(arg_attr_match, strv_freep);
82STATIC_DESTRUCTOR_REGISTER(arg_attr_nomatch, strv_freep);
83STATIC_DESTRUCTOR_REGISTER(arg_name_match, strv_freep);
84STATIC_DESTRUCTOR_REGISTER(arg_parent_match, strv_freep);
85STATIC_DESTRUCTOR_REGISTER(arg_property_match, strv_freep);
86STATIC_DESTRUCTOR_REGISTER(arg_subsystem_match, strv_freep);
87STATIC_DESTRUCTOR_REGISTER(arg_subsystem_nomatch, strv_freep);
88STATIC_DESTRUCTOR_REGISTER(arg_sysname_match, strv_freep);
89STATIC_DESTRUCTOR_REGISTER(arg_tag_match, strv_freep);
cb3f6c9e 90
9117d94b
LP
91/* Put a limit on --tree descent level to not exhaust our stack */
92#define TREE_DEPTH_MAX 64
93
6cdc63d1
DT
94static 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
102DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(query_type, QueryType);
103
9ec6e95b 104static bool skip_attribute(const char *name) {
4881a0d2
YW
105 assert(name);
106
5992f362
ZJS
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");
20bee04c
KS
116}
117
21df1465
YW
118typedef struct SysAttr {
119 const char *name;
120 const char *value;
121} SysAttr;
122
123static int sysattr_compare(const SysAttr *a, const SysAttr *b) {
4881a0d2
YW
124 assert(a);
125 assert(b);
126
21df1465
YW
127 return strcmp(a->name, b->name);
128}
129
130static int print_all_attributes(sd_device *device, bool is_parent) {
131 _cleanup_free_ SysAttr *sysattrs = NULL;
a1af8372 132 const char *value;
319a4f4b 133 size_t n_items = 0;
3a90bef5 134 int r;
912541b0 135
4881a0d2
YW
136 assert(device);
137
ce12bd19
YW
138 if (is_parent)
139 puts("");
140
21df1465
YW
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
6d6308f6 157 FOREACH_DEVICE_SYSATTR(device, name) {
912541b0
KS
158 size_t len;
159
912541b0
KS
160 if (skip_attribute(name))
161 continue;
162
3a90bef5
YW
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
a24e3938
LP
176 } else if (ERRNO_IS_PRIVILEGE(r))
177 value = "(not readable)";
3a90bef5 178 else
912541b0 179 continue;
912541b0 180
319a4f4b 181 if (!GREEDY_REALLOC(sysattrs, n_items + 1))
21df1465
YW
182 return log_oom();
183
184 sysattrs[n_items] = (SysAttr) {
185 .name = name,
186 .value = value,
187 };
188 n_items++;
912541b0 189 }
21df1465
YW
190
191 typesafe_qsort(sysattrs, n_items, sysattr_compare);
192
ce12bd19
YW
193 FOREACH_ARRAY(i, sysattrs, n_items)
194 printf(" %s{%s}==\"%s\"\n", is_parent ? "ATTRS" : "ATTR", i->name, i->value);
21df1465
YW
195
196 return 0;
be9b51f6
KS
197}
198
13aca847
YW
199static int print_device_chain(sd_device *device) {
200 sd_device *child, *parent;
21df1465 201 int r;
912541b0 202
4881a0d2
YW
203 assert(device);
204
912541b0
KS
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
ce12bd19 213 r = print_all_attributes(device, /* is_parent = */ false);
21df1465
YW
214 if (r < 0)
215 return r;
912541b0 216
13aca847 217 for (child = device; sd_device_get_parent(child, &parent) >= 0; child = parent) {
ce12bd19 218 r = print_all_attributes(parent, /* is_parent = */ true);
21df1465
YW
219 if (r < 0)
220 return r;
13aca847 221 }
912541b0
KS
222
223 return 0;
1aa1e248
KS
224}
225
67ea8a4c
YW
226static 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
308static int print_device_chain_in_json(sd_device *device) {
309 sd_device *child, *parent;
310 int r;
311
312 assert(device);
313
ebc64de2
LP
314 arg_json_format_flags |=SD_JSON_FORMAT_SEQ;
315
67ea8a4c
YW
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
9117d94b 329static int print_record(sd_device *device, const char *prefix) {
a1af8372 330 const char *str, *subsys;
a0e90259
LP
331 dev_t devnum;
332 uint64_t q;
333 int i, ifi;
912541b0 334
4881a0d2
YW
335 assert(device);
336
9117d94b
LP
337 prefix = strempty(prefix);
338
a0e90259
LP
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
13005c8f
LP
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 */
a0e90259
LP
355
356 assert_se(sd_device_get_devpath(device, &str) >= 0);
9117d94b 357 printf("%sP: %s%s%s\n", prefix, ansi_highlight_white(), str, ansi_normal());
912541b0 358
a0e90259 359 if (sd_device_get_sysname(device, &str) >= 0)
9117d94b 360 printf("%sM: %s%s%s\n", prefix, ansi_highlight_white(), str, ansi_normal());
a0e90259
LP
361
362 if (sd_device_get_sysnum(device, &str) >= 0)
9117d94b 363 printf("%sR: %s%s%s\n", prefix, ansi_highlight_white(), str, ansi_normal());
a0e90259 364
3fe0fd20
YW
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
a0e90259 368 if (sd_device_get_subsystem(device, &subsys) >= 0)
9117d94b 369 printf("%sU: %s%s%s\n", prefix, ansi_highlight_green(), subsys, ansi_normal());
a0e90259 370
3fe0fd20
YW
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
a0e90259 374 if (sd_device_get_devtype(device, &str) >= 0)
9117d94b 375 printf("%sT: %s%s%s\n", prefix, ansi_highlight_green(), str, ansi_normal());
a0e90259
LP
376
377 if (sd_device_get_devnum(device, &devnum) >= 0)
9117d94b
LP
378 printf("%sD: %s%c %u:%u%s\n",
379 prefix,
13005c8f
LP
380 ansi_highlight_cyan(),
381 streq_ptr(subsys, "block") ? 'b' : 'c', major(devnum), minor(devnum),
382 ansi_normal());
a0e90259
LP
383
384 if (sd_device_get_ifindex(device, &ifi) >= 0)
9117d94b 385 printf("%sI: %s%i%s\n", prefix, ansi_highlight_cyan(), ifi, ansi_normal());
a0e90259 386
d539f791 387 if (sd_device_get_devname(device, &str) >= 0) {
a1af8372
DDM
388 const char *val;
389
d539f791 390 assert_se(val = path_startswith(str, "/dev/"));
9117d94b 391 printf("%sN: %s%s%s\n", prefix, ansi_highlight_cyan(), val, ansi_normal());
912541b0 392
a0e90259 393 if (device_get_devlink_priority(device, &i) >= 0)
9117d94b 394 printf("%sL: %s%i%s\n", prefix, ansi_highlight_cyan(), i, ansi_normal());
912541b0 395
a1af8372
DDM
396 FOREACH_DEVICE_DEVLINK(device, link) {
397 assert_se(val = path_startswith(link, "/dev/"));
9117d94b 398 printf("%sS: %s%s%s\n", prefix, ansi_highlight_cyan(), val, ansi_normal());
a0e90259 399 }
d539f791 400 }
13aca847 401
a0e90259 402 if (sd_device_get_diskseq(device, &q) >= 0)
9117d94b 403 printf("%sQ: %s%" PRIu64 "%s\n", prefix, ansi_highlight_magenta(), q, ansi_normal());
a0e90259
LP
404
405 if (sd_device_get_driver(device, &str) >= 0)
9117d94b 406 printf("%sV: %s%s%s\n", prefix, ansi_highlight_yellow4(), str, ansi_normal());
a0e90259 407
a1af8372
DDM
408 FOREACH_DEVICE_PROPERTY(device, key, val)
409 printf("%sE: %s=%s\n", prefix, key, val);
912541b0 410
9117d94b
LP
411 if (isempty(prefix))
412 puts("");
668e7c0c 413 return 0;
31de3a2b
KS
414}
415
309a747f
LP
416static int record_to_json(sd_device *device, sd_json_variant **ret) {
417 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
d6e5f170
DDM
418 const char *str;
419 int r;
420
421 assert(device);
422 assert(ret);
423
3fe0fd20
YW
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. */
d6e5f170
DDM
427
428 if (sd_device_get_sysname(device, &str) >= 0) {
309a747f 429 r = sd_json_variant_set_field_string(&v, "SYSNAME", str);
d6e5f170
DDM
430 if (r < 0)
431 return r;
432 }
433
434 if (sd_device_get_sysnum(device, &str) >= 0) {
309a747f 435 r = sd_json_variant_set_field_string(&v, "SYSNUM", str);
d6e5f170
DDM
436 if (r < 0)
437 return r;
438 }
439
3fe0fd20
YW
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
d6e5f170 452 FOREACH_DEVICE_PROPERTY(device, key, val) {
309a747f 453 r = sd_json_variant_set_field_string(&v, key, val);
d6e5f170
DDM
454 if (r < 0)
455 return r;
456 }
457
458 *ret = TAKE_PTR(v);
459 return 0;
460}
461
4ef3019c 462static int stat_device(void) {
912541b0
KS
463 struct stat statbuf;
464
4ef3019c 465 assert(arg_name);
4881a0d2 466
4ef3019c 467 if (stat(arg_name, &statbuf) != 0)
755700bb 468 return -errno;
912541b0 469
4ef3019c
DT
470 if (arg_export) {
471 const char *prefix = arg_export_prefix ?: "INFO_";
1fa2f38f
ZJS
472 printf("%sMAJOR=%u\n"
473 "%sMINOR=%u\n",
912541b0
KS
474 prefix, major(statbuf.st_dev),
475 prefix, minor(statbuf.st_dev));
476 } else
1fa2f38f 477 printf("%u:%u\n", major(statbuf.st_dev), minor(statbuf.st_dev));
912541b0 478 return 0;
f338bac8
KS
479}
480
d89b3004
DT
481static 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
499static int setup_matches(sd_device_enumerator *e) {
13aca847 500 int r;
912541b0 501
a6b4b2fa 502 assert(e);
912541b0 503
d89b3004
DT
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
585static 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
13aca847
YW
602 r = device_enumerator_scan_devices(e);
603 if (r < 0)
df5a4889 604 return log_error_errno(r, "Failed to scan devices: %m");
13aca847
YW
605
606 FOREACH_DEVICE_AND_SUBSYSTEM(e, d)
23441a3d 607 if (sd_json_format_enabled(arg_json_format_flags)) {
309a747f 608 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
d6e5f170
DDM
609
610 r = record_to_json(d, &v);
611 if (r < 0)
612 return r;
613
309a747f 614 (void) sd_json_variant_dump(v, arg_json_format_flags, stdout, NULL);
23441a3d
ZJS
615 } else
616 (void) print_record(d, NULL);
755700bb 617
912541b0 618 return 0;
bf7ad0ea
KS
619}
620
9ec6e95b 621static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
4881a0d2
YW
622 assert(dir);
623
912541b0
KS
624 if (depth <= 0)
625 return;
626
8fb3f009 627 FOREACH_DIRENT_ALL(dent, dir, break) {
912541b0
KS
628 struct stat stats;
629
28d6e854 630 if (dot_or_dot_dot(dent->d_name))
912541b0 631 continue;
28d6e854 632 if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) < 0)
912541b0
KS
633 continue;
634 if ((stats.st_mode & mask) != 0)
635 continue;
636 if (S_ISDIR(stats.st_mode)) {
7b6d1683 637 _cleanup_closedir_ DIR *subdir = NULL;
912541b0 638
7b6d1683
LP
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);
200c7fa6
LP
644
645 (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
646 } else
647 (void) unlinkat(dirfd(dir), dent->d_name, 0);
912541b0 648 }
9ead6627
KS
649}
650
7ec62414
MW
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).
7ec62414 656 */
636ab001 657static void cleanup_dir_after_db_cleanup(DIR *dir, DIR *datadir) {
9e0bd1d6
YW
658 assert(dir);
659 assert(datadir);
7ec62414
MW
660
661 FOREACH_DIRENT_ALL(dent, dir, break) {
7ec62414
MW
662 if (dot_or_dot_dot(dent->d_name))
663 continue;
636ab001
YW
664
665 if (faccessat(dirfd(datadir), dent->d_name, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
666 /* The corresponding udev database file still exists.
2b4cdac9 667 * Assuming the persistent flag is set for the database. */
7ec62414 668 continue;
7ec62414 669
636ab001 670 (void) unlinkat(dirfd(dir), dent->d_name, 0);
7ec62414 671 }
7ec62414
MW
672}
673
674static void cleanup_dirs_after_db_cleanup(DIR *dir, DIR *datadir) {
9e0bd1d6
YW
675 assert(dir);
676 assert(datadir);
7ec62414
MW
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)) {
7b6d1683 686 _cleanup_closedir_ DIR *subdir = NULL;
7ec62414 687
7b6d1683
LP
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);
636ab001
YW
693
694 (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
7ec62414
MW
695 } else
696 (void) unlinkat(dirfd(dir), dent->d_name, 0);
697 }
698}
699
8ea3c1fb 700static int cleanup_db(void) {
bd979801 701 _cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL;
912541b0 702
755700bb 703 dir1 = opendir("/run/udev/data");
13aca847 704 if (dir1)
755700bb 705 cleanup_dir(dir1, S_ISVTX, 1);
912541b0 706
755700bb 707 dir2 = opendir("/run/udev/links");
13aca847 708 if (dir2)
7ec62414 709 cleanup_dirs_after_db_cleanup(dir2, dir1);
912541b0 710
755700bb 711 dir3 = opendir("/run/udev/tags");
13aca847 712 if (dir3)
7ec62414 713 cleanup_dirs_after_db_cleanup(dir3, dir1);
912541b0 714
755700bb 715 dir4 = opendir("/run/udev/static_node-tags");
13aca847 716 if (dir4)
755700bb 717 cleanup_dir(dir4, 0, 2);
84b6ad70 718
bd979801
YW
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. */
8ea3c1fb
DT
721
722 return 0;
9ead6627
KS
723}
724
668e7c0c
ZJS
725static int query_device(QueryType query, sd_device* device) {
726 int r;
727
728 assert(device);
729
79893116 730 switch (query) {
668e7c0c
ZJS
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
d539f791
ZJS
738 if (!arg_root)
739 assert_se(node = path_startswith(node, "/dev/"));
740 printf("%s\n", node);
668e7c0c
ZJS
741 return 0;
742 }
743
744 case QUERY_SYMLINK: {
a1af8372 745 const char *prefix = "";
668e7c0c
ZJS
746
747 FOREACH_DEVICE_DEVLINK(device, devlink) {
d539f791
ZJS
748 if (!arg_root)
749 assert_se(devlink = path_startswith(devlink, "/dev/"));
750 printf("%s%s", prefix, devlink);
751 prefix = " ";
668e7c0c 752 }
d539f791 753 puts("");
668e7c0c
ZJS
754 return 0;
755 }
756
757 case QUERY_PATH: {
758 const char *devpath;
5ac0162c 759
668e7c0c
ZJS
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
a1af8372 768 case QUERY_PROPERTY:
6c1482b2
FS
769 FOREACH_DEVICE_PROPERTY(device, key, value) {
770 if (arg_properties && !strv_contains(arg_properties, key))
771 continue;
772
668e7c0c
ZJS
773 if (arg_export)
774 printf("%s%s='%s'\n", strempty(arg_export_prefix), key, value);
6c1482b2
FS
775 else if (arg_value)
776 printf("%s\n", value);
668e7c0c
ZJS
777 else
778 printf("%s=%s\n", key, value);
6c1482b2
FS
779 }
780
668e7c0c 781 return 0;
668e7c0c
ZJS
782
783 case QUERY_ALL:
23441a3d 784 if (sd_json_format_enabled(arg_json_format_flags)) {
309a747f 785 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
d6e5f170
DDM
786
787 r = record_to_json(device, &v);
788 if (r < 0)
789 return r;
790
309a747f 791 (void) sd_json_variant_dump(v, arg_json_format_flags, stdout, NULL);
23441a3d
ZJS
792 } else
793 return print_record(device, NULL);
d6e5f170
DDM
794
795 return 0;
668e7c0c 796
4881a0d2
YW
797 default:
798 assert_not_reached();
799 }
668e7c0c
ZJS
800}
801
802static int help(void) {
5ac0162c
LP
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"
73527992 806 " -V --version Print version of the program\n"
5ac0162c
LP
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"
6c1482b2
FS
813 " --property=NAME Show only properties by this name\n"
814 " --value When showing properties, print only their values\n"
5ac0162c
LP
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"
9117d94b 820 " -t --tree Show tree of devices\n"
5ac0162c
LP
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"
ae760f4b 826 " -w --wait-for-initialization[=SECONDS]\n"
b6ec23a0 827 " Wait for device to be initialized\n"
d6e5f170 828 " --no-pager Do not pipe output into a pager\n"
a6b4b2fa
DDM
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",
bc556335 846 program_invocation_short_name);
ee4a776d
YW
847
848 return 0;
5ac0162c
LP
849}
850
9117d94b
LP
851static int draw_tree(
852 sd_device *parent,
853 sd_device *const array[], size_t n,
854 const char *prefix,
855 unsigned level);
856
857static 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
1ae9b0cf 872 printf("%s%s%s\n", prefix, glyph(more ? GLYPH_TREE_BRANCH : GLYPH_TREE_RIGHT), str);
9117d94b 873
1ae9b0cf 874 subprefix = strjoin(prefix, glyph(more ? GLYPH_TREE_VERTICAL : GLYPH_TREE_SPACE));
9117d94b
LP
875 if (!subprefix)
876 return log_oom();
877
1ae9b0cf 878 subsubprefix = strjoin(subprefix, glyph(GLYPH_VERTICAL_DOTTED), " ");
9117d94b
LP
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
887static 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
953static 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
bd4297e7
YW
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
9117d94b
LP
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
d89b3004 1008static int parse_argv(int argc, char *argv[]) {
912541b0 1009
6c1482b2
FS
1010 enum {
1011 ARG_PROPERTY = 0x100,
1012 ARG_VALUE,
b6ec23a0 1013 ARG_NO_PAGER,
d6e5f170 1014 ARG_JSON,
a6b4b2fa
DDM
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,
6c1482b2
FS
1026 };
1027
912541b0 1028 static const struct option options[] = {
a6b4b2fa
DDM
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 },
912541b0
KS
1058 {}
1059 };
1060
d89b3004
DT
1061 int c, r;
1062
1063 assert(argc >= 0);
1064 assert(argv);
912541b0 1065
9117d94b 1066 while ((c = getopt_long(argc, argv, "atced:n:p:q:rxP:w::Vh", options, NULL)) >= 0)
7643ac9a 1067 switch (c) {
d89b3004 1068
6c1482b2
FS
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;
d89b3004 1082
6c1482b2
FS
1083 case ARG_VALUE:
1084 arg_value = true;
1085 break;
d89b3004 1086
ee4a776d 1087 case 'n':
3c79311a
ZJS
1088 case 'p': {
1089 const char *prefix = c == 'n' ? "/dev/" : "/sys/";
1090 char *path;
4f5d327a 1091
3c79311a
ZJS
1092 path = path_join(path_startswith(optarg, prefix) ? NULL : prefix, optarg);
1093 if (!path)
1094 return log_oom();
4f5d327a 1095
d89b3004 1096 r = strv_consume(&arg_devices, path);
13aca847 1097 if (r < 0)
3c79311a 1098 return log_oom();
912541b0 1099 break;
3c79311a
ZJS
1100 }
1101
912541b0 1102 case 'q':
6cdc63d1
DT
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
a852f5c2 1108 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown query type '%s'", optarg);
6cdc63d1 1109 }
912541b0 1110 break;
d89b3004 1111
912541b0 1112 case 'r':
668e7c0c 1113 arg_root = true;
912541b0 1114 break;
d89b3004 1115
912541b0 1116 case 'd':
d89b3004
DT
1117 arg_action_type = ACTION_DEVICE_ID_FILE;
1118 r = free_and_strdup(&arg_name, optarg);
1cb7d29d 1119 if (r < 0)
c4abe719 1120 return log_oom();
912541b0 1121 break;
d89b3004 1122
912541b0 1123 case 'a':
d89b3004 1124 arg_action_type = ACTION_ATTRIBUTE_WALK;
912541b0 1125 break;
d89b3004 1126
9117d94b 1127 case 't':
d89b3004 1128 arg_action_type = ACTION_TREE;
9117d94b 1129 break;
d89b3004 1130
912541b0 1131 case 'e':
d89b3004 1132 arg_action_type = ACTION_EXPORT;
d6e5f170 1133 break;
d89b3004 1134
912541b0 1135 case 'c':
d89b3004
DT
1136 arg_action_type = ACTION_CLEANUP_DB;
1137 break;
1138
912541b0 1139 case 'x':
668e7c0c 1140 arg_export = true;
912541b0 1141 break;
d89b3004 1142
912541b0 1143 case 'P':
2277e845 1144 arg_export = true;
668e7c0c 1145 arg_export_prefix = optarg;
912541b0 1146 break;
d89b3004 1147
ae760f4b
YW
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;
d89b3004 1156
912541b0 1157 case 'V':
51b006e1 1158 return print_version();
d89b3004 1159
912541b0 1160 case 'h':
ee4a776d 1161 return help();
d89b3004 1162
b6ec23a0
ZJS
1163 case ARG_NO_PAGER:
1164 arg_pager_flags |= PAGER_DISABLE;
1165 break;
d6e5f170
DDM
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
a6b4b2fa 1173 case ARG_SUBSYSTEM_MATCH:
d89b3004 1174 r = strv_extend(&arg_subsystem_match, optarg);
a6b4b2fa 1175 if (r < 0)
d89b3004
DT
1176 return log_oom();
1177 break;
a6b4b2fa 1178
d89b3004
DT
1179 case ARG_SUBSYSTEM_NOMATCH:
1180 r = strv_extend(&arg_subsystem_nomatch, optarg);
a6b4b2fa 1181 if (r < 0)
d89b3004 1182 return log_oom();
a6b4b2fa
DDM
1183 break;
1184
1185 case ARG_ATTR_MATCH:
d89b3004
DT
1186 if (!strchr(optarg, '='))
1187 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
a852f5c2 1188 "Expected <ATTR>=<value> instead of '%s'", optarg);
a6b4b2fa 1189
d89b3004 1190 r = strv_extend(&arg_attr_match, optarg);
a6b4b2fa 1191 if (r < 0)
d89b3004 1192 return log_oom();
a6b4b2fa 1193 break;
a6b4b2fa 1194
d89b3004
DT
1195 case ARG_ATTR_NOMATCH:
1196 if (!strchr(optarg, '='))
1197 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
a852f5c2 1198 "Expected <ATTR>=<value> instead of '%s'", optarg);
a6b4b2fa 1199
d89b3004 1200 r = strv_extend(&arg_attr_nomatch, optarg);
a6b4b2fa 1201 if (r < 0)
d89b3004
DT
1202 return log_oom();
1203 break;
a6b4b2fa 1204
d89b3004
DT
1205 case ARG_PROPERTY_MATCH:
1206 if (!strchr(optarg, '='))
1207 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
a852f5c2 1208 "Expected <PROPERTY>=<value> instead of '%s'", optarg);
a6b4b2fa 1209
d89b3004 1210 r = strv_extend(&arg_property_match, optarg);
a6b4b2fa 1211 if (r < 0)
d89b3004 1212 return log_oom();
a6b4b2fa 1213 break;
a6b4b2fa
DDM
1214
1215 case ARG_TAG_MATCH:
d89b3004 1216 r = strv_extend(&arg_tag_match, optarg);
a6b4b2fa 1217 if (r < 0)
d89b3004 1218 return log_oom();
a6b4b2fa
DDM
1219 break;
1220
1221 case ARG_SYSNAME_MATCH:
d89b3004 1222 r = strv_extend(&arg_sysname_match, optarg);
a6b4b2fa 1223 if (r < 0)
d89b3004 1224 return log_oom();
a6b4b2fa
DDM
1225 break;
1226
1227 case ARG_NAME_MATCH:
d89b3004 1228 r = strv_extend(&arg_name_match, optarg);
a6b4b2fa 1229 if (r < 0)
d89b3004
DT
1230 return log_oom();
1231 break;
a6b4b2fa 1232
d89b3004
DT
1233 case ARG_PARENT_MATCH:
1234 r = strv_extend(&arg_parent_match, optarg);
a6b4b2fa 1235 if (r < 0)
d89b3004 1236 return log_oom();
a6b4b2fa 1237 break;
a6b4b2fa
DDM
1238
1239 case ARG_INITIALIZED_MATCH:
d89b3004
DT
1240 arg_initialized_match = MATCH_INITIALIZED_YES;
1241 break;
a6b4b2fa 1242
d89b3004
DT
1243 case ARG_INITIALIZED_NOMATCH:
1244 arg_initialized_match = MATCH_INITIALIZED_NO;
a6b4b2fa
DDM
1245 break;
1246
ee4a776d
YW
1247 case '?':
1248 return -EINVAL;
d89b3004 1249
912541b0 1250 default:
04499a70 1251 assert_not_reached();
912541b0 1252 }
912541b0 1253
d89b3004 1254 r = strv_extend_strv(&arg_devices, argv + optind, /* filter_duplicates= */ false);
3c79311a
ZJS
1255 if (r < 0)
1256 return log_error_errno(r, "Failed to build argument list: %m");
d89b3004 1257
31767b92 1258 if (IN_SET(arg_action_type, ACTION_DEVICE_ID_FILE, ACTION_CLEANUP_DB) && !strv_isempty(arg_devices))
d89b3004 1259 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
31767b92 1260 "Devices are not allowed with -d/--device-id-of-file and -c/--cleanup-db.");
19a29798 1261
d89b3004
DT
1262 if (!IN_SET(arg_action_type, ACTION_DEVICE_ID_FILE, ACTION_CLEANUP_DB, ACTION_EXPORT, ACTION_TREE) &&
1263 strv_isempty(arg_devices))
3c79311a
ZJS
1264 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1265 "A device name or path is required");
d89b3004
DT
1266
1267 if (IN_SET(arg_action_type, ACTION_ATTRIBUTE_WALK, ACTION_TREE) && strv_length(arg_devices) > 1)
3c79311a 1268 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
9117d94b 1269 "Only one device may be specified with -a/--attribute-walk and -t/--tree");
668e7c0c 1270
6c1482b2
FS
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
d89b3004
DT
1275 return 1;
1276}
1277
1278int info_main(int argc, char *argv[], void *userdata) {
337f45d8 1279 int r;
d89b3004
DT
1280
1281 r = parse_argv(argc, argv);
1282 if (r <= 0)
1283 return r;
1284
8ea3c1fb
DT
1285 if (arg_action_type == ACTION_CLEANUP_DB)
1286 return cleanup_db();
d89b3004
DT
1287
1288 if (arg_action_type == ACTION_DEVICE_ID_FILE)
4ef3019c 1289 return stat_device();
d89b3004 1290
cbef829f
ZJS
1291 pager_open(arg_pager_flags);
1292
d89b3004
DT
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);
9117d94b
LP
1298 return print_tree(NULL);
1299 }
1300
337f45d8 1301 int ret = 0;
d89b3004 1302 STRV_FOREACH(p, arg_devices) {
3c79311a
ZJS
1303 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
1304
d0ba749e 1305 r = find_device(*p, /* prefix = */ NULL, &device);
7ba77d8f
LP
1306 if (r < 0) {
1307 if (r == -EINVAL)
d0ba749e 1308 log_error_errno(r, "Bad argument \"%s\", expected an absolute path in /dev/ or /sys/, device ID, or a unit name: %m", *p);
7ba77d8f
LP
1309 else
1310 log_error_errno(r, "Unknown device \"%s\": %m", *p);
1311
337f45d8 1312 RET_GATHER(ret, r);
7ba77d8f
LP
1313 continue;
1314 }
3c79311a 1315
ae760f4b 1316 if (arg_wait_for_initialization_timeout > 0) {
7a555216
YW
1317 sd_device *d;
1318
9e3d9067
LP
1319 r = device_wait_for_initialization(
1320 device,
1321 NULL,
4f89ce0c 1322 arg_wait_for_initialization_timeout,
9e3d9067 1323 &d);
ae760f4b
YW
1324 if (r < 0)
1325 return r;
7a555216
YW
1326
1327 sd_device_unref(device);
1328 device = d;
ae760f4b
YW
1329 }
1330
d89b3004
DT
1331 if (arg_action_type == ACTION_QUERY)
1332 r = query_device(arg_query, device);
1333 else if (arg_action_type == ACTION_ATTRIBUTE_WALK) {
23441a3d 1334 if (sd_json_format_enabled(arg_json_format_flags))
67ea8a4c 1335 r = print_device_chain_in_json(device);
23441a3d
ZJS
1336 else
1337 r = print_device_chain(device);
d89b3004 1338 } else if (arg_action_type == ACTION_TREE)
9117d94b 1339 r = print_tree(device);
3c79311a 1340 else
04499a70 1341 assert_not_reached();
3c79311a
ZJS
1342 if (r < 0)
1343 return r;
912541b0 1344 }
87171e46 1345
7ba77d8f 1346 return ret;
87171e46 1347}