]> git.ipfire.org Git - thirdparty/systemd.git/blame - udev/udevadm-info.c
use size definitions from libudev
[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
01618658
KS
18#include "config.h"
19
be9b51f6
KS
20#include <stdlib.h>
21#include <string.h>
22#include <stdio.h>
1aa1e248 23#include <stddef.h>
034f35d7 24#include <ctype.h>
87171e46
KS
25#include <stdarg.h>
26#include <unistd.h>
1aa1e248 27#include <dirent.h>
87171e46 28#include <errno.h>
11f1bb5a 29#include <getopt.h>
492e76c9
KS
30#include <sys/stat.h>
31#include <sys/types.h>
be9b51f6 32
869fc2f1 33#include "udev.h"
be9b51f6 34
7d563a17 35static void print_all_attributes(struct udev *udev, const char *devpath, const char *key)
be9b51f6 36{
17fcfb59 37 char path[UTIL_PATH_SIZE];
1aa1e248
KS
38 DIR *dir;
39 struct dirent *dent;
40
31c1f537
KS
41 util_strlcpy(path, udev_get_sys_path(udev), sizeof(path));
42 util_strlcat(path, devpath, sizeof(path));
1aa1e248
KS
43
44 dir = opendir(path);
45 if (dir != NULL) {
46 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
492e76c9 47 struct stat statbuf;
17fcfb59 48 char filename[UTIL_PATH_SIZE];
1aa1e248 49 char *attr_value;
17fcfb59 50 char value[UTIL_NAME_SIZE];
1aa1e248
KS
51 size_t len;
52
492e76c9
KS
53 if (dent->d_name[0] == '.')
54 continue;
55
e9b64770
KS
56 if (strcmp(dent->d_name, "uevent") == 0)
57 continue;
4c46d72a
KS
58 if (strcmp(dent->d_name, "dev") == 0)
59 continue;
e9b64770 60
31c1f537
KS
61 util_strlcpy(filename, path, sizeof(filename));
62 util_strlcat(filename, "/", sizeof(filename));
63 util_strlcat(filename, dent->d_name, sizeof(filename));
492e76c9
KS
64 if (lstat(filename, &statbuf) != 0)
65 continue;
66 if (S_ISLNK(statbuf.st_mode))
67 continue;
68
7d563a17 69 attr_value = sysfs_attr_get_value(udev, devpath, dent->d_name);
1aa1e248
KS
70 if (attr_value == NULL)
71 continue;
31c1f537 72 len = util_strlcpy(value, attr_value, sizeof(value));
1f7a36f2
MM
73 if(len >= sizeof(value))
74 len = sizeof(value) - 1;
7d563a17 75 dbg(udev, "attr '%s'='%s'(%zi)\n", dent->d_name, value, len);
1aa1e248 76
1aa1e248
KS
77 /* skip nonprintable attributes */
78 while (len && isprint(value[len-1]))
79 len--;
80 if (len) {
7d563a17 81 dbg(udev, "attribute value of '%s' non-printable, skip\n", dent->d_name);
1aa1e248
KS
82 continue;
83 }
6276fdd2 84
95776dc6 85 printf(" %s{%s}==\"%s\"\n", key, dent->d_name, value);
6276fdd2 86 }
be9b51f6
KS
87 }
88 printf("\n");
be9b51f6
KS
89}
90
7d563a17 91static int print_device_chain(struct udev *udev, const char *devpath)
87171e46 92{
1aa1e248 93 struct sysfs_device *dev;
e48fc108 94
7d563a17 95 dev = sysfs_device_get(udev, devpath);
bc2a89fa
KS
96 if (dev == NULL)
97 return -1;
98
1aa1e248 99 printf("\n"
6cf19e52
KS
100 "Udevinfo starts with the device specified by the devpath and then\n"
101 "walks up the chain of parent devices. It prints for every device\n"
102 "found, all possible attributes in the udev rules key format.\n"
103 "A rule to match, can be composed by the attributes of the device\n"
104 "and the attributes from one single parent device.\n"
dba8c18b 105 "\n");
f61d732a 106
1aa1e248 107 printf(" looking at device '%s':\n", dev->devpath);
95776dc6 108 printf(" KERNEL==\"%s\"\n", dev->kernel);
1aa1e248 109 printf(" SUBSYSTEM==\"%s\"\n", dev->subsystem);
22f5934a 110 printf(" DRIVER==\"%s\"\n", dev->driver);
7d563a17 111 print_all_attributes(udev, dev->devpath, "ATTR");
034f35d7 112
1aa1e248
KS
113 /* walk up the chain of devices */
114 while (1) {
7d563a17 115 dev = sysfs_device_get_parent(udev, dev);
1aa1e248 116 if (dev == NULL)
034f35d7 117 break;
6cf19e52 118 printf(" looking at parent device '%s':\n", dev->devpath);
95776dc6 119 printf(" KERNELS==\"%s\"\n", dev->kernel);
64e0ce85 120 printf(" SUBSYSTEMS==\"%s\"\n", dev->subsystem);
95776dc6 121 printf(" DRIVERS==\"%s\"\n", dev->driver);
1aa1e248 122
7d563a17 123 print_all_attributes(udev, dev->devpath, "ATTRS");
be9b51f6
KS
124 }
125
1aa1e248
KS
126 return 0;
127}
128
7d563a17 129static void print_record(struct udevice *udevice)
1aa1e248
KS
130{
131 struct name_entry *name_loop;
132
7d563a17
KS
133 printf("P: %s\n", udevice->dev->devpath);
134 printf("N: %s\n", udevice->name);
135 list_for_each_entry(name_loop, &udevice->symlink_list, node)
1aa1e248 136 printf("S: %s\n", name_loop->name);
7d563a17
KS
137 if (udevice->link_priority != 0)
138 printf("L: %i\n", udevice->link_priority);
139 if (udevice->partitions != 0)
140 printf("A:%u\n", udevice->partitions);
141 if (udevice->ignore_remove)
142 printf("R:%u\n", udevice->ignore_remove);
143 list_for_each_entry(name_loop, &udevice->env_list, node)
1aa1e248 144 printf("E: %s\n", name_loop->name);
be9b51f6 145}
87171e46 146
7d563a17
KS
147static void export_db(struct udev *udev)
148{
db481105
KS
149 LIST_HEAD(name_list);
150 struct name_entry *name_loop;
db481105 151
7d563a17 152 udev_db_get_all_entries(udev, &name_list);
fb179207 153 list_for_each_entry(name_loop, &name_list, node) {
7d563a17 154 struct udevice *udevice_db;
db481105 155
7d563a17
KS
156 udevice_db = udev_device_init(udev);
157 if (udevice_db == NULL)
1aa1e248 158 continue;
7d563a17
KS
159 if (udev_db_get_device(udevice_db, name_loop->name) == 0)
160 print_record(udevice_db);
3045132a 161 printf("\n");
7d563a17 162 udev_device_cleanup(udevice_db);
db481105 163 }
7d563a17 164 name_list_cleanup(udev, &name_list);
9fe1a96d
KS
165}
166
7d563a17 167static int lookup_device_by_name(struct udev *udev, struct udevice **udevice, const char *name)
31de3a2b
KS
168{
169 LIST_HEAD(name_list);
be8594ab 170 int count;
31de3a2b
KS
171 struct name_entry *device;
172 int rc = -1;
173
7d563a17 174 count = udev_db_get_devices_by_name(udev, name, &name_list);
be8594ab 175 if (count <= 0)
31de3a2b
KS
176 goto out;
177
7d563a17 178 info(udev, "found %i devices for '%s'\n", count, name);
be8594ab
KS
179
180 /* select the device that seems to match */
31de3a2b 181 list_for_each_entry(device, &name_list, node) {
7d563a17 182 struct udevice *udevice_loop;
17fcfb59 183 char filename[UTIL_PATH_SIZE];
31de3a2b
KS
184 struct stat statbuf;
185
7d563a17
KS
186 udevice_loop = udev_device_init(udev);
187 if (udevice_loop == NULL)
44aff4cd 188 break;
7d563a17 189 if (udev_db_get_device(udevice_loop, device->name) != 0)
44aff4cd 190 goto next;
7d563a17 191 info(udev, "found db entry '%s'\n", device->name);
31de3a2b 192
44aff4cd 193 /* make sure, we don't get a link of a different device */
31c1f537
KS
194 util_strlcpy(filename, udev_get_dev_path(udev), sizeof(filename));
195 util_strlcat(filename, "/", sizeof(filename));
196 util_strlcat(filename, name, sizeof(filename));
31de3a2b 197 if (stat(filename, &statbuf) != 0)
44aff4cd 198 goto next;
7d563a17
KS
199 if (major(udevice_loop->devt) > 0 && udevice_loop->devt != statbuf.st_rdev) {
200 info(udev, "skip '%s', dev_t doesn't match\n", udevice_loop->name);
44aff4cd 201 goto next;
31de3a2b 202 }
be8594ab 203 rc = 0;
7d563a17 204 *udevice = udevice_loop;
be8594ab 205 break;
44aff4cd 206next:
7d563a17 207 udev_device_cleanup(udevice_loop);
31de3a2b
KS
208 }
209out:
7d563a17 210 name_list_cleanup(udev, &name_list);
31de3a2b
KS
211 return rc;
212}
213
d1acfc3e 214static int stat_device(const char *name, int export, const char *prefix)
f338bac8
KS
215{
216 struct stat statbuf;
217
218 if (stat(name, &statbuf) != 0)
219 return -1;
220
d1acfc3e
KS
221 if (export) {
222 if (prefix == NULL)
223 prefix = "INFO_";
224 printf("%sMAJOR=%d\n"
225 "%sMINOR=%d\n",
226 prefix, major(statbuf.st_dev),
227 prefix, minor(statbuf.st_dev));
228 } else
cce9d773 229 printf("%d:%d\n", major(statbuf.st_dev), minor(statbuf.st_dev));
f338bac8
KS
230 return 0;
231}
232
7d563a17 233int udevadm_info(struct udev *udev, int argc, char *argv[])
87171e46 234{
7d563a17 235 struct udevice *udevice = NULL;
87171e46 236 int root = 0;
d1acfc3e
KS
237 int export = 0;
238 const char *export_prefix = NULL;
24ca5c33 239
e97717ba 240 static const struct option options[] = {
11f1bb5a
KS
241 { "name", 1, NULL, 'n' },
242 { "path", 1, NULL, 'p' },
243 { "query", 1, NULL, 'q' },
244 { "attribute-walk", 0, NULL, 'a' },
245 { "export-db", 0, NULL, 'e' },
246 { "root", 0, NULL, 'r' },
f338bac8 247 { "device-id-of-file", 1, NULL, 'd' },
d1acfc3e
KS
248 { "export", 0, NULL, 'x' },
249 { "export-prefix", 1, NULL, 'P' },
88caa9da 250 { "version", 0, NULL, 1 }, /* -V outputs braindead format */
11f1bb5a
KS
251 { "help", 0, NULL, 'h' },
252 {}
253 };
254
24ca5c33
KS
255 enum action_type {
256 ACTION_NONE,
257 ACTION_QUERY,
258 ACTION_ATTRIBUTE_WALK,
259 ACTION_ROOT,
f338bac8 260 ACTION_DEVICE_ID_FILE,
24ca5c33
KS
261 } action = ACTION_NONE;
262
b8476286
KS
263 enum query_type {
264 QUERY_NONE,
265 QUERY_NAME,
266 QUERY_PATH,
267 QUERY_SYMLINK,
268 QUERY_ENV,
269 QUERY_ALL,
270 } query = QUERY_NONE;
24ca5c33 271
17fcfb59
KS
272 char path[UTIL_PATH_SIZE] = "";
273 char name[UTIL_PATH_SIZE] = "";
e48fc108 274 struct name_entry *name_loop;
1aa1e248 275 int rc = 0;
e48fc108 276
87171e46 277 while (1) {
7d563a17
KS
278 int option;
279
d1acfc3e 280 option = getopt_long(argc, argv, "aed:n:p:q:rxPVh", options, NULL);
87171e46
KS
281 if (option == -1)
282 break;
283
7d563a17 284 dbg(udev, "option '%c'\n", option);
87171e46
KS
285 switch (option) {
286 case 'n':
2362eea6 287 /* remove /dev if given */
7d563a17 288 if (strncmp(optarg, udev_get_dev_path(udev), strlen(udev_get_dev_path(udev))) == 0)
31c1f537 289 util_strlcpy(name, &optarg[strlen(udev_get_dev_path(udev))+1], sizeof(name));
2362eea6 290 else
31c1f537 291 util_strlcpy(name, optarg, sizeof(name));
ecc9ec57 292 util_remove_trailing_chars(name, '/');
7d563a17 293 dbg(udev, "name: %s\n", name);
87171e46 294 break;
87171e46 295 case 'p':
2362eea6 296 /* remove /sys if given */
7d563a17 297 if (strncmp(optarg, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) == 0)
31c1f537 298 util_strlcpy(path, &optarg[strlen(udev_get_sys_path(udev))], sizeof(path));
1aa1e248 299 else
31c1f537 300 util_strlcpy(path, optarg, sizeof(path));
ecc9ec57 301 util_remove_trailing_chars(path, '/');
e7e194a0
KS
302
303 /* possibly resolve to real devpath */
17fcfb59
KS
304 if (util_resolve_sys_link(udev, path, sizeof(path)) != 0) {
305 char temp[UTIL_PATH_SIZE];
e7e194a0
KS
306 char *pos;
307
308 /* also check if the parent is a link */
31c1f537 309 util_strlcpy(temp, path, sizeof(temp));
e7e194a0
KS
310 pos = strrchr(temp, '/');
311 if (pos != 0) {
17fcfb59 312 char tail[UTIL_PATH_SIZE];
e7e194a0 313
31c1f537 314 util_strlcpy(tail, pos, sizeof(tail));
e7e194a0 315 pos[0] = '\0';
17fcfb59 316 if (util_resolve_sys_link(udev, temp, sizeof(temp)) == 0) {
31c1f537
KS
317 util_strlcpy(path, temp, sizeof(path));
318 util_strlcat(path, tail, sizeof(path));
e7e194a0
KS
319 }
320 }
321 }
7d563a17 322 dbg(udev, "path: %s\n", path);
87171e46 323 break;
87171e46 324 case 'q':
24ca5c33 325 action = ACTION_QUERY;
87171e46 326 if (strcmp(optarg, "name") == 0) {
b8476286 327 query = QUERY_NAME;
87171e46
KS
328 break;
329 }
87171e46 330 if (strcmp(optarg, "symlink") == 0) {
b8476286 331 query = QUERY_SYMLINK;
87171e46
KS
332 break;
333 }
87171e46 334 if (strcmp(optarg, "path") == 0) {
b8476286
KS
335 query = QUERY_PATH;
336 break;
337 }
b8476286
KS
338 if (strcmp(optarg, "env") == 0) {
339 query = QUERY_ENV;
87171e46
KS
340 break;
341 }
8ea84a8a 342 if (strcmp(optarg, "all") == 0) {
b8476286 343 query = QUERY_ALL;
8ea84a8a
KS
344 break;
345 }
61b1b706 346 fprintf(stderr, "unknown query type\n");
1aa1e248 347 rc = 2;
e48fc108 348 goto exit;
87171e46 349 case 'r':
24ca5c33
KS
350 if (action == ACTION_NONE)
351 action = ACTION_ROOT;
87171e46
KS
352 root = 1;
353 break;
f338bac8
KS
354 case 'd':
355 action = ACTION_DEVICE_ID_FILE;
31c1f537 356 util_strlcpy(name, optarg, sizeof(name));
f338bac8 357 break;
87171e46 358 case 'a':
24ca5c33 359 action = ACTION_ATTRIBUTE_WALK;
87171e46 360 break;
24ca5c33 361 case 'e':
7d563a17 362 export_db(udev);
e48fc108 363 goto exit;
d1acfc3e
KS
364 case 'x':
365 export = 1;
366 break;
367 case 'P':
368 export_prefix = optarg;
369 break;
88caa9da 370 case 1:
01618658 371 printf("%s\n", VERSION);
88caa9da 372 goto exit;
87171e46 373 case 'V':
01618658 374 printf("udevinfo, version %s\n", VERSION);
e48fc108 375 goto exit;
87171e46 376 case 'h':
225cb03b 377 printf("Usage: udevadm info OPTIONS\n"
f338bac8
KS
378 " --query=<type> query database for the specified value:\n"
379 " name name of device node\n"
380 " symlink pointing to node\n"
381 " path sysfs device path\n"
382 " env the device related imported environment\n"
383 " all all values\n"
384 " --path=<devpath> sysfs device path used for query or chain\n"
385 " --name=<name> node or symlink name used for query\n"
386 " --root prepend to query result or print udev_root\n"
387 " --attribute-walk print all key matches while walking along chain\n"
388 " of parent devices\n"
389 " --device-id-of-file=<file> print major/minor of underlying device\n"
390 " --export-db export the content of the udev database\n"
391 " --help print this text\n"
e3396a2d
KS
392 "\n");
393 goto exit;
87171e46 394 default:
24ca5c33 395 goto exit;
87171e46
KS
396 }
397 }
398
24ca5c33
KS
399 /* run action */
400 switch (action) {
401 case ACTION_QUERY:
2362eea6 402 /* needs devpath or node/symlink name for query */
87171e46 403 if (path[0] != '\0') {
7d563a17
KS
404 udevice = udev_device_init(udev);
405 if (udevice == NULL) {
44aff4cd
KS
406 rc = 1;
407 goto exit;
408 }
7d563a17 409 if (udev_db_get_device(udevice, path) != 0) {
2362eea6 410 fprintf(stderr, "no record for '%s' in database\n", path);
1aa1e248 411 rc = 3;
87171e46
KS
412 goto exit;
413 }
24ca5c33 414 } else if (name[0] != '\0') {
7d563a17 415 if (lookup_device_by_name(udev, &udevice, name) != 0) {
2362eea6
KS
416 fprintf(stderr, "node name not found\n");
417 rc = 4;
87171e46
KS
418 goto exit;
419 }
24ca5c33 420 } else {
11f1bb5a 421 fprintf(stderr, "query needs --path or node --name specified\n");
1aa1e248 422 rc = 4;
24ca5c33 423 goto exit;
87171e46
KS
424 }
425
87171e46 426 switch(query) {
b8476286 427 case QUERY_NAME:
e48fc108 428 if (root)
7d563a17 429 printf("%s/%s\n", udev_get_dev_path(udev), udevice->name);
e48fc108 430 else
7d563a17 431 printf("%s\n", udevice->name);
24ca5c33 432 break;
b8476286 433 case QUERY_SYMLINK:
7d563a17
KS
434 list_for_each_entry(name_loop, &udevice->symlink_list, node) {
435 char c = name_loop->node.next != &udevice->symlink_list ? ' ' : '\n';
18770246
KS
436
437 if (root)
7d563a17 438 printf("%s/%s%c", udev_get_dev_path(udev), name_loop->name, c);
18770246
KS
439 else
440 printf("%s%c", name_loop->name, c);
441 }
24ca5c33 442 break;
b8476286 443 case QUERY_PATH:
7d563a17 444 printf("%s\n", udevice->dev->devpath);
e48fc108 445 goto exit;
b8476286 446 case QUERY_ENV:
7d563a17 447 list_for_each_entry(name_loop, &udevice->env_list, node)
b8476286 448 printf("%s\n", name_loop->name);
24ca5c33 449 break;
b8476286 450 case QUERY_ALL:
7d563a17 451 print_record(udevice);
24ca5c33 452 break;
87171e46 453 default:
e3396a2d 454 fprintf(stderr, "unknown query type\n");
24ca5c33 455 break;
87171e46 456 }
24ca5c33
KS
457 break;
458 case ACTION_ATTRIBUTE_WALK:
1aa0c52b 459 if (path[0] != '\0') {
7d563a17 460 if (print_device_chain(udev, path) != 0) {
fd807192 461 fprintf(stderr, "no valid sysfs device found\n");
bc2a89fa
KS
462 rc = 4;
463 goto exit;
464 }
1aa0c52b 465 } else if (name[0] != '\0') {
7d563a17 466 if (lookup_device_by_name(udev, &udevice, name) != 0) {
1aa0c52b
KS
467 fprintf(stderr, "node name not found\n");
468 rc = 4;
469 goto exit;
470 }
7d563a17 471 if (print_device_chain(udev, udevice->dev->devpath) != 0) {
fd807192 472 fprintf(stderr, "no valid sysfs device found\n");
bc2a89fa
KS
473 rc = 4;
474 goto exit;
475 }
1aa0c52b 476 } else {
11f1bb5a 477 fprintf(stderr, "attribute walk needs --path or node --name specified\n");
1aa0c52b
KS
478 rc = 5;
479 goto exit;
480 }
24ca5c33 481 break;
f338bac8 482 case ACTION_DEVICE_ID_FILE:
d1acfc3e 483 if (stat_device(name, export, export_prefix) != 0)
f338bac8
KS
484 rc = 6;
485 break;
24ca5c33 486 case ACTION_ROOT:
7d563a17 487 printf("%s\n", udev_get_dev_path(udev));
24ca5c33
KS
488 break;
489 default:
e3396a2d 490 fprintf(stderr, "missing option\n");
1aa1e248 491 rc = 1;
24ca5c33 492 break;
87171e46
KS
493 }
494
e48fc108 495exit:
7d563a17 496 udev_device_cleanup(udevice);
1aa1e248 497 return rc;
87171e46 498}