]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/udevadm-info.c
udevadm: improve error output when a device is not specified or specified wrong
[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 }
83 printf("\n");
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
13aca847 134 if (sd_device_get_devname(device, &str) >= 0)
fbd0b64f 135 printf("N: %s\n", str + STRLEN("/dev/"));
912541b0 136
13aca847 137 if (device_get_devlink_priority(device, &i) >= 0)
912541b0
KS
138 printf("L: %i\n", i);
139
13aca847
YW
140 FOREACH_DEVICE_DEVLINK(device, str)
141 printf("S: %s\n", str + STRLEN("/dev/"));
142
143 FOREACH_DEVICE_PROPERTY(device, str, val)
144 printf("E: %s=%s\n", str, val);
912541b0 145
912541b0 146 printf("\n");
668e7c0c 147 return 0;
31de3a2b
KS
148}
149
9ec6e95b 150static int stat_device(const char *name, bool export, const char *prefix) {
912541b0
KS
151 struct stat statbuf;
152
153 if (stat(name, &statbuf) != 0)
755700bb 154 return -errno;
912541b0
KS
155
156 if (export) {
13aca847 157 if (!prefix)
912541b0 158 prefix = "INFO_";
1fa2f38f
ZJS
159 printf("%sMAJOR=%u\n"
160 "%sMINOR=%u\n",
912541b0
KS
161 prefix, major(statbuf.st_dev),
162 prefix, minor(statbuf.st_dev));
163 } else
1fa2f38f 164 printf("%u:%u\n", major(statbuf.st_dev), minor(statbuf.st_dev));
912541b0 165 return 0;
f338bac8
KS
166}
167
2024ed61 168static int export_devices(void) {
13aca847
YW
169 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
170 sd_device *d;
171 int r;
912541b0 172
13aca847
YW
173 r = sd_device_enumerator_new(&e);
174 if (r < 0)
175 return r;
755700bb 176
13aca847
YW
177 r = sd_device_enumerator_allow_uninitialized(e);
178 if (r < 0)
179 return r;
912541b0 180
13aca847
YW
181 r = device_enumerator_scan_devices(e);
182 if (r < 0)
183 return r;
184
185 FOREACH_DEVICE_AND_SUBSYSTEM(e, d)
186 print_record(d);
755700bb 187
912541b0 188 return 0;
bf7ad0ea
KS
189}
190
9ec6e95b 191static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
912541b0
KS
192 struct dirent *dent;
193
194 if (depth <= 0)
195 return;
196
8fb3f009 197 FOREACH_DIRENT_ALL(dent, dir, break) {
912541b0
KS
198 struct stat stats;
199
200 if (dent->d_name[0] == '.')
201 continue;
202 if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0)
203 continue;
204 if ((stats.st_mode & mask) != 0)
205 continue;
206 if (S_ISDIR(stats.st_mode)) {
13aca847 207 _cleanup_closedir_ DIR *dir2 = NULL;
912541b0
KS
208
209 dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
13aca847 210 if (dir2)
912541b0 211 cleanup_dir(dir2, mask, depth-1);
200c7fa6
LP
212
213 (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
214 } else
215 (void) unlinkat(dirfd(dir), dent->d_name, 0);
912541b0 216 }
9ead6627
KS
217}
218
2024ed61 219static void cleanup_db(void) {
755700bb 220 _cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL, *dir5 = NULL;
912541b0 221
755700bb 222 (void) unlink("/run/udev/queue.bin");
912541b0 223
755700bb 224 dir1 = opendir("/run/udev/data");
13aca847 225 if (dir1)
755700bb 226 cleanup_dir(dir1, S_ISVTX, 1);
912541b0 227
755700bb 228 dir2 = opendir("/run/udev/links");
13aca847 229 if (dir2)
755700bb 230 cleanup_dir(dir2, 0, 2);
912541b0 231
755700bb 232 dir3 = opendir("/run/udev/tags");
13aca847 233 if (dir3)
755700bb 234 cleanup_dir(dir3, 0, 2);
912541b0 235
755700bb 236 dir4 = opendir("/run/udev/static_node-tags");
13aca847 237 if (dir4)
755700bb 238 cleanup_dir(dir4, 0, 2);
84b6ad70 239
755700bb 240 dir5 = opendir("/run/udev/watch");
13aca847 241 if (dir5)
755700bb 242 cleanup_dir(dir5, 0, 1);
9ead6627
KS
243}
244
668e7c0c
ZJS
245static int query_device(QueryType query, sd_device* device) {
246 int r;
247
248 assert(device);
249
250 switch(query) {
251 case QUERY_NAME: {
252 const char *node;
253
254 r = sd_device_get_devname(device, &node);
255 if (r < 0)
256 return log_error_errno(r, "No device node found: %m");
257
258 if (arg_root)
259 printf("%s\n", node);
260 else
261 printf("%s\n", node + STRLEN("/dev/"));
262 return 0;
263 }
264
265 case QUERY_SYMLINK: {
266 const char *devlink;
267 bool first = true;
268
269 FOREACH_DEVICE_DEVLINK(device, devlink) {
270 if (!first)
271 printf(" ");
272 if (arg_root)
273 printf("%s", devlink);
274 else
275 printf("%s", devlink + STRLEN("/dev/"));
276
277 first = false;
278 }
279 printf("\n");
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"
334 , program_invocation_short_name);
ee4a776d
YW
335
336 return 0;
5ac0162c
LP
337}
338
3d05193e 339int info_main(int argc, char *argv[], void *userdata) {
13aca847 340 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
c4abe719 341 _cleanup_free_ char *name = NULL;
ee4a776d 342 int c, r;
912541b0
KS
343
344 static const struct option options[] = {
7643ac9a
ZJS
345 { "name", required_argument, NULL, 'n' },
346 { "path", required_argument, NULL, 'p' },
347 { "query", required_argument, NULL, 'q' },
348 { "attribute-walk", no_argument, NULL, 'a' },
349 { "cleanup-db", no_argument, NULL, 'c' },
350 { "export-db", no_argument, NULL, 'e' },
351 { "root", no_argument, NULL, 'r' },
912541b0 352 { "device-id-of-file", required_argument, NULL, 'd' },
7643ac9a
ZJS
353 { "export", no_argument, NULL, 'x' },
354 { "export-prefix", required_argument, NULL, 'P' },
355 { "version", no_argument, NULL, 'V' },
356 { "help", no_argument, NULL, 'h' },
912541b0
KS
357 {}
358 };
359
668e7c0c
ZJS
360 ActionType action = ACTION_QUERY;
361 QueryType query = QUERY_ALL;
912541b0 362
7643ac9a
ZJS
363 while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL)) >= 0)
364 switch (c) {
ee4a776d
YW
365 case 'n':
366 if (device) {
367 log_error("device already specified");
368 return -EINVAL;
912541b0 369 }
4f5d327a 370
13aca847
YW
371 r = find_device(optarg, "/dev/", &device);
372 if (r < 0)
373 return log_error_errno(r, "device node not found: %m");
912541b0
KS
374 break;
375 case 'p':
ee4a776d
YW
376 if (device) {
377 log_error("device already specified");
378 return -EINVAL;
912541b0 379 }
4f5d327a 380
13aca847
YW
381 r = find_device(optarg, "/sys", &device);
382 if (r < 0)
383 return log_error_errno(r, "syspath not found: %m");
912541b0
KS
384 break;
385 case 'q':
386 action = ACTION_QUERY;
44433ebd 387 if (streq(optarg, "property") || streq(optarg, "env"))
912541b0 388 query = QUERY_PROPERTY;
44433ebd 389 else if (streq(optarg, "name"))
912541b0 390 query = QUERY_NAME;
44433ebd 391 else if (streq(optarg, "symlink"))
912541b0 392 query = QUERY_SYMLINK;
44433ebd 393 else if (streq(optarg, "path"))
912541b0 394 query = QUERY_PATH;
44433ebd 395 else if (streq(optarg, "all"))
912541b0 396 query = QUERY_ALL;
44433ebd 397 else {
ee4a776d
YW
398 log_error("unknown query type");
399 return -EINVAL;
912541b0
KS
400 }
401 break;
402 case 'r':
668e7c0c 403 arg_root = true;
912541b0 404 break;
912541b0
KS
405 case 'd':
406 action = ACTION_DEVICE_ID_FILE;
1cb7d29d
YW
407 r = free_and_strdup(&name, optarg);
408 if (r < 0)
c4abe719 409 return log_oom();
912541b0
KS
410 break;
411 case 'a':
412 action = ACTION_ATTRIBUTE_WALK;
413 break;
414 case 'e':
ee4a776d 415 return export_devices();
912541b0 416 case 'c':
2024ed61 417 cleanup_db();
44433ebd 418 return 0;
912541b0 419 case 'x':
668e7c0c 420 arg_export = true;
912541b0
KS
421 break;
422 case 'P':
668e7c0c 423 arg_export_prefix = optarg;
912541b0
KS
424 break;
425 case 'V':
51b006e1 426 return print_version();
912541b0 427 case 'h':
ee4a776d
YW
428 return help();
429 case '?':
430 return -EINVAL;
912541b0 431 default:
ee4a776d 432 assert_not_reached("Unknown option");
912541b0 433 }
912541b0 434
668e7c0c
ZJS
435 if (IN_SET(action, ACTION_QUERY, ACTION_ATTRIBUTE_WALK) &&
436 !device) {
437 /* A device argument is required. It may be an option or a positional arg. */
912541b0 438
668e7c0c
ZJS
439 if (!argv[optind])
440 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
441 "A device name or path is required");
912541b0 442
668e7c0c
ZJS
443 r = find_device(argv[optind], NULL, &device);
444 if (r == -EINVAL)
445 return log_error_errno(r, "Bad argument \"%s\", an absolute path in /dev/ or /sys expected: %m",
446 argv[optind]);
447 if (r < 0)
448 return log_error_errno(r, "Unknown device \"%s\": %m", argv[optind]);
449 }
13aca847 450
668e7c0c
ZJS
451 switch (action) {
452 case ACTION_QUERY:
453 assert(device);
454 return query_device(query, device);
19a29798 455
912541b0 456 case ACTION_ATTRIBUTE_WALK:
668e7c0c
ZJS
457 assert(device);
458 return print_device_chain(device);
459
912541b0 460 case ACTION_DEVICE_ID_FILE:
668e7c0c
ZJS
461 assert(name);
462 return stat_device(name, arg_export, arg_export_prefix);
912541b0 463 }
87171e46 464
668e7c0c 465 assert_not_reached("Unknown action");
87171e46 466}