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