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