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