]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udevadm-info.c
tree-wide: drop empty lines in comments
[thirdparty/systemd.git] / src / udev / udevadm-info.c
1 /* SPDX-License-Identifier: GPL-2.0+ */
2
3 #include <ctype.h>
4 #include <errno.h>
5 #include <fcntl.h>
6 #include <getopt.h>
7 #include <stddef.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <sys/stat.h>
11 #include <unistd.h>
12
13 #include "dirent-util.h"
14 #include "fd-util.h"
15 #include "string-util.h"
16 #include "udev-util.h"
17 #include "udev.h"
18 #include "udevadm-util.h"
19
20 static bool skip_attribute(const char *name) {
21 static const char* const skip[] = {
22 "uevent",
23 "dev",
24 "modalias",
25 "resource",
26 "driver",
27 "subsystem",
28 "module",
29 };
30 unsigned int i;
31
32 for (i = 0; i < ELEMENTSOF(skip); i++)
33 if (streq(name, skip[i]))
34 return true;
35 return false;
36 }
37
38 static void print_all_attributes(struct udev_device *device, const char *key) {
39 struct udev_list_entry *sysattr;
40
41 udev_list_entry_foreach(sysattr, udev_device_get_sysattr_list_entry(device)) {
42 const char *name;
43 const char *value;
44 size_t len;
45
46 name = udev_list_entry_get_name(sysattr);
47 if (skip_attribute(name))
48 continue;
49
50 value = udev_device_get_sysattr_value(device, name);
51 if (value == NULL)
52 continue;
53
54 /* skip any values that look like a path */
55 if (value[0] == '/')
56 continue;
57
58 /* skip nonprintable attributes */
59 len = strlen(value);
60 while (len > 0 && isprint(value[len-1]))
61 len--;
62 if (len > 0)
63 continue;
64
65 printf(" %s{%s}==\"%s\"\n", key, name, value);
66 }
67 printf("\n");
68 }
69
70 static int print_device_chain(struct udev_device *device) {
71 struct udev_device *device_parent;
72 const char *str;
73
74 printf("\n"
75 "Udevadm info starts with the device specified by the devpath and then\n"
76 "walks up the chain of parent devices. It prints for every device\n"
77 "found, all possible attributes in the udev rules key format.\n"
78 "A rule to match, can be composed by the attributes of the device\n"
79 "and the attributes from one single parent device.\n"
80 "\n");
81
82 printf(" looking at device '%s':\n", udev_device_get_devpath(device));
83 printf(" KERNEL==\"%s\"\n", udev_device_get_sysname(device));
84 str = udev_device_get_subsystem(device);
85 if (str == NULL)
86 str = "";
87 printf(" SUBSYSTEM==\"%s\"\n", str);
88 str = udev_device_get_driver(device);
89 if (str == NULL)
90 str = "";
91 printf(" DRIVER==\"%s\"\n", str);
92 print_all_attributes(device, "ATTR");
93
94 device_parent = device;
95 do {
96 device_parent = udev_device_get_parent(device_parent);
97 if (device_parent == NULL)
98 break;
99 printf(" looking at parent device '%s':\n", udev_device_get_devpath(device_parent));
100 printf(" KERNELS==\"%s\"\n", udev_device_get_sysname(device_parent));
101 str = udev_device_get_subsystem(device_parent);
102 if (str == NULL)
103 str = "";
104 printf(" SUBSYSTEMS==\"%s\"\n", str);
105 str = udev_device_get_driver(device_parent);
106 if (str == NULL)
107 str = "";
108 printf(" DRIVERS==\"%s\"\n", str);
109 print_all_attributes(device_parent, "ATTRS");
110 } while (device_parent != NULL);
111
112 return 0;
113 }
114
115 static void print_record(struct udev_device *device) {
116 const char *str;
117 int i;
118 struct udev_list_entry *list_entry;
119
120 printf("P: %s\n", udev_device_get_devpath(device));
121
122 str = udev_device_get_devnode(device);
123 if (str != NULL)
124 printf("N: %s\n", str + STRLEN("/dev/"));
125
126 i = udev_device_get_devlink_priority(device);
127 if (i != 0)
128 printf("L: %i\n", i);
129
130 udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device))
131 printf("S: %s\n",
132 udev_list_entry_get_name(list_entry) + STRLEN("/dev/"));
133
134 udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device))
135 printf("E: %s=%s\n",
136 udev_list_entry_get_name(list_entry),
137 udev_list_entry_get_value(list_entry));
138 printf("\n");
139 }
140
141 static int stat_device(const char *name, bool export, const char *prefix) {
142 struct stat statbuf;
143
144 if (stat(name, &statbuf) != 0)
145 return -errno;
146
147 if (export) {
148 if (prefix == NULL)
149 prefix = "INFO_";
150 printf("%sMAJOR=%u\n"
151 "%sMINOR=%u\n",
152 prefix, major(statbuf.st_dev),
153 prefix, minor(statbuf.st_dev));
154 } else
155 printf("%u:%u\n", major(statbuf.st_dev), minor(statbuf.st_dev));
156 return 0;
157 }
158
159 static int export_devices(struct udev *udev) {
160 _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *udev_enumerate;
161 struct udev_list_entry *list_entry;
162
163 udev_enumerate = udev_enumerate_new(udev);
164 if (udev_enumerate == NULL)
165 return -ENOMEM;
166
167 udev_enumerate_scan_devices(udev_enumerate);
168 udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
169 _cleanup_(udev_device_unrefp) struct udev_device *device;
170
171 device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry));
172 if (device != NULL)
173 print_record(device);
174 }
175
176 return 0;
177 }
178
179 static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
180 struct dirent *dent;
181
182 if (depth <= 0)
183 return;
184
185 FOREACH_DIRENT_ALL(dent, dir, break) {
186 struct stat stats;
187
188 if (dent->d_name[0] == '.')
189 continue;
190 if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0)
191 continue;
192 if ((stats.st_mode & mask) != 0)
193 continue;
194 if (S_ISDIR(stats.st_mode)) {
195 _cleanup_closedir_ DIR *dir2;
196
197 dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
198 if (dir2 != NULL)
199 cleanup_dir(dir2, mask, depth-1);
200
201 (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
202 } else
203 (void) unlinkat(dirfd(dir), dent->d_name, 0);
204 }
205 }
206
207 static void cleanup_db(struct udev *udev) {
208 _cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL, *dir5 = NULL;
209
210 (void) unlink("/run/udev/queue.bin");
211
212 dir1 = opendir("/run/udev/data");
213 if (dir1 != NULL)
214 cleanup_dir(dir1, S_ISVTX, 1);
215
216 dir2 = opendir("/run/udev/links");
217 if (dir2 != NULL)
218 cleanup_dir(dir2, 0, 2);
219
220 dir3 = opendir("/run/udev/tags");
221 if (dir3 != NULL)
222 cleanup_dir(dir3, 0, 2);
223
224 dir4 = opendir("/run/udev/static_node-tags");
225 if (dir4 != NULL)
226 cleanup_dir(dir4, 0, 2);
227
228 dir5 = opendir("/run/udev/watch");
229 if (dir5 != NULL)
230 cleanup_dir(dir5, 0, 1);
231 }
232
233 static void help(void) {
234
235 printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
236 "Query sysfs or the udev database.\n\n"
237 " -h --help Print this message\n"
238 " -V --version Print version of the program\n"
239 " -q --query=TYPE Query device information:\n"
240 " name Name of device node\n"
241 " symlink Pointing to node\n"
242 " path sysfs device path\n"
243 " property The device properties\n"
244 " all All values\n"
245 " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
246 " -n --name=NAME Node or symlink name used for query or attribute walk\n"
247 " -r --root Prepend dev directory to path names\n"
248 " -a --attribute-walk Print all key matches walking along the chain\n"
249 " of parent devices\n"
250 " -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
251 " -x --export Export key/value pairs\n"
252 " -P --export-prefix Export the key name with a prefix\n"
253 " -e --export-db Export the content of the udev database\n"
254 " -c --cleanup-db Clean up the udev database\n"
255 , program_invocation_short_name);
256 }
257
258 static int uinfo(struct udev *udev, int argc, char *argv[]) {
259 _cleanup_(udev_device_unrefp) struct udev_device *device = NULL;
260 bool root = 0;
261 bool export = 0;
262 const char *export_prefix = NULL;
263 char name[UTIL_PATH_SIZE];
264 struct udev_list_entry *list_entry;
265 int c;
266
267 static const struct option options[] = {
268 { "name", required_argument, NULL, 'n' },
269 { "path", required_argument, NULL, 'p' },
270 { "query", required_argument, NULL, 'q' },
271 { "attribute-walk", no_argument, NULL, 'a' },
272 { "cleanup-db", no_argument, NULL, 'c' },
273 { "export-db", no_argument, NULL, 'e' },
274 { "root", no_argument, NULL, 'r' },
275 { "device-id-of-file", required_argument, NULL, 'd' },
276 { "export", no_argument, NULL, 'x' },
277 { "export-prefix", required_argument, NULL, 'P' },
278 { "version", no_argument, NULL, 'V' },
279 { "help", no_argument, NULL, 'h' },
280 {}
281 };
282
283 enum action_type {
284 ACTION_QUERY,
285 ACTION_ATTRIBUTE_WALK,
286 ACTION_DEVICE_ID_FILE,
287 } action = ACTION_QUERY;
288
289 enum query_type {
290 QUERY_NAME,
291 QUERY_PATH,
292 QUERY_SYMLINK,
293 QUERY_PROPERTY,
294 QUERY_ALL,
295 } query = QUERY_ALL;
296
297 while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL)) >= 0)
298 switch (c) {
299 case 'n': {
300 if (device != NULL) {
301 fprintf(stderr, "device already specified\n");
302 return 2;
303 }
304
305 device = find_device(udev, optarg, "/dev/");
306 if (device == NULL) {
307 fprintf(stderr, "device node not found\n");
308 return 2;
309 }
310 break;
311 }
312 case 'p':
313 if (device != NULL) {
314 fprintf(stderr, "device already specified\n");
315 return 2;
316 }
317
318 device = find_device(udev, optarg, "/sys");
319 if (device == NULL) {
320 fprintf(stderr, "syspath not found\n");
321 return 2;
322 }
323 break;
324 case 'q':
325 action = ACTION_QUERY;
326 if (streq(optarg, "property") || streq(optarg, "env"))
327 query = QUERY_PROPERTY;
328 else if (streq(optarg, "name"))
329 query = QUERY_NAME;
330 else if (streq(optarg, "symlink"))
331 query = QUERY_SYMLINK;
332 else if (streq(optarg, "path"))
333 query = QUERY_PATH;
334 else if (streq(optarg, "all"))
335 query = QUERY_ALL;
336 else {
337 fprintf(stderr, "unknown query type\n");
338 return 3;
339 }
340 break;
341 case 'r':
342 root = true;
343 break;
344 case 'd':
345 action = ACTION_DEVICE_ID_FILE;
346 strscpy(name, sizeof(name), optarg);
347 break;
348 case 'a':
349 action = ACTION_ATTRIBUTE_WALK;
350 break;
351 case 'e':
352 if (export_devices(udev) < 0)
353 return 1;
354 return 0;
355 case 'c':
356 cleanup_db(udev);
357 return 0;
358 case 'x':
359 export = true;
360 break;
361 case 'P':
362 export_prefix = optarg;
363 break;
364 case 'V':
365 print_version();
366 return 0;
367 case 'h':
368 help();
369 return 0;
370 default:
371 return 1;
372 }
373
374 switch (action) {
375 case ACTION_QUERY:
376 if (!device) {
377 if (!argv[optind]) {
378 help();
379 return 2;
380 }
381 device = find_device(udev, argv[optind], NULL);
382 if (!device) {
383 fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
384 return 4;
385 }
386 }
387
388 switch(query) {
389 case QUERY_NAME: {
390 const char *node = udev_device_get_devnode(device);
391
392 if (node == NULL) {
393 fprintf(stderr, "no device node found\n");
394 return 5;
395 }
396
397 if (root)
398 printf("%s\n", udev_device_get_devnode(device));
399 else
400 printf("%s\n",
401 udev_device_get_devnode(device) + STRLEN("/dev/"));
402 break;
403 }
404 case QUERY_SYMLINK:
405 list_entry = udev_device_get_devlinks_list_entry(device);
406 while (list_entry != NULL) {
407 if (root)
408 printf("%s", udev_list_entry_get_name(list_entry));
409 else
410 printf("%s",
411 udev_list_entry_get_name(list_entry) + STRLEN("/dev/"));
412 list_entry = udev_list_entry_get_next(list_entry);
413 if (list_entry != NULL)
414 printf(" ");
415 }
416 printf("\n");
417 break;
418 case QUERY_PATH:
419 printf("%s\n", udev_device_get_devpath(device));
420 return 0;
421 case QUERY_PROPERTY:
422 list_entry = udev_device_get_properties_list_entry(device);
423 while (list_entry != NULL) {
424 if (export)
425 printf("%s%s='%s'\n", strempty(export_prefix),
426 udev_list_entry_get_name(list_entry),
427 udev_list_entry_get_value(list_entry));
428 else
429 printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
430
431 list_entry = udev_list_entry_get_next(list_entry);
432 }
433 break;
434 case QUERY_ALL:
435 print_record(device);
436 break;
437 default:
438 assert_not_reached("unknown query type");
439 }
440 break;
441 case ACTION_ATTRIBUTE_WALK:
442 if (!device && argv[optind]) {
443 device = find_device(udev, argv[optind], NULL);
444 if (!device) {
445 fprintf(stderr, "Unknown device, absolute path in /dev/ or /sys expected.\n");
446 return 4;
447 }
448 }
449 if (!device) {
450 fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
451 return 4;
452 }
453 print_device_chain(device);
454 break;
455 case ACTION_DEVICE_ID_FILE:
456 if (stat_device(name, export, export_prefix) != 0)
457 return 1;
458 break;
459 }
460
461 return 0;
462 }
463
464 const struct udevadm_cmd udevadm_info = {
465 .name = "info",
466 .cmd = uinfo,
467 .help = "Query sysfs or the udev database",
468 };