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