]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/udevadm-info.c
udevadm: cleanup_dir: use dot_or_dot_dot()
[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"
3ffd4af2 19#include "fd-util.h"
21df1465 20#include "sort-util.h"
6c1482b2 21#include "static-destruct.h"
0f4b93c4 22#include "string-table.h"
07630cea 23#include "string-util.h"
ae760f4b 24#include "udev-util.h"
d6170d27 25#include "udevadm-util.h"
0f4b93c4 26#include "udevadm.h"
be9b51f6 27
668e7c0c
ZJS
28typedef enum ActionType {
29 ACTION_QUERY,
30 ACTION_ATTRIBUTE_WALK,
31 ACTION_DEVICE_ID_FILE,
32} ActionType;
33
34typedef enum QueryType {
35 QUERY_NAME,
36 QUERY_PATH,
37 QUERY_SYMLINK,
38 QUERY_PROPERTY,
39 QUERY_ALL,
40} QueryType;
41
6c1482b2 42static char **arg_properties = NULL;
668e7c0c
ZJS
43static bool arg_root = false;
44static bool arg_export = false;
6c1482b2 45static bool arg_value = false;
668e7c0c 46static const char *arg_export_prefix = NULL;
ae760f4b 47static usec_t arg_wait_for_initialization_timeout = 0;
668e7c0c 48
9ec6e95b 49static bool skip_attribute(const char *name) {
5992f362
ZJS
50 /* Those are either displayed separately or should not be shown at all. */
51 return STR_IN_SET(name,
52 "uevent",
53 "dev",
54 "modalias",
55 "resource",
56 "driver",
57 "subsystem",
58 "module");
20bee04c
KS
59}
60
21df1465
YW
61typedef struct SysAttr {
62 const char *name;
63 const char *value;
64} SysAttr;
65
6c1482b2
FS
66STATIC_DESTRUCTOR_REGISTER(arg_properties, strv_freep);
67
21df1465
YW
68static int sysattr_compare(const SysAttr *a, const SysAttr *b) {
69 return strcmp(a->name, b->name);
70}
71
72static int print_all_attributes(sd_device *device, bool is_parent) {
73 _cleanup_free_ SysAttr *sysattrs = NULL;
13aca847 74 const char *name, *value;
319a4f4b 75 size_t n_items = 0;
3a90bef5 76 int r;
912541b0 77
21df1465
YW
78 value = NULL;
79 (void) sd_device_get_devpath(device, &value);
80 printf(" looking at %sdevice '%s':\n", is_parent ? "parent " : "", strempty(value));
81
82 value = NULL;
83 (void) sd_device_get_sysname(device, &value);
84 printf(" %s==\"%s\"\n", is_parent ? "KERNELS" : "KERNEL", strempty(value));
85
86 value = NULL;
87 (void) sd_device_get_subsystem(device, &value);
88 printf(" %s==\"%s\"\n", is_parent ? "SUBSYSTEMS" : "SUBSYSTEM", strempty(value));
89
90 value = NULL;
91 (void) sd_device_get_driver(device, &value);
92 printf(" %s==\"%s\"\n", is_parent ? "DRIVERS" : "DRIVER", strempty(value));
93
6d6308f6 94 FOREACH_DEVICE_SYSATTR(device, name) {
912541b0
KS
95 size_t len;
96
912541b0
KS
97 if (skip_attribute(name))
98 continue;
99
3a90bef5
YW
100 r = sd_device_get_sysattr_value(device, name, &value);
101 if (r >= 0) {
102 /* skip any values that look like a path */
103 if (value[0] == '/')
104 continue;
105
106 /* skip nonprintable attributes */
107 len = strlen(value);
108 while (len > 0 && isprint((unsigned char) value[len-1]))
109 len--;
110 if (len > 0)
111 continue;
112
113 } else if (r == -EPERM)
114 value = "(write-only)";
115 else
912541b0 116 continue;
912541b0 117
319a4f4b 118 if (!GREEDY_REALLOC(sysattrs, n_items + 1))
21df1465
YW
119 return log_oom();
120
121 sysattrs[n_items] = (SysAttr) {
122 .name = name,
123 .value = value,
124 };
125 n_items++;
912541b0 126 }
21df1465
YW
127
128 typesafe_qsort(sysattrs, n_items, sysattr_compare);
129
130 for (size_t i = 0; i < n_items; i++)
131 printf(" %s{%s}==\"%s\"\n", is_parent ? "ATTRS" : "ATTR", sysattrs[i].name, sysattrs[i].value);
132
d539f791 133 puts("");
21df1465
YW
134
135 return 0;
be9b51f6
KS
136}
137
13aca847
YW
138static int print_device_chain(sd_device *device) {
139 sd_device *child, *parent;
21df1465 140 int r;
912541b0
KS
141
142 printf("\n"
143 "Udevadm info starts with the device specified by the devpath and then\n"
144 "walks up the chain of parent devices. It prints for every device\n"
145 "found, all possible attributes in the udev rules key format.\n"
146 "A rule to match, can be composed by the attributes of the device\n"
147 "and the attributes from one single parent device.\n"
148 "\n");
149
21df1465
YW
150 r = print_all_attributes(device, false);
151 if (r < 0)
152 return r;
912541b0 153
13aca847 154 for (child = device; sd_device_get_parent(child, &parent) >= 0; child = parent) {
21df1465
YW
155 r = print_all_attributes(parent, true);
156 if (r < 0)
157 return r;
13aca847 158 }
912541b0
KS
159
160 return 0;
1aa1e248
KS
161}
162
668e7c0c 163static int print_record(sd_device *device) {
13aca847 164 const char *str, *val;
912541b0 165 int i;
912541b0 166
13aca847
YW
167 (void) sd_device_get_devpath(device, &str);
168 printf("P: %s\n", str);
912541b0 169
d539f791
ZJS
170 if (sd_device_get_devname(device, &str) >= 0) {
171 assert_se(val = path_startswith(str, "/dev/"));
172 printf("N: %s\n", val);
173 }
912541b0 174
13aca847 175 if (device_get_devlink_priority(device, &i) >= 0)
912541b0
KS
176 printf("L: %i\n", i);
177
d539f791
ZJS
178 FOREACH_DEVICE_DEVLINK(device, str) {
179 assert_se(val = path_startswith(str, "/dev/"));
180 printf("S: %s\n", val);
181 }
13aca847
YW
182
183 FOREACH_DEVICE_PROPERTY(device, str, val)
184 printf("E: %s=%s\n", str, val);
912541b0 185
d539f791 186 puts("");
668e7c0c 187 return 0;
31de3a2b
KS
188}
189
9ec6e95b 190static int stat_device(const char *name, bool export, const char *prefix) {
912541b0
KS
191 struct stat statbuf;
192
193 if (stat(name, &statbuf) != 0)
755700bb 194 return -errno;
912541b0
KS
195
196 if (export) {
13aca847 197 if (!prefix)
912541b0 198 prefix = "INFO_";
1fa2f38f
ZJS
199 printf("%sMAJOR=%u\n"
200 "%sMINOR=%u\n",
912541b0
KS
201 prefix, major(statbuf.st_dev),
202 prefix, minor(statbuf.st_dev));
203 } else
1fa2f38f 204 printf("%u:%u\n", major(statbuf.st_dev), minor(statbuf.st_dev));
912541b0 205 return 0;
f338bac8
KS
206}
207
2024ed61 208static int export_devices(void) {
13aca847
YW
209 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
210 sd_device *d;
211 int r;
912541b0 212
13aca847
YW
213 r = sd_device_enumerator_new(&e);
214 if (r < 0)
df5a4889 215 return log_oom();
755700bb 216
13aca847
YW
217 r = sd_device_enumerator_allow_uninitialized(e);
218 if (r < 0)
df5a4889 219 return log_error_errno(r, "Failed to set allowing uninitialized flag: %m");
912541b0 220
13aca847
YW
221 r = device_enumerator_scan_devices(e);
222 if (r < 0)
df5a4889 223 return log_error_errno(r, "Failed to scan devices: %m");
13aca847
YW
224
225 FOREACH_DEVICE_AND_SUBSYSTEM(e, d)
df5a4889 226 (void) print_record(d);
755700bb 227
912541b0 228 return 0;
bf7ad0ea
KS
229}
230
9ec6e95b 231static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
912541b0
KS
232 if (depth <= 0)
233 return;
234
28d6e854
MW
235 assert(dir);
236
8fb3f009 237 FOREACH_DIRENT_ALL(dent, dir, break) {
912541b0
KS
238 struct stat stats;
239
28d6e854 240 if (dot_or_dot_dot(dent->d_name))
912541b0 241 continue;
28d6e854 242 if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) < 0)
912541b0
KS
243 continue;
244 if ((stats.st_mode & mask) != 0)
245 continue;
246 if (S_ISDIR(stats.st_mode)) {
13aca847 247 _cleanup_closedir_ DIR *dir2 = NULL;
912541b0
KS
248
249 dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
13aca847 250 if (dir2)
912541b0 251 cleanup_dir(dir2, mask, depth-1);
200c7fa6
LP
252
253 (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
254 } else
255 (void) unlinkat(dirfd(dir), dent->d_name, 0);
912541b0 256 }
9ead6627
KS
257}
258
2024ed61 259static void cleanup_db(void) {
755700bb 260 _cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL, *dir5 = NULL;
912541b0 261
755700bb 262 dir1 = opendir("/run/udev/data");
13aca847 263 if (dir1)
755700bb 264 cleanup_dir(dir1, S_ISVTX, 1);
912541b0 265
755700bb 266 dir2 = opendir("/run/udev/links");
13aca847 267 if (dir2)
755700bb 268 cleanup_dir(dir2, 0, 2);
912541b0 269
755700bb 270 dir3 = opendir("/run/udev/tags");
13aca847 271 if (dir3)
755700bb 272 cleanup_dir(dir3, 0, 2);
912541b0 273
755700bb 274 dir4 = opendir("/run/udev/static_node-tags");
13aca847 275 if (dir4)
755700bb 276 cleanup_dir(dir4, 0, 2);
84b6ad70 277
755700bb 278 dir5 = opendir("/run/udev/watch");
13aca847 279 if (dir5)
755700bb 280 cleanup_dir(dir5, 0, 1);
9ead6627
KS
281}
282
668e7c0c
ZJS
283static int query_device(QueryType query, sd_device* device) {
284 int r;
285
286 assert(device);
287
288 switch(query) {
289 case QUERY_NAME: {
290 const char *node;
291
292 r = sd_device_get_devname(device, &node);
293 if (r < 0)
294 return log_error_errno(r, "No device node found: %m");
295
d539f791
ZJS
296 if (!arg_root)
297 assert_se(node = path_startswith(node, "/dev/"));
298 printf("%s\n", node);
668e7c0c
ZJS
299 return 0;
300 }
301
302 case QUERY_SYMLINK: {
d539f791 303 const char *devlink, *prefix = "";
668e7c0c
ZJS
304
305 FOREACH_DEVICE_DEVLINK(device, devlink) {
d539f791
ZJS
306 if (!arg_root)
307 assert_se(devlink = path_startswith(devlink, "/dev/"));
308 printf("%s%s", prefix, devlink);
309 prefix = " ";
668e7c0c 310 }
d539f791 311 puts("");
668e7c0c
ZJS
312 return 0;
313 }
314
315 case QUERY_PATH: {
316 const char *devpath;
5ac0162c 317
668e7c0c
ZJS
318 r = sd_device_get_devpath(device, &devpath);
319 if (r < 0)
320 return log_error_errno(r, "Failed to get device path: %m");
321
322 printf("%s\n", devpath);
323 return 0;
324 }
325
326 case QUERY_PROPERTY: {
327 const char *key, *value;
328
6c1482b2
FS
329 FOREACH_DEVICE_PROPERTY(device, key, value) {
330 if (arg_properties && !strv_contains(arg_properties, key))
331 continue;
332
668e7c0c
ZJS
333 if (arg_export)
334 printf("%s%s='%s'\n", strempty(arg_export_prefix), key, value);
6c1482b2
FS
335 else if (arg_value)
336 printf("%s\n", value);
668e7c0c
ZJS
337 else
338 printf("%s=%s\n", key, value);
6c1482b2
FS
339 }
340
668e7c0c
ZJS
341 return 0;
342 }
343
344 case QUERY_ALL:
345 return print_record(device);
346 }
347
04499a70 348 assert_not_reached();
668e7c0c
ZJS
349 return 0;
350}
351
352static int help(void) {
5ac0162c
LP
353 printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
354 "Query sysfs or the udev database.\n\n"
355 " -h --help Print this message\n"
73527992 356 " -V --version Print version of the program\n"
5ac0162c
LP
357 " -q --query=TYPE Query device information:\n"
358 " name Name of device node\n"
359 " symlink Pointing to node\n"
360 " path sysfs device path\n"
361 " property The device properties\n"
362 " all All values\n"
6c1482b2
FS
363 " --property=NAME Show only properties by this name\n"
364 " --value When showing properties, print only their values\n"
5ac0162c
LP
365 " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
366 " -n --name=NAME Node or symlink name used for query or attribute walk\n"
367 " -r --root Prepend dev directory to path names\n"
368 " -a --attribute-walk Print all key matches walking along the chain\n"
369 " of parent devices\n"
370 " -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
371 " -x --export Export key/value pairs\n"
372 " -P --export-prefix Export the key name with a prefix\n"
373 " -e --export-db Export the content of the udev database\n"
374 " -c --cleanup-db Clean up the udev database\n"
ae760f4b 375 " -w --wait-for-initialization[=SECONDS]\n"
bc556335
DDM
376 " Wait for device to be initialized\n",
377 program_invocation_short_name);
ee4a776d
YW
378
379 return 0;
5ac0162c
LP
380}
381
3d05193e 382int info_main(int argc, char *argv[], void *userdata) {
3c79311a 383 _cleanup_strv_free_ char **devices = NULL;
c4abe719 384 _cleanup_free_ char *name = NULL;
ee4a776d 385 int c, r;
912541b0 386
6c1482b2
FS
387 enum {
388 ARG_PROPERTY = 0x100,
389 ARG_VALUE,
390 };
391
912541b0 392 static const struct option options[] = {
6c1482b2
FS
393 { "attribute-walk", no_argument, NULL, 'a' },
394 { "cleanup-db", no_argument, NULL, 'c' },
395 { "device-id-of-file", required_argument, NULL, 'd' },
396 { "export", no_argument, NULL, 'x' },
397 { "export-db", no_argument, NULL, 'e' },
398 { "export-prefix", required_argument, NULL, 'P' },
399 { "help", no_argument, NULL, 'h' },
400 { "name", required_argument, NULL, 'n' },
401 { "path", required_argument, NULL, 'p' },
402 { "property", required_argument, NULL, ARG_PROPERTY },
403 { "query", required_argument, NULL, 'q' },
404 { "root", no_argument, NULL, 'r' },
405 { "value", no_argument, NULL, ARG_VALUE },
406 { "version", no_argument, NULL, 'V' },
407 { "wait-for-initialization", optional_argument, NULL, 'w' },
912541b0
KS
408 {}
409 };
410
668e7c0c
ZJS
411 ActionType action = ACTION_QUERY;
412 QueryType query = QUERY_ALL;
912541b0 413
ae760f4b 414 while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:w::Vh", options, NULL)) >= 0)
7643ac9a 415 switch (c) {
6c1482b2
FS
416 case ARG_PROPERTY:
417 /* Make sure that if the empty property list was specified, we won't show any
418 properties. */
419 if (isempty(optarg) && !arg_properties) {
420 arg_properties = new0(char*, 1);
421 if (!arg_properties)
422 return log_oom();
423 } else {
424 r = strv_split_and_extend(&arg_properties, optarg, ",", true);
425 if (r < 0)
426 return log_oom();
427 }
428 break;
429 case ARG_VALUE:
430 arg_value = true;
431 break;
ee4a776d 432 case 'n':
3c79311a
ZJS
433 case 'p': {
434 const char *prefix = c == 'n' ? "/dev/" : "/sys/";
435 char *path;
4f5d327a 436
3c79311a
ZJS
437 path = path_join(path_startswith(optarg, prefix) ? NULL : prefix, optarg);
438 if (!path)
439 return log_oom();
4f5d327a 440
3c79311a 441 r = strv_consume(&devices, path);
13aca847 442 if (r < 0)
3c79311a 443 return log_oom();
912541b0 444 break;
3c79311a
ZJS
445 }
446
912541b0
KS
447 case 'q':
448 action = ACTION_QUERY;
44433ebd 449 if (streq(optarg, "property") || streq(optarg, "env"))
912541b0 450 query = QUERY_PROPERTY;
44433ebd 451 else if (streq(optarg, "name"))
912541b0 452 query = QUERY_NAME;
44433ebd 453 else if (streq(optarg, "symlink"))
912541b0 454 query = QUERY_SYMLINK;
44433ebd 455 else if (streq(optarg, "path"))
912541b0 456 query = QUERY_PATH;
44433ebd 457 else if (streq(optarg, "all"))
912541b0 458 query = QUERY_ALL;
47c8fcbe
YW
459 else
460 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "unknown query type");
912541b0
KS
461 break;
462 case 'r':
668e7c0c 463 arg_root = true;
912541b0 464 break;
912541b0
KS
465 case 'd':
466 action = ACTION_DEVICE_ID_FILE;
1cb7d29d
YW
467 r = free_and_strdup(&name, optarg);
468 if (r < 0)
c4abe719 469 return log_oom();
912541b0
KS
470 break;
471 case 'a':
472 action = ACTION_ATTRIBUTE_WALK;
473 break;
474 case 'e':
ee4a776d 475 return export_devices();
912541b0 476 case 'c':
2024ed61 477 cleanup_db();
44433ebd 478 return 0;
912541b0 479 case 'x':
668e7c0c 480 arg_export = true;
912541b0
KS
481 break;
482 case 'P':
2277e845 483 arg_export = true;
668e7c0c 484 arg_export_prefix = optarg;
912541b0 485 break;
ae760f4b
YW
486 case 'w':
487 if (optarg) {
488 r = parse_sec(optarg, &arg_wait_for_initialization_timeout);
489 if (r < 0)
490 return log_error_errno(r, "Failed to parse timeout value: %m");
491 } else
492 arg_wait_for_initialization_timeout = USEC_INFINITY;
493 break;
912541b0 494 case 'V':
51b006e1 495 return print_version();
912541b0 496 case 'h':
ee4a776d
YW
497 return help();
498 case '?':
499 return -EINVAL;
912541b0 500 default:
04499a70 501 assert_not_reached();
912541b0 502 }
912541b0 503
3c79311a
ZJS
504 if (action == ACTION_DEVICE_ID_FILE) {
505 if (argv[optind])
668e7c0c 506 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
3c79311a
ZJS
507 "Positional arguments are not allowed with -d/--device-id-of-file.");
508 assert(name);
509 return stat_device(name, arg_export, arg_export_prefix);
668e7c0c 510 }
13aca847 511
3c79311a
ZJS
512 r = strv_extend_strv(&devices, argv + optind, false);
513 if (r < 0)
514 return log_error_errno(r, "Failed to build argument list: %m");
19a29798 515
3c79311a
ZJS
516 if (strv_isempty(devices))
517 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
518 "A device name or path is required");
519 if (action == ACTION_ATTRIBUTE_WALK && strv_length(devices) > 1)
520 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
521 "Only one device may be specified with -a/--attribute-walk");
668e7c0c 522
6c1482b2
FS
523 if (arg_export && arg_value)
524 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
525 "-x/--export or -P/--export-prefix cannot be used with --value");
526
3c79311a
ZJS
527 char **p;
528 STRV_FOREACH(p, devices) {
529 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
530
531 r = find_device(*p, NULL, &device);
532 if (r == -EINVAL)
533 return log_error_errno(r, "Bad argument \"%s\", expected an absolute path in /dev/ or /sys or a unit name: %m", *p);
534 if (r < 0)
535 return log_error_errno(r, "Unknown device \"%s\": %m", *p);
536
ae760f4b 537 if (arg_wait_for_initialization_timeout > 0) {
7a555216
YW
538 sd_device *d;
539
9e3d9067
LP
540 r = device_wait_for_initialization(
541 device,
542 NULL,
543 usec_add(now(CLOCK_MONOTONIC), arg_wait_for_initialization_timeout),
544 &d);
ae760f4b
YW
545 if (r < 0)
546 return r;
7a555216
YW
547
548 sd_device_unref(device);
549 device = d;
ae760f4b
YW
550 }
551
3c79311a
ZJS
552 if (action == ACTION_QUERY)
553 r = query_device(query, device);
554 else if (action == ACTION_ATTRIBUTE_WALK)
555 r = print_device_chain(device);
556 else
04499a70 557 assert_not_reached();
3c79311a
ZJS
558 if (r < 0)
559 return r;
912541b0 560 }
87171e46 561
3c79311a 562 return 0;
87171e46 563}