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