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(NULL);
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 char filename[PATH_SIZE];
189 udev_device_init(udev);
190 if (udev_db_get_device(udev, device->name) != 0)
192 info("found db entry '%s'\n", device->name);
194 /* make sure, we don't get a link of a differnt device */
195 strlcpy(filename, udev_root, sizeof(filename));
196 strlcat(filename, "/", sizeof(filename));
197 strlcat(filename, name, sizeof(filename));
198 if (stat(filename, &statbuf) != 0)
200 if (major(udev->devt) > 0 && udev->devt != statbuf.st_rdev) {
201 info("skip '%s', dev_t doesn't match\n", udev->name);
208 name_list_cleanup(&name_list);
212 static int stat_device(const char *name, int export, const char *prefix)
216 if (stat(name, &statbuf) != 0)
222 printf("%sMAJOR=%d\n"
224 prefix, major(statbuf.st_dev),
225 prefix, minor(statbuf.st_dev));
227 printf("%d %d\n", major(statbuf.st_dev), minor(statbuf.st_dev));
231 int udevinfo(int argc, char *argv[], char *envp[])
234 struct udevice *udev;
237 const char *export_prefix = NULL;
239 static const struct option options[] = {
240 { "name", 1, NULL, 'n' },
241 { "path", 1, NULL, 'p' },
242 { "query", 1, NULL, 'q' },
243 { "attribute-walk", 0, NULL, 'a' },
244 { "export-db", 0, NULL, 'e' },
245 { "root", 0, NULL, 'r' },
246 { "device-id-of-file", 1, NULL, 'd' },
247 { "export", 0, NULL, 'x' },
248 { "export-prefix", 1, NULL, 'P' },
249 { "version", 0, NULL, 1 }, /* -V outputs braindead format */
250 { "help", 0, NULL, 'h' },
257 ACTION_ATTRIBUTE_WALK,
259 ACTION_DEVICE_ID_FILE,
260 } action = ACTION_NONE;
269 } query = QUERY_NONE;
271 char path[PATH_SIZE] = "";
272 char name[PATH_SIZE] = "";
273 struct name_entry *name_loop;
276 logging_init("udevinfo");
280 udev = udev_device_init(NULL);
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 if (udev_db_get_device(udev, path) != 0) {
412 fprintf(stderr, "no record for '%s' in database\n", path);
416 } else if (name[0] != '\0') {
417 if (lookup_device_by_name(udev, name) != 0) {
418 fprintf(stderr, "node name not found\n");
423 fprintf(stderr, "query needs --path or node --name specified\n");
431 printf("%s/%s\n", udev_root, udev->name);
433 printf("%s\n", udev->name);
436 list_for_each_entry(name_loop, &udev->symlink_list, node) {
437 char c = name_loop->node.next != &udev->symlink_list ? ' ' : '\n';
440 printf("%s/%s%c", udev_root, name_loop->name, c);
442 printf("%s%c", name_loop->name, c);
446 printf("%s\n", udev->dev->devpath);
449 list_for_each_entry(name_loop, &udev->env_list, node)
450 printf("%s\n", name_loop->name);
456 fprintf(stderr, "unknown query type\n");
460 case ACTION_ATTRIBUTE_WALK:
461 if (path[0] != '\0') {
462 if (print_device_chain(path) != 0) {
463 fprintf(stderr, "no valid sysfs device found\n");
467 } else if (name[0] != '\0') {
468 if (lookup_device_by_name(udev, name) != 0) {
469 fprintf(stderr, "node name not found\n");
473 if (print_device_chain(udev->dev->devpath) != 0) {
474 fprintf(stderr, "no valid sysfs device found\n");
479 fprintf(stderr, "attribute walk needs --path or node --name specified\n");
484 case ACTION_DEVICE_ID_FILE:
485 if (stat_device(name, export, export_prefix) != 0)
489 printf("%s\n", udev_root);
492 fprintf(stderr, "missing option\n");
498 udev_device_cleanup(udev);