]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udevadm-info.c
udev: remove configuration options for /dev, /sys, /run directories
[thirdparty/systemd.git] / src / udev / udevadm-info.c
1 /*
2 * Copyright (C) 2004-2009 Kay Sievers <kay.sievers@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 (strcmp(name, skip[i]) == 0)
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/watch");
255 if (dir != NULL) {
256 cleanup_dir(dir, 0, 1);
257 closedir(dir);
258 }
259
260 dir = opendir("/run/udev/firmware-missing");
261 if (dir != NULL) {
262 cleanup_dir(dir, 0, 1);
263 closedir(dir);
264 }
265 }
266
267 static int uinfo(struct udev *udev, int argc, char *argv[])
268 {
269 struct udev_device *device = NULL;
270 bool root = 0;
271 bool export = 0;
272 const char *export_prefix = NULL;
273 char path[UTIL_PATH_SIZE];
274 char name[UTIL_PATH_SIZE];
275 struct udev_list_entry *list_entry;
276 int rc = 0;
277
278 static const struct option options[] = {
279 { "name", required_argument, NULL, 'n' },
280 { "path", required_argument, NULL, 'p' },
281 { "query", required_argument, NULL, 'q' },
282 { "attribute-walk", no_argument, NULL, 'a' },
283 { "cleanup-db", no_argument, NULL, 'c' },
284 { "export-db", no_argument, NULL, 'e' },
285 { "root", no_argument, NULL, 'r' },
286 { "run", no_argument, NULL, 'R' },
287 { "device-id-of-file", required_argument, NULL, 'd' },
288 { "export", no_argument, NULL, 'x' },
289 { "export-prefix", required_argument, NULL, 'P' },
290 { "version", no_argument, NULL, 'V' },
291 { "help", no_argument, NULL, 'h' },
292 {}
293 };
294
295 enum action_type {
296 ACTION_NONE,
297 ACTION_QUERY,
298 ACTION_ATTRIBUTE_WALK,
299 ACTION_ROOT,
300 ACTION_DEVICE_ID_FILE,
301 } action = ACTION_NONE;
302
303 enum query_type {
304 QUERY_NONE,
305 QUERY_NAME,
306 QUERY_PATH,
307 QUERY_SYMLINK,
308 QUERY_PROPERTY,
309 QUERY_ALL,
310 } query = QUERY_NONE;
311
312 for (;;) {
313 int option;
314 struct stat statbuf;
315
316 option = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL);
317 if (option == -1)
318 break;
319
320 switch (option) {
321 case 'n':
322 if (device != NULL) {
323 fprintf(stderr, "device already specified\n");
324 rc = 2;
325 goto exit;
326 }
327 /* add /dev if not given */
328 if (strncmp(optarg, "/dev", strlen("/dev")) != 0)
329 util_strscpyl(name, sizeof(name), "/dev/", optarg, NULL);
330 else
331 util_strscpy(name, sizeof(name), optarg);
332 util_remove_trailing_chars(name, '/');
333 if (stat(name, &statbuf) < 0) {
334 fprintf(stderr, "device node not found\n");
335 rc = 2;
336 goto exit;
337 } else {
338 char type;
339
340 if (S_ISBLK(statbuf.st_mode)) {
341 type = 'b';
342 } else if (S_ISCHR(statbuf.st_mode)) {
343 type = 'c';
344 } else {
345 fprintf(stderr, "device node has wrong file type\n");
346 rc = 2;
347 goto exit;
348 }
349 device = udev_device_new_from_devnum(udev, type, statbuf.st_rdev);
350 if (device == NULL) {
351 fprintf(stderr, "device node not found\n");
352 rc = 2;
353 goto exit;
354 }
355 }
356 break;
357 case 'p':
358 if (device != NULL) {
359 fprintf(stderr, "device already specified\n");
360 rc = 2;
361 goto exit;
362 }
363 /* add sys dir if needed */
364 if (strncmp(optarg, "/sys", strlen("/sys")) != 0)
365 util_strscpyl(path, sizeof(path), "/sys", optarg, NULL);
366 else
367 util_strscpy(path, sizeof(path), optarg);
368 util_remove_trailing_chars(path, '/');
369 device = udev_device_new_from_syspath(udev, path);
370 if (device == NULL) {
371 fprintf(stderr, "device path not found\n");
372 rc = 2;
373 goto exit;
374 }
375 break;
376 case 'q':
377 action = ACTION_QUERY;
378 if (strcmp(optarg, "property") == 0 || strcmp(optarg, "env") == 0) {
379 query = QUERY_PROPERTY;
380 } else if (strcmp(optarg, "name") == 0) {
381 query = QUERY_NAME;
382 } else if (strcmp(optarg, "symlink") == 0) {
383 query = QUERY_SYMLINK;
384 } else if (strcmp(optarg, "path") == 0) {
385 query = QUERY_PATH;
386 } else if (strcmp(optarg, "all") == 0) {
387 query = QUERY_ALL;
388 } else {
389 fprintf(stderr, "unknown query type\n");
390 rc = 3;
391 goto exit;
392 }
393 break;
394 case 'r':
395 if (action == ACTION_NONE)
396 action = ACTION_ROOT;
397 root = true;
398 break;
399 case 'R':
400 printf("/run/udev\n");
401 goto exit;
402 case 'd':
403 action = ACTION_DEVICE_ID_FILE;
404 util_strscpy(name, sizeof(name), optarg);
405 break;
406 case 'a':
407 action = ACTION_ATTRIBUTE_WALK;
408 break;
409 case 'e':
410 export_devices(udev);
411 goto exit;
412 case 'c':
413 cleanup_db(udev);
414 goto exit;
415 case 'x':
416 export = true;
417 break;
418 case 'P':
419 export_prefix = optarg;
420 break;
421 case 'V':
422 printf("%s\n", VERSION);
423 goto exit;
424 case 'h':
425 printf("Usage: udevadm info OPTIONS\n"
426 " --query=<type> query device information:\n"
427 " name name of device node\n"
428 " symlink pointing to node\n"
429 " path sys device path\n"
430 " property the device properties\n"
431 " all all values\n"
432 " --path=<syspath> sys device path used for query or attribute walk\n"
433 " --name=<name> node or symlink name used for query or attribute walk\n"
434 " --root prepend dev directory to path names\n"
435 " --attribute-walk print all key matches while walking along the chain\n"
436 " of parent devices\n"
437 " --device-id-of-file=<file> print major:minor of device containing this file\n"
438 " --export export key/value pairs\n"
439 " --export-prefix export the key name with a prefix\n"
440 " --export-db export the content of the udev database\n"
441 " --cleanup-db cleanup the udev database\n"
442 " --help\n\n");
443 goto exit;
444 default:
445 rc = 1;
446 goto exit;
447 }
448 }
449
450 switch (action) {
451 case ACTION_QUERY:
452 if (device == NULL) {
453 fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
454 rc = 4;
455 goto exit;
456 }
457
458 switch(query) {
459 case QUERY_NAME: {
460 const char *node = udev_device_get_devnode(device);
461
462 if (node == NULL) {
463 fprintf(stderr, "no device node found\n");
464 rc = 5;
465 goto exit;
466 }
467
468 if (root)
469 printf("%s\n", udev_device_get_devnode(device));
470 else
471 printf("%s\n", udev_device_get_devnode(device) + strlen("/dev/"));
472 break;
473 }
474 case QUERY_SYMLINK:
475 list_entry = udev_device_get_devlinks_list_entry(device);
476 while (list_entry != NULL) {
477 if (root)
478 printf("%s", udev_list_entry_get_name(list_entry));
479 else
480 printf("%s", udev_list_entry_get_name(list_entry) + strlen("/dev/"));
481 list_entry = udev_list_entry_get_next(list_entry);
482 if (list_entry != NULL)
483 printf(" ");
484 }
485 printf("\n");
486 break;
487 case QUERY_PATH:
488 printf("%s\n", udev_device_get_devpath(device));
489 goto exit;
490 case QUERY_PROPERTY:
491 list_entry = udev_device_get_properties_list_entry(device);
492 while (list_entry != NULL) {
493 if (export) {
494 const char *prefix = export_prefix;
495
496 if (prefix == NULL)
497 prefix = "";
498 printf("%s%s='%s'\n", prefix,
499 udev_list_entry_get_name(list_entry),
500 udev_list_entry_get_value(list_entry));
501 } else {
502 printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
503 }
504 list_entry = udev_list_entry_get_next(list_entry);
505 }
506 break;
507 case QUERY_ALL:
508 print_record(device);
509 break;
510 default:
511 fprintf(stderr, "unknown query type\n");
512 break;
513 }
514 break;
515 case ACTION_ATTRIBUTE_WALK:
516 if (device == NULL) {
517 fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
518 rc = 4;
519 goto exit;
520 }
521 print_device_chain(device);
522 break;
523 case ACTION_DEVICE_ID_FILE:
524 if (stat_device(name, export, export_prefix) != 0)
525 rc = 1;
526 break;
527 case ACTION_ROOT:
528 printf("/dev\n");
529 break;
530 default:
531 fprintf(stderr, "missing option\n");
532 rc = 1;
533 break;
534 }
535
536 exit:
537 udev_device_unref(device);
538 return rc;
539 }
540
541 const struct udevadm_cmd udevadm_info = {
542 .name = "info",
543 .cmd = uinfo,
544 .help = "query sysfs or the udev database",
545 };