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