]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udevadm-info.c
add test/src to .gitignore
[thirdparty/systemd.git] / src / 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{
74e8a45e 36 static const char const *skip[] = {
20bee04c
KS
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
93b0f384 53static void print_all_attributes(struct udev_device *device, const char *key)
be9b51f6 54{
2fdaa983 55 struct udev *udev = udev_device_get_udev(device);
95ce1875 56 struct udev_list_entry *sysattr;
1aa1e248 57
95ce1875
TE
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;
1aa1e248 62
95ce1875 63 name = udev_list_entry_get_name(sysattr);
20bee04c 64 if (skip_attribute(name))
95ce1875 65 continue;
e9b64770 66
95ce1875
TE
67 value = udev_device_get_sysattr_value(device, name);
68 if (value == NULL)
69 continue;
20bee04c 70 dbg(udev, "attr '%s'='%s'\n", name, value);
492e76c9 71
2fdaa983
KS
72 /* skip any values that look like a path */
73 if (value[0] == '/')
74 continue;
75
95ce1875
TE
76 /* skip nonprintable attributes */
77 len = strlen(value);
78 while (len > 0 && isprint(value[len-1]))
79 len--;
80 if (len > 0) {
20bee04c 81 dbg(udev, "attribute value of '%s' non-printable, skip\n", name);
95ce1875 82 continue;
6276fdd2 83 }
95ce1875
TE
84
85 printf(" %s{%s}==\"%s\"\n", key, name, value);
be9b51f6
KS
86 }
87 printf("\n");
be9b51f6
KS
88}
89
93b0f384 90static int print_device_chain(struct udev_device *device)
87171e46 91{
b2d9e4f2 92 struct udev_device *device_parent;
4ad3a37f 93 const char *str;
e48fc108 94
1aa1e248 95 printf("\n"
b620292b 96 "Udevadm info starts with the device specified by the devpath and then\n"
6cf19e52
KS
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"
dba8c18b 101 "\n");
f61d732a 102
4ad3a37f
KS
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);
93b0f384 113 print_all_attributes(device, "ATTR");
4ad3a37f 114
b2d9e4f2
KS
115 device_parent = device;
116 do {
117 device_parent = udev_device_get_parent(device_parent);
4ad3a37f 118 if (device_parent == NULL)
034f35d7 119 break;
b2d9e4f2
KS
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);
4ad3a37f
KS
123 if (str == NULL)
124 str = "";
125 printf(" SUBSYSTEMS==\"%s\"\n", str);
b2d9e4f2 126 str = udev_device_get_driver(device_parent);
4ad3a37f
KS
127 if (str == NULL)
128 str = "";
129 printf(" DRIVERS==\"%s\"\n", str);
93b0f384 130 print_all_attributes(device_parent, "ATTRS");
b2d9e4f2 131 } while (device_parent != NULL);
be9b51f6 132
1aa1e248
KS
133 return 0;
134}
135
93b0f384 136static void print_record(struct udev_device *device)
31de3a2b 137{
93b0f384 138 size_t len;
ee137da3 139 const char *str;
93b0f384 140 int i;
0de33a61 141 struct udev_list_entry *list_entry;
93b0f384
KS
142
143 printf("P: %s\n", udev_device_get_devpath(device));
bf7ad0ea 144
93b0f384 145 len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
ee137da3
KS
146 str = udev_device_get_devnode(device);
147 if (str != NULL)
148 printf("N: %s\n", &str[len+1]);
bf7ad0ea 149
aa8734ff 150 i = udev_device_get_devlink_priority(device);
93b0f384
KS
151 if (i != 0)
152 printf("L: %i\n", i);
bf7ad0ea 153
0de33a61 154 udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device)) {
bf7ad0ea 155 len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
0de33a61 156 printf("S: %s\n", &udev_list_entry_get_name(list_entry)[len+1]);
31de3a2b 157 }
93b0f384 158
0de33a61
KS
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));
bf7ad0ea 163 printf("\n");
31de3a2b
KS
164}
165
88163971 166static int stat_device(const char *name, bool export, const char *prefix)
f338bac8
KS
167{
168 struct stat statbuf;
169
170 if (stat(name, &statbuf) != 0)
171 return -1;
172
d1acfc3e
KS
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
cce9d773 181 printf("%d:%d\n", major(statbuf.st_dev), minor(statbuf.st_dev));
f338bac8
KS
182 return 0;
183}
184
bf7ad0ea
KS
185static int export_devices(struct udev *udev)
186{
438d4c3c 187 struct udev_enumerate *udev_enumerate;
0de33a61 188 struct udev_list_entry *list_entry;
bf7ad0ea 189
438d4c3c
KS
190 udev_enumerate = udev_enumerate_new(udev);
191 if (udev_enumerate == NULL)
bf7ad0ea 192 return -1;
c97f839e 193 udev_enumerate_scan_devices(udev_enumerate);
438d4c3c 194 udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
bf7ad0ea
KS
195 struct udev_device *device;
196
0de33a61 197 device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry));
bf7ad0ea 198 if (device != NULL) {
b38a5801 199 print_record(device);
bf7ad0ea
KS
200 udev_device_unref(device);
201 }
bf7ad0ea 202 }
438d4c3c 203 udev_enumerate_unref(udev_enumerate);
bf7ad0ea
KS
204 return 0;
205}
206
9ead6627
KS
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
1985c76e 282static int uinfo(struct udev *udev, int argc, char *argv[])
87171e46 283{
93b0f384 284 struct udev_device *device = NULL;
88163971
KS
285 bool root = 0;
286 bool export = 0;
d1acfc3e 287 const char *export_prefix = NULL;
93b0f384
KS
288 char path[UTIL_PATH_SIZE];
289 char name[UTIL_PATH_SIZE];
0de33a61 290 struct udev_list_entry *list_entry;
93b0f384 291 int rc = 0;
24ca5c33 292
e97717ba 293 static const struct option options[] = {
033e9f8c
KS
294 { "name", required_argument, NULL, 'n' },
295 { "path", required_argument, NULL, 'p' },
ff5d99e0 296 { "query", required_argument, NULL, 'q' },
033e9f8c 297 { "attribute-walk", no_argument, NULL, 'a' },
9ead6627 298 { "cleanup-db", no_argument, NULL, 'c' },
033e9f8c
KS
299 { "export-db", no_argument, NULL, 'e' },
300 { "root", no_argument, NULL, 'r' },
4ec9c3e7 301 { "run", no_argument, NULL, 'R' },
033e9f8c
KS
302 { "device-id-of-file", required_argument, NULL, 'd' },
303 { "export", no_argument, NULL, 'x' },
304 { "export-prefix", required_argument, NULL, 'P' },
50025605 305 { "version", no_argument, NULL, 'V' },
033e9f8c 306 { "help", no_argument, NULL, 'h' },
11f1bb5a
KS
307 {}
308 };
309
24ca5c33
KS
310 enum action_type {
311 ACTION_NONE,
312 ACTION_QUERY,
313 ACTION_ATTRIBUTE_WALK,
314 ACTION_ROOT,
f338bac8 315 ACTION_DEVICE_ID_FILE,
24ca5c33
KS
316 } action = ACTION_NONE;
317
b8476286
KS
318 enum query_type {
319 QUERY_NONE,
320 QUERY_NAME,
321 QUERY_PATH,
322 QUERY_SYMLINK,
0254b211 323 QUERY_PROPERTY,
b8476286
KS
324 QUERY_ALL,
325 } query = QUERY_NONE;
24ca5c33 326
88cbfb09 327 for (;;) {
7d563a17 328 int option;
bf7ad0ea 329 struct stat statbuf;
7d563a17 330
9ead6627 331 option = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL);
87171e46
KS
332 if (option == -1)
333 break;
334
7d563a17 335 dbg(udev, "option '%c'\n", option);
87171e46
KS
336 switch (option) {
337 case 'n':
93b0f384
KS
338 if (device != NULL) {
339 fprintf(stderr, "device already specified\n");
340 rc = 2;
341 goto exit;
342 }
2362eea6 343 /* remove /dev if given */
065db052
KS
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);
ecc9ec57 348 util_remove_trailing_chars(name, '/');
bf7ad0ea
KS
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 }
87171e46 372 break;
87171e46 373 case 'p':
93b0f384
KS
374 if (device != NULL) {
375 fprintf(stderr, "device already specified\n");
376 rc = 2;
377 goto exit;
378 }
bc8184ed 379 /* add sys dir if needed */
065db052
KS
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);
ecc9ec57 384 util_remove_trailing_chars(path, '/');
8753fadf 385 device = udev_device_new_from_syspath(udev, path);
bf7ad0ea
KS
386 if (device == NULL) {
387 fprintf(stderr, "device path not found\n");
388 rc = 2;
389 goto exit;
390 }
87171e46 391 break;
87171e46 392 case 'q':
24ca5c33 393 action = ACTION_QUERY;
ff5d99e0 394 if (strcmp(optarg, "property") == 0 || strcmp(optarg, "env") == 0) {
0254b211
KS
395 query = QUERY_PROPERTY;
396 } else if (strcmp(optarg, "name") == 0) {
b8476286 397 query = QUERY_NAME;
0254b211 398 } else if (strcmp(optarg, "symlink") == 0) {
b8476286 399 query = QUERY_SYMLINK;
0254b211 400 } else if (strcmp(optarg, "path") == 0) {
b8476286 401 query = QUERY_PATH;
0254b211 402 } else if (strcmp(optarg, "all") == 0) {
b8476286 403 query = QUERY_ALL;
0254b211
KS
404 } else {
405 fprintf(stderr, "unknown query type\n");
406 rc = 3;
407 goto exit;
8ea84a8a 408 }
0254b211 409 break;
87171e46 410 case 'r':
24ca5c33
KS
411 if (action == ACTION_NONE)
412 action = ACTION_ROOT;
88163971 413 root = true;
87171e46 414 break;
4ec9c3e7
KS
415 case 'R':
416 printf("%s\n", udev_get_run_path(udev));
417 goto exit;
f338bac8
KS
418 case 'd':
419 action = ACTION_DEVICE_ID_FILE;
065db052 420 util_strscpy(name, sizeof(name), optarg);
f338bac8 421 break;
87171e46 422 case 'a':
24ca5c33 423 action = ACTION_ATTRIBUTE_WALK;
87171e46 424 break;
24ca5c33 425 case 'e':
bf7ad0ea 426 export_devices(udev);
e48fc108 427 goto exit;
9ead6627
KS
428 case 'c':
429 cleanup_db(udev);
430 goto exit;
d1acfc3e 431 case 'x':
88163971 432 export = true;
d1acfc3e
KS
433 break;
434 case 'P':
435 export_prefix = optarg;
436 break;
87171e46 437 case 'V':
50025605 438 printf("%s\n", VERSION);
e48fc108 439 goto exit;
87171e46 440 case 'h':
225cb03b 441 printf("Usage: udevadm info OPTIONS\n"
cd42b50d 442 " --query=<type> query device information:\n"
f338bac8
KS
443 " name name of device node\n"
444 " symlink pointing to node\n"
cd42b50d 445 " path sys device path\n"
0254b211 446 " property the device properties\n"
f338bac8 447 " all all values\n"
cd42b50d
KS
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"
f338bac8 452 " of parent devices\n"
cd42b50d 453 " --device-id-of-file=<file> print major:minor of device containing this file\n"
4281da1f
KS
454 " --export export key/value pairs\n"
455 " --export-prefix export the key name with a prefix\n"
f338bac8 456 " --export-db export the content of the udev database\n"
9ead6627 457 " --cleanup-db cleanup the udev database\n"
f1e7e360 458 " --help\n\n");
e3396a2d 459 goto exit;
87171e46 460 default:
e261c5f6 461 rc = 1;
24ca5c33 462 goto exit;
87171e46
KS
463 }
464 }
465
24ca5c33
KS
466 switch (action) {
467 case ACTION_QUERY:
93b0f384 468 if (device == NULL) {
9a8047fa 469 fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
1aa1e248 470 rc = 4;
24ca5c33 471 goto exit;
87171e46
KS
472 }
473
87171e46 474 switch(query) {
299650f1
KS
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
93b0f384 484 if (root) {
fb762bb9 485 printf("%s\n", udev_device_get_devnode(device));
93b0f384 486 } else {
299650f1
KS
487 size_t len = strlen(udev_get_dev_path(udev));
488
489 printf("%s\n", &udev_device_get_devnode(device)[len+1]);
18770246 490 }
24ca5c33 491 break;
299650f1 492 }
93b0f384 493 case QUERY_SYMLINK:
0de33a61
KS
494 list_entry = udev_device_get_devlinks_list_entry(device);
495 while (list_entry != NULL) {
bf7ad0ea 496 if (root) {
0de33a61 497 printf("%s", udev_list_entry_get_name(list_entry));
bf7ad0ea
KS
498 } else {
499 size_t len;
500
501 len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
0de33a61 502 printf("%s", &udev_list_entry_get_name(list_entry)[len+1]);
bf7ad0ea 503 }
0de33a61
KS
504 list_entry = udev_list_entry_get_next(list_entry);
505 if (list_entry != NULL)
bf7ad0ea
KS
506 printf(" ");
507 }
508 printf("\n");
93b0f384 509 break;
b8476286 510 case QUERY_PATH:
93b0f384 511 printf("%s\n", udev_device_get_devpath(device));
e48fc108 512 goto exit;
0254b211 513 case QUERY_PROPERTY:
0de33a61
KS
514 list_entry = udev_device_get_properties_list_entry(device);
515 while (list_entry != NULL) {
88163971
KS
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 }
0de33a61 527 list_entry = udev_list_entry_get_next(list_entry);
bf7ad0ea 528 }
24ca5c33 529 break;
b8476286 530 case QUERY_ALL:
93b0f384 531 print_record(device);
24ca5c33 532 break;
87171e46 533 default:
e3396a2d 534 fprintf(stderr, "unknown query type\n");
24ca5c33 535 break;
87171e46 536 }
24ca5c33
KS
537 break;
538 case ACTION_ATTRIBUTE_WALK:
93b0f384 539 if (device == NULL) {
9a8047fa 540 fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
62b9dfb6 541 rc = 4;
1aa0c52b
KS
542 goto exit;
543 }
93b0f384 544 print_device_chain(device);
24ca5c33 545 break;
f338bac8 546 case ACTION_DEVICE_ID_FILE:
d1acfc3e 547 if (stat_device(name, export, export_prefix) != 0)
62b9dfb6 548 rc = 1;
f338bac8 549 break;
24ca5c33 550 case ACTION_ROOT:
7d563a17 551 printf("%s\n", udev_get_dev_path(udev));
24ca5c33
KS
552 break;
553 default:
e3396a2d 554 fprintf(stderr, "missing option\n");
1aa1e248 555 rc = 1;
24ca5c33 556 break;
87171e46
KS
557 }
558
e48fc108 559exit:
93b0f384 560 udev_device_unref(device);
1aa1e248 561 return rc;
87171e46 562}
1985c76e
KS
563
564const struct udevadm_cmd udevadm_info = {
565 .name = "info",
566 .cmd = uinfo,
567 .help = "query sysfs or the udev database",
568};