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