]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/udevadm-info.c
logind: remove redundant entries from logind's default controller lists too
[thirdparty/systemd.git] / src / udev / udevadm-info.c
CommitLineData
be9b51f6 1/*
fc206fbe 2 * Copyright (C) 2004-2009 Kay Sievers <kay.sievers@vrfy.org>
be9b51f6 3 *
55e9959b
KS
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.
be9b51f6 8 *
55e9959b
KS
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/>.
be9b51f6
KS
16 */
17
18#include <stdlib.h>
19#include <string.h>
20#include <stdio.h>
1aa1e248 21#include <stddef.h>
034f35d7 22#include <ctype.h>
87171e46
KS
23#include <stdarg.h>
24#include <unistd.h>
1aa1e248 25#include <dirent.h>
87171e46 26#include <errno.h>
11f1bb5a 27#include <getopt.h>
e6c1a2bd 28#include <fcntl.h>
492e76c9
KS
29#include <sys/stat.h>
30#include <sys/types.h>
be9b51f6 31
869fc2f1 32#include "udev.h"
be9b51f6 33
20bee04c
KS
34static bool skip_attribute(const char *name)
35{
912541b0
KS
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;
20bee04c
KS
51}
52
93b0f384 53static void print_all_attributes(struct udev_device *device, const char *key)
be9b51f6 54{
912541b0
KS
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;
912541b0
KS
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--;
baa30fbc 78 if (len > 0)
912541b0 79 continue;
912541b0
KS
80
81 printf(" %s{%s}==\"%s\"\n", key, name, value);
82 }
83 printf("\n");
be9b51f6
KS
84}
85
93b0f384 86static int print_device_chain(struct udev_device *device)
87171e46 87{
912541b0
KS
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;
1aa1e248
KS
130}
131
93b0f384 132static void print_record(struct udev_device *device)
31de3a2b 133{
912541b0
KS
134 size_t len;
135 const char *str;
136 int i;
137 struct udev_list_entry *list_entry;
138
139 printf("P: %s\n", udev_device_get_devpath(device));
140
141 len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
142 str = udev_device_get_devnode(device);
143 if (str != NULL)
144 printf("N: %s\n", &str[len+1]);
145
146 i = udev_device_get_devlink_priority(device);
147 if (i != 0)
148 printf("L: %i\n", i);
149
150 udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device)) {
151 len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
152 printf("S: %s\n", &udev_list_entry_get_name(list_entry)[len+1]);
153 }
154
155 udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device))
156 printf("E: %s=%s\n",
157 udev_list_entry_get_name(list_entry),
158 udev_list_entry_get_value(list_entry));
159 printf("\n");
31de3a2b
KS
160}
161
88163971 162static int stat_device(const char *name, bool export, const char *prefix)
f338bac8 163{
912541b0
KS
164 struct stat statbuf;
165
166 if (stat(name, &statbuf) != 0)
167 return -1;
168
169 if (export) {
170 if (prefix == NULL)
171 prefix = "INFO_";
172 printf("%sMAJOR=%d\n"
173 "%sMINOR=%d\n",
174 prefix, major(statbuf.st_dev),
175 prefix, minor(statbuf.st_dev));
176 } else
177 printf("%d:%d\n", major(statbuf.st_dev), minor(statbuf.st_dev));
178 return 0;
f338bac8
KS
179}
180
bf7ad0ea
KS
181static int export_devices(struct udev *udev)
182{
912541b0
KS
183 struct udev_enumerate *udev_enumerate;
184 struct udev_list_entry *list_entry;
185
186 udev_enumerate = udev_enumerate_new(udev);
187 if (udev_enumerate == NULL)
188 return -1;
189 udev_enumerate_scan_devices(udev_enumerate);
190 udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
191 struct udev_device *device;
192
193 device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry));
194 if (device != NULL) {
195 print_record(device);
196 udev_device_unref(device);
197 }
198 }
199 udev_enumerate_unref(udev_enumerate);
200 return 0;
bf7ad0ea
KS
201}
202
9ead6627
KS
203static void cleanup_dir(DIR *dir, mode_t mask, int depth)
204{
912541b0
KS
205 struct dirent *dent;
206
207 if (depth <= 0)
208 return;
209
210 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
211 struct stat stats;
212
213 if (dent->d_name[0] == '.')
214 continue;
215 if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0)
216 continue;
217 if ((stats.st_mode & mask) != 0)
218 continue;
219 if (S_ISDIR(stats.st_mode)) {
220 DIR *dir2;
221
222 dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
223 if (dir2 != NULL) {
224 cleanup_dir(dir2, mask, depth-1);
225 closedir(dir2);
226 }
227 unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
228 } else {
229 unlinkat(dirfd(dir), dent->d_name, 0);
230 }
231 }
9ead6627
KS
232}
233
234static void cleanup_db(struct udev *udev)
235{
912541b0
KS
236 char filename[UTIL_PATH_SIZE];
237 DIR *dir;
238
239 util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/queue.bin", NULL);
240 unlink(filename);
241
242 util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/data", NULL);
243 dir = opendir(filename);
244 if (dir != NULL) {
245 cleanup_dir(dir, S_ISVTX, 1);
246 closedir(dir);
247 }
248
249 util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/links", NULL);
250 dir = opendir(filename);
251 if (dir != NULL) {
252 cleanup_dir(dir, 0, 2);
253 closedir(dir);
254 }
255
256 util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/tags", NULL);
257 dir = opendir(filename);
258 if (dir != NULL) {
259 cleanup_dir(dir, 0, 2);
260 closedir(dir);
261 }
262
263 util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/watch", NULL);
264 dir = opendir(filename);
265 if (dir != NULL) {
266 cleanup_dir(dir, 0, 1);
267 closedir(dir);
268 }
269
270 util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/firmware-missing", NULL);
271 dir = opendir(filename);
272 if (dir != NULL) {
273 cleanup_dir(dir, 0, 1);
274 closedir(dir);
275 }
9ead6627
KS
276}
277
1985c76e 278static int uinfo(struct udev *udev, int argc, char *argv[])
87171e46 279{
912541b0
KS
280 struct udev_device *device = NULL;
281 bool root = 0;
282 bool export = 0;
283 const char *export_prefix = NULL;
284 char path[UTIL_PATH_SIZE];
285 char name[UTIL_PATH_SIZE];
286 struct udev_list_entry *list_entry;
287 int rc = 0;
288
289 static const struct option options[] = {
290 { "name", required_argument, NULL, 'n' },
291 { "path", required_argument, NULL, 'p' },
292 { "query", required_argument, NULL, 'q' },
293 { "attribute-walk", no_argument, NULL, 'a' },
294 { "cleanup-db", no_argument, NULL, 'c' },
295 { "export-db", no_argument, NULL, 'e' },
296 { "root", no_argument, NULL, 'r' },
297 { "run", no_argument, NULL, 'R' },
298 { "device-id-of-file", required_argument, NULL, 'd' },
299 { "export", no_argument, NULL, 'x' },
300 { "export-prefix", required_argument, NULL, 'P' },
301 { "version", no_argument, NULL, 'V' },
302 { "help", no_argument, NULL, 'h' },
303 {}
304 };
305
306 enum action_type {
307 ACTION_NONE,
308 ACTION_QUERY,
309 ACTION_ATTRIBUTE_WALK,
310 ACTION_ROOT,
311 ACTION_DEVICE_ID_FILE,
312 } action = ACTION_NONE;
313
314 enum query_type {
315 QUERY_NONE,
316 QUERY_NAME,
317 QUERY_PATH,
318 QUERY_SYMLINK,
319 QUERY_PROPERTY,
320 QUERY_ALL,
321 } query = QUERY_NONE;
322
323 for (;;) {
324 int option;
325 struct stat statbuf;
326
327 option = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL);
328 if (option == -1)
329 break;
330
912541b0
KS
331 switch (option) {
332 case 'n':
333 if (device != NULL) {
334 fprintf(stderr, "device already specified\n");
335 rc = 2;
336 goto exit;
337 }
338 /* remove /dev if given */
339 if (strncmp(optarg, udev_get_dev_path(udev), strlen(udev_get_dev_path(udev))) != 0)
340 util_strscpyl(name, sizeof(name), udev_get_dev_path(udev), "/", optarg, NULL);
341 else
342 util_strscpy(name, sizeof(name), optarg);
343 util_remove_trailing_chars(name, '/');
344 if (stat(name, &statbuf) < 0) {
345 fprintf(stderr, "device node not found\n");
346 rc = 2;
347 goto exit;
348 } else {
349 char type;
350
351 if (S_ISBLK(statbuf.st_mode)) {
352 type = 'b';
353 } else if (S_ISCHR(statbuf.st_mode)) {
354 type = 'c';
355 } else {
356 fprintf(stderr, "device node has wrong file type\n");
357 rc = 2;
358 goto exit;
359 }
360 device = udev_device_new_from_devnum(udev, type, statbuf.st_rdev);
361 if (device == NULL) {
362 fprintf(stderr, "device node not found\n");
363 rc = 2;
364 goto exit;
365 }
366 }
367 break;
368 case 'p':
369 if (device != NULL) {
370 fprintf(stderr, "device already specified\n");
371 rc = 2;
372 goto exit;
373 }
374 /* add sys dir if needed */
375 if (strncmp(optarg, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) != 0)
376 util_strscpyl(path, sizeof(path), udev_get_sys_path(udev), optarg, NULL);
377 else
378 util_strscpy(path, sizeof(path), optarg);
379 util_remove_trailing_chars(path, '/');
380 device = udev_device_new_from_syspath(udev, path);
381 if (device == NULL) {
382 fprintf(stderr, "device path not found\n");
383 rc = 2;
384 goto exit;
385 }
386 break;
387 case 'q':
388 action = ACTION_QUERY;
389 if (strcmp(optarg, "property") == 0 || strcmp(optarg, "env") == 0) {
390 query = QUERY_PROPERTY;
391 } else if (strcmp(optarg, "name") == 0) {
392 query = QUERY_NAME;
393 } else if (strcmp(optarg, "symlink") == 0) {
394 query = QUERY_SYMLINK;
395 } else if (strcmp(optarg, "path") == 0) {
396 query = QUERY_PATH;
397 } else if (strcmp(optarg, "all") == 0) {
398 query = QUERY_ALL;
399 } else {
400 fprintf(stderr, "unknown query type\n");
401 rc = 3;
402 goto exit;
403 }
404 break;
405 case 'r':
406 if (action == ACTION_NONE)
407 action = ACTION_ROOT;
408 root = true;
409 break;
410 case 'R':
411 printf("%s\n", udev_get_run_path(udev));
412 goto exit;
413 case 'd':
414 action = ACTION_DEVICE_ID_FILE;
415 util_strscpy(name, sizeof(name), optarg);
416 break;
417 case 'a':
418 action = ACTION_ATTRIBUTE_WALK;
419 break;
420 case 'e':
421 export_devices(udev);
422 goto exit;
423 case 'c':
424 cleanup_db(udev);
425 goto exit;
426 case 'x':
427 export = true;
428 break;
429 case 'P':
430 export_prefix = optarg;
431 break;
432 case 'V':
433 printf("%s\n", VERSION);
434 goto exit;
435 case 'h':
436 printf("Usage: udevadm info OPTIONS\n"
437 " --query=<type> query device information:\n"
438 " name name of device node\n"
439 " symlink pointing to node\n"
440 " path sys device path\n"
441 " property the device properties\n"
442 " all all values\n"
443 " --path=<syspath> sys device path used for query or attribute walk\n"
444 " --name=<name> node or symlink name used for query or attribute walk\n"
445 " --root prepend dev directory to path names\n"
446 " --attribute-walk print all key matches while walking along the chain\n"
447 " of parent devices\n"
448 " --device-id-of-file=<file> print major:minor of device containing this file\n"
449 " --export export key/value pairs\n"
450 " --export-prefix export the key name with a prefix\n"
451 " --export-db export the content of the udev database\n"
452 " --cleanup-db cleanup the udev database\n"
453 " --help\n\n");
454 goto exit;
455 default:
456 rc = 1;
457 goto exit;
458 }
459 }
460
461 switch (action) {
462 case ACTION_QUERY:
463 if (device == NULL) {
464 fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
465 rc = 4;
466 goto exit;
467 }
468
469 switch(query) {
470 case QUERY_NAME: {
471 const char *node = udev_device_get_devnode(device);
472
473 if (node == NULL) {
474 fprintf(stderr, "no device node found\n");
475 rc = 5;
476 goto exit;
477 }
478
479 if (root) {
480 printf("%s\n", udev_device_get_devnode(device));
481 } else {
482 size_t len = strlen(udev_get_dev_path(udev));
483
484 printf("%s\n", &udev_device_get_devnode(device)[len+1]);
485 }
486 break;
487 }
488 case QUERY_SYMLINK:
489 list_entry = udev_device_get_devlinks_list_entry(device);
490 while (list_entry != NULL) {
491 if (root) {
492 printf("%s", udev_list_entry_get_name(list_entry));
493 } else {
494 size_t len;
495
496 len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
497 printf("%s", &udev_list_entry_get_name(list_entry)[len+1]);
498 }
499 list_entry = udev_list_entry_get_next(list_entry);
500 if (list_entry != NULL)
501 printf(" ");
502 }
503 printf("\n");
504 break;
505 case QUERY_PATH:
506 printf("%s\n", udev_device_get_devpath(device));
507 goto exit;
508 case QUERY_PROPERTY:
509 list_entry = udev_device_get_properties_list_entry(device);
510 while (list_entry != NULL) {
511 if (export) {
512 const char *prefix = export_prefix;
513
514 if (prefix == NULL)
515 prefix = "";
516 printf("%s%s='%s'\n", prefix,
517 udev_list_entry_get_name(list_entry),
518 udev_list_entry_get_value(list_entry));
519 } else {
520 printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
521 }
522 list_entry = udev_list_entry_get_next(list_entry);
523 }
524 break;
525 case QUERY_ALL:
526 print_record(device);
527 break;
528 default:
529 fprintf(stderr, "unknown query type\n");
530 break;
531 }
532 break;
533 case ACTION_ATTRIBUTE_WALK:
534 if (device == NULL) {
535 fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
536 rc = 4;
537 goto exit;
538 }
539 print_device_chain(device);
540 break;
541 case ACTION_DEVICE_ID_FILE:
542 if (stat_device(name, export, export_prefix) != 0)
543 rc = 1;
544 break;
545 case ACTION_ROOT:
546 printf("%s\n", udev_get_dev_path(udev));
547 break;
548 default:
549 fprintf(stderr, "missing option\n");
550 rc = 1;
551 break;
552 }
87171e46 553
e48fc108 554exit:
912541b0
KS
555 udev_device_unref(device);
556 return rc;
87171e46 557}
1985c76e
KS
558
559const struct udevadm_cmd udevadm_info = {
912541b0
KS
560 .name = "info",
561 .cmd = uinfo,
562 .help = "query sysfs or the udev database",
1985c76e 563};