]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udevadm-info.c
f52a03dfd022cda50a0afce02558b8067c53d504
[thirdparty/systemd.git] / src / udev / udevadm-info.c
1 /*
2 * Copyright (C) 2004-2009 Kay Sievers <kay@vrfy.org>
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <stdlib.h>
19 #include <string.h>
20 #include <stdio.h>
21 #include <stddef.h>
22 #include <ctype.h>
23 #include <stdarg.h>
24 #include <unistd.h>
25 #include <dirent.h>
26 #include <errno.h>
27 #include <getopt.h>
28 #include <fcntl.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31
32 #include "udev.h"
33 #include "udev-util.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", udev_list_entry_get_name(list_entry) + strlen("/dev/"));
148
149 udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device))
150 printf("E: %s=%s\n",
151 udev_list_entry_get_name(list_entry),
152 udev_list_entry_get_value(list_entry));
153 printf("\n");
154 }
155
156 static int stat_device(const char *name, bool export, const char *prefix) {
157 struct stat statbuf;
158
159 if (stat(name, &statbuf) != 0)
160 return -1;
161
162 if (export) {
163 if (prefix == NULL)
164 prefix = "INFO_";
165 printf("%sMAJOR=%d\n"
166 "%sMINOR=%d\n",
167 prefix, major(statbuf.st_dev),
168 prefix, minor(statbuf.st_dev));
169 } else
170 printf("%d:%d\n", major(statbuf.st_dev), minor(statbuf.st_dev));
171 return 0;
172 }
173
174 static int export_devices(struct udev *udev) {
175 struct udev_enumerate *udev_enumerate;
176 struct udev_list_entry *list_entry;
177
178 udev_enumerate = udev_enumerate_new(udev);
179 if (udev_enumerate == NULL)
180 return -1;
181 udev_enumerate_scan_devices(udev_enumerate);
182 udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
183 struct udev_device *device;
184
185 device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry));
186 if (device != NULL) {
187 print_record(device);
188 udev_device_unref(device);
189 }
190 }
191 udev_enumerate_unref(udev_enumerate);
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 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
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 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 closedir(dir2);
217 }
218 unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
219 } else {
220 unlinkat(dirfd(dir), dent->d_name, 0);
221 }
222 }
223 }
224
225 static void cleanup_db(struct udev *udev) {
226 DIR *dir;
227
228 unlink("/run/udev/queue.bin");
229
230 dir = opendir("/run/udev/data");
231 if (dir != NULL) {
232 cleanup_dir(dir, S_ISVTX, 1);
233 closedir(dir);
234 }
235
236 dir = opendir("/run/udev/links");
237 if (dir != NULL) {
238 cleanup_dir(dir, 0, 2);
239 closedir(dir);
240 }
241
242 dir = opendir("/run/udev/tags");
243 if (dir != NULL) {
244 cleanup_dir(dir, 0, 2);
245 closedir(dir);
246 }
247
248 dir = opendir("/run/udev/static_node-tags");
249 if (dir != NULL) {
250 cleanup_dir(dir, 0, 2);
251 closedir(dir);
252 }
253
254 dir = opendir("/run/udev/watch");
255 if (dir != NULL) {
256 cleanup_dir(dir, 0, 1);
257 closedir(dir);
258 }
259 }
260
261 static void help(void) {
262
263 printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
264 "Query sysfs or the udev database.\n\n"
265 " -h --help Print this message\n"
266 " --version Print version of the program\n"
267 " -q --query=TYPE Query device information:\n"
268 " name Name of device node\n"
269 " symlink Pointing to node\n"
270 " path sysfs device path\n"
271 " property The device properties\n"
272 " all All values\n"
273 " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
274 " -n --name=NAME Node or symlink name used for query or attribute walk\n"
275 " -r --root Prepend dev directory to path names\n"
276 " -a --attribute-walk Print all key matches walking along the chain\n"
277 " of parent devices\n"
278 " -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
279 " -x --export Export key/value pairs\n"
280 " -P --export-prefix Export the key name with a prefix\n"
281 " -e --export-db Export the content of the udev database\n"
282 " -c --cleanup-db Clean up the udev database\n"
283 , program_invocation_short_name);
284 }
285
286 static int uinfo(struct udev *udev, int argc, char *argv[]) {
287 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
288 bool root = 0;
289 bool export = 0;
290 const char *export_prefix = NULL;
291 char name[UTIL_PATH_SIZE];
292 struct udev_list_entry *list_entry;
293 int c;
294
295 static const struct option options[] = {
296 { "name", required_argument, NULL, 'n' },
297 { "path", required_argument, NULL, 'p' },
298 { "query", required_argument, NULL, 'q' },
299 { "attribute-walk", no_argument, NULL, 'a' },
300 { "cleanup-db", no_argument, NULL, 'c' },
301 { "export-db", no_argument, NULL, 'e' },
302 { "root", no_argument, NULL, 'r' },
303 { "device-id-of-file", required_argument, NULL, 'd' },
304 { "export", no_argument, NULL, 'x' },
305 { "export-prefix", required_argument, NULL, 'P' },
306 { "version", no_argument, NULL, 'V' },
307 { "help", no_argument, NULL, 'h' },
308 {}
309 };
310
311 enum action_type {
312 ACTION_QUERY,
313 ACTION_ATTRIBUTE_WALK,
314 ACTION_DEVICE_ID_FILE,
315 } action = ACTION_QUERY;
316
317 enum query_type {
318 QUERY_NAME,
319 QUERY_PATH,
320 QUERY_SYMLINK,
321 QUERY_PROPERTY,
322 QUERY_ALL,
323 } query = QUERY_ALL;
324
325 while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL)) >= 0)
326 switch (c) {
327 case 'n': {
328 if (device != NULL) {
329 fprintf(stderr, "device already specified\n");
330 return 2;
331 }
332
333 device = find_device(udev, optarg, "/dev/");
334 if (device == NULL) {
335 fprintf(stderr, "device node not found\n");
336 return 2;
337 }
338 break;
339 }
340 case 'p':
341 if (device != NULL) {
342 fprintf(stderr, "device already specified\n");
343 return 2;
344 }
345
346 device = find_device(udev, optarg, "/sys");
347 if (device == NULL) {
348 fprintf(stderr, "syspath not found\n");
349 return 2;
350 }
351 break;
352 case 'q':
353 action = ACTION_QUERY;
354 if (streq(optarg, "property") || streq(optarg, "env"))
355 query = QUERY_PROPERTY;
356 else if (streq(optarg, "name"))
357 query = QUERY_NAME;
358 else if (streq(optarg, "symlink"))
359 query = QUERY_SYMLINK;
360 else if (streq(optarg, "path"))
361 query = QUERY_PATH;
362 else if (streq(optarg, "all"))
363 query = QUERY_ALL;
364 else {
365 fprintf(stderr, "unknown query type\n");
366 return 3;
367 }
368 break;
369 case 'r':
370 root = true;
371 break;
372 case 'd':
373 action = ACTION_DEVICE_ID_FILE;
374 strscpy(name, sizeof(name), optarg);
375 break;
376 case 'a':
377 action = ACTION_ATTRIBUTE_WALK;
378 break;
379 case 'e':
380 export_devices(udev);
381 return 0;
382 case 'c':
383 cleanup_db(udev);
384 return 0;
385 case 'x':
386 export = true;
387 break;
388 case 'P':
389 export_prefix = optarg;
390 break;
391 case 'V':
392 printf("%s\n", VERSION);
393 return 0;
394 case 'h':
395 help();
396 return 0;
397 default:
398 return 1;
399 }
400
401 switch (action) {
402 case ACTION_QUERY:
403 if (!device) {
404 if (!argv[optind]) {
405 help();
406 return 2;
407 }
408 device = find_device(udev, argv[optind], NULL);
409 if (!device) {
410 fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
411 return 4;
412 }
413 }
414
415 switch(query) {
416 case QUERY_NAME: {
417 const char *node = udev_device_get_devnode(device);
418
419 if (node == NULL) {
420 fprintf(stderr, "no device node found\n");
421 return 5;
422 }
423
424 if (root)
425 printf("%s\n", udev_device_get_devnode(device));
426 else
427 printf("%s\n", udev_device_get_devnode(device) + strlen("/dev/"));
428 break;
429 }
430 case QUERY_SYMLINK:
431 list_entry = udev_device_get_devlinks_list_entry(device);
432 while (list_entry != NULL) {
433 if (root)
434 printf("%s", udev_list_entry_get_name(list_entry));
435 else
436 printf("%s", udev_list_entry_get_name(list_entry) + strlen("/dev/"));
437 list_entry = udev_list_entry_get_next(list_entry);
438 if (list_entry != NULL)
439 printf(" ");
440 }
441 printf("\n");
442 break;
443 case QUERY_PATH:
444 printf("%s\n", udev_device_get_devpath(device));
445 return 0;
446 case QUERY_PROPERTY:
447 list_entry = udev_device_get_properties_list_entry(device);
448 while (list_entry != NULL) {
449 if (export) {
450 const char *prefix = export_prefix;
451
452 if (prefix == NULL)
453 prefix = "";
454 printf("%s%s='%s'\n", prefix,
455 udev_list_entry_get_name(list_entry),
456 udev_list_entry_get_value(list_entry));
457 } else {
458 printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
459 }
460 list_entry = udev_list_entry_get_next(list_entry);
461 }
462 break;
463 case QUERY_ALL:
464 print_record(device);
465 break;
466 default:
467 assert_not_reached("unknown query type");
468 }
469 break;
470 case ACTION_ATTRIBUTE_WALK:
471 if (!device && argv[optind]) {
472 device = find_device(udev, argv[optind], NULL);
473 if (!device) {
474 fprintf(stderr, "Unknown device, absolute path in /dev/ or /sys expected.\n");
475 return 4;
476 }
477 }
478 if (!device) {
479 fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
480 return 4;
481 }
482 print_device_chain(device);
483 break;
484 case ACTION_DEVICE_ID_FILE:
485 if (stat_device(name, export, export_prefix) != 0)
486 return 1;
487 break;
488 }
489
490 return 0;
491 }
492
493 const struct udevadm_cmd udevadm_info = {
494 .name = "info",
495 .cmd = uinfo,
496 .help = "Query sysfs or the udev database",
497 };