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