]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/udevadm-info.c
scsi_id: cleanup
[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
18#include <stdlib.h>
19#include <string.h>
20#include <stdio.h>
1aa1e248 21#include <stddef.h>
034f35d7 22#include <ctype.h>
87171e46
KS
23#include <stdarg.h>
24#include <unistd.h>
1aa1e248 25#include <dirent.h>
87171e46 26#include <errno.h>
11f1bb5a 27#include <getopt.h>
e6c1a2bd 28#include <fcntl.h>
492e76c9
KS
29#include <sys/stat.h>
30#include <sys/types.h>
be9b51f6 31
869fc2f1 32#include "udev.h"
be9b51f6 33
20bee04c
KS
34static bool skip_attribute(const char *name)
35{
46512cd9 36 static const char* const skip[] = {
912541b0
KS
37 "uevent",
38 "dev",
39 "modalias",
40 "resource",
41 "driver",
42 "subsystem",
43 "module",
44 };
45 unsigned int i;
46
8fef0ff2 47 for (i = 0; i < ELEMENTSOF(skip); i++)
090be865 48 if (streq(name, skip[i]))
912541b0
KS
49 return true;
50 return false;
20bee04c
KS
51}
52
93b0f384 53static void print_all_attributes(struct udev_device *device, const char *key)
be9b51f6 54{
912541b0
KS
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;
912541b0
KS
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--;
baa30fbc 78 if (len > 0)
912541b0 79 continue;
912541b0
KS
80
81 printf(" %s{%s}==\"%s\"\n", key, name, value);
82 }
83 printf("\n");
be9b51f6
KS
84}
85
93b0f384 86static int print_device_chain(struct udev_device *device)
87171e46 87{
912541b0
KS
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;
1aa1e248
KS
130}
131
93b0f384 132static void print_record(struct udev_device *device)
31de3a2b 133{
912541b0
KS
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
912541b0
KS
140 str = udev_device_get_devnode(device);
141 if (str != NULL)
6ada823a 142 printf("N: %s\n", str + strlen("/dev/"));
912541b0
KS
143
144 i = udev_device_get_devlink_priority(device);
145 if (i != 0)
146 printf("L: %i\n", i);
147
6ada823a
KS
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/"));
912541b0
KS
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");
31de3a2b
KS
156}
157
88163971 158static int stat_device(const char *name, bool export, const char *prefix)
f338bac8 159{
912541b0
KS
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;
f338bac8
KS
175}
176
bf7ad0ea
KS
177static int export_devices(struct udev *udev)
178{
912541b0
KS
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;
bf7ad0ea
KS
197}
198
9ead6627
KS
199static void cleanup_dir(DIR *dir, mode_t mask, int depth)
200{
912541b0
KS
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 }
9ead6627
KS
228}
229
230static void cleanup_db(struct udev *udev)
231{
912541b0
KS
232 DIR *dir;
233
6ada823a 234 unlink("/run/udev/queue.bin");
912541b0 235
6ada823a 236 dir = opendir("/run/udev/data");
912541b0
KS
237 if (dir != NULL) {
238 cleanup_dir(dir, S_ISVTX, 1);
239 closedir(dir);
240 }
241
6ada823a 242 dir = opendir("/run/udev/links");
912541b0
KS
243 if (dir != NULL) {
244 cleanup_dir(dir, 0, 2);
245 closedir(dir);
246 }
247
6ada823a 248 dir = opendir("/run/udev/tags");
912541b0
KS
249 if (dir != NULL) {
250 cleanup_dir(dir, 0, 2);
251 closedir(dir);
252 }
253
84b6ad70
TG
254 dir = opendir("/run/udev/static_node-tags");
255 if (dir != NULL) {
256 cleanup_dir(dir, 0, 2);
257 closedir(dir);
258 }
259
6ada823a 260 dir = opendir("/run/udev/watch");
912541b0
KS
261 if (dir != NULL) {
262 cleanup_dir(dir, 0, 1);
263 closedir(dir);
264 }
9ead6627
KS
265}
266
4f5d327a
KS
267static 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)) {
d5a89d7d 272 strscpyl(name, sizeof(name), prefix, id, NULL);
4f5d327a
KS
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
1985c76e 297static int uinfo(struct udev *udev, int argc, char *argv[])
87171e46 298{
912541b0
KS
299 struct udev_device *device = NULL;
300 bool root = 0;
301 bool export = 0;
302 const char *export_prefix = NULL;
912541b0
KS
303 char name[UTIL_PATH_SIZE];
304 struct udev_list_entry *list_entry;
305 int rc = 0;
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' },
912541b0
KS
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
4f5d327a
KS
323 static const char *usage =
324 "Usage: udevadm info OPTIONS\n"
325 " --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 " --path=<syspath> sys device path used for query or attribute walk\n"
332 " --name=<name> node or symlink name used for query or attribute walk\n"
333 " --root prepend dev directory to path names\n"
334 " --attribute-walk print all key matches while walking along the chain\n"
335 " of parent devices\n"
336 " --device-id-of-file=<file> print major:minor of device containing this file\n"
337 " --export export key/value pairs\n"
338 " --export-prefix export the key name with a prefix\n"
339 " --export-db export the content of the udev database\n"
340 " --cleanup-db cleanup the udev database\n"
341 " --help\n";
342
912541b0 343 enum action_type {
912541b0
KS
344 ACTION_QUERY,
345 ACTION_ATTRIBUTE_WALK,
912541b0 346 ACTION_DEVICE_ID_FILE,
4f5d327a 347 } action = ACTION_QUERY;
912541b0
KS
348
349 enum query_type {
912541b0
KS
350 QUERY_NAME,
351 QUERY_PATH,
352 QUERY_SYMLINK,
353 QUERY_PROPERTY,
354 QUERY_ALL,
4f5d327a 355 } query = QUERY_ALL;
912541b0
KS
356
357 for (;;) {
358 int option;
912541b0
KS
359
360 option = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL);
361 if (option == -1)
362 break;
363
912541b0 364 switch (option) {
4f5d327a 365 case 'n': {
912541b0
KS
366 if (device != NULL) {
367 fprintf(stderr, "device already specified\n");
368 rc = 2;
369 goto exit;
370 }
4f5d327a
KS
371
372 device = find_device(udev, optarg, "/dev/");
373 if (device == NULL) {
912541b0
KS
374 fprintf(stderr, "device node not found\n");
375 rc = 2;
376 goto exit;
912541b0
KS
377 }
378 break;
4f5d327a 379 }
912541b0
KS
380 case 'p':
381 if (device != NULL) {
382 fprintf(stderr, "device already specified\n");
383 rc = 2;
384 goto exit;
385 }
4f5d327a
KS
386
387 device = find_device(udev, optarg, "/sys");
912541b0 388 if (device == NULL) {
4f5d327a 389 fprintf(stderr, "syspath not found\n");
912541b0
KS
390 rc = 2;
391 goto exit;
392 }
393 break;
394 case 'q':
395 action = ACTION_QUERY;
090be865 396 if (streq(optarg, "property") || streq(optarg, "env")) {
912541b0 397 query = QUERY_PROPERTY;
090be865 398 } else if (streq(optarg, "name")) {
912541b0 399 query = QUERY_NAME;
090be865 400 } else if (streq(optarg, "symlink")) {
912541b0 401 query = QUERY_SYMLINK;
090be865 402 } else if (streq(optarg, "path")) {
912541b0 403 query = QUERY_PATH;
090be865 404 } else if (streq(optarg, "all")) {
912541b0
KS
405 query = QUERY_ALL;
406 } else {
407 fprintf(stderr, "unknown query type\n");
408 rc = 3;
409 goto exit;
410 }
411 break;
412 case 'r':
912541b0
KS
413 root = true;
414 break;
912541b0
KS
415 case 'd':
416 action = ACTION_DEVICE_ID_FILE;
d5a89d7d 417 strscpy(name, sizeof(name), optarg);
912541b0
KS
418 break;
419 case 'a':
420 action = ACTION_ATTRIBUTE_WALK;
421 break;
422 case 'e':
423 export_devices(udev);
424 goto exit;
425 case 'c':
426 cleanup_db(udev);
427 goto exit;
428 case 'x':
429 export = true;
430 break;
431 case 'P':
432 export_prefix = optarg;
433 break;
434 case 'V':
435 printf("%s\n", VERSION);
436 goto exit;
437 case 'h':
4f5d327a 438 printf("%s\n", usage);
912541b0
KS
439 goto exit;
440 default:
441 rc = 1;
442 goto exit;
443 }
444 }
445
446 switch (action) {
447 case ACTION_QUERY:
4f5d327a
KS
448 if (!device) {
449 if (!argv[optind]) {
450 fprintf(stderr, "%s\n", usage);
451 rc = 2;
452 goto exit;
453 }
454 device = find_device(udev, argv[optind], NULL);
455 if (!device) {
456 fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
457 rc = 4;
458 goto exit;
459 }
912541b0
KS
460 }
461
462 switch(query) {
463 case QUERY_NAME: {
464 const char *node = udev_device_get_devnode(device);
465
466 if (node == NULL) {
467 fprintf(stderr, "no device node found\n");
468 rc = 5;
469 goto exit;
470 }
471
6ada823a 472 if (root)
912541b0 473 printf("%s\n", udev_device_get_devnode(device));
6ada823a
KS
474 else
475 printf("%s\n", udev_device_get_devnode(device) + strlen("/dev/"));
912541b0
KS
476 break;
477 }
478 case QUERY_SYMLINK:
479 list_entry = udev_device_get_devlinks_list_entry(device);
480 while (list_entry != NULL) {
6ada823a 481 if (root)
912541b0 482 printf("%s", udev_list_entry_get_name(list_entry));
6ada823a
KS
483 else
484 printf("%s", udev_list_entry_get_name(list_entry) + strlen("/dev/"));
912541b0
KS
485 list_entry = udev_list_entry_get_next(list_entry);
486 if (list_entry != NULL)
487 printf(" ");
488 }
489 printf("\n");
490 break;
491 case QUERY_PATH:
492 printf("%s\n", udev_device_get_devpath(device));
493 goto exit;
494 case QUERY_PROPERTY:
495 list_entry = udev_device_get_properties_list_entry(device);
496 while (list_entry != NULL) {
497 if (export) {
498 const char *prefix = export_prefix;
499
500 if (prefix == NULL)
501 prefix = "";
502 printf("%s%s='%s'\n", prefix,
503 udev_list_entry_get_name(list_entry),
504 udev_list_entry_get_value(list_entry));
505 } else {
506 printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
507 }
508 list_entry = udev_list_entry_get_next(list_entry);
509 }
510 break;
511 case QUERY_ALL:
512 print_record(device);
513 break;
514 default:
515 fprintf(stderr, "unknown query type\n");
516 break;
517 }
518 break;
519 case ACTION_ATTRIBUTE_WALK:
4f5d327a
KS
520 if (!device && argv[optind]) {
521 device = find_device(udev, argv[optind], NULL);
522 if (!device) {
523 fprintf(stderr, "Unknown device, absolute path in /dev/ or /sys expected.\n");
524 rc = 4;
525 goto exit;
526 }
527 }
528 if (!device) {
529 fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
912541b0
KS
530 rc = 4;
531 goto exit;
532 }
533 print_device_chain(device);
534 break;
535 case ACTION_DEVICE_ID_FILE:
536 if (stat_device(name, export, export_prefix) != 0)
537 rc = 1;
538 break;
912541b0 539 }
87171e46 540
e48fc108 541exit:
912541b0
KS
542 udev_device_unref(device);
543 return rc;
87171e46 544}
1985c76e
KS
545
546const struct udevadm_cmd udevadm_info = {
912541b0
KS
547 .name = "info",
548 .cmd = uinfo,
549 .help = "query sysfs or the udev database",
1985c76e 550};