]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/udevadm-info.c
tree-wide: drop redundant _cleanup_ macros (#8810)
[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)
fbd0b64f 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 146 udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device))
fbd0b64f
LP
147 printf("S: %s\n",
148 udev_list_entry_get_name(list_entry) + STRLEN("/dev/"));
912541b0
KS
149
150 udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device))
151 printf("E: %s=%s\n",
152 udev_list_entry_get_name(list_entry),
153 udev_list_entry_get_value(list_entry));
154 printf("\n");
31de3a2b
KS
155}
156
9ec6e95b 157static int stat_device(const char *name, bool export, const char *prefix) {
912541b0
KS
158 struct stat statbuf;
159
160 if (stat(name, &statbuf) != 0)
755700bb 161 return -errno;
912541b0
KS
162
163 if (export) {
164 if (prefix == NULL)
165 prefix = "INFO_";
1fa2f38f
ZJS
166 printf("%sMAJOR=%u\n"
167 "%sMINOR=%u\n",
912541b0
KS
168 prefix, major(statbuf.st_dev),
169 prefix, minor(statbuf.st_dev));
170 } else
1fa2f38f 171 printf("%u:%u\n", major(statbuf.st_dev), minor(statbuf.st_dev));
912541b0 172 return 0;
f338bac8
KS
173}
174
9ec6e95b 175static int export_devices(struct udev *udev) {
8e766630 176 _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *udev_enumerate;
912541b0
KS
177 struct udev_list_entry *list_entry;
178
179 udev_enumerate = udev_enumerate_new(udev);
180 if (udev_enumerate == NULL)
755700bb
ZJS
181 return -ENOMEM;
182
912541b0
KS
183 udev_enumerate_scan_devices(udev_enumerate);
184 udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
8e766630 185 _cleanup_(udev_device_unrefp) struct udev_device *device;
912541b0
KS
186
187 device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry));
755700bb 188 if (device != NULL)
912541b0 189 print_record(device);
912541b0 190 }
755700bb 191
912541b0 192 return 0;
bf7ad0ea
KS
193}
194
9ec6e95b 195static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
912541b0
KS
196 struct dirent *dent;
197
198 if (depth <= 0)
199 return;
200
8fb3f009 201 FOREACH_DIRENT_ALL(dent, dir, break) {
912541b0
KS
202 struct stat stats;
203
204 if (dent->d_name[0] == '.')
205 continue;
206 if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0)
207 continue;
208 if ((stats.st_mode & mask) != 0)
209 continue;
210 if (S_ISDIR(stats.st_mode)) {
200c7fa6 211 _cleanup_closedir_ DIR *dir2;
912541b0
KS
212
213 dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
200c7fa6 214 if (dir2 != NULL)
912541b0 215 cleanup_dir(dir2, mask, depth-1);
200c7fa6
LP
216
217 (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
218 } else
219 (void) unlinkat(dirfd(dir), dent->d_name, 0);
912541b0 220 }
9ead6627
KS
221}
222
9ec6e95b 223static void cleanup_db(struct udev *udev) {
755700bb 224 _cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL, *dir5 = NULL;
912541b0 225
755700bb 226 (void) unlink("/run/udev/queue.bin");
912541b0 227
755700bb
ZJS
228 dir1 = opendir("/run/udev/data");
229 if (dir1 != NULL)
230 cleanup_dir(dir1, S_ISVTX, 1);
912541b0 231
755700bb
ZJS
232 dir2 = opendir("/run/udev/links");
233 if (dir2 != NULL)
234 cleanup_dir(dir2, 0, 2);
912541b0 235
755700bb
ZJS
236 dir3 = opendir("/run/udev/tags");
237 if (dir3 != NULL)
238 cleanup_dir(dir3, 0, 2);
912541b0 239
755700bb
ZJS
240 dir4 = opendir("/run/udev/static_node-tags");
241 if (dir4 != NULL)
242 cleanup_dir(dir4, 0, 2);
84b6ad70 243
755700bb
ZJS
244 dir5 = opendir("/run/udev/watch");
245 if (dir5 != NULL)
246 cleanup_dir(dir5, 0, 1);
9ead6627
KS
247}
248
5ac0162c
LP
249static void help(void) {
250
251 printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
252 "Query sysfs or the udev database.\n\n"
253 " -h --help Print this message\n"
73527992 254 " -V --version Print version of the program\n"
5ac0162c
LP
255 " -q --query=TYPE Query device information:\n"
256 " name Name of device node\n"
257 " symlink Pointing to node\n"
258 " path sysfs device path\n"
259 " property The device properties\n"
260 " all All values\n"
261 " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
262 " -n --name=NAME Node or symlink name used for query or attribute walk\n"
263 " -r --root Prepend dev directory to path names\n"
264 " -a --attribute-walk Print all key matches walking along the chain\n"
265 " of parent devices\n"
266 " -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
267 " -x --export Export key/value pairs\n"
268 " -P --export-prefix Export the key name with a prefix\n"
269 " -e --export-db Export the content of the udev database\n"
270 " -c --cleanup-db Clean up the udev database\n"
271 , program_invocation_short_name);
272}
273
9ec6e95b 274static int uinfo(struct udev *udev, int argc, char *argv[]) {
8e766630 275 _cleanup_(udev_device_unrefp) struct udev_device *device = NULL;
912541b0
KS
276 bool root = 0;
277 bool export = 0;
278 const char *export_prefix = NULL;
912541b0
KS
279 char name[UTIL_PATH_SIZE];
280 struct udev_list_entry *list_entry;
44433ebd 281 int c;
912541b0
KS
282
283 static const struct option options[] = {
7643ac9a
ZJS
284 { "name", required_argument, NULL, 'n' },
285 { "path", required_argument, NULL, 'p' },
286 { "query", required_argument, NULL, 'q' },
287 { "attribute-walk", no_argument, NULL, 'a' },
288 { "cleanup-db", no_argument, NULL, 'c' },
289 { "export-db", no_argument, NULL, 'e' },
290 { "root", no_argument, NULL, 'r' },
912541b0 291 { "device-id-of-file", required_argument, NULL, 'd' },
7643ac9a
ZJS
292 { "export", no_argument, NULL, 'x' },
293 { "export-prefix", required_argument, NULL, 'P' },
294 { "version", no_argument, NULL, 'V' },
295 { "help", no_argument, NULL, 'h' },
912541b0
KS
296 {}
297 };
298
299 enum action_type {
912541b0
KS
300 ACTION_QUERY,
301 ACTION_ATTRIBUTE_WALK,
912541b0 302 ACTION_DEVICE_ID_FILE,
4f5d327a 303 } action = ACTION_QUERY;
912541b0
KS
304
305 enum query_type {
912541b0
KS
306 QUERY_NAME,
307 QUERY_PATH,
308 QUERY_SYMLINK,
309 QUERY_PROPERTY,
310 QUERY_ALL,
4f5d327a 311 } query = QUERY_ALL;
912541b0 312
7643ac9a
ZJS
313 while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL)) >= 0)
314 switch (c) {
4f5d327a 315 case 'n': {
912541b0
KS
316 if (device != NULL) {
317 fprintf(stderr, "device already specified\n");
44433ebd 318 return 2;
912541b0 319 }
4f5d327a
KS
320
321 device = find_device(udev, optarg, "/dev/");
322 if (device == NULL) {
912541b0 323 fprintf(stderr, "device node not found\n");
44433ebd 324 return 2;
912541b0
KS
325 }
326 break;
4f5d327a 327 }
912541b0
KS
328 case 'p':
329 if (device != NULL) {
330 fprintf(stderr, "device already specified\n");
44433ebd 331 return 2;
912541b0 332 }
4f5d327a
KS
333
334 device = find_device(udev, optarg, "/sys");
912541b0 335 if (device == NULL) {
4f5d327a 336 fprintf(stderr, "syspath not found\n");
44433ebd 337 return 2;
912541b0
KS
338 }
339 break;
340 case 'q':
341 action = ACTION_QUERY;
44433ebd 342 if (streq(optarg, "property") || streq(optarg, "env"))
912541b0 343 query = QUERY_PROPERTY;
44433ebd 344 else if (streq(optarg, "name"))
912541b0 345 query = QUERY_NAME;
44433ebd 346 else if (streq(optarg, "symlink"))
912541b0 347 query = QUERY_SYMLINK;
44433ebd 348 else if (streq(optarg, "path"))
912541b0 349 query = QUERY_PATH;
44433ebd 350 else if (streq(optarg, "all"))
912541b0 351 query = QUERY_ALL;
44433ebd 352 else {
912541b0 353 fprintf(stderr, "unknown query type\n");
44433ebd 354 return 3;
912541b0
KS
355 }
356 break;
357 case 'r':
912541b0
KS
358 root = true;
359 break;
912541b0
KS
360 case 'd':
361 action = ACTION_DEVICE_ID_FILE;
d5a89d7d 362 strscpy(name, sizeof(name), optarg);
912541b0
KS
363 break;
364 case 'a':
365 action = ACTION_ATTRIBUTE_WALK;
366 break;
367 case 'e':
755700bb
ZJS
368 if (export_devices(udev) < 0)
369 return 1;
44433ebd 370 return 0;
912541b0
KS
371 case 'c':
372 cleanup_db(udev);
44433ebd 373 return 0;
912541b0
KS
374 case 'x':
375 export = true;
376 break;
377 case 'P':
378 export_prefix = optarg;
379 break;
380 case 'V':
5639df9a 381 print_version();
44433ebd 382 return 0;
912541b0 383 case 'h':
5ac0162c 384 help();
44433ebd 385 return 0;
912541b0 386 default:
44433ebd 387 return 1;
912541b0 388 }
912541b0
KS
389
390 switch (action) {
391 case ACTION_QUERY:
4f5d327a
KS
392 if (!device) {
393 if (!argv[optind]) {
5ac0162c 394 help();
44433ebd 395 return 2;
4f5d327a
KS
396 }
397 device = find_device(udev, argv[optind], NULL);
398 if (!device) {
399 fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
44433ebd 400 return 4;
4f5d327a 401 }
912541b0
KS
402 }
403
404 switch(query) {
405 case QUERY_NAME: {
406 const char *node = udev_device_get_devnode(device);
407
408 if (node == NULL) {
409 fprintf(stderr, "no device node found\n");
44433ebd 410 return 5;
912541b0
KS
411 }
412
6ada823a 413 if (root)
912541b0 414 printf("%s\n", udev_device_get_devnode(device));
6ada823a 415 else
fbd0b64f
LP
416 printf("%s\n",
417 udev_device_get_devnode(device) + STRLEN("/dev/"));
912541b0
KS
418 break;
419 }
420 case QUERY_SYMLINK:
421 list_entry = udev_device_get_devlinks_list_entry(device);
422 while (list_entry != NULL) {
6ada823a 423 if (root)
912541b0 424 printf("%s", udev_list_entry_get_name(list_entry));
6ada823a 425 else
fbd0b64f
LP
426 printf("%s",
427 udev_list_entry_get_name(list_entry) + STRLEN("/dev/"));
912541b0
KS
428 list_entry = udev_list_entry_get_next(list_entry);
429 if (list_entry != NULL)
430 printf(" ");
431 }
432 printf("\n");
433 break;
434 case QUERY_PATH:
435 printf("%s\n", udev_device_get_devpath(device));
44433ebd 436 return 0;
912541b0
KS
437 case QUERY_PROPERTY:
438 list_entry = udev_device_get_properties_list_entry(device);
439 while (list_entry != NULL) {
19a29798
ZJS
440 if (export)
441 printf("%s%s='%s'\n", strempty(export_prefix),
912541b0
KS
442 udev_list_entry_get_name(list_entry),
443 udev_list_entry_get_value(list_entry));
19a29798 444 else
912541b0 445 printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
19a29798 446
912541b0
KS
447 list_entry = udev_list_entry_get_next(list_entry);
448 }
449 break;
450 case QUERY_ALL:
451 print_record(device);
452 break;
453 default:
bdd13f6b 454 assert_not_reached("unknown query type");
912541b0
KS
455 }
456 break;
457 case ACTION_ATTRIBUTE_WALK:
4f5d327a
KS
458 if (!device && argv[optind]) {
459 device = find_device(udev, argv[optind], NULL);
460 if (!device) {
461 fprintf(stderr, "Unknown device, absolute path in /dev/ or /sys expected.\n");
44433ebd 462 return 4;
4f5d327a
KS
463 }
464 }
465 if (!device) {
466 fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
44433ebd 467 return 4;
912541b0
KS
468 }
469 print_device_chain(device);
470 break;
471 case ACTION_DEVICE_ID_FILE:
472 if (stat_device(name, export, export_prefix) != 0)
44433ebd 473 return 1;
912541b0 474 break;
912541b0 475 }
87171e46 476
44433ebd 477 return 0;
87171e46 478}
1985c76e
KS
479
480const struct udevadm_cmd udevadm_info = {
912541b0
KS
481 .name = "info",
482 .cmd = uinfo,
5ac0162c 483 .help = "Query sysfs or the udev database",
1985c76e 484};