2 * Copyright (C) 2004-2006 Kay Sievers <kay.sievers@vrfy.org>
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation version 2 of the License.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
32 #include <sys/types.h>
36 static void print_all_attributes(const char *devpath, const char *key)
42 strlcpy(path, sysfs_path, sizeof(path));
43 strlcat(path, devpath, sizeof(path));
47 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
49 char filename[PATH_SIZE];
51 char value[NAME_SIZE];
54 if (dent->d_name[0] == '.')
57 if (strcmp(dent->d_name, "uevent") == 0)
59 if (strcmp(dent->d_name, "dev") == 0)
62 strlcpy(filename, path, sizeof(filename));
63 strlcat(filename, "/", sizeof(filename));
64 strlcat(filename, dent->d_name, sizeof(filename));
65 if (lstat(filename, &statbuf) != 0)
67 if (S_ISLNK(statbuf.st_mode))
70 attr_value = sysfs_attr_get_value(devpath, dent->d_name);
71 if (attr_value == NULL)
73 len = strlcpy(value, attr_value, sizeof(value));
74 if(len >= sizeof(value))
75 len = sizeof(value) - 1;
76 dbg("attr '%s'='%s'(%zi)\n", dent->d_name, value, len);
78 /* remove trailing newlines */
79 while (len && value[len-1] == '\n')
82 /* skip nonprintable attributes */
83 while (len && isprint(value[len-1]))
86 dbg("attribute value of '%s' non-printable, skip\n", dent->d_name);
90 printf(" %s{%s}==\"%s\"\n", key, dent->d_name, value);
96 static int print_device_chain(const char *devpath)
98 struct sysfs_device *dev;
100 dev = sysfs_device_get(devpath);
105 "Udevinfo starts with the device specified by the devpath and then\n"
106 "walks up the chain of parent devices. It prints for every device\n"
107 "found, all possible attributes in the udev rules key format.\n"
108 "A rule to match, can be composed by the attributes of the device\n"
109 "and the attributes from one single parent device.\n"
112 printf(" looking at device '%s':\n", dev->devpath);
113 printf(" KERNEL==\"%s\"\n", dev->kernel);
114 printf(" SUBSYSTEM==\"%s\"\n", dev->subsystem);
115 printf(" DRIVER==\"%s\"\n", dev->driver);
116 print_all_attributes(dev->devpath, "ATTR");
118 /* walk up the chain of devices */
120 dev = sysfs_device_get_parent(dev);
123 printf(" looking at parent device '%s':\n", dev->devpath);
124 printf(" KERNELS==\"%s\"\n", dev->kernel);
125 printf(" SUBSYSTEMS==\"%s\"\n", dev->subsystem);
126 printf(" DRIVERS==\"%s\"\n", dev->driver);
128 print_all_attributes(dev->devpath, "ATTRS");
134 static void print_record(struct udevice *udev)
136 struct name_entry *name_loop;
138 printf("P: %s\n", udev->dev->devpath);
139 printf("N: %s\n", udev->name);
140 list_for_each_entry(name_loop, &udev->symlink_list, node)
141 printf("S: %s\n", name_loop->name);
142 if (udev->link_priority != 0)
143 printf("L: %i\n", udev->link_priority);
144 if (udev->partitions != 0)
145 printf("A:%u\n", udev->partitions);
146 if (udev->ignore_remove)
147 printf("R:%u\n", udev->ignore_remove);
148 list_for_each_entry(name_loop, &udev->env_list, node)
149 printf("E: %s\n", name_loop->name);
152 static void export_db(void) {
153 LIST_HEAD(name_list);
154 struct name_entry *name_loop;
156 udev_db_get_all_entries(&name_list);
157 list_for_each_entry(name_loop, &name_list, node) {
158 struct udevice *udev_db;
160 udev_db = udev_device_init();
163 if (udev_db_get_device(udev_db, name_loop->name) == 0)
164 print_record(udev_db);
166 udev_device_cleanup(udev_db);
168 name_list_cleanup(&name_list);
171 static int lookup_device_by_name(struct udevice **udev, const char *name)
173 LIST_HEAD(name_list);
175 struct name_entry *device;
178 count = udev_db_get_devices_by_name(name, &name_list);
182 info("found %i devices for '%s'\n", count, name);
184 /* select the device that seems to match */
185 list_for_each_entry(device, &name_list, node) {
186 struct udevice *udev_loop;
187 char filename[PATH_SIZE];
190 udev_loop = udev_device_init();
191 if (udev_loop == NULL)
193 if (udev_db_get_device(udev_loop, device->name) != 0)
195 info("found db entry '%s'\n", device->name);
197 /* make sure, we don't get a link of a different device */
198 strlcpy(filename, udev_root, sizeof(filename));
199 strlcat(filename, "/", sizeof(filename));
200 strlcat(filename, name, sizeof(filename));
201 if (stat(filename, &statbuf) != 0)
203 if (major(udev_loop->devt) > 0 && udev_loop->devt != statbuf.st_rdev) {
204 info("skip '%s', dev_t doesn't match\n", udev_loop->name);
211 udev_device_cleanup(udev_loop);
214 name_list_cleanup(&name_list);
218 static int stat_device(const char *name, int export, const char *prefix)
222 if (stat(name, &statbuf) != 0)
228 printf("%sMAJOR=%d\n"
230 prefix, major(statbuf.st_dev),
231 prefix, minor(statbuf.st_dev));
233 printf("%d:%d\n", major(statbuf.st_dev), minor(statbuf.st_dev));
237 int udevadm_info(int argc, char *argv[])
240 struct udevice *udev = NULL;
243 const char *export_prefix = NULL;
245 static const struct option options[] = {
246 { "name", 1, NULL, 'n' },
247 { "path", 1, NULL, 'p' },
248 { "query", 1, NULL, 'q' },
249 { "attribute-walk", 0, NULL, 'a' },
250 { "export-db", 0, NULL, 'e' },
251 { "root", 0, NULL, 'r' },
252 { "device-id-of-file", 1, NULL, 'd' },
253 { "export", 0, NULL, 'x' },
254 { "export-prefix", 1, NULL, 'P' },
255 { "version", 0, NULL, 1 }, /* -V outputs braindead format */
256 { "help", 0, NULL, 'h' },
263 ACTION_ATTRIBUTE_WALK,
265 ACTION_DEVICE_ID_FILE,
266 } action = ACTION_NONE;
275 } query = QUERY_NONE;
277 char path[PATH_SIZE] = "";
278 char name[PATH_SIZE] = "";
279 struct name_entry *name_loop;
283 option = getopt_long(argc, argv, "aed:n:p:q:rxPVh", options, NULL);
287 dbg("option '%c'\n", option);
290 /* remove /dev if given */
291 if (strncmp(optarg, udev_root, strlen(udev_root)) == 0)
292 strlcpy(name, &optarg[strlen(udev_root)+1], sizeof(name));
294 strlcpy(name, optarg, sizeof(name));
295 remove_trailing_chars(name, '/');
296 dbg("name: %s\n", name);
299 /* remove /sys if given */
300 if (strncmp(optarg, sysfs_path, strlen(sysfs_path)) == 0)
301 strlcpy(path, &optarg[strlen(sysfs_path)], sizeof(path));
303 strlcpy(path, optarg, sizeof(path));
304 remove_trailing_chars(path, '/');
306 /* possibly resolve to real devpath */
307 if (sysfs_resolve_link(path, sizeof(path)) != 0) {
308 char temp[PATH_SIZE];
311 /* also check if the parent is a link */
312 strlcpy(temp, path, sizeof(temp));
313 pos = strrchr(temp, '/');
315 char tail[PATH_SIZE];
317 strlcpy(tail, pos, sizeof(tail));
319 if (sysfs_resolve_link(temp, sizeof(temp)) == 0) {
320 strlcpy(path, temp, sizeof(path));
321 strlcat(path, tail, sizeof(path));
325 dbg("path: %s\n", path);
328 action = ACTION_QUERY;
329 if (strcmp(optarg, "name") == 0) {
333 if (strcmp(optarg, "symlink") == 0) {
334 query = QUERY_SYMLINK;
337 if (strcmp(optarg, "path") == 0) {
341 if (strcmp(optarg, "env") == 0) {
345 if (strcmp(optarg, "all") == 0) {
349 fprintf(stderr, "unknown query type\n");
353 if (action == ACTION_NONE)
354 action = ACTION_ROOT;
358 action = ACTION_DEVICE_ID_FILE;
359 strlcpy(name, optarg, sizeof(name));
362 action = ACTION_ATTRIBUTE_WALK;
371 export_prefix = optarg;
374 printf("%s\n", VERSION);
377 printf("udevinfo, version %s\n", VERSION);
380 printf("Usage: udevadm info OPTIONS\n"
381 " --query=<type> query database for the specified value:\n"
382 " name name of device node\n"
383 " symlink pointing to node\n"
384 " path sysfs device path\n"
385 " env the device related imported environment\n"
387 " --path=<devpath> sysfs device path used for query or chain\n"
388 " --name=<name> node or symlink name used for query\n"
389 " --root prepend to query result or print udev_root\n"
390 " --attribute-walk print all key matches while walking along chain\n"
391 " of parent devices\n"
392 " --device-id-of-file=<file> print major/minor of underlying device\n"
393 " --export-db export the content of the udev database\n"
394 " --help print this text\n"
405 /* needs devpath or node/symlink name for query */
406 if (path[0] != '\0') {
407 udev = udev_device_init();
412 if (udev_db_get_device(udev, path) != 0) {
413 fprintf(stderr, "no record for '%s' in database\n", path);
417 } else if (name[0] != '\0') {
418 if (lookup_device_by_name(&udev, name) != 0) {
419 fprintf(stderr, "node name not found\n");
424 fprintf(stderr, "query needs --path or node --name specified\n");
432 printf("%s/%s\n", udev_root, udev->name);
434 printf("%s\n", udev->name);
437 list_for_each_entry(name_loop, &udev->symlink_list, node) {
438 char c = name_loop->node.next != &udev->symlink_list ? ' ' : '\n';
441 printf("%s/%s%c", udev_root, name_loop->name, c);
443 printf("%s%c", name_loop->name, c);
447 printf("%s\n", udev->dev->devpath);
450 list_for_each_entry(name_loop, &udev->env_list, node)
451 printf("%s\n", name_loop->name);
457 fprintf(stderr, "unknown query type\n");
461 case ACTION_ATTRIBUTE_WALK:
462 if (path[0] != '\0') {
463 if (print_device_chain(path) != 0) {
464 fprintf(stderr, "no valid sysfs device found\n");
468 } else if (name[0] != '\0') {
469 if (lookup_device_by_name(&udev, name) != 0) {
470 fprintf(stderr, "node name not found\n");
474 if (print_device_chain(udev->dev->devpath) != 0) {
475 fprintf(stderr, "no valid sysfs device found\n");
480 fprintf(stderr, "attribute walk needs --path or node --name specified\n");
485 case ACTION_DEVICE_ID_FILE:
486 if (stat_device(name, export, export_prefix) != 0)
490 printf("%s\n", udev_root);
493 fprintf(stderr, "missing option\n");
499 udev_device_cleanup(udev);