]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/udevadm-info.c
util-lib: split out fd-related operations into fd-util.[ch]
[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
034f35d7 18#include <ctype.h>
1aa1e248 19#include <dirent.h>
87171e46 20#include <errno.h>
e6c1a2bd 21#include <fcntl.h>
07630cea
LP
22#include <getopt.h>
23#include <stddef.h>
24#include <stdio.h>
25#include <string.h>
492e76c9 26#include <sys/stat.h>
07630cea 27#include <unistd.h>
be9b51f6 28
3ffd4af2 29#include "fd-util.h"
07630cea 30#include "string-util.h"
44433ebd 31#include "udev-util.h"
07630cea 32#include "udev.h"
d6170d27 33#include "udevadm-util.h"
be9b51f6 34
9ec6e95b 35static bool skip_attribute(const char *name) {
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
9ec6e95b 53static void print_all_attributes(struct udev_device *device, const char *key) {
912541b0
KS
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;
912541b0
KS
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--;
baa30fbc 77 if (len > 0)
912541b0 78 continue;
912541b0
KS
79
80 printf(" %s{%s}==\"%s\"\n", key, name, value);
81 }
82 printf("\n");
be9b51f6
KS
83}
84
9ec6e95b 85static int print_device_chain(struct udev_device *device) {
912541b0
KS
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;
1aa1e248
KS
128}
129
9ec6e95b 130static void print_record(struct udev_device *device) {
912541b0
KS
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
912541b0
KS
137 str = udev_device_get_devnode(device);
138 if (str != NULL)
6ada823a 139 printf("N: %s\n", str + strlen("/dev/"));
912541b0
KS
140
141 i = udev_device_get_devlink_priority(device);
142 if (i != 0)
143 printf("L: %i\n", i);
144
6ada823a
KS
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/"));
912541b0
KS
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");
31de3a2b
KS
153}
154
9ec6e95b 155static int stat_device(const char *name, bool export, const char *prefix) {
912541b0
KS
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_";
1fa2f38f
ZJS
164 printf("%sMAJOR=%u\n"
165 "%sMINOR=%u\n",
912541b0
KS
166 prefix, major(statbuf.st_dev),
167 prefix, minor(statbuf.st_dev));
168 } else
1fa2f38f 169 printf("%u:%u\n", major(statbuf.st_dev), minor(statbuf.st_dev));
912541b0 170 return 0;
f338bac8
KS
171}
172
9ec6e95b 173static int export_devices(struct udev *udev) {
912541b0
KS
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;
bf7ad0ea
KS
192}
193
9ec6e95b 194static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
912541b0
KS
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)) {
200c7fa6 210 _cleanup_closedir_ DIR *dir2;
912541b0
KS
211
212 dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
200c7fa6 213 if (dir2 != NULL)
912541b0 214 cleanup_dir(dir2, mask, depth-1);
200c7fa6
LP
215
216 (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
217 } else
218 (void) unlinkat(dirfd(dir), dent->d_name, 0);
912541b0 219 }
9ead6627
KS
220}
221
9ec6e95b 222static void cleanup_db(struct udev *udev) {
912541b0
KS
223 DIR *dir;
224
6ada823a 225 unlink("/run/udev/queue.bin");
912541b0 226
6ada823a 227 dir = opendir("/run/udev/data");
912541b0
KS
228 if (dir != NULL) {
229 cleanup_dir(dir, S_ISVTX, 1);
230 closedir(dir);
231 }
232
6ada823a 233 dir = opendir("/run/udev/links");
912541b0
KS
234 if (dir != NULL) {
235 cleanup_dir(dir, 0, 2);
236 closedir(dir);
237 }
238
6ada823a 239 dir = opendir("/run/udev/tags");
912541b0
KS
240 if (dir != NULL) {
241 cleanup_dir(dir, 0, 2);
242 closedir(dir);
243 }
244
84b6ad70
TG
245 dir = opendir("/run/udev/static_node-tags");
246 if (dir != NULL) {
247 cleanup_dir(dir, 0, 2);
248 closedir(dir);
249 }
250
6ada823a 251 dir = opendir("/run/udev/watch");
912541b0
KS
252 if (dir != NULL) {
253 cleanup_dir(dir, 0, 1);
254 closedir(dir);
255 }
9ead6627
KS
256}
257
5ac0162c
LP
258static 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
9ec6e95b 283static int uinfo(struct udev *udev, int argc, char *argv[]) {
44433ebd 284 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
912541b0
KS
285 bool root = 0;
286 bool export = 0;
287 const char *export_prefix = NULL;
912541b0
KS
288 char name[UTIL_PATH_SIZE];
289 struct udev_list_entry *list_entry;
44433ebd 290 int c;
912541b0
KS
291
292 static const struct option options[] = {
7643ac9a
ZJS
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' },
912541b0 300 { "device-id-of-file", required_argument, NULL, 'd' },
7643ac9a
ZJS
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' },
912541b0
KS
305 {}
306 };
307
308 enum action_type {
912541b0
KS
309 ACTION_QUERY,
310 ACTION_ATTRIBUTE_WALK,
912541b0 311 ACTION_DEVICE_ID_FILE,
4f5d327a 312 } action = ACTION_QUERY;
912541b0
KS
313
314 enum query_type {
912541b0
KS
315 QUERY_NAME,
316 QUERY_PATH,
317 QUERY_SYMLINK,
318 QUERY_PROPERTY,
319 QUERY_ALL,
4f5d327a 320 } query = QUERY_ALL;
912541b0 321
7643ac9a
ZJS
322 while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL)) >= 0)
323 switch (c) {
4f5d327a 324 case 'n': {
912541b0
KS
325 if (device != NULL) {
326 fprintf(stderr, "device already specified\n");
44433ebd 327 return 2;
912541b0 328 }
4f5d327a
KS
329
330 device = find_device(udev, optarg, "/dev/");
331 if (device == NULL) {
912541b0 332 fprintf(stderr, "device node not found\n");
44433ebd 333 return 2;
912541b0
KS
334 }
335 break;
4f5d327a 336 }
912541b0
KS
337 case 'p':
338 if (device != NULL) {
339 fprintf(stderr, "device already specified\n");
44433ebd 340 return 2;
912541b0 341 }
4f5d327a
KS
342
343 device = find_device(udev, optarg, "/sys");
912541b0 344 if (device == NULL) {
4f5d327a 345 fprintf(stderr, "syspath not found\n");
44433ebd 346 return 2;
912541b0
KS
347 }
348 break;
349 case 'q':
350 action = ACTION_QUERY;
44433ebd 351 if (streq(optarg, "property") || streq(optarg, "env"))
912541b0 352 query = QUERY_PROPERTY;
44433ebd 353 else if (streq(optarg, "name"))
912541b0 354 query = QUERY_NAME;
44433ebd 355 else if (streq(optarg, "symlink"))
912541b0 356 query = QUERY_SYMLINK;
44433ebd 357 else if (streq(optarg, "path"))
912541b0 358 query = QUERY_PATH;
44433ebd 359 else if (streq(optarg, "all"))
912541b0 360 query = QUERY_ALL;
44433ebd 361 else {
912541b0 362 fprintf(stderr, "unknown query type\n");
44433ebd 363 return 3;
912541b0
KS
364 }
365 break;
366 case 'r':
912541b0
KS
367 root = true;
368 break;
912541b0
KS
369 case 'd':
370 action = ACTION_DEVICE_ID_FILE;
d5a89d7d 371 strscpy(name, sizeof(name), optarg);
912541b0
KS
372 break;
373 case 'a':
374 action = ACTION_ATTRIBUTE_WALK;
375 break;
376 case 'e':
377 export_devices(udev);
44433ebd 378 return 0;
912541b0
KS
379 case 'c':
380 cleanup_db(udev);
44433ebd 381 return 0;
912541b0
KS
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);
44433ebd 390 return 0;
912541b0 391 case 'h':
5ac0162c 392 help();
44433ebd 393 return 0;
912541b0 394 default:
44433ebd 395 return 1;
912541b0 396 }
912541b0
KS
397
398 switch (action) {
399 case ACTION_QUERY:
4f5d327a
KS
400 if (!device) {
401 if (!argv[optind]) {
5ac0162c 402 help();
44433ebd 403 return 2;
4f5d327a
KS
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");
44433ebd 408 return 4;
4f5d327a 409 }
912541b0
KS
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");
44433ebd 418 return 5;
912541b0
KS
419 }
420
6ada823a 421 if (root)
912541b0 422 printf("%s\n", udev_device_get_devnode(device));
6ada823a
KS
423 else
424 printf("%s\n", udev_device_get_devnode(device) + strlen("/dev/"));
912541b0
KS
425 break;
426 }
427 case QUERY_SYMLINK:
428 list_entry = udev_device_get_devlinks_list_entry(device);
429 while (list_entry != NULL) {
6ada823a 430 if (root)
912541b0 431 printf("%s", udev_list_entry_get_name(list_entry));
6ada823a
KS
432 else
433 printf("%s", udev_list_entry_get_name(list_entry) + strlen("/dev/"));
912541b0
KS
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));
44433ebd 442 return 0;
912541b0
KS
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:
bdd13f6b 464 assert_not_reached("unknown query type");
912541b0
KS
465 }
466 break;
467 case ACTION_ATTRIBUTE_WALK:
4f5d327a
KS
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");
44433ebd 472 return 4;
4f5d327a
KS
473 }
474 }
475 if (!device) {
476 fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
44433ebd 477 return 4;
912541b0
KS
478 }
479 print_device_chain(device);
480 break;
481 case ACTION_DEVICE_ID_FILE:
482 if (stat_device(name, export, export_prefix) != 0)
44433ebd 483 return 1;
912541b0 484 break;
912541b0 485 }
87171e46 486
44433ebd 487 return 0;
87171e46 488}
1985c76e
KS
489
490const struct udevadm_cmd udevadm_info = {
912541b0
KS
491 .name = "info",
492 .cmd = uinfo,
5ac0162c 493 .help = "Query sysfs or the udev database",
1985c76e 494};