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