]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/udev/udevadm-info.c
util: split out escaping code into escape.[ch]
[thirdparty/systemd.git] / src / udev / udevadm-info.c
CommitLineData
be9b51f6 1/*
1298001e 2 * Copyright (C) 2004-2009 Kay Sievers <kay@vrfy.org>
be9b51f6 3 *
55e9959b
KS
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.
be9b51f6 8 *
55e9959b
KS
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/>.
be9b51f6
KS
16 */
17
be9b51f6
KS
18#include <string.h>
19#include <stdio.h>
1aa1e248 20#include <stddef.h>
034f35d7 21#include <ctype.h>
87171e46 22#include <unistd.h>
1aa1e248 23#include <dirent.h>
87171e46 24#include <errno.h>
11f1bb5a 25#include <getopt.h>
e6c1a2bd 26#include <fcntl.h>
492e76c9 27#include <sys/stat.h>
be9b51f6 28
869fc2f1 29#include "udev.h"
44433ebd 30#include "udev-util.h"
d6170d27 31#include "udevadm-util.h"
be9b51f6 32
9ec6e95b 33static bool skip_attribute(const char *name) {
46512cd9 34 static const char* const skip[] = {
912541b0
KS
35 "uevent",
36 "dev",
37 "modalias",
38 "resource",
39 "driver",
40 "subsystem",
41 "module",
42 };
43 unsigned int i;
44
8fef0ff2 45 for (i = 0; i < ELEMENTSOF(skip); i++)
090be865 46 if (streq(name, skip[i]))
912541b0
KS
47 return true;
48 return false;
20bee04c
KS
49}
50
9ec6e95b 51static void print_all_attributes(struct udev_device *device, const char *key) {
912541b0
KS
52 struct udev_list_entry *sysattr;
53
54 udev_list_entry_foreach(sysattr, udev_device_get_sysattr_list_entry(device)) {
55 const char *name;
56 const char *value;
57 size_t len;
58
59 name = udev_list_entry_get_name(sysattr);
60 if (skip_attribute(name))
61 continue;
62
63 value = udev_device_get_sysattr_value(device, name);
64 if (value == NULL)
65 continue;
912541b0
KS
66
67 /* skip any values that look like a path */
68 if (value[0] == '/')
69 continue;
70
71 /* skip nonprintable attributes */
72 len = strlen(value);
73 while (len > 0 && isprint(value[len-1]))
74 len--;
baa30fbc 75 if (len > 0)
912541b0 76 continue;
912541b0
KS
77
78 printf(" %s{%s}==\"%s\"\n", key, name, value);
79 }
80 printf("\n");
be9b51f6
KS
81}
82
9ec6e95b 83static int print_device_chain(struct udev_device *device) {
912541b0
KS
84 struct udev_device *device_parent;
85 const char *str;
86
87 printf("\n"
88 "Udevadm info starts with the device specified by the devpath and then\n"
89 "walks up the chain of parent devices. It prints for every device\n"
90 "found, all possible attributes in the udev rules key format.\n"
91 "A rule to match, can be composed by the attributes of the device\n"
92 "and the attributes from one single parent device.\n"
93 "\n");
94
95 printf(" looking at device '%s':\n", udev_device_get_devpath(device));
96 printf(" KERNEL==\"%s\"\n", udev_device_get_sysname(device));
97 str = udev_device_get_subsystem(device);
98 if (str == NULL)
99 str = "";
100 printf(" SUBSYSTEM==\"%s\"\n", str);
101 str = udev_device_get_driver(device);
102 if (str == NULL)
103 str = "";
104 printf(" DRIVER==\"%s\"\n", str);
105 print_all_attributes(device, "ATTR");
106
107 device_parent = device;
108 do {
109 device_parent = udev_device_get_parent(device_parent);
110 if (device_parent == NULL)
111 break;
112 printf(" looking at parent device '%s':\n", udev_device_get_devpath(device_parent));
113 printf(" KERNELS==\"%s\"\n", udev_device_get_sysname(device_parent));
114 str = udev_device_get_subsystem(device_parent);
115 if (str == NULL)
116 str = "";
117 printf(" SUBSYSTEMS==\"%s\"\n", str);
118 str = udev_device_get_driver(device_parent);
119 if (str == NULL)
120 str = "";
121 printf(" DRIVERS==\"%s\"\n", str);
122 print_all_attributes(device_parent, "ATTRS");
123 } while (device_parent != NULL);
124
125 return 0;
1aa1e248
KS
126}
127
9ec6e95b 128static void print_record(struct udev_device *device) {
912541b0
KS
129 const char *str;
130 int i;
131 struct udev_list_entry *list_entry;
132
133 printf("P: %s\n", udev_device_get_devpath(device));
134
912541b0
KS
135 str = udev_device_get_devnode(device);
136 if (str != NULL)
6ada823a 137 printf("N: %s\n", str + strlen("/dev/"));
912541b0
KS
138
139 i = udev_device_get_devlink_priority(device);
140 if (i != 0)
141 printf("L: %i\n", i);
142
6ada823a
KS
143 udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device))
144 printf("S: %s\n", udev_list_entry_get_name(list_entry) + strlen("/dev/"));
912541b0
KS
145
146 udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device))
147 printf("E: %s=%s\n",
148 udev_list_entry_get_name(list_entry),
149 udev_list_entry_get_value(list_entry));
150 printf("\n");
31de3a2b
KS
151}
152
9ec6e95b 153static int stat_device(const char *name, bool export, const char *prefix) {
912541b0
KS
154 struct stat statbuf;
155
156 if (stat(name, &statbuf) != 0)
157 return -1;
158
159 if (export) {
160 if (prefix == NULL)
161 prefix = "INFO_";
1fa2f38f
ZJS
162 printf("%sMAJOR=%u\n"
163 "%sMINOR=%u\n",
912541b0
KS
164 prefix, major(statbuf.st_dev),
165 prefix, minor(statbuf.st_dev));
166 } else
1fa2f38f 167 printf("%u:%u\n", major(statbuf.st_dev), minor(statbuf.st_dev));
912541b0 168 return 0;
f338bac8
KS
169}
170
9ec6e95b 171static int export_devices(struct udev *udev) {
912541b0
KS
172 struct udev_enumerate *udev_enumerate;
173 struct udev_list_entry *list_entry;
174
175 udev_enumerate = udev_enumerate_new(udev);
176 if (udev_enumerate == NULL)
177 return -1;
178 udev_enumerate_scan_devices(udev_enumerate);
179 udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
180 struct udev_device *device;
181
182 device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry));
183 if (device != NULL) {
184 print_record(device);
185 udev_device_unref(device);
186 }
187 }
188 udev_enumerate_unref(udev_enumerate);
189 return 0;
bf7ad0ea
KS
190}
191
9ec6e95b 192static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
912541b0
KS
193 struct dirent *dent;
194
195 if (depth <= 0)
196 return;
197
198 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
199 struct stat stats;
200
201 if (dent->d_name[0] == '.')
202 continue;
203 if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0)
204 continue;
205 if ((stats.st_mode & mask) != 0)
206 continue;
207 if (S_ISDIR(stats.st_mode)) {
200c7fa6 208 _cleanup_closedir_ DIR *dir2;
912541b0
KS
209
210 dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
200c7fa6 211 if (dir2 != NULL)
912541b0 212 cleanup_dir(dir2, mask, depth-1);
200c7fa6
LP
213
214 (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
215 } else
216 (void) unlinkat(dirfd(dir), dent->d_name, 0);
912541b0 217 }
9ead6627
KS
218}
219
9ec6e95b 220static void cleanup_db(struct udev *udev) {
912541b0
KS
221 DIR *dir;
222
6ada823a 223 unlink("/run/udev/queue.bin");
912541b0 224
6ada823a 225 dir = opendir("/run/udev/data");
912541b0
KS
226 if (dir != NULL) {
227 cleanup_dir(dir, S_ISVTX, 1);
228 closedir(dir);
229 }
230
6ada823a 231 dir = opendir("/run/udev/links");
912541b0
KS
232 if (dir != NULL) {
233 cleanup_dir(dir, 0, 2);
234 closedir(dir);
235 }
236
6ada823a 237 dir = opendir("/run/udev/tags");
912541b0
KS
238 if (dir != NULL) {
239 cleanup_dir(dir, 0, 2);
240 closedir(dir);
241 }
242
84b6ad70
TG
243 dir = opendir("/run/udev/static_node-tags");
244 if (dir != NULL) {
245 cleanup_dir(dir, 0, 2);
246 closedir(dir);
247 }
248
6ada823a 249 dir = opendir("/run/udev/watch");
912541b0
KS
250 if (dir != NULL) {
251 cleanup_dir(dir, 0, 1);
252 closedir(dir);
253 }
9ead6627
KS
254}
255
5ac0162c
LP
256static void help(void) {
257
258 printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
259 "Query sysfs or the udev database.\n\n"
260 " -h --help Print this message\n"
261 " --version Print version of the program\n"
262 " -q --query=TYPE Query device information:\n"
263 " name Name of device node\n"
264 " symlink Pointing to node\n"
265 " path sysfs device path\n"
266 " property The device properties\n"
267 " all All values\n"
268 " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
269 " -n --name=NAME Node or symlink name used for query or attribute walk\n"
270 " -r --root Prepend dev directory to path names\n"
271 " -a --attribute-walk Print all key matches walking along the chain\n"
272 " of parent devices\n"
273 " -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
274 " -x --export Export key/value pairs\n"
275 " -P --export-prefix Export the key name with a prefix\n"
276 " -e --export-db Export the content of the udev database\n"
277 " -c --cleanup-db Clean up the udev database\n"
278 , program_invocation_short_name);
279}
280
9ec6e95b 281static int uinfo(struct udev *udev, int argc, char *argv[]) {
44433ebd 282 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
912541b0
KS
283 bool root = 0;
284 bool export = 0;
285 const char *export_prefix = NULL;
912541b0
KS
286 char name[UTIL_PATH_SIZE];
287 struct udev_list_entry *list_entry;
44433ebd 288 int c;
912541b0
KS
289
290 static const struct option options[] = {
7643ac9a
ZJS
291 { "name", required_argument, NULL, 'n' },
292 { "path", required_argument, NULL, 'p' },
293 { "query", required_argument, NULL, 'q' },
294 { "attribute-walk", no_argument, NULL, 'a' },
295 { "cleanup-db", no_argument, NULL, 'c' },
296 { "export-db", no_argument, NULL, 'e' },
297 { "root", no_argument, NULL, 'r' },
912541b0 298 { "device-id-of-file", required_argument, NULL, 'd' },
7643ac9a
ZJS
299 { "export", no_argument, NULL, 'x' },
300 { "export-prefix", required_argument, NULL, 'P' },
301 { "version", no_argument, NULL, 'V' },
302 { "help", no_argument, NULL, 'h' },
912541b0
KS
303 {}
304 };
305
306 enum action_type {
912541b0
KS
307 ACTION_QUERY,
308 ACTION_ATTRIBUTE_WALK,
912541b0 309 ACTION_DEVICE_ID_FILE,
4f5d327a 310 } action = ACTION_QUERY;
912541b0
KS
311
312 enum query_type {
912541b0
KS
313 QUERY_NAME,
314 QUERY_PATH,
315 QUERY_SYMLINK,
316 QUERY_PROPERTY,
317 QUERY_ALL,
4f5d327a 318 } query = QUERY_ALL;
912541b0 319
7643ac9a
ZJS
320 while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL)) >= 0)
321 switch (c) {
4f5d327a 322 case 'n': {
912541b0
KS
323 if (device != NULL) {
324 fprintf(stderr, "device already specified\n");
44433ebd 325 return 2;
912541b0 326 }
4f5d327a
KS
327
328 device = find_device(udev, optarg, "/dev/");
329 if (device == NULL) {
912541b0 330 fprintf(stderr, "device node not found\n");
44433ebd 331 return 2;
912541b0
KS
332 }
333 break;
4f5d327a 334 }
912541b0
KS
335 case 'p':
336 if (device != NULL) {
337 fprintf(stderr, "device already specified\n");
44433ebd 338 return 2;
912541b0 339 }
4f5d327a
KS
340
341 device = find_device(udev, optarg, "/sys");
912541b0 342 if (device == NULL) {
4f5d327a 343 fprintf(stderr, "syspath not found\n");
44433ebd 344 return 2;
912541b0
KS
345 }
346 break;
347 case 'q':
348 action = ACTION_QUERY;
44433ebd 349 if (streq(optarg, "property") || streq(optarg, "env"))
912541b0 350 query = QUERY_PROPERTY;
44433ebd 351 else if (streq(optarg, "name"))
912541b0 352 query = QUERY_NAME;
44433ebd 353 else if (streq(optarg, "symlink"))
912541b0 354 query = QUERY_SYMLINK;
44433ebd 355 else if (streq(optarg, "path"))
912541b0 356 query = QUERY_PATH;
44433ebd 357 else if (streq(optarg, "all"))
912541b0 358 query = QUERY_ALL;
44433ebd 359 else {
912541b0 360 fprintf(stderr, "unknown query type\n");
44433ebd 361 return 3;
912541b0
KS
362 }
363 break;
364 case 'r':
912541b0
KS
365 root = true;
366 break;
912541b0
KS
367 case 'd':
368 action = ACTION_DEVICE_ID_FILE;
d5a89d7d 369 strscpy(name, sizeof(name), optarg);
912541b0
KS
370 break;
371 case 'a':
372 action = ACTION_ATTRIBUTE_WALK;
373 break;
374 case 'e':
375 export_devices(udev);
44433ebd 376 return 0;
912541b0
KS
377 case 'c':
378 cleanup_db(udev);
44433ebd 379 return 0;
912541b0
KS
380 case 'x':
381 export = true;
382 break;
383 case 'P':
384 export_prefix = optarg;
385 break;
386 case 'V':
387 printf("%s\n", VERSION);
44433ebd 388 return 0;
912541b0 389 case 'h':
5ac0162c 390 help();
44433ebd 391 return 0;
912541b0 392 default:
44433ebd 393 return 1;
912541b0 394 }
912541b0
KS
395
396 switch (action) {
397 case ACTION_QUERY:
4f5d327a
KS
398 if (!device) {
399 if (!argv[optind]) {
5ac0162c 400 help();
44433ebd 401 return 2;
4f5d327a
KS
402 }
403 device = find_device(udev, argv[optind], NULL);
404 if (!device) {
405 fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
44433ebd 406 return 4;
4f5d327a 407 }
912541b0
KS
408 }
409
410 switch(query) {
411 case QUERY_NAME: {
412 const char *node = udev_device_get_devnode(device);
413
414 if (node == NULL) {
415 fprintf(stderr, "no device node found\n");
44433ebd 416 return 5;
912541b0
KS
417 }
418
6ada823a 419 if (root)
912541b0 420 printf("%s\n", udev_device_get_devnode(device));
6ada823a
KS
421 else
422 printf("%s\n", udev_device_get_devnode(device) + strlen("/dev/"));
912541b0
KS
423 break;
424 }
425 case QUERY_SYMLINK:
426 list_entry = udev_device_get_devlinks_list_entry(device);
427 while (list_entry != NULL) {
6ada823a 428 if (root)
912541b0 429 printf("%s", udev_list_entry_get_name(list_entry));
6ada823a
KS
430 else
431 printf("%s", udev_list_entry_get_name(list_entry) + strlen("/dev/"));
912541b0
KS
432 list_entry = udev_list_entry_get_next(list_entry);
433 if (list_entry != NULL)
434 printf(" ");
435 }
436 printf("\n");
437 break;
438 case QUERY_PATH:
439 printf("%s\n", udev_device_get_devpath(device));
44433ebd 440 return 0;
912541b0
KS
441 case QUERY_PROPERTY:
442 list_entry = udev_device_get_properties_list_entry(device);
443 while (list_entry != NULL) {
444 if (export) {
445 const char *prefix = export_prefix;
446
447 if (prefix == NULL)
448 prefix = "";
449 printf("%s%s='%s'\n", prefix,
450 udev_list_entry_get_name(list_entry),
451 udev_list_entry_get_value(list_entry));
452 } else {
453 printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
454 }
455 list_entry = udev_list_entry_get_next(list_entry);
456 }
457 break;
458 case QUERY_ALL:
459 print_record(device);
460 break;
461 default:
bdd13f6b 462 assert_not_reached("unknown query type");
912541b0
KS
463 }
464 break;
465 case ACTION_ATTRIBUTE_WALK:
4f5d327a
KS
466 if (!device && argv[optind]) {
467 device = find_device(udev, argv[optind], NULL);
468 if (!device) {
469 fprintf(stderr, "Unknown device, absolute path in /dev/ or /sys expected.\n");
44433ebd 470 return 4;
4f5d327a
KS
471 }
472 }
473 if (!device) {
474 fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n");
44433ebd 475 return 4;
912541b0
KS
476 }
477 print_device_chain(device);
478 break;
479 case ACTION_DEVICE_ID_FILE:
480 if (stat_device(name, export, export_prefix) != 0)
44433ebd 481 return 1;
912541b0 482 break;
912541b0 483 }
87171e46 484
44433ebd 485 return 0;
87171e46 486}
1985c76e
KS
487
488const struct udevadm_cmd udevadm_info = {
912541b0
KS
489 .name = "info",
490 .cmd = uinfo,
5ac0162c 491 .help = "Query sysfs or the udev database",
1985c76e 492};