]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udevadm-info.c
udev: export tags of "dead" device nodes to /run/udev/static_node-tags/
[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
34 static bool skip_attribute(const char *name)
35 {
36 static const char* const skip[] = {
37 "uevent",
38 "dev",
39 "modalias",
40 "resource",
41 "driver",
42 "subsystem",
43 "module",
44 };
45 unsigned int i;
46
47 for (i = 0; i < ELEMENTSOF(skip); i++)
48 if (streq(name, skip[i]))
49 return true;
50 return false;
51 }
52
53 static void print_all_attributes(struct udev_device *device, const char *key)
54 {
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 {
88 struct udev_device *device_parent;
89 const char *str;
90
91 printf("\n"
92 "Udevadm info starts with the device specified by the devpath and then\n"
93 "walks up the chain of parent devices. It prints for every device\n"
94 "found, all possible attributes in the udev rules key format.\n"
95 "A rule to match, can be composed by the attributes of the device\n"
96 "and the attributes from one single parent device.\n"
97 "\n");
98
99 printf(" looking at device '%s':\n", udev_device_get_devpath(device));
100 printf(" KERNEL==\"%s\"\n", udev_device_get_sysname(device));
101 str = udev_device_get_subsystem(device);
102 if (str == NULL)
103 str = "";
104 printf(" SUBSYSTEM==\"%s\"\n", str);
105 str = udev_device_get_driver(device);
106 if (str == NULL)
107 str = "";
108 printf(" DRIVER==\"%s\"\n", str);
109 print_all_attributes(device, "ATTR");
110
111 device_parent = device;
112 do {
113 device_parent = udev_device_get_parent(device_parent);
114 if (device_parent == NULL)
115 break;
116 printf(" looking at parent device '%s':\n", udev_device_get_devpath(device_parent));
117 printf(" KERNELS==\"%s\"\n", udev_device_get_sysname(device_parent));
118 str = udev_device_get_subsystem(device_parent);
119 if (str == NULL)
120 str = "";
121 printf(" SUBSYSTEMS==\"%s\"\n", str);
122 str = udev_device_get_driver(device_parent);
123 if (str == NULL)
124 str = "";
125 printf(" DRIVERS==\"%s\"\n", str);
126 print_all_attributes(device_parent, "ATTRS");
127 } while (device_parent != NULL);
128
129 return 0;
130 }
131
132 static void print_record(struct udev_device *device)
133 {
134 const char *str;
135 int i;
136 struct udev_list_entry *list_entry;
137
138 printf("P: %s\n", udev_device_get_devpath(device));
139
140 str = udev_device_get_devnode(device);
141 if (str != NULL)
142 printf("N: %s\n", str + strlen("/dev/"));
143
144 i = udev_device_get_devlink_priority(device);
145 if (i != 0)
146 printf("L: %i\n", i);
147
148 udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device))
149 printf("S: %s\n", udev_list_entry_get_name(list_entry) + strlen("/dev/"));
150
151 udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device))
152 printf("E: %s=%s\n",
153 udev_list_entry_get_name(list_entry),
154 udev_list_entry_get_value(list_entry));
155 printf("\n");
156 }
157
158 static int stat_device(const char *name, bool export, const char *prefix)
159 {
160 struct stat statbuf;
161
162 if (stat(name, &statbuf) != 0)
163 return -1;
164
165 if (export) {
166 if (prefix == NULL)
167 prefix = "INFO_";
168 printf("%sMAJOR=%d\n"
169 "%sMINOR=%d\n",
170 prefix, major(statbuf.st_dev),
171 prefix, minor(statbuf.st_dev));
172 } else
173 printf("%d:%d\n", major(statbuf.st_dev), minor(statbuf.st_dev));
174 return 0;
175 }
176
177 static int export_devices(struct udev *udev)
178 {
179 struct udev_enumerate *udev_enumerate;
180 struct udev_list_entry *list_entry;
181
182 udev_enumerate = udev_enumerate_new(udev);
183 if (udev_enumerate == NULL)
184 return -1;
185 udev_enumerate_scan_devices(udev_enumerate);
186 udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
187 struct udev_device *device;
188
189 device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry));
190 if (device != NULL) {
191 print_record(device);
192 udev_device_unref(device);
193 }
194 }
195 udev_enumerate_unref(udev_enumerate);
196 return 0;
197 }
198
199 static void cleanup_dir(DIR *dir, mode_t mask, int depth)
200 {
201 struct dirent *dent;
202
203 if (depth <= 0)
204 return;
205
206 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
207 struct stat stats;
208
209 if (dent->d_name[0] == '.')
210 continue;
211 if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0)
212 continue;
213 if ((stats.st_mode & mask) != 0)
214 continue;
215 if (S_ISDIR(stats.st_mode)) {
216 DIR *dir2;
217
218 dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
219 if (dir2 != NULL) {
220 cleanup_dir(dir2, mask, depth-1);
221 closedir(dir2);
222 }
223 unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
224 } else {
225 unlinkat(dirfd(dir), dent->d_name, 0);
226 }
227 }
228 }
229
230 static void cleanup_db(struct udev *udev)
231 {
232 DIR *dir;
233
234 unlink("/run/udev/queue.bin");
235
236 dir = opendir("/run/udev/data");
237 if (dir != NULL) {
238 cleanup_dir(dir, S_ISVTX, 1);
239 closedir(dir);
240 }
241
242 dir = opendir("/run/udev/links");
243 if (dir != NULL) {
244 cleanup_dir(dir, 0, 2);
245 closedir(dir);
246 }
247
248 dir = opendir("/run/udev/tags");
249 if (dir != NULL) {
250 cleanup_dir(dir, 0, 2);
251 closedir(dir);
252 }
253
254 dir = opendir("/run/udev/static_node-tags");
255 if (dir != NULL) {
256 cleanup_dir(dir, 0, 2);
257 closedir(dir);
258 }
259
260 dir = opendir("/run/udev/watch");
261 if (dir != NULL) {
262 cleanup_dir(dir, 0, 1);
263 closedir(dir);
264 }
265 }
266
267 static struct udev_device *find_device(struct udev *udev, const char *id, const char *prefix)
268 {
269 char name[UTIL_PATH_SIZE];
270
271 if (prefix && !startswith(id, prefix)) {
272 strscpyl(name, sizeof(name), prefix, id, NULL);
273 id = name;
274 }
275
276 if (startswith(id, "/dev/")) {
277 struct stat statbuf;
278 char type;
279
280 if (stat(id, &statbuf) < 0)
281 return NULL;
282
283 if (S_ISBLK(statbuf.st_mode))
284 type = 'b';
285 else if (S_ISCHR(statbuf.st_mode))
286 type = 'c';
287 else
288 return NULL;
289
290 return udev_device_new_from_devnum(udev, type, statbuf.st_rdev);
291 } else if (startswith(id, "/sys/"))
292 return udev_device_new_from_syspath(udev, id);
293 else
294 return NULL;
295 }
296
297 static int uinfo(struct udev *udev, int argc, char *argv[])
298 {
299 struct udev_device *device = NULL;
300 bool root = 0;
301 bool export = 0;
302 const char *export_prefix = NULL;
303 char name[UTIL_PATH_SIZE];
304 struct udev_list_entry *list_entry;
305 int rc = 0;
306
307 static const struct option options[] = {
308 { "name", required_argument, NULL, 'n' },
309 { "path", required_argument, NULL, 'p' },
310 { "query", required_argument, NULL, 'q' },
311 { "attribute-walk", no_argument, NULL, 'a' },
312 { "cleanup-db", no_argument, NULL, 'c' },
313 { "export-db", no_argument, NULL, 'e' },
314 { "root", no_argument, NULL, 'r' },
315 { "device-id-of-file", required_argument, NULL, 'd' },
316 { "export", no_argument, NULL, 'x' },
317 { "export-prefix", required_argument, NULL, 'P' },
318 { "version", no_argument, NULL, 'V' },
319 { "help", no_argument, NULL, 'h' },
320 {}
321 };
322
323 static const char *usage =
324 "Usage: udevadm info OPTIONS\n"
325 " --query=<type> query device information:\n"
326 " name name of device node\n"
327 " symlink pointing to node\n"
328 " path sys device path\n"
329 " property the device properties\n"
330 " all all values\n"
331 " --path=<syspath> sys device path used for query or attribute walk\n"
332 " --name=<name> node or symlink name used for query or attribute walk\n"
333 " --root prepend dev directory to path names\n"
334 " --attribute-walk print all key matches while walking along the chain\n"
335 " of parent devices\n"
336 " --device-id-of-file=<file> print major:minor of device containing this file\n"
337 " --export export key/value pairs\n"
338 " --export-prefix export the key name with a prefix\n"
339 " --export-db export the content of the udev database\n"
340 " --cleanup-db cleanup the udev database\n"
341 " --help\n";
342
343 enum action_type {
344 ACTION_QUERY,
345 ACTION_ATTRIBUTE_WALK,
346 ACTION_DEVICE_ID_FILE,
347 } action = ACTION_QUERY;
348
349 enum query_type {
350 QUERY_NAME,
351 QUERY_PATH,
352 QUERY_SYMLINK,
353 QUERY_PROPERTY,
354 QUERY_ALL,
355 } query = QUERY_ALL;
356
357 for (;;) {
358 int option;
359
360 option = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL);
361 if (option == -1)
362 break;
363
364 switch (option) {
365 case 'n': {
366 if (device != NULL) {
367 fprintf(stderr, "device already specified\n");
368 rc = 2;
369 goto exit;
370 }
371
372 device = find_device(udev, optarg, "/dev/");
373 if (device == NULL) {
374 fprintf(stderr, "device node not found\n");
375 rc = 2;
376 goto exit;
377 }
378 break;
379 }
380 case 'p':
381 if (device != NULL) {
382 fprintf(stderr, "device already specified\n");
383 rc = 2;
384 goto exit;
385 }
386
387 device = find_device(udev, optarg, "/sys");
388 if (device == NULL) {
389 fprintf(stderr, "syspath not found\n");
390 rc = 2;
391 goto exit;
392 }
393 break;
394 case 'q':
395 action = ACTION_QUERY;
396 if (streq(optarg, "property") || streq(optarg, "env")) {
397 query = QUERY_PROPERTY;
398 } else if (streq(optarg, "name")) {
399 query = QUERY_NAME;
400 } else if (streq(optarg, "symlink")) {
401 query = QUERY_SYMLINK;
402 } else if (streq(optarg, "path")) {
403 query = QUERY_PATH;
404 } else if (streq(optarg, "all")) {
405 query = QUERY_ALL;
406 } else {
407 fprintf(stderr, "unknown query type\n");
408 rc = 3;
409 goto exit;
410 }
411 break;
412 case 'r':
413 root = true;
414 break;
415 case 'd':
416 action = ACTION_DEVICE_ID_FILE;
417 strscpy(name, sizeof(name), optarg);
418 break;
419 case 'a':
420 action = ACTION_ATTRIBUTE_WALK;
421 break;
422 case 'e':
423 export_devices(udev);
424 goto exit;
425 case 'c':
426 cleanup_db(udev);
427 goto exit;
428 case 'x':
429 export = true;
430 break;
431 case 'P':
432 export_prefix = optarg;
433 break;
434 case 'V':
435 printf("%s\n", VERSION);
436 goto exit;
437 case 'h':
438 printf("%s\n", usage);
439 goto exit;
440 default:
441 rc = 1;
442 goto exit;
443 }
444 }
445
446 switch (action) {
447 case ACTION_QUERY:
448 if (!device) {
449 if (!argv[optind]) {
450 fprintf(stderr, "%s\n", usage);
451 rc = 2;
452 goto exit;
453 }
454 device = find_device(udev, argv[optind], NULL);
455 if (!device) {
456 fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
457 rc = 4;
458 goto exit;
459 }
460 }
461
462 switch(query) {
463 case QUERY_NAME: {
464 const char *node = udev_device_get_devnode(device);
465
466 if (node == NULL) {
467 fprintf(stderr, "no device node found\n");
468 rc = 5;
469 goto exit;
470 }
471
472 if (root)
473 printf("%s\n", udev_device_get_devnode(device));
474 else
475 printf("%s\n", udev_device_get_devnode(device) + strlen("/dev/"));
476 break;
477 }
478 case QUERY_SYMLINK:
479 list_entry = udev_device_get_devlinks_list_entry(device);
480 while (list_entry != NULL) {
481 if (root)
482 printf("%s", udev_list_entry_get_name(list_entry));
483 else
484 printf("%s", udev_list_entry_get_name(list_entry) + strlen("/dev/"));
485 list_entry = udev_list_entry_get_next(list_entry);
486 if (list_entry != NULL)
487 printf(" ");
488 }
489 printf("\n");
490 break;
491 case QUERY_PATH:
492 printf("%s\n", udev_device_get_devpath(device));
493 goto exit;
494 case QUERY_PROPERTY:
495 list_entry = udev_device_get_properties_list_entry(device);
496 while (list_entry != NULL) {
497 if (export) {
498 const char *prefix = export_prefix;
499
500 if (prefix == NULL)
501 prefix = "";
502 printf("%s%s='%s'\n", prefix,
503 udev_list_entry_get_name(list_entry),
504 udev_list_entry_get_value(list_entry));
505 } else {
506 printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
507 }
508 list_entry = udev_list_entry_get_next(list_entry);
509 }
510 break;
511 case QUERY_ALL:
512 print_record(device);
513 break;
514 default:
515 fprintf(stderr, "unknown query type\n");
516 break;
517 }
518 break;
519 case ACTION_ATTRIBUTE_WALK:
520 if (!device && argv[optind]) {
521 device = find_device(udev, argv[optind], NULL);
522 if (!device) {
523 fprintf(stderr, "Unknown device, absolute path in /dev/ or /sys expected.\n");
524 rc = 4;
525 goto exit;
526 }
527 }
528 if (!device) {
529 fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
530 rc = 4;
531 goto exit;
532 }
533 print_device_chain(device);
534 break;
535 case ACTION_DEVICE_ID_FILE:
536 if (stat_device(name, export, export_prefix) != 0)
537 rc = 1;
538 break;
539 }
540
541 exit:
542 udev_device_unref(device);
543 return rc;
544 }
545
546 const struct udevadm_cmd udevadm_info = {
547 .name = "info",
548 .cmd = uinfo,
549 .help = "query sysfs or the udev database",
550 };