]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udevadm-info.c
Merge pull request #2056 from evverx/expose-soft-limits-on-the-bus
[thirdparty/systemd.git] / src / udev / udevadm-info.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /*
4 * Copyright (C) 2004-2009 Kay Sievers <kay@vrfy.org>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <ctype.h>
21 #include <dirent.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <getopt.h>
25 #include <stddef.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30
31 #include "fd-util.h"
32 #include "string-util.h"
33 #include "udev-util.h"
34 #include "udev.h"
35 #include "udevadm-util.h"
36
37 static bool skip_attribute(const char *name) {
38 static const char* const skip[] = {
39 "uevent",
40 "dev",
41 "modalias",
42 "resource",
43 "driver",
44 "subsystem",
45 "module",
46 };
47 unsigned int i;
48
49 for (i = 0; i < ELEMENTSOF(skip); i++)
50 if (streq(name, skip[i]))
51 return true;
52 return false;
53 }
54
55 static void print_all_attributes(struct udev_device *device, const char *key) {
56 struct udev_list_entry *sysattr;
57
58 udev_list_entry_foreach(sysattr, udev_device_get_sysattr_list_entry(device)) {
59 const char *name;
60 const char *value;
61 size_t len;
62
63 name = udev_list_entry_get_name(sysattr);
64 if (skip_attribute(name))
65 continue;
66
67 value = udev_device_get_sysattr_value(device, name);
68 if (value == NULL)
69 continue;
70
71 /* skip any values that look like a path */
72 if (value[0] == '/')
73 continue;
74
75 /* skip nonprintable attributes */
76 len = strlen(value);
77 while (len > 0 && isprint(value[len-1]))
78 len--;
79 if (len > 0)
80 continue;
81
82 printf(" %s{%s}==\"%s\"\n", key, name, value);
83 }
84 printf("\n");
85 }
86
87 static int print_device_chain(struct udev_device *device) {
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 const char *str;
134 int i;
135 struct udev_list_entry *list_entry;
136
137 printf("P: %s\n", udev_device_get_devpath(device));
138
139 str = udev_device_get_devnode(device);
140 if (str != NULL)
141 printf("N: %s\n", str + strlen("/dev/"));
142
143 i = udev_device_get_devlink_priority(device);
144 if (i != 0)
145 printf("L: %i\n", i);
146
147 udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device))
148 printf("S: %s\n", udev_list_entry_get_name(list_entry) + strlen("/dev/"));
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");
155 }
156
157 static int stat_device(const char *name, bool export, const char *prefix) {
158 struct stat statbuf;
159
160 if (stat(name, &statbuf) != 0)
161 return -1;
162
163 if (export) {
164 if (prefix == NULL)
165 prefix = "INFO_";
166 printf("%sMAJOR=%u\n"
167 "%sMINOR=%u\n",
168 prefix, major(statbuf.st_dev),
169 prefix, minor(statbuf.st_dev));
170 } else
171 printf("%u:%u\n", major(statbuf.st_dev), minor(statbuf.st_dev));
172 return 0;
173 }
174
175 static int export_devices(struct udev *udev) {
176 struct udev_enumerate *udev_enumerate;
177 struct udev_list_entry *list_entry;
178
179 udev_enumerate = udev_enumerate_new(udev);
180 if (udev_enumerate == NULL)
181 return -1;
182 udev_enumerate_scan_devices(udev_enumerate);
183 udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
184 struct udev_device *device;
185
186 device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry));
187 if (device != NULL) {
188 print_record(device);
189 udev_device_unref(device);
190 }
191 }
192 udev_enumerate_unref(udev_enumerate);
193 return 0;
194 }
195
196 static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
197 struct dirent *dent;
198
199 if (depth <= 0)
200 return;
201
202 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
203 struct stat stats;
204
205 if (dent->d_name[0] == '.')
206 continue;
207 if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0)
208 continue;
209 if ((stats.st_mode & mask) != 0)
210 continue;
211 if (S_ISDIR(stats.st_mode)) {
212 _cleanup_closedir_ DIR *dir2;
213
214 dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
215 if (dir2 != NULL)
216 cleanup_dir(dir2, mask, depth-1);
217
218 (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
219 } else
220 (void) unlinkat(dirfd(dir), dent->d_name, 0);
221 }
222 }
223
224 static void cleanup_db(struct udev *udev) {
225 DIR *dir;
226
227 unlink("/run/udev/queue.bin");
228
229 dir = opendir("/run/udev/data");
230 if (dir != NULL) {
231 cleanup_dir(dir, S_ISVTX, 1);
232 closedir(dir);
233 }
234
235 dir = opendir("/run/udev/links");
236 if (dir != NULL) {
237 cleanup_dir(dir, 0, 2);
238 closedir(dir);
239 }
240
241 dir = opendir("/run/udev/tags");
242 if (dir != NULL) {
243 cleanup_dir(dir, 0, 2);
244 closedir(dir);
245 }
246
247 dir = opendir("/run/udev/static_node-tags");
248 if (dir != NULL) {
249 cleanup_dir(dir, 0, 2);
250 closedir(dir);
251 }
252
253 dir = opendir("/run/udev/watch");
254 if (dir != NULL) {
255 cleanup_dir(dir, 0, 1);
256 closedir(dir);
257 }
258 }
259
260 static void help(void) {
261
262 printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
263 "Query sysfs or the udev database.\n\n"
264 " -h --help Print this message\n"
265 " --version Print version of the program\n"
266 " -q --query=TYPE Query device information:\n"
267 " name Name of device node\n"
268 " symlink Pointing to node\n"
269 " path sysfs device path\n"
270 " property The device properties\n"
271 " all All values\n"
272 " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
273 " -n --name=NAME Node or symlink name used for query or attribute walk\n"
274 " -r --root Prepend dev directory to path names\n"
275 " -a --attribute-walk Print all key matches walking along the chain\n"
276 " of parent devices\n"
277 " -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
278 " -x --export Export key/value pairs\n"
279 " -P --export-prefix Export the key name with a prefix\n"
280 " -e --export-db Export the content of the udev database\n"
281 " -c --cleanup-db Clean up the udev database\n"
282 , program_invocation_short_name);
283 }
284
285 static int uinfo(struct udev *udev, int argc, char *argv[]) {
286 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
287 bool root = 0;
288 bool export = 0;
289 const char *export_prefix = NULL;
290 char name[UTIL_PATH_SIZE];
291 struct udev_list_entry *list_entry;
292 int c;
293
294 static const struct option options[] = {
295 { "name", required_argument, NULL, 'n' },
296 { "path", required_argument, NULL, 'p' },
297 { "query", required_argument, NULL, 'q' },
298 { "attribute-walk", no_argument, NULL, 'a' },
299 { "cleanup-db", no_argument, NULL, 'c' },
300 { "export-db", no_argument, NULL, 'e' },
301 { "root", no_argument, NULL, 'r' },
302 { "device-id-of-file", required_argument, NULL, 'd' },
303 { "export", no_argument, NULL, 'x' },
304 { "export-prefix", required_argument, NULL, 'P' },
305 { "version", no_argument, NULL, 'V' },
306 { "help", no_argument, NULL, 'h' },
307 {}
308 };
309
310 enum action_type {
311 ACTION_QUERY,
312 ACTION_ATTRIBUTE_WALK,
313 ACTION_DEVICE_ID_FILE,
314 } action = ACTION_QUERY;
315
316 enum query_type {
317 QUERY_NAME,
318 QUERY_PATH,
319 QUERY_SYMLINK,
320 QUERY_PROPERTY,
321 QUERY_ALL,
322 } query = QUERY_ALL;
323
324 while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL)) >= 0)
325 switch (c) {
326 case 'n': {
327 if (device != NULL) {
328 fprintf(stderr, "device already specified\n");
329 return 2;
330 }
331
332 device = find_device(udev, optarg, "/dev/");
333 if (device == NULL) {
334 fprintf(stderr, "device node not found\n");
335 return 2;
336 }
337 break;
338 }
339 case 'p':
340 if (device != NULL) {
341 fprintf(stderr, "device already specified\n");
342 return 2;
343 }
344
345 device = find_device(udev, optarg, "/sys");
346 if (device == NULL) {
347 fprintf(stderr, "syspath not found\n");
348 return 2;
349 }
350 break;
351 case 'q':
352 action = ACTION_QUERY;
353 if (streq(optarg, "property") || streq(optarg, "env"))
354 query = QUERY_PROPERTY;
355 else if (streq(optarg, "name"))
356 query = QUERY_NAME;
357 else if (streq(optarg, "symlink"))
358 query = QUERY_SYMLINK;
359 else if (streq(optarg, "path"))
360 query = QUERY_PATH;
361 else if (streq(optarg, "all"))
362 query = QUERY_ALL;
363 else {
364 fprintf(stderr, "unknown query type\n");
365 return 3;
366 }
367 break;
368 case 'r':
369 root = true;
370 break;
371 case 'd':
372 action = ACTION_DEVICE_ID_FILE;
373 strscpy(name, sizeof(name), optarg);
374 break;
375 case 'a':
376 action = ACTION_ATTRIBUTE_WALK;
377 break;
378 case 'e':
379 export_devices(udev);
380 return 0;
381 case 'c':
382 cleanup_db(udev);
383 return 0;
384 case 'x':
385 export = true;
386 break;
387 case 'P':
388 export_prefix = optarg;
389 break;
390 case 'V':
391 printf("%s\n", VERSION);
392 return 0;
393 case 'h':
394 help();
395 return 0;
396 default:
397 return 1;
398 }
399
400 switch (action) {
401 case ACTION_QUERY:
402 if (!device) {
403 if (!argv[optind]) {
404 help();
405 return 2;
406 }
407 device = find_device(udev, argv[optind], NULL);
408 if (!device) {
409 fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
410 return 4;
411 }
412 }
413
414 switch(query) {
415 case QUERY_NAME: {
416 const char *node = udev_device_get_devnode(device);
417
418 if (node == NULL) {
419 fprintf(stderr, "no device node found\n");
420 return 5;
421 }
422
423 if (root)
424 printf("%s\n", udev_device_get_devnode(device));
425 else
426 printf("%s\n", udev_device_get_devnode(device) + strlen("/dev/"));
427 break;
428 }
429 case QUERY_SYMLINK:
430 list_entry = udev_device_get_devlinks_list_entry(device);
431 while (list_entry != NULL) {
432 if (root)
433 printf("%s", udev_list_entry_get_name(list_entry));
434 else
435 printf("%s", udev_list_entry_get_name(list_entry) + strlen("/dev/"));
436 list_entry = udev_list_entry_get_next(list_entry);
437 if (list_entry != NULL)
438 printf(" ");
439 }
440 printf("\n");
441 break;
442 case QUERY_PATH:
443 printf("%s\n", udev_device_get_devpath(device));
444 return 0;
445 case QUERY_PROPERTY:
446 list_entry = udev_device_get_properties_list_entry(device);
447 while (list_entry != NULL) {
448 if (export) {
449 const char *prefix = export_prefix;
450
451 if (prefix == NULL)
452 prefix = "";
453 printf("%s%s='%s'\n", prefix,
454 udev_list_entry_get_name(list_entry),
455 udev_list_entry_get_value(list_entry));
456 } else {
457 printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
458 }
459 list_entry = udev_list_entry_get_next(list_entry);
460 }
461 break;
462 case QUERY_ALL:
463 print_record(device);
464 break;
465 default:
466 assert_not_reached("unknown query type");
467 }
468 break;
469 case ACTION_ATTRIBUTE_WALK:
470 if (!device && argv[optind]) {
471 device = find_device(udev, argv[optind], NULL);
472 if (!device) {
473 fprintf(stderr, "Unknown device, absolute path in /dev/ or /sys expected.\n");
474 return 4;
475 }
476 }
477 if (!device) {
478 fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
479 return 4;
480 }
481 print_device_chain(device);
482 break;
483 case ACTION_DEVICE_ID_FILE:
484 if (stat_device(name, export, export_prefix) != 0)
485 return 1;
486 break;
487 }
488
489 return 0;
490 }
491
492 const struct udevadm_cmd udevadm_info = {
493 .name = "info",
494 .cmd = uinfo,
495 .help = "Query sysfs or the udev database",
496 };