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