]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/udev/udevadm-info.c
udevadm-trigger: modernize code a bit
[thirdparty/systemd.git] / src / udev / udevadm-info.c
... / ...
CommitLineData
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 "device-enumerator-private.h"
16#include "device-private.h"
17#include "device-util.h"
18#include "dirent-util.h"
19#include "fd-util.h"
20#include "libudev-private.h"
21#include "string-util.h"
22#include "udevadm.h"
23#include "udevadm-util.h"
24
25static 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 int i;
36
37 for (i = 0; i < ELEMENTSOF(skip); i++)
38 if (streq(name, skip[i]))
39 return true;
40 return false;
41}
42
43static 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
68static 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
109static 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
131static 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
149static 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
172static 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
200static 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
226static 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
253int info_main(int argc, char *argv[], void *userdata) {
254 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
255 bool root = 0;
256 bool export = 0;
257 const char *export_prefix = NULL;
258 char name[UTIL_PATH_SIZE];
259 int c, r;
260
261 static const struct option options[] = {
262 { "name", required_argument, NULL, 'n' },
263 { "path", required_argument, NULL, 'p' },
264 { "query", required_argument, NULL, 'q' },
265 { "attribute-walk", no_argument, NULL, 'a' },
266 { "cleanup-db", no_argument, NULL, 'c' },
267 { "export-db", no_argument, NULL, 'e' },
268 { "root", no_argument, NULL, 'r' },
269 { "device-id-of-file", required_argument, NULL, 'd' },
270 { "export", no_argument, NULL, 'x' },
271 { "export-prefix", required_argument, NULL, 'P' },
272 { "version", no_argument, NULL, 'V' },
273 { "help", no_argument, NULL, 'h' },
274 {}
275 };
276
277 enum action_type {
278 ACTION_QUERY,
279 ACTION_ATTRIBUTE_WALK,
280 ACTION_DEVICE_ID_FILE,
281 } action = ACTION_QUERY;
282
283 enum query_type {
284 QUERY_NAME,
285 QUERY_PATH,
286 QUERY_SYMLINK,
287 QUERY_PROPERTY,
288 QUERY_ALL,
289 } query = QUERY_ALL;
290
291 while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL)) >= 0)
292 switch (c) {
293 case 'n':
294 if (device) {
295 log_error("device already specified");
296 return -EINVAL;
297 }
298
299 r = find_device(optarg, "/dev/", &device);
300 if (r < 0)
301 return log_error_errno(r, "device node not found: %m");
302 break;
303 case 'p':
304 if (device) {
305 log_error("device already specified");
306 return -EINVAL;
307 }
308
309 r = find_device(optarg, "/sys", &device);
310 if (r < 0)
311 return log_error_errno(r, "syspath not found: %m");
312 break;
313 case 'q':
314 action = ACTION_QUERY;
315 if (streq(optarg, "property") || streq(optarg, "env"))
316 query = QUERY_PROPERTY;
317 else if (streq(optarg, "name"))
318 query = QUERY_NAME;
319 else if (streq(optarg, "symlink"))
320 query = QUERY_SYMLINK;
321 else if (streq(optarg, "path"))
322 query = QUERY_PATH;
323 else if (streq(optarg, "all"))
324 query = QUERY_ALL;
325 else {
326 log_error("unknown query type");
327 return -EINVAL;
328 }
329 break;
330 case 'r':
331 root = true;
332 break;
333 case 'd':
334 action = ACTION_DEVICE_ID_FILE;
335 strscpy(name, sizeof(name), optarg);
336 break;
337 case 'a':
338 action = ACTION_ATTRIBUTE_WALK;
339 break;
340 case 'e':
341 return export_devices();
342 case 'c':
343 cleanup_db();
344 return 0;
345 case 'x':
346 export = true;
347 break;
348 case 'P':
349 export_prefix = optarg;
350 break;
351 case 'V':
352 return print_version();
353 case 'h':
354 return help();
355 case '?':
356 return -EINVAL;
357 default:
358 assert_not_reached("Unknown option");
359 }
360
361 switch (action) {
362 case ACTION_QUERY:
363 if (!device) {
364 if (!argv[optind]) {
365 help();
366 return -EINVAL;
367 }
368 r = find_device(argv[optind], NULL, &device);
369 if (r < 0)
370 return log_error_errno(r, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected: %m");
371 }
372
373 switch(query) {
374 case QUERY_NAME: {
375 const char *node;
376
377 r = sd_device_get_devname(device, &node);
378 if (r < 0)
379 return log_error_errno(r, "no device node found: %m");
380
381 if (root)
382 printf("%s\n", node);
383 else
384 printf("%s\n", node + STRLEN("/dev/"));
385 break;
386 }
387 case QUERY_SYMLINK: {
388 const char *devlink;
389 bool first = true;
390
391 FOREACH_DEVICE_DEVLINK(device, devlink) {
392 if (!first)
393 printf(" ");
394 if (root)
395 printf("%s", devlink);
396 else
397 printf("%s", devlink + STRLEN("/dev/"));
398
399 first = false;
400 }
401 printf("\n");
402 break;
403 }
404 case QUERY_PATH: {
405 const char *devpath;
406
407 r = sd_device_get_devpath(device, &devpath);
408 if (r < 0)
409 return log_error_errno(r, "Failed to get device path: %m");
410
411 printf("%s\n", devpath);
412 return 0;
413 }
414 case QUERY_PROPERTY: {
415 const char *key, *value;
416
417 FOREACH_DEVICE_PROPERTY(device, key, value)
418 if (export)
419 printf("%s%s='%s'\n", strempty(export_prefix), key, value);
420 else
421 printf("%s=%s\n", key, value);
422
423 break;
424 }
425 case QUERY_ALL:
426 print_record(device);
427 break;
428 default:
429 assert_not_reached("unknown query type");
430 }
431 break;
432 case ACTION_ATTRIBUTE_WALK:
433 if (!device && argv[optind]) {
434 r = find_device(argv[optind], NULL, &device);
435 if (r < 0)
436 return log_error_errno(r, "Unknown device, absolute path in /dev/ or /sys expected: %m");
437 }
438 if (!device) {
439 log_error("Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.");
440 return -EINVAL;
441 }
442 print_device_chain(device);
443 break;
444 case ACTION_DEVICE_ID_FILE:
445 r = stat_device(name, export, export_prefix);
446 if (r < 0)
447 return r;
448 break;
449 }
450
451 return 0;
452}