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