]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udevadm-info.c
sd-*.h: clean up exported (or to-be-exported) header files
[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 "string-util.h"
30 #include "udev-util.h"
31 #include "udev.h"
32 #include "udevadm-util.h"
33
34 static bool skip_attribute(const char *name) {
35 static const char* const skip[] = {
36 "uevent",
37 "dev",
38 "modalias",
39 "resource",
40 "driver",
41 "subsystem",
42 "module",
43 };
44 unsigned int i;
45
46 for (i = 0; i < ELEMENTSOF(skip); i++)
47 if (streq(name, skip[i]))
48 return true;
49 return false;
50 }
51
52 static void print_all_attributes(struct udev_device *device, const char *key) {
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;
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--;
76 if (len > 0)
77 continue;
78
79 printf(" %s{%s}==\"%s\"\n", key, name, value);
80 }
81 printf("\n");
82 }
83
84 static int print_device_chain(struct udev_device *device) {
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;
127 }
128
129 static void print_record(struct udev_device *device) {
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
136 str = udev_device_get_devnode(device);
137 if (str != NULL)
138 printf("N: %s\n", str + strlen("/dev/"));
139
140 i = udev_device_get_devlink_priority(device);
141 if (i != 0)
142 printf("L: %i\n", i);
143
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/"));
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");
152 }
153
154 static int stat_device(const char *name, bool export, const char *prefix) {
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_";
163 printf("%sMAJOR=%u\n"
164 "%sMINOR=%u\n",
165 prefix, major(statbuf.st_dev),
166 prefix, minor(statbuf.st_dev));
167 } else
168 printf("%u:%u\n", major(statbuf.st_dev), minor(statbuf.st_dev));
169 return 0;
170 }
171
172 static int export_devices(struct udev *udev) {
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;
191 }
192
193 static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
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)) {
209 _cleanup_closedir_ DIR *dir2;
210
211 dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
212 if (dir2 != NULL)
213 cleanup_dir(dir2, mask, depth-1);
214
215 (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
216 } else
217 (void) unlinkat(dirfd(dir), dent->d_name, 0);
218 }
219 }
220
221 static void cleanup_db(struct udev *udev) {
222 DIR *dir;
223
224 unlink("/run/udev/queue.bin");
225
226 dir = opendir("/run/udev/data");
227 if (dir != NULL) {
228 cleanup_dir(dir, S_ISVTX, 1);
229 closedir(dir);
230 }
231
232 dir = opendir("/run/udev/links");
233 if (dir != NULL) {
234 cleanup_dir(dir, 0, 2);
235 closedir(dir);
236 }
237
238 dir = opendir("/run/udev/tags");
239 if (dir != NULL) {
240 cleanup_dir(dir, 0, 2);
241 closedir(dir);
242 }
243
244 dir = opendir("/run/udev/static_node-tags");
245 if (dir != NULL) {
246 cleanup_dir(dir, 0, 2);
247 closedir(dir);
248 }
249
250 dir = opendir("/run/udev/watch");
251 if (dir != NULL) {
252 cleanup_dir(dir, 0, 1);
253 closedir(dir);
254 }
255 }
256
257 static 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
282 static int uinfo(struct udev *udev, int argc, char *argv[]) {
283 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
284 bool root = 0;
285 bool export = 0;
286 const char *export_prefix = NULL;
287 char name[UTIL_PATH_SIZE];
288 struct udev_list_entry *list_entry;
289 int c;
290
291 static const struct option options[] = {
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' },
299 { "device-id-of-file", required_argument, NULL, 'd' },
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' },
304 {}
305 };
306
307 enum action_type {
308 ACTION_QUERY,
309 ACTION_ATTRIBUTE_WALK,
310 ACTION_DEVICE_ID_FILE,
311 } action = ACTION_QUERY;
312
313 enum query_type {
314 QUERY_NAME,
315 QUERY_PATH,
316 QUERY_SYMLINK,
317 QUERY_PROPERTY,
318 QUERY_ALL,
319 } query = QUERY_ALL;
320
321 while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL)) >= 0)
322 switch (c) {
323 case 'n': {
324 if (device != NULL) {
325 fprintf(stderr, "device already specified\n");
326 return 2;
327 }
328
329 device = find_device(udev, optarg, "/dev/");
330 if (device == NULL) {
331 fprintf(stderr, "device node not found\n");
332 return 2;
333 }
334 break;
335 }
336 case 'p':
337 if (device != NULL) {
338 fprintf(stderr, "device already specified\n");
339 return 2;
340 }
341
342 device = find_device(udev, optarg, "/sys");
343 if (device == NULL) {
344 fprintf(stderr, "syspath not found\n");
345 return 2;
346 }
347 break;
348 case 'q':
349 action = ACTION_QUERY;
350 if (streq(optarg, "property") || streq(optarg, "env"))
351 query = QUERY_PROPERTY;
352 else if (streq(optarg, "name"))
353 query = QUERY_NAME;
354 else if (streq(optarg, "symlink"))
355 query = QUERY_SYMLINK;
356 else if (streq(optarg, "path"))
357 query = QUERY_PATH;
358 else if (streq(optarg, "all"))
359 query = QUERY_ALL;
360 else {
361 fprintf(stderr, "unknown query type\n");
362 return 3;
363 }
364 break;
365 case 'r':
366 root = true;
367 break;
368 case 'd':
369 action = ACTION_DEVICE_ID_FILE;
370 strscpy(name, sizeof(name), optarg);
371 break;
372 case 'a':
373 action = ACTION_ATTRIBUTE_WALK;
374 break;
375 case 'e':
376 export_devices(udev);
377 return 0;
378 case 'c':
379 cleanup_db(udev);
380 return 0;
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);
389 return 0;
390 case 'h':
391 help();
392 return 0;
393 default:
394 return 1;
395 }
396
397 switch (action) {
398 case ACTION_QUERY:
399 if (!device) {
400 if (!argv[optind]) {
401 help();
402 return 2;
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");
407 return 4;
408 }
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");
417 return 5;
418 }
419
420 if (root)
421 printf("%s\n", udev_device_get_devnode(device));
422 else
423 printf("%s\n", udev_device_get_devnode(device) + strlen("/dev/"));
424 break;
425 }
426 case QUERY_SYMLINK:
427 list_entry = udev_device_get_devlinks_list_entry(device);
428 while (list_entry != NULL) {
429 if (root)
430 printf("%s", udev_list_entry_get_name(list_entry));
431 else
432 printf("%s", udev_list_entry_get_name(list_entry) + strlen("/dev/"));
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));
441 return 0;
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:
463 assert_not_reached("unknown query type");
464 }
465 break;
466 case ACTION_ATTRIBUTE_WALK:
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");
471 return 4;
472 }
473 }
474 if (!device) {
475 fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
476 return 4;
477 }
478 print_device_chain(device);
479 break;
480 case ACTION_DEVICE_ID_FILE:
481 if (stat_device(name, export, export_prefix) != 0)
482 return 1;
483 break;
484 }
485
486 return 0;
487 }
488
489 const struct udevadm_cmd udevadm_info = {
490 .name = "info",
491 .cmd = uinfo,
492 .help = "Query sysfs or the udev database",
493 };