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 udevinfo(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;
282 logging_init("udevinfo");
287 option = getopt_long(argc, argv, "aed:n:p:q:rxPVh", options, NULL);
291 dbg("option '%c'\n", option);
294 /* remove /dev if given */
295 if (strncmp(optarg, udev_root, strlen(udev_root)) == 0)
296 strlcpy(name, &optarg[strlen(udev_root)+1], sizeof(name));
298 strlcpy(name, optarg, sizeof(name));
299 remove_trailing_chars(name, '/');
300 dbg("name: %s\n", name);
303 /* remove /sys if given */
304 if (strncmp(optarg, sysfs_path, strlen(sysfs_path)) == 0)
305 strlcpy(path, &optarg[strlen(sysfs_path)], sizeof(path));
307 strlcpy(path, optarg, sizeof(path));
308 remove_trailing_chars(path, '/');
310 /* possibly resolve to real devpath */
311 if (sysfs_resolve_link(path, sizeof(path)) != 0) {
312 char temp[PATH_SIZE];
315 /* also check if the parent is a link */
316 strlcpy(temp, path, sizeof(temp));
317 pos = strrchr(temp, '/');
319 char tail[PATH_SIZE];
321 strlcpy(tail, pos, sizeof(tail));
323 if (sysfs_resolve_link(temp, sizeof(temp)) == 0) {
324 strlcpy(path, temp, sizeof(path));
325 strlcat(path, tail, sizeof(path));
329 dbg("path: %s\n", path);
332 action = ACTION_QUERY;
333 if (strcmp(optarg, "name") == 0) {
337 if (strcmp(optarg, "symlink") == 0) {
338 query = QUERY_SYMLINK;
341 if (strcmp(optarg, "path") == 0) {
345 if (strcmp(optarg, "env") == 0) {
349 if (strcmp(optarg, "all") == 0) {
353 fprintf(stderr, "unknown query type\n");
357 if (action == ACTION_NONE)
358 action = ACTION_ROOT;
362 action = ACTION_DEVICE_ID_FILE;
363 strlcpy(name, optarg, sizeof(name));
366 action = ACTION_ATTRIBUTE_WALK;
375 export_prefix = optarg;
378 printf("%s\n", VERSION);
381 printf("udevinfo, version %s\n", VERSION);
384 printf("Usage: udevadm info OPTIONS\n"
385 " --query=<type> query database for the specified value:\n"
386 " name name of device node\n"
387 " symlink pointing to node\n"
388 " path sysfs device path\n"
389 " env the device related imported environment\n"
391 " --path=<devpath> sysfs device path used for query or chain\n"
392 " --name=<name> node or symlink name used for query\n"
393 " --root prepend to query result or print udev_root\n"
394 " --attribute-walk print all key matches while walking along chain\n"
395 " of parent devices\n"
396 " --device-id-of-file=<file> print major/minor of underlying device\n"
397 " --export-db export the content of the udev database\n"
398 " --help print this text\n"
409 /* needs devpath or node/symlink name for query */
410 if (path[0] != '\0') {
411 udev = udev_device_init();
416 if (udev_db_get_device(udev, path) != 0) {
417 fprintf(stderr, "no record for '%s' in database\n", path);
421 } else if (name[0] != '\0') {
422 if (lookup_device_by_name(&udev, name) != 0) {
423 fprintf(stderr, "node name not found\n");
428 fprintf(stderr, "query needs --path or node --name specified\n");
436 printf("%s/%s\n", udev_root, udev->name);
438 printf("%s\n", udev->name);
441 list_for_each_entry(name_loop, &udev->symlink_list, node) {
442 char c = name_loop->node.next != &udev->symlink_list ? ' ' : '\n';
445 printf("%s/%s%c", udev_root, name_loop->name, c);
447 printf("%s%c", name_loop->name, c);
451 printf("%s\n", udev->dev->devpath);
454 list_for_each_entry(name_loop, &udev->env_list, node)
455 printf("%s\n", name_loop->name);
461 fprintf(stderr, "unknown query type\n");
465 case ACTION_ATTRIBUTE_WALK:
466 if (path[0] != '\0') {
467 if (print_device_chain(path) != 0) {
468 fprintf(stderr, "no valid sysfs device found\n");
472 } else if (name[0] != '\0') {
473 if (lookup_device_by_name(&udev, name) != 0) {
474 fprintf(stderr, "node name not found\n");
478 if (print_device_chain(udev->dev->devpath) != 0) {
479 fprintf(stderr, "no valid sysfs device found\n");
484 fprintf(stderr, "attribute walk needs --path or node --name specified\n");
489 case ACTION_DEVICE_ID_FILE:
490 if (stat_device(name, export, export_prefix) != 0)
494 printf("%s\n", udev_root);
497 fprintf(stderr, "missing option\n");
503 udev_device_cleanup(udev);