]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/udevadm-info.c
Merge pull request #7610 from poettering/stdio-nolocking
[thirdparty/systemd.git] / src / udev / udevadm-info.c
CommitLineData
e7145211 1/* SPDX-License-Identifier: GPL-2.0+ */
be9b51f6 2/*
1298001e 3 * Copyright (C) 2004-2009 Kay Sievers <kay@vrfy.org>
be9b51f6 4 *
55e9959b
KS
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
be9b51f6 9 *
55e9959b
KS
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
be9b51f6
KS
17 */
18
034f35d7 19#include <ctype.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
8fb3f009 29#include "dirent-util.h"
3ffd4af2 30#include "fd-util.h"
07630cea 31#include "string-util.h"
44433ebd 32#include "udev-util.h"
07630cea 33#include "udev.h"
d6170d27 34#include "udevadm-util.h"
be9b51f6 35
9ec6e95b 36static bool skip_attribute(const char *name) {
46512cd9 37 static const char* const skip[] = {
912541b0
KS
38 "uevent",
39 "dev",
40 "modalias",
41 "resource",
42 "driver",
43 "subsystem",
44 "module",
45 };
46 unsigned int i;
47
8fef0ff2 48 for (i = 0; i < ELEMENTSOF(skip); i++)
090be865 49 if (streq(name, skip[i]))
912541b0
KS
50 return true;
51 return false;
20bee04c
KS
52}
53
9ec6e95b 54static void print_all_attributes(struct udev_device *device, const char *key) {
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
9ec6e95b 86static int print_device_chain(struct udev_device *device) {
912541b0
KS
87 struct udev_device *device_parent;
88 const char *str;
89
90 printf("\n"
91 "Udevadm info starts with the device specified by the devpath and then\n"
92 "walks up the chain of parent devices. It prints for every device\n"
93 "found, all possible attributes in the udev rules key format.\n"
94 "A rule to match, can be composed by the attributes of the device\n"
95 "and the attributes from one single parent device.\n"
96 "\n");
97
98 printf(" looking at device '%s':\n", udev_device_get_devpath(device));
99 printf(" KERNEL==\"%s\"\n", udev_device_get_sysname(device));
100 str = udev_device_get_subsystem(device);
101 if (str == NULL)
102 str = "";
103 printf(" SUBSYSTEM==\"%s\"\n", str);
104 str = udev_device_get_driver(device);
105 if (str == NULL)
106 str = "";
107 printf(" DRIVER==\"%s\"\n", str);
108 print_all_attributes(device, "ATTR");
109
110 device_parent = device;
111 do {
112 device_parent = udev_device_get_parent(device_parent);
113 if (device_parent == NULL)
114 break;
115 printf(" looking at parent device '%s':\n", udev_device_get_devpath(device_parent));
116 printf(" KERNELS==\"%s\"\n", udev_device_get_sysname(device_parent));
117 str = udev_device_get_subsystem(device_parent);
118 if (str == NULL)
119 str = "";
120 printf(" SUBSYSTEMS==\"%s\"\n", str);
121 str = udev_device_get_driver(device_parent);
122 if (str == NULL)
123 str = "";
124 printf(" DRIVERS==\"%s\"\n", str);
125 print_all_attributes(device_parent, "ATTRS");
126 } while (device_parent != NULL);
127
128 return 0;
1aa1e248
KS
129}
130
9ec6e95b 131static void print_record(struct udev_device *device) {
912541b0
KS
132 const char *str;
133 int i;
134 struct udev_list_entry *list_entry;
135
136 printf("P: %s\n", udev_device_get_devpath(device));
137
912541b0
KS
138 str = udev_device_get_devnode(device);
139 if (str != NULL)
6ada823a 140 printf("N: %s\n", str + strlen("/dev/"));
912541b0
KS
141
142 i = udev_device_get_devlink_priority(device);
143 if (i != 0)
144 printf("L: %i\n", i);
145
6ada823a
KS
146 udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device))
147 printf("S: %s\n", udev_list_entry_get_name(list_entry) + strlen("/dev/"));
912541b0
KS
148
149 udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device))
150 printf("E: %s=%s\n",
151 udev_list_entry_get_name(list_entry),
152 udev_list_entry_get_value(list_entry));
153 printf("\n");
31de3a2b
KS
154}
155
9ec6e95b 156static int stat_device(const char *name, bool export, const char *prefix) {
912541b0
KS
157 struct stat statbuf;
158
159 if (stat(name, &statbuf) != 0)
755700bb 160 return -errno;
912541b0
KS
161
162 if (export) {
163 if (prefix == NULL)
164 prefix = "INFO_";
1fa2f38f
ZJS
165 printf("%sMAJOR=%u\n"
166 "%sMINOR=%u\n",
912541b0
KS
167 prefix, major(statbuf.st_dev),
168 prefix, minor(statbuf.st_dev));
169 } else
1fa2f38f 170 printf("%u:%u\n", major(statbuf.st_dev), minor(statbuf.st_dev));
912541b0 171 return 0;
f338bac8
KS
172}
173
9ec6e95b 174static int export_devices(struct udev *udev) {
755700bb 175 _cleanup_udev_enumerate_unref_ struct udev_enumerate *udev_enumerate;
912541b0
KS
176 struct udev_list_entry *list_entry;
177
178 udev_enumerate = udev_enumerate_new(udev);
179 if (udev_enumerate == NULL)
755700bb
ZJS
180 return -ENOMEM;
181
912541b0
KS
182 udev_enumerate_scan_devices(udev_enumerate);
183 udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
755700bb 184 _cleanup_udev_device_unref_ struct udev_device *device;
912541b0
KS
185
186 device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry));
755700bb 187 if (device != NULL)
912541b0 188 print_record(device);
912541b0 189 }
755700bb 190
912541b0 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
8fb3f009 200 FOREACH_DIRENT_ALL(dent, dir, break) {
912541b0
KS
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) {
755700bb 223 _cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL, *dir5 = NULL;
912541b0 224
755700bb 225 (void) unlink("/run/udev/queue.bin");
912541b0 226
755700bb
ZJS
227 dir1 = opendir("/run/udev/data");
228 if (dir1 != NULL)
229 cleanup_dir(dir1, S_ISVTX, 1);
912541b0 230
755700bb
ZJS
231 dir2 = opendir("/run/udev/links");
232 if (dir2 != NULL)
233 cleanup_dir(dir2, 0, 2);
912541b0 234
755700bb
ZJS
235 dir3 = opendir("/run/udev/tags");
236 if (dir3 != NULL)
237 cleanup_dir(dir3, 0, 2);
912541b0 238
755700bb
ZJS
239 dir4 = opendir("/run/udev/static_node-tags");
240 if (dir4 != NULL)
241 cleanup_dir(dir4, 0, 2);
84b6ad70 242
755700bb
ZJS
243 dir5 = opendir("/run/udev/watch");
244 if (dir5 != NULL)
245 cleanup_dir(dir5, 0, 1);
9ead6627
KS
246}
247
5ac0162c
LP
248static void help(void) {
249
250 printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
251 "Query sysfs or the udev database.\n\n"
252 " -h --help Print this message\n"
73527992 253 " -V --version Print version of the program\n"
5ac0162c
LP
254 " -q --query=TYPE Query device information:\n"
255 " name Name of device node\n"
256 " symlink Pointing to node\n"
257 " path sysfs device path\n"
258 " property The device properties\n"
259 " all All values\n"
260 " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
261 " -n --name=NAME Node or symlink name used for query or attribute walk\n"
262 " -r --root Prepend dev directory to path names\n"
263 " -a --attribute-walk Print all key matches walking along the chain\n"
264 " of parent devices\n"
265 " -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
266 " -x --export Export key/value pairs\n"
267 " -P --export-prefix Export the key name with a prefix\n"
268 " -e --export-db Export the content of the udev database\n"
269 " -c --cleanup-db Clean up the udev database\n"
270 , program_invocation_short_name);
271}
272
9ec6e95b 273static int uinfo(struct udev *udev, int argc, char *argv[]) {
44433ebd 274 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
912541b0
KS
275 bool root = 0;
276 bool export = 0;
277 const char *export_prefix = NULL;
912541b0
KS
278 char name[UTIL_PATH_SIZE];
279 struct udev_list_entry *list_entry;
44433ebd 280 int c;
912541b0
KS
281
282 static const struct option options[] = {
7643ac9a
ZJS
283 { "name", required_argument, NULL, 'n' },
284 { "path", required_argument, NULL, 'p' },
285 { "query", required_argument, NULL, 'q' },
286 { "attribute-walk", no_argument, NULL, 'a' },
287 { "cleanup-db", no_argument, NULL, 'c' },
288 { "export-db", no_argument, NULL, 'e' },
289 { "root", no_argument, NULL, 'r' },
912541b0 290 { "device-id-of-file", required_argument, NULL, 'd' },
7643ac9a
ZJS
291 { "export", no_argument, NULL, 'x' },
292 { "export-prefix", required_argument, NULL, 'P' },
293 { "version", no_argument, NULL, 'V' },
294 { "help", no_argument, NULL, 'h' },
912541b0
KS
295 {}
296 };
297
298 enum action_type {
912541b0
KS
299 ACTION_QUERY,
300 ACTION_ATTRIBUTE_WALK,
912541b0 301 ACTION_DEVICE_ID_FILE,
4f5d327a 302 } action = ACTION_QUERY;
912541b0
KS
303
304 enum query_type {
912541b0
KS
305 QUERY_NAME,
306 QUERY_PATH,
307 QUERY_SYMLINK,
308 QUERY_PROPERTY,
309 QUERY_ALL,
4f5d327a 310 } query = QUERY_ALL;
912541b0 311
7643ac9a
ZJS
312 while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL)) >= 0)
313 switch (c) {
4f5d327a 314 case 'n': {
912541b0
KS
315 if (device != NULL) {
316 fprintf(stderr, "device already specified\n");
44433ebd 317 return 2;
912541b0 318 }
4f5d327a
KS
319
320 device = find_device(udev, optarg, "/dev/");
321 if (device == NULL) {
912541b0 322 fprintf(stderr, "device node not found\n");
44433ebd 323 return 2;
912541b0
KS
324 }
325 break;
4f5d327a 326 }
912541b0
KS
327 case 'p':
328 if (device != NULL) {
329 fprintf(stderr, "device already specified\n");
44433ebd 330 return 2;
912541b0 331 }
4f5d327a
KS
332
333 device = find_device(udev, optarg, "/sys");
912541b0 334 if (device == NULL) {
4f5d327a 335 fprintf(stderr, "syspath not found\n");
44433ebd 336 return 2;
912541b0
KS
337 }
338 break;
339 case 'q':
340 action = ACTION_QUERY;
44433ebd 341 if (streq(optarg, "property") || streq(optarg, "env"))
912541b0 342 query = QUERY_PROPERTY;
44433ebd 343 else if (streq(optarg, "name"))
912541b0 344 query = QUERY_NAME;
44433ebd 345 else if (streq(optarg, "symlink"))
912541b0 346 query = QUERY_SYMLINK;
44433ebd 347 else if (streq(optarg, "path"))
912541b0 348 query = QUERY_PATH;
44433ebd 349 else if (streq(optarg, "all"))
912541b0 350 query = QUERY_ALL;
44433ebd 351 else {
912541b0 352 fprintf(stderr, "unknown query type\n");
44433ebd 353 return 3;
912541b0
KS
354 }
355 break;
356 case 'r':
912541b0
KS
357 root = true;
358 break;
912541b0
KS
359 case 'd':
360 action = ACTION_DEVICE_ID_FILE;
d5a89d7d 361 strscpy(name, sizeof(name), optarg);
912541b0
KS
362 break;
363 case 'a':
364 action = ACTION_ATTRIBUTE_WALK;
365 break;
366 case 'e':
755700bb
ZJS
367 if (export_devices(udev) < 0)
368 return 1;
44433ebd 369 return 0;
912541b0
KS
370 case 'c':
371 cleanup_db(udev);
44433ebd 372 return 0;
912541b0
KS
373 case 'x':
374 export = true;
375 break;
376 case 'P':
377 export_prefix = optarg;
378 break;
379 case 'V':
5639df9a 380 print_version();
44433ebd 381 return 0;
912541b0 382 case 'h':
5ac0162c 383 help();
44433ebd 384 return 0;
912541b0 385 default:
44433ebd 386 return 1;
912541b0 387 }
912541b0
KS
388
389 switch (action) {
390 case ACTION_QUERY:
4f5d327a
KS
391 if (!device) {
392 if (!argv[optind]) {
5ac0162c 393 help();
44433ebd 394 return 2;
4f5d327a
KS
395 }
396 device = find_device(udev, argv[optind], NULL);
397 if (!device) {
398 fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
44433ebd 399 return 4;
4f5d327a 400 }
912541b0
KS
401 }
402
403 switch(query) {
404 case QUERY_NAME: {
405 const char *node = udev_device_get_devnode(device);
406
407 if (node == NULL) {
408 fprintf(stderr, "no device node found\n");
44433ebd 409 return 5;
912541b0
KS
410 }
411
6ada823a 412 if (root)
912541b0 413 printf("%s\n", udev_device_get_devnode(device));
6ada823a
KS
414 else
415 printf("%s\n", udev_device_get_devnode(device) + strlen("/dev/"));
912541b0
KS
416 break;
417 }
418 case QUERY_SYMLINK:
419 list_entry = udev_device_get_devlinks_list_entry(device);
420 while (list_entry != NULL) {
6ada823a 421 if (root)
912541b0 422 printf("%s", udev_list_entry_get_name(list_entry));
6ada823a
KS
423 else
424 printf("%s", udev_list_entry_get_name(list_entry) + strlen("/dev/"));
912541b0
KS
425 list_entry = udev_list_entry_get_next(list_entry);
426 if (list_entry != NULL)
427 printf(" ");
428 }
429 printf("\n");
430 break;
431 case QUERY_PATH:
432 printf("%s\n", udev_device_get_devpath(device));
44433ebd 433 return 0;
912541b0
KS
434 case QUERY_PROPERTY:
435 list_entry = udev_device_get_properties_list_entry(device);
436 while (list_entry != NULL) {
19a29798
ZJS
437 if (export)
438 printf("%s%s='%s'\n", strempty(export_prefix),
912541b0
KS
439 udev_list_entry_get_name(list_entry),
440 udev_list_entry_get_value(list_entry));
19a29798 441 else
912541b0 442 printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
19a29798 443
912541b0
KS
444 list_entry = udev_list_entry_get_next(list_entry);
445 }
446 break;
447 case QUERY_ALL:
448 print_record(device);
449 break;
450 default:
bdd13f6b 451 assert_not_reached("unknown query type");
912541b0
KS
452 }
453 break;
454 case ACTION_ATTRIBUTE_WALK:
4f5d327a
KS
455 if (!device && argv[optind]) {
456 device = find_device(udev, argv[optind], NULL);
457 if (!device) {
458 fprintf(stderr, "Unknown device, absolute path in /dev/ or /sys expected.\n");
44433ebd 459 return 4;
4f5d327a
KS
460 }
461 }
462 if (!device) {
463 fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
44433ebd 464 return 4;
912541b0
KS
465 }
466 print_device_chain(device);
467 break;
468 case ACTION_DEVICE_ID_FILE:
469 if (stat_device(name, export, export_prefix) != 0)
44433ebd 470 return 1;
912541b0 471 break;
912541b0 472 }
87171e46 473
44433ebd 474 return 0;
87171e46 475}
1985c76e
KS
476
477const struct udevadm_cmd udevadm_info = {
912541b0
KS
478 .name = "info",
479 .cmd = uinfo,
5ac0162c 480 .help = "Query sysfs or the udev database",
1985c76e 481};