]> git.ipfire.org Git - thirdparty/systemd.git/blame - udev/udevadm-info.c
udevadm: info - honor --export, --export-prefix=
[thirdparty/systemd.git] / udev / udevadm-info.c
CommitLineData
be9b51f6 1/*
fc206fbe 2 * Copyright (C) 2004-2009 Kay Sievers <kay.sievers@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
18#include <stdlib.h>
19#include <string.h>
20#include <stdio.h>
1aa1e248 21#include <stddef.h>
034f35d7 22#include <ctype.h>
87171e46
KS
23#include <stdarg.h>
24#include <unistd.h>
1aa1e248 25#include <dirent.h>
87171e46 26#include <errno.h>
11f1bb5a 27#include <getopt.h>
e6c1a2bd 28#include <fcntl.h>
492e76c9
KS
29#include <sys/stat.h>
30#include <sys/types.h>
be9b51f6 31
869fc2f1 32#include "udev.h"
be9b51f6 33
93b0f384 34static void print_all_attributes(struct udev_device *device, const char *key)
be9b51f6 35{
4aa0b15e 36 struct udev *udev = udev_device_get_udev(device);
1aa1e248
KS
37 DIR *dir;
38 struct dirent *dent;
39
93b0f384 40 dir = opendir(udev_device_get_syspath(device));
1aa1e248
KS
41 if (dir != NULL) {
42 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
492e76c9 43 struct stat statbuf;
93b0f384 44 const char *value;
1aa1e248
KS
45 size_t len;
46
492e76c9
KS
47 if (dent->d_name[0] == '.')
48 continue;
49
e9b64770
KS
50 if (strcmp(dent->d_name, "uevent") == 0)
51 continue;
4c46d72a
KS
52 if (strcmp(dent->d_name, "dev") == 0)
53 continue;
e9b64770 54
e6c1a2bd 55 if (fstatat(dirfd(dir), dent->d_name, &statbuf, AT_SYMLINK_NOFOLLOW) != 0)
492e76c9
KS
56 continue;
57 if (S_ISLNK(statbuf.st_mode))
58 continue;
59
69239210 60 value = udev_device_get_sysattr_value(device, dent->d_name);
93b0f384 61 if (value == NULL)
1aa1e248 62 continue;
78715f65 63 dbg(udev, "attr '%s'='%s'\n", dent->d_name, value);
1aa1e248 64
1aa1e248 65 /* skip nonprintable attributes */
93b0f384
KS
66 len = strlen(value);
67 while (len > 0 && isprint(value[len-1]))
1aa1e248 68 len--;
93b0f384 69 if (len > 0) {
4aa0b15e 70 dbg(udev, "attribute value of '%s' non-printable, skip\n", dent->d_name);
1aa1e248
KS
71 continue;
72 }
6276fdd2 73
95776dc6 74 printf(" %s{%s}==\"%s\"\n", key, dent->d_name, value);
6276fdd2 75 }
4ad3a37f 76 closedir(dir);
be9b51f6
KS
77 }
78 printf("\n");
be9b51f6
KS
79}
80
93b0f384 81static int print_device_chain(struct udev_device *device)
87171e46 82{
b2d9e4f2 83 struct udev_device *device_parent;
4ad3a37f 84 const char *str;
e48fc108 85
1aa1e248 86 printf("\n"
b620292b 87 "Udevadm info starts with the device specified by the devpath and then\n"
6cf19e52
KS
88 "walks up the chain of parent devices. It prints for every device\n"
89 "found, all possible attributes in the udev rules key format.\n"
90 "A rule to match, can be composed by the attributes of the device\n"
91 "and the attributes from one single parent device.\n"
dba8c18b 92 "\n");
f61d732a 93
4ad3a37f
KS
94 printf(" looking at device '%s':\n", udev_device_get_devpath(device));
95 printf(" KERNEL==\"%s\"\n", udev_device_get_sysname(device));
96 str = udev_device_get_subsystem(device);
97 if (str == NULL)
98 str = "";
99 printf(" SUBSYSTEM==\"%s\"\n", str);
100 str = udev_device_get_driver(device);
101 if (str == NULL)
102 str = "";
103 printf(" DRIVER==\"%s\"\n", str);
93b0f384 104 print_all_attributes(device, "ATTR");
4ad3a37f 105
b2d9e4f2
KS
106 device_parent = device;
107 do {
108 device_parent = udev_device_get_parent(device_parent);
4ad3a37f 109 if (device_parent == NULL)
034f35d7 110 break;
b2d9e4f2
KS
111 printf(" looking at parent device '%s':\n", udev_device_get_devpath(device_parent));
112 printf(" KERNELS==\"%s\"\n", udev_device_get_sysname(device_parent));
113 str = udev_device_get_subsystem(device_parent);
4ad3a37f
KS
114 if (str == NULL)
115 str = "";
116 printf(" SUBSYSTEMS==\"%s\"\n", str);
b2d9e4f2 117 str = udev_device_get_driver(device_parent);
4ad3a37f
KS
118 if (str == NULL)
119 str = "";
120 printf(" DRIVERS==\"%s\"\n", str);
93b0f384 121 print_all_attributes(device_parent, "ATTRS");
b2d9e4f2 122 } while (device_parent != NULL);
be9b51f6 123
1aa1e248
KS
124 return 0;
125}
126
93b0f384 127static void print_record(struct udev_device *device)
31de3a2b 128{
93b0f384 129 size_t len;
ee137da3 130 const char *str;
93b0f384 131 int i;
0de33a61 132 struct udev_list_entry *list_entry;
93b0f384
KS
133
134 printf("P: %s\n", udev_device_get_devpath(device));
bf7ad0ea 135
93b0f384 136 len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
ee137da3
KS
137 str = udev_device_get_devnode(device);
138 if (str != NULL)
139 printf("N: %s\n", &str[len+1]);
bf7ad0ea 140
aa8734ff 141 i = udev_device_get_devlink_priority(device);
93b0f384
KS
142 if (i != 0)
143 printf("L: %i\n", i);
bf7ad0ea 144
0de33a61 145 udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device)) {
bf7ad0ea 146 len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
0de33a61 147 printf("S: %s\n", &udev_list_entry_get_name(list_entry)[len+1]);
31de3a2b 148 }
93b0f384 149
0de33a61
KS
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));
bf7ad0ea 154 printf("\n");
31de3a2b
KS
155}
156
88163971 157static int stat_device(const char *name, bool export, const char *prefix)
f338bac8
KS
158{
159 struct stat statbuf;
160
161 if (stat(name, &statbuf) != 0)
162 return -1;
163
d1acfc3e
KS
164 if (export) {
165 if (prefix == NULL)
166 prefix = "INFO_";
167 printf("%sMAJOR=%d\n"
168 "%sMINOR=%d\n",
169 prefix, major(statbuf.st_dev),
170 prefix, minor(statbuf.st_dev));
171 } else
cce9d773 172 printf("%d:%d\n", major(statbuf.st_dev), minor(statbuf.st_dev));
f338bac8
KS
173 return 0;
174}
175
bf7ad0ea
KS
176static int export_devices(struct udev *udev)
177{
438d4c3c 178 struct udev_enumerate *udev_enumerate;
0de33a61 179 struct udev_list_entry *list_entry;
bf7ad0ea 180
438d4c3c
KS
181 udev_enumerate = udev_enumerate_new(udev);
182 if (udev_enumerate == NULL)
bf7ad0ea 183 return -1;
c97f839e 184 udev_enumerate_scan_devices(udev_enumerate);
438d4c3c 185 udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
bf7ad0ea
KS
186 struct udev_device *device;
187
0de33a61 188 device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry));
bf7ad0ea 189 if (device != NULL) {
b38a5801 190 print_record(device);
bf7ad0ea
KS
191 udev_device_unref(device);
192 }
bf7ad0ea 193 }
438d4c3c 194 udev_enumerate_unref(udev_enumerate);
bf7ad0ea
KS
195 return 0;
196}
197
7d563a17 198int udevadm_info(struct udev *udev, int argc, char *argv[])
87171e46 199{
93b0f384 200 struct udev_device *device = NULL;
88163971
KS
201 bool root = 0;
202 bool export = 0;
d1acfc3e 203 const char *export_prefix = NULL;
93b0f384
KS
204 char path[UTIL_PATH_SIZE];
205 char name[UTIL_PATH_SIZE];
0de33a61 206 struct udev_list_entry *list_entry;
93b0f384 207 int rc = 0;
24ca5c33 208
e97717ba 209 static const struct option options[] = {
033e9f8c
KS
210 { "name", required_argument, NULL, 'n' },
211 { "path", required_argument, NULL, 'p' },
ff5d99e0 212 { "query", required_argument, NULL, 'q' },
033e9f8c
KS
213 { "attribute-walk", no_argument, NULL, 'a' },
214 { "export-db", no_argument, NULL, 'e' },
215 { "root", no_argument, NULL, 'r' },
216 { "device-id-of-file", required_argument, NULL, 'd' },
217 { "export", no_argument, NULL, 'x' },
218 { "export-prefix", required_argument, NULL, 'P' },
50025605 219 { "version", no_argument, NULL, 'V' },
033e9f8c 220 { "help", no_argument, NULL, 'h' },
11f1bb5a
KS
221 {}
222 };
223
24ca5c33
KS
224 enum action_type {
225 ACTION_NONE,
226 ACTION_QUERY,
227 ACTION_ATTRIBUTE_WALK,
228 ACTION_ROOT,
f338bac8 229 ACTION_DEVICE_ID_FILE,
24ca5c33
KS
230 } action = ACTION_NONE;
231
b8476286
KS
232 enum query_type {
233 QUERY_NONE,
234 QUERY_NAME,
235 QUERY_PATH,
236 QUERY_SYMLINK,
0254b211 237 QUERY_PROPERTY,
b8476286
KS
238 QUERY_ALL,
239 } query = QUERY_NONE;
24ca5c33 240
88cbfb09 241 for (;;) {
7d563a17 242 int option;
bf7ad0ea 243 struct stat statbuf;
7d563a17 244
163c0365 245 option = getopt_long(argc, argv, "aed:n:p:q:rxP:Vh", options, NULL);
87171e46
KS
246 if (option == -1)
247 break;
248
7d563a17 249 dbg(udev, "option '%c'\n", option);
87171e46
KS
250 switch (option) {
251 case 'n':
93b0f384
KS
252 if (device != NULL) {
253 fprintf(stderr, "device already specified\n");
254 rc = 2;
255 goto exit;
256 }
2362eea6 257 /* remove /dev if given */
065db052
KS
258 if (strncmp(optarg, udev_get_dev_path(udev), strlen(udev_get_dev_path(udev))) != 0)
259 util_strscpyl(name, sizeof(name), udev_get_dev_path(udev), "/", optarg, NULL);
260 else
261 util_strscpy(name, sizeof(name), optarg);
ecc9ec57 262 util_remove_trailing_chars(name, '/');
bf7ad0ea
KS
263 if (stat(name, &statbuf) < 0) {
264 fprintf(stderr, "device node not found\n");
265 rc = 2;
266 goto exit;
267 } else {
268 char type;
269
270 if (S_ISBLK(statbuf.st_mode)) {
271 type = 'b';
272 } else if (S_ISCHR(statbuf.st_mode)) {
273 type = 'c';
274 } else {
275 fprintf(stderr, "device node has wrong file type\n");
276 rc = 2;
277 goto exit;
278 }
279 device = udev_device_new_from_devnum(udev, type, statbuf.st_rdev);
280 if (device == NULL) {
281 fprintf(stderr, "device node not found\n");
282 rc = 2;
283 goto exit;
284 }
285 }
87171e46 286 break;
87171e46 287 case 'p':
93b0f384
KS
288 if (device != NULL) {
289 fprintf(stderr, "device already specified\n");
290 rc = 2;
291 goto exit;
292 }
bc8184ed 293 /* add sys dir if needed */
065db052
KS
294 if (strncmp(optarg, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) != 0)
295 util_strscpyl(path, sizeof(path), udev_get_sys_path(udev), optarg, NULL);
296 else
297 util_strscpy(path, sizeof(path), optarg);
ecc9ec57 298 util_remove_trailing_chars(path, '/');
8753fadf 299 device = udev_device_new_from_syspath(udev, path);
bf7ad0ea
KS
300 if (device == NULL) {
301 fprintf(stderr, "device path not found\n");
302 rc = 2;
303 goto exit;
304 }
87171e46 305 break;
87171e46 306 case 'q':
24ca5c33 307 action = ACTION_QUERY;
ff5d99e0 308 if (strcmp(optarg, "property") == 0 || strcmp(optarg, "env") == 0) {
0254b211
KS
309 query = QUERY_PROPERTY;
310 } else if (strcmp(optarg, "name") == 0) {
b8476286 311 query = QUERY_NAME;
0254b211 312 } else if (strcmp(optarg, "symlink") == 0) {
b8476286 313 query = QUERY_SYMLINK;
0254b211 314 } else if (strcmp(optarg, "path") == 0) {
b8476286 315 query = QUERY_PATH;
0254b211 316 } else if (strcmp(optarg, "all") == 0) {
b8476286 317 query = QUERY_ALL;
0254b211
KS
318 } else {
319 fprintf(stderr, "unknown query type\n");
320 rc = 3;
321 goto exit;
8ea84a8a 322 }
0254b211 323 break;
87171e46 324 case 'r':
24ca5c33
KS
325 if (action == ACTION_NONE)
326 action = ACTION_ROOT;
88163971 327 root = true;
87171e46 328 break;
f338bac8
KS
329 case 'd':
330 action = ACTION_DEVICE_ID_FILE;
065db052 331 util_strscpy(name, sizeof(name), optarg);
f338bac8 332 break;
87171e46 333 case 'a':
24ca5c33 334 action = ACTION_ATTRIBUTE_WALK;
87171e46 335 break;
24ca5c33 336 case 'e':
bf7ad0ea 337 export_devices(udev);
e48fc108 338 goto exit;
d1acfc3e 339 case 'x':
88163971 340 export = true;
d1acfc3e
KS
341 break;
342 case 'P':
343 export_prefix = optarg;
344 break;
87171e46 345 case 'V':
50025605 346 printf("%s\n", VERSION);
e48fc108 347 goto exit;
87171e46 348 case 'h':
225cb03b 349 printf("Usage: udevadm info OPTIONS\n"
cd42b50d 350 " --query=<type> query device information:\n"
f338bac8
KS
351 " name name of device node\n"
352 " symlink pointing to node\n"
cd42b50d 353 " path sys device path\n"
0254b211 354 " property the device properties\n"
f338bac8 355 " all all values\n"
cd42b50d
KS
356 " --path=<syspath> sys device path used for query or attribute walk\n"
357 " --name=<name> node or symlink name used for query or attribute walk\n"
358 " --root prepend dev directory to path names\n"
359 " --attribute-walk print all key matches while walking along the chain\n"
f338bac8 360 " of parent devices\n"
cd42b50d 361 " --device-id-of-file=<file> print major:minor of device containing this file\n"
f338bac8 362 " --export-db export the content of the udev database\n"
f1e7e360 363 " --help\n\n");
e3396a2d 364 goto exit;
87171e46 365 default:
24ca5c33 366 goto exit;
87171e46
KS
367 }
368 }
369
24ca5c33
KS
370 switch (action) {
371 case ACTION_QUERY:
93b0f384 372 if (device == NULL) {
9a8047fa 373 fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
1aa1e248 374 rc = 4;
24ca5c33 375 goto exit;
87171e46
KS
376 }
377
87171e46 378 switch(query) {
299650f1
KS
379 case QUERY_NAME: {
380 const char *node = udev_device_get_devnode(device);
381
382 if (node == NULL) {
383 fprintf(stderr, "no device node found\n");
384 rc = 5;
385 goto exit;
386 }
387
93b0f384 388 if (root) {
fb762bb9 389 printf("%s\n", udev_device_get_devnode(device));
93b0f384 390 } else {
299650f1
KS
391 size_t len = strlen(udev_get_dev_path(udev));
392
393 printf("%s\n", &udev_device_get_devnode(device)[len+1]);
18770246 394 }
24ca5c33 395 break;
299650f1 396 }
93b0f384 397 case QUERY_SYMLINK:
0de33a61
KS
398 list_entry = udev_device_get_devlinks_list_entry(device);
399 while (list_entry != NULL) {
bf7ad0ea 400 if (root) {
0de33a61 401 printf("%s", udev_list_entry_get_name(list_entry));
bf7ad0ea
KS
402 } else {
403 size_t len;
404
405 len = strlen(udev_get_dev_path(udev_device_get_udev(device)));
0de33a61 406 printf("%s", &udev_list_entry_get_name(list_entry)[len+1]);
bf7ad0ea 407 }
0de33a61
KS
408 list_entry = udev_list_entry_get_next(list_entry);
409 if (list_entry != NULL)
bf7ad0ea
KS
410 printf(" ");
411 }
412 printf("\n");
93b0f384 413 break;
b8476286 414 case QUERY_PATH:
93b0f384 415 printf("%s\n", udev_device_get_devpath(device));
e48fc108 416 goto exit;
0254b211 417 case QUERY_PROPERTY:
0de33a61
KS
418 list_entry = udev_device_get_properties_list_entry(device);
419 while (list_entry != NULL) {
88163971
KS
420 if (export) {
421 const char *prefix = export_prefix;
422
423 if (prefix == NULL)
424 prefix = "";
425 printf("%s%s='%s'\n", prefix,
426 udev_list_entry_get_name(list_entry),
427 udev_list_entry_get_value(list_entry));
428 } else {
429 printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
430 }
0de33a61 431 list_entry = udev_list_entry_get_next(list_entry);
bf7ad0ea 432 }
24ca5c33 433 break;
b8476286 434 case QUERY_ALL:
93b0f384 435 print_record(device);
24ca5c33 436 break;
87171e46 437 default:
e3396a2d 438 fprintf(stderr, "unknown query type\n");
24ca5c33 439 break;
87171e46 440 }
24ca5c33
KS
441 break;
442 case ACTION_ATTRIBUTE_WALK:
93b0f384 443 if (device == NULL) {
9a8047fa 444 fprintf(stderr, "query needs a valid device specified by --path= or --name=\n");
62b9dfb6 445 rc = 4;
1aa0c52b
KS
446 goto exit;
447 }
93b0f384 448 print_device_chain(device);
24ca5c33 449 break;
f338bac8 450 case ACTION_DEVICE_ID_FILE:
d1acfc3e 451 if (stat_device(name, export, export_prefix) != 0)
62b9dfb6 452 rc = 1;
f338bac8 453 break;
24ca5c33 454 case ACTION_ROOT:
7d563a17 455 printf("%s\n", udev_get_dev_path(udev));
24ca5c33
KS
456 break;
457 default:
e3396a2d 458 fprintf(stderr, "missing option\n");
1aa1e248 459 rc = 1;
24ca5c33 460 break;
87171e46
KS
461 }
462
e48fc108 463exit:
93b0f384 464 udev_device_unref(device);
1aa1e248 465 return rc;
87171e46 466}