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