]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udevadm-info.c
Merge pull request #10484 from yuwata/udev-spawn-simplify
[thirdparty/systemd.git] / src / udev / udevadm-info.c
1 /* SPDX-License-Identifier: GPL-2.0+ */
2
3 #include <ctype.h>
4 #include <errno.h>
5 #include <fcntl.h>
6 #include <getopt.h>
7 #include <stddef.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <sys/stat.h>
11 #include <unistd.h>
12
13 #include "sd-device.h"
14
15 #include "alloc-util.h"
16 #include "device-enumerator-private.h"
17 #include "device-private.h"
18 #include "device-util.h"
19 #include "dirent-util.h"
20 #include "fd-util.h"
21 #include "string-util.h"
22 #include "udevadm.h"
23 #include "udevadm-util.h"
24
25 static bool skip_attribute(const char *name) {
26 static const char* const skip[] = {
27 "uevent",
28 "dev",
29 "modalias",
30 "resource",
31 "driver",
32 "subsystem",
33 "module",
34 };
35 unsigned i;
36
37 for (i = 0; i < ELEMENTSOF(skip); i++)
38 if (streq(name, skip[i]))
39 return true;
40 return false;
41 }
42
43 static void print_all_attributes(sd_device *device, const char *key) {
44 const char *name, *value;
45
46 FOREACH_DEVICE_PROPERTY(device, name, value) {
47 size_t len;
48
49 if (skip_attribute(name))
50 continue;
51
52 /* skip any values that look like a path */
53 if (value[0] == '/')
54 continue;
55
56 /* skip nonprintable attributes */
57 len = strlen(value);
58 while (len > 0 && isprint(value[len-1]))
59 len--;
60 if (len > 0)
61 continue;
62
63 printf(" %s{%s}==\"%s\"\n", key, name, value);
64 }
65 printf("\n");
66 }
67
68 static int print_device_chain(sd_device *device) {
69 sd_device *child, *parent;
70 const char *str;
71
72 printf("\n"
73 "Udevadm info starts with the device specified by the devpath and then\n"
74 "walks up the chain of parent devices. It prints for every device\n"
75 "found, all possible attributes in the udev rules key format.\n"
76 "A rule to match, can be composed by the attributes of the device\n"
77 "and the attributes from one single parent device.\n"
78 "\n");
79
80 (void) sd_device_get_devpath(device, &str);
81 printf(" looking at device '%s':\n", str);
82 (void) sd_device_get_sysname(device, &str);
83 printf(" KERNEL==\"%s\"\n", str);
84 if (sd_device_get_subsystem(device, &str) < 0)
85 str = "";
86 printf(" SUBSYSTEM==\"%s\"\n", str);
87 if (sd_device_get_driver(device, &str) < 0)
88 str = "";
89 printf(" DRIVER==\"%s\"\n", str);
90 print_all_attributes(device, "ATTR");
91
92 for (child = device; sd_device_get_parent(child, &parent) >= 0; child = parent) {
93 (void) sd_device_get_devpath(parent, &str);
94 printf(" looking at parent device '%s':\n", str);
95 (void) sd_device_get_sysname(parent, &str);
96 printf(" KERNELS==\"%s\"\n", str);
97 if (sd_device_get_subsystem(parent, &str) < 0)
98 str = "";
99 printf(" SUBSYSTEMS==\"%s\"\n", str);
100 if (sd_device_get_driver(parent, &str) < 0)
101 str = "";
102 printf(" DRIVERS==\"%s\"\n", str);
103 print_all_attributes(parent, "ATTRS");
104 }
105
106 return 0;
107 }
108
109 static void print_record(sd_device *device) {
110 const char *str, *val;
111 int i;
112
113 (void) sd_device_get_devpath(device, &str);
114 printf("P: %s\n", str);
115
116 if (sd_device_get_devname(device, &str) >= 0)
117 printf("N: %s\n", str + STRLEN("/dev/"));
118
119 if (device_get_devlink_priority(device, &i) >= 0)
120 printf("L: %i\n", i);
121
122 FOREACH_DEVICE_DEVLINK(device, str)
123 printf("S: %s\n", str + STRLEN("/dev/"));
124
125 FOREACH_DEVICE_PROPERTY(device, str, val)
126 printf("E: %s=%s\n", str, val);
127
128 printf("\n");
129 }
130
131 static int stat_device(const char *name, bool export, const char *prefix) {
132 struct stat statbuf;
133
134 if (stat(name, &statbuf) != 0)
135 return -errno;
136
137 if (export) {
138 if (!prefix)
139 prefix = "INFO_";
140 printf("%sMAJOR=%u\n"
141 "%sMINOR=%u\n",
142 prefix, major(statbuf.st_dev),
143 prefix, minor(statbuf.st_dev));
144 } else
145 printf("%u:%u\n", major(statbuf.st_dev), minor(statbuf.st_dev));
146 return 0;
147 }
148
149 static int export_devices(void) {
150 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
151 sd_device *d;
152 int r;
153
154 r = sd_device_enumerator_new(&e);
155 if (r < 0)
156 return r;
157
158 r = sd_device_enumerator_allow_uninitialized(e);
159 if (r < 0)
160 return r;
161
162 r = device_enumerator_scan_devices(e);
163 if (r < 0)
164 return r;
165
166 FOREACH_DEVICE_AND_SUBSYSTEM(e, d)
167 print_record(d);
168
169 return 0;
170 }
171
172 static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
173 struct dirent *dent;
174
175 if (depth <= 0)
176 return;
177
178 FOREACH_DIRENT_ALL(dent, dir, break) {
179 struct stat stats;
180
181 if (dent->d_name[0] == '.')
182 continue;
183 if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0)
184 continue;
185 if ((stats.st_mode & mask) != 0)
186 continue;
187 if (S_ISDIR(stats.st_mode)) {
188 _cleanup_closedir_ DIR *dir2 = NULL;
189
190 dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
191 if (dir2)
192 cleanup_dir(dir2, mask, depth-1);
193
194 (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
195 } else
196 (void) unlinkat(dirfd(dir), dent->d_name, 0);
197 }
198 }
199
200 static void cleanup_db(void) {
201 _cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL, *dir5 = NULL;
202
203 (void) unlink("/run/udev/queue.bin");
204
205 dir1 = opendir("/run/udev/data");
206 if (dir1)
207 cleanup_dir(dir1, S_ISVTX, 1);
208
209 dir2 = opendir("/run/udev/links");
210 if (dir2)
211 cleanup_dir(dir2, 0, 2);
212
213 dir3 = opendir("/run/udev/tags");
214 if (dir3)
215 cleanup_dir(dir3, 0, 2);
216
217 dir4 = opendir("/run/udev/static_node-tags");
218 if (dir4)
219 cleanup_dir(dir4, 0, 2);
220
221 dir5 = opendir("/run/udev/watch");
222 if (dir5)
223 cleanup_dir(dir5, 0, 1);
224 }
225
226 static int help(void) {
227
228 printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
229 "Query sysfs or the udev database.\n\n"
230 " -h --help Print this message\n"
231 " -V --version Print version of the program\n"
232 " -q --query=TYPE Query device information:\n"
233 " name Name of device node\n"
234 " symlink Pointing to node\n"
235 " path sysfs device path\n"
236 " property The device properties\n"
237 " all All values\n"
238 " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
239 " -n --name=NAME Node or symlink name used for query or attribute walk\n"
240 " -r --root Prepend dev directory to path names\n"
241 " -a --attribute-walk Print all key matches walking along the chain\n"
242 " of parent devices\n"
243 " -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
244 " -x --export Export key/value pairs\n"
245 " -P --export-prefix Export the key name with a prefix\n"
246 " -e --export-db Export the content of the udev database\n"
247 " -c --cleanup-db Clean up the udev database\n"
248 , program_invocation_short_name);
249
250 return 0;
251 }
252
253 int info_main(int argc, char *argv[], void *userdata) {
254 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
255 bool root = false, export = false;
256 _cleanup_free_ char *name = NULL;
257 const char *export_prefix = NULL;
258 int c, r;
259
260 static const struct option options[] = {
261 { "name", required_argument, NULL, 'n' },
262 { "path", required_argument, NULL, 'p' },
263 { "query", required_argument, NULL, 'q' },
264 { "attribute-walk", no_argument, NULL, 'a' },
265 { "cleanup-db", no_argument, NULL, 'c' },
266 { "export-db", no_argument, NULL, 'e' },
267 { "root", no_argument, NULL, 'r' },
268 { "device-id-of-file", required_argument, NULL, 'd' },
269 { "export", no_argument, NULL, 'x' },
270 { "export-prefix", required_argument, NULL, 'P' },
271 { "version", no_argument, NULL, 'V' },
272 { "help", no_argument, NULL, 'h' },
273 {}
274 };
275
276 enum action_type {
277 ACTION_QUERY,
278 ACTION_ATTRIBUTE_WALK,
279 ACTION_DEVICE_ID_FILE,
280 } action = ACTION_QUERY;
281
282 enum query_type {
283 QUERY_NAME,
284 QUERY_PATH,
285 QUERY_SYMLINK,
286 QUERY_PROPERTY,
287 QUERY_ALL,
288 } query = QUERY_ALL;
289
290 while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL)) >= 0)
291 switch (c) {
292 case 'n':
293 if (device) {
294 log_error("device already specified");
295 return -EINVAL;
296 }
297
298 r = find_device(optarg, "/dev/", &device);
299 if (r < 0)
300 return log_error_errno(r, "device node not found: %m");
301 break;
302 case 'p':
303 if (device) {
304 log_error("device already specified");
305 return -EINVAL;
306 }
307
308 r = find_device(optarg, "/sys", &device);
309 if (r < 0)
310 return log_error_errno(r, "syspath not found: %m");
311 break;
312 case 'q':
313 action = ACTION_QUERY;
314 if (streq(optarg, "property") || streq(optarg, "env"))
315 query = QUERY_PROPERTY;
316 else if (streq(optarg, "name"))
317 query = QUERY_NAME;
318 else if (streq(optarg, "symlink"))
319 query = QUERY_SYMLINK;
320 else if (streq(optarg, "path"))
321 query = QUERY_PATH;
322 else if (streq(optarg, "all"))
323 query = QUERY_ALL;
324 else {
325 log_error("unknown query type");
326 return -EINVAL;
327 }
328 break;
329 case 'r':
330 root = true;
331 break;
332 case 'd':
333 action = ACTION_DEVICE_ID_FILE;
334 r = free_and_strdup(&name, optarg);
335 if (r < 0)
336 return log_oom();
337 break;
338 case 'a':
339 action = ACTION_ATTRIBUTE_WALK;
340 break;
341 case 'e':
342 return export_devices();
343 case 'c':
344 cleanup_db();
345 return 0;
346 case 'x':
347 export = true;
348 break;
349 case 'P':
350 export_prefix = optarg;
351 break;
352 case 'V':
353 return print_version();
354 case 'h':
355 return help();
356 case '?':
357 return -EINVAL;
358 default:
359 assert_not_reached("Unknown option");
360 }
361
362 switch (action) {
363 case ACTION_QUERY:
364 if (!device) {
365 if (!argv[optind]) {
366 help();
367 return -EINVAL;
368 }
369 r = find_device(argv[optind], NULL, &device);
370 if (r < 0)
371 return log_error_errno(r, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected: %m");
372 }
373
374 switch(query) {
375 case QUERY_NAME: {
376 const char *node;
377
378 r = sd_device_get_devname(device, &node);
379 if (r < 0)
380 return log_error_errno(r, "no device node found: %m");
381
382 if (root)
383 printf("%s\n", node);
384 else
385 printf("%s\n", node + STRLEN("/dev/"));
386 break;
387 }
388 case QUERY_SYMLINK: {
389 const char *devlink;
390 bool first = true;
391
392 FOREACH_DEVICE_DEVLINK(device, devlink) {
393 if (!first)
394 printf(" ");
395 if (root)
396 printf("%s", devlink);
397 else
398 printf("%s", devlink + STRLEN("/dev/"));
399
400 first = false;
401 }
402 printf("\n");
403 break;
404 }
405 case QUERY_PATH: {
406 const char *devpath;
407
408 r = sd_device_get_devpath(device, &devpath);
409 if (r < 0)
410 return log_error_errno(r, "Failed to get device path: %m");
411
412 printf("%s\n", devpath);
413 return 0;
414 }
415 case QUERY_PROPERTY: {
416 const char *key, *value;
417
418 FOREACH_DEVICE_PROPERTY(device, key, value)
419 if (export)
420 printf("%s%s='%s'\n", strempty(export_prefix), key, value);
421 else
422 printf("%s=%s\n", key, value);
423
424 break;
425 }
426 case QUERY_ALL:
427 print_record(device);
428 break;
429 default:
430 assert_not_reached("unknown query type");
431 }
432 break;
433 case ACTION_ATTRIBUTE_WALK:
434 if (!device && argv[optind]) {
435 r = find_device(argv[optind], NULL, &device);
436 if (r < 0)
437 return log_error_errno(r, "Unknown device, absolute path in /dev/ or /sys expected: %m");
438 }
439 if (!device) {
440 log_error("Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.");
441 return -EINVAL;
442 }
443 print_device_chain(device);
444 break;
445 case ACTION_DEVICE_ID_FILE:
446 r = stat_device(name, export, export_prefix);
447 if (r < 0)
448 return r;
449 break;
450 }
451
452 return 0;
453 }