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