]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/udevadm-info.c
udev: use string_table_lookup() where we can
[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"
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;
43
9ec6e95b 44static bool skip_attribute(const char *name) {
46512cd9 45 static const char* const skip[] = {
912541b0
KS
46 "uevent",
47 "dev",
48 "modalias",
49 "resource",
50 "driver",
51 "subsystem",
52 "module",
53 };
912541b0 54
0f4b93c4 55 return string_table_lookup(skip, ELEMENTSOF(skip), name) >= 0;
20bee04c
KS
56}
57
13aca847
YW
58static void print_all_attributes(sd_device *device, const char *key) {
59 const char *name, *value;
912541b0 60
6d6308f6 61 FOREACH_DEVICE_SYSATTR(device, name) {
912541b0
KS
62 size_t len;
63
912541b0
KS
64 if (skip_attribute(name))
65 continue;
66
6d6308f6
MM
67 if (sd_device_get_sysattr_value(device, name, &value) < 0)
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;
47c8fcbe
YW
390 else
391 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "unknown query type");
912541b0
KS
392 break;
393 case 'r':
668e7c0c 394 arg_root = true;
912541b0 395 break;
912541b0
KS
396 case 'd':
397 action = ACTION_DEVICE_ID_FILE;
1cb7d29d
YW
398 r = free_and_strdup(&name, optarg);
399 if (r < 0)
c4abe719 400 return log_oom();
912541b0
KS
401 break;
402 case 'a':
403 action = ACTION_ATTRIBUTE_WALK;
404 break;
405 case 'e':
ee4a776d 406 return export_devices();
912541b0 407 case 'c':
2024ed61 408 cleanup_db();
44433ebd 409 return 0;
912541b0 410 case 'x':
668e7c0c 411 arg_export = true;
912541b0
KS
412 break;
413 case 'P':
2277e845 414 arg_export = true;
668e7c0c 415 arg_export_prefix = optarg;
912541b0
KS
416 break;
417 case 'V':
51b006e1 418 return print_version();
912541b0 419 case 'h':
ee4a776d
YW
420 return help();
421 case '?':
422 return -EINVAL;
912541b0 423 default:
ee4a776d 424 assert_not_reached("Unknown option");
912541b0 425 }
912541b0 426
3c79311a
ZJS
427 if (action == ACTION_DEVICE_ID_FILE) {
428 if (argv[optind])
668e7c0c 429 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
3c79311a
ZJS
430 "Positional arguments are not allowed with -d/--device-id-of-file.");
431 assert(name);
432 return stat_device(name, arg_export, arg_export_prefix);
668e7c0c 433 }
13aca847 434
3c79311a
ZJS
435 r = strv_extend_strv(&devices, argv + optind, false);
436 if (r < 0)
437 return log_error_errno(r, "Failed to build argument list: %m");
19a29798 438
3c79311a
ZJS
439 if (strv_isempty(devices))
440 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
441 "A device name or path is required");
442 if (action == ACTION_ATTRIBUTE_WALK && strv_length(devices) > 1)
443 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
444 "Only one device may be specified with -a/--attribute-walk");
668e7c0c 445
3c79311a
ZJS
446 char **p;
447 STRV_FOREACH(p, devices) {
448 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
449
450 r = find_device(*p, NULL, &device);
451 if (r == -EINVAL)
452 return log_error_errno(r, "Bad argument \"%s\", expected an absolute path in /dev/ or /sys or a unit name: %m", *p);
453 if (r < 0)
454 return log_error_errno(r, "Unknown device \"%s\": %m", *p);
455
456 if (action == ACTION_QUERY)
457 r = query_device(query, device);
458 else if (action == ACTION_ATTRIBUTE_WALK)
459 r = print_device_chain(device);
460 else
461 assert_not_reached("Unknown action");
462 if (r < 0)
463 return r;
912541b0 464 }
87171e46 465
3c79311a 466 return 0;
87171e46 467}