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