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