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.
30 #include <sys/types.h>
34 static void print_all_attributes(const char *devpath, const char *key)
40 strlcpy(path, sysfs_path, sizeof(path));
41 strlcat(path, devpath, sizeof(path));
45 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
47 char filename[PATH_SIZE];
49 char value[NAME_SIZE];
52 if (dent->d_name[0] == '.')
55 if (strcmp(dent->d_name, "uevent") == 0)
57 if (strcmp(dent->d_name, "dev") == 0)
60 strlcpy(filename, path, sizeof(filename));
61 strlcat(filename, "/", sizeof(filename));
62 strlcat(filename, dent->d_name, sizeof(filename));
63 if (lstat(filename, &statbuf) != 0)
65 if (S_ISLNK(statbuf.st_mode))
68 attr_value = sysfs_attr_get_value(devpath, dent->d_name);
69 if (attr_value == NULL)
71 len = strlcpy(value, attr_value, sizeof(value));
72 if(len >= sizeof(value))
73 len = sizeof(value) - 1;
74 dbg("attr '%s'='%s'(%zi)\n", dent->d_name, value, len);
76 /* remove trailing newlines */
77 while (len && value[len-1] == '\n')
80 /* skip nonprintable attributes */
81 while (len && isprint(value[len-1]))
84 dbg("attribute value of '%s' non-printable, skip\n", dent->d_name);
88 printf(" %s{%s}==\"%s\"\n", key, dent->d_name, value);
94 static int print_device_chain(const char *devpath)
96 struct sysfs_device *dev;
98 dev = sysfs_device_get(devpath);
103 "Udevinfo starts with the device specified by the devpath and then\n"
104 "walks up the chain of parent devices. It prints for every device\n"
105 "found, all possible attributes in the udev rules key format.\n"
106 "A rule to match, can be composed by the attributes of the device\n"
107 "and the attributes from one single parent device.\n"
110 printf(" looking at device '%s':\n", dev->devpath);
111 printf(" KERNEL==\"%s\"\n", dev->kernel);
112 printf(" SUBSYSTEM==\"%s\"\n", dev->subsystem);
113 printf(" DRIVER==\"%s\"\n", dev->driver);
114 print_all_attributes(dev->devpath, "ATTR");
116 /* walk up the chain of devices */
118 dev = sysfs_device_get_parent(dev);
121 printf(" looking at parent device '%s':\n", dev->devpath);
122 printf(" KERNELS==\"%s\"\n", dev->kernel);
123 printf(" SUBSYSTEMS==\"%s\"\n", dev->subsystem);
124 printf(" DRIVERS==\"%s\"\n", dev->driver);
126 print_all_attributes(dev->devpath, "ATTRS");
132 static void print_record(struct udevice *udev)
134 struct name_entry *name_loop;
136 printf("P: %s\n", udev->dev->devpath);
137 printf("N: %s\n", udev->name);
138 list_for_each_entry(name_loop, &udev->symlink_list, node)
139 printf("S: %s\n", name_loop->name);
140 if (udev->link_priority != 0)
141 printf("L: %i\n", udev->link_priority);
142 if (udev->partitions != 0)
143 printf("A:%u\n", udev->partitions);
144 if (udev->ignore_remove)
145 printf("R:%u\n", udev->ignore_remove);
146 list_for_each_entry(name_loop, &udev->env_list, node)
147 printf("E: %s\n", name_loop->name);
150 static void export_db(void) {
151 LIST_HEAD(name_list);
152 struct name_entry *name_loop;
154 udev_db_get_all_entries(&name_list);
155 list_for_each_entry(name_loop, &name_list, node) {
156 struct udevice *udev_db;
158 udev_db = udev_device_init(NULL);
161 if (udev_db_get_device(udev_db, name_loop->name) == 0)
162 print_record(udev_db);
164 udev_device_cleanup(udev_db);
166 name_list_cleanup(&name_list);
169 static int lookup_device_by_name(struct udevice *udev, const char *name)
171 LIST_HEAD(name_list);
173 struct name_entry *device;
176 count = udev_db_get_devices_by_name(name, &name_list);
180 info("found %i devices for '%s'\n", count, name);
182 /* select the device that seems to match */
183 list_for_each_entry(device, &name_list, node) {
184 char filename[PATH_SIZE];
187 udev_device_init(udev);
188 if (udev_db_get_device(udev, device->name) != 0)
190 info("found db entry '%s'\n", device->name);
192 /* make sure, we don't get a link of a differnt device */
193 strlcpy(filename, udev_root, sizeof(filename));
194 strlcat(filename, "/", sizeof(filename));
195 strlcat(filename, name, sizeof(filename));
196 if (stat(filename, &statbuf) != 0)
198 if (major(udev->devt) > 0 && udev->devt != statbuf.st_rdev) {
199 info("skip '%s', dev_t doesn't match\n", udev->name);
206 name_list_cleanup(&name_list);
210 static int stat_device(const char *name, int export, const char *prefix)
214 if (stat(name, &statbuf) != 0)
220 printf("%sMAJOR=%d\n"
222 prefix, major(statbuf.st_dev),
223 prefix, minor(statbuf.st_dev));
225 printf("%d %d\n", major(statbuf.st_dev), minor(statbuf.st_dev));
229 int udevinfo(int argc, char *argv[], char *envp[])
232 struct udevice *udev;
235 const char *export_prefix = NULL;
237 static const struct option options[] = {
238 { "name", 1, NULL, 'n' },
239 { "path", 1, NULL, 'p' },
240 { "query", 1, NULL, 'q' },
241 { "attribute-walk", 0, NULL, 'a' },
242 { "export-db", 0, NULL, 'e' },
243 { "root", 0, NULL, 'r' },
244 { "device-id-of-file", 1, NULL, 'd' },
245 { "export", 0, NULL, 'x' },
246 { "export-prefix", 1, NULL, 'P' },
247 { "version", 0, NULL, 1 }, /* -V outputs braindead format */
248 { "help", 0, NULL, 'h' },
255 ACTION_ATTRIBUTE_WALK,
257 ACTION_DEVICE_ID_FILE,
258 } action = ACTION_NONE;
267 } query = QUERY_NONE;
269 char path[PATH_SIZE] = "";
270 char name[PATH_SIZE] = "";
271 struct name_entry *name_loop;
274 logging_init("udevinfo");
278 udev = udev_device_init(NULL);
285 option = getopt_long(argc, argv, "aed:n:p:q:rxPVh", options, NULL);
289 dbg("option '%c'\n", option);
292 /* remove /dev if given */
293 if (strncmp(optarg, udev_root, strlen(udev_root)) == 0)
294 strlcpy(name, &optarg[strlen(udev_root)+1], sizeof(name));
296 strlcpy(name, optarg, sizeof(name));
297 remove_trailing_chars(name, '/');
298 dbg("name: %s\n", name);
301 /* remove /sys if given */
302 if (strncmp(optarg, sysfs_path, strlen(sysfs_path)) == 0)
303 strlcpy(path, &optarg[strlen(sysfs_path)], sizeof(path));
305 strlcpy(path, optarg, sizeof(path));
306 remove_trailing_chars(path, '/');
308 /* possibly resolve to real devpath */
309 if (sysfs_resolve_link(path, sizeof(path)) != 0) {
310 char temp[PATH_SIZE];
313 /* also check if the parent is a link */
314 strlcpy(temp, path, sizeof(temp));
315 pos = strrchr(temp, '/');
317 char tail[PATH_SIZE];
319 strlcpy(tail, pos, sizeof(tail));
321 if (sysfs_resolve_link(temp, sizeof(temp)) == 0) {
322 strlcpy(path, temp, sizeof(path));
323 strlcat(path, tail, sizeof(path));
327 dbg("path: %s\n", path);
330 action = ACTION_QUERY;
331 if (strcmp(optarg, "name") == 0) {
335 if (strcmp(optarg, "symlink") == 0) {
336 query = QUERY_SYMLINK;
339 if (strcmp(optarg, "path") == 0) {
343 if (strcmp(optarg, "env") == 0) {
347 if (strcmp(optarg, "all") == 0) {
351 fprintf(stderr, "unknown query type\n");
355 if (action == ACTION_NONE)
356 action = ACTION_ROOT;
360 action = ACTION_DEVICE_ID_FILE;
361 strlcpy(name, optarg, sizeof(name));
364 action = ACTION_ATTRIBUTE_WALK;
373 export_prefix = optarg;
376 printf("%s\n", UDEV_VERSION);
379 printf("udevinfo, version %s\n", UDEV_VERSION);
382 printf("Usage: udevadm info OPTIONS\n"
383 " --query=<type> query database for the specified value:\n"
384 " name name of device node\n"
385 " symlink pointing to node\n"
386 " path sysfs device path\n"
387 " env the device related imported environment\n"
389 " --path=<devpath> sysfs device path used for query or chain\n"
390 " --name=<name> node or symlink name used for query\n"
391 " --root prepend to query result or print udev_root\n"
392 " --attribute-walk print all key matches while walking along chain\n"
393 " of parent devices\n"
394 " --device-id-of-file=<file> print major/minor of underlying device\n"
395 " --export-db export the content of the udev database\n"
396 " --help print this text\n"
407 /* needs devpath or node/symlink name for query */
408 if (path[0] != '\0') {
409 if (udev_db_get_device(udev, path) != 0) {
410 fprintf(stderr, "no record for '%s' in database\n", path);
414 } else if (name[0] != '\0') {
415 if (lookup_device_by_name(udev, name) != 0) {
416 fprintf(stderr, "node name not found\n");
421 fprintf(stderr, "query needs --path or node --name specified\n");
429 printf("%s/%s\n", udev_root, udev->name);
431 printf("%s\n", udev->name);
434 list_for_each_entry(name_loop, &udev->symlink_list, node) {
435 char c = name_loop->node.next != &udev->symlink_list ? ' ' : '\n';
438 printf("%s/%s%c", udev_root, name_loop->name, c);
440 printf("%s%c", name_loop->name, c);
444 printf("%s\n", udev->dev->devpath);
447 list_for_each_entry(name_loop, &udev->env_list, node)
448 printf("%s\n", name_loop->name);
454 fprintf(stderr, "unknown query type\n");
458 case ACTION_ATTRIBUTE_WALK:
459 if (path[0] != '\0') {
460 if (print_device_chain(path) != 0) {
461 fprintf(stderr, "no valid sysfs device found\n");
465 } else if (name[0] != '\0') {
466 if (lookup_device_by_name(udev, name) != 0) {
467 fprintf(stderr, "node name not found\n");
471 if (print_device_chain(udev->dev->devpath) != 0) {
472 fprintf(stderr, "no valid sysfs device found\n");
477 fprintf(stderr, "attribute walk needs --path or node --name specified\n");
482 case ACTION_DEVICE_ID_FILE:
483 if (stat_device(name, export, export_prefix) != 0)
487 printf("%s\n", udev_root);
490 fprintf(stderr, "missing option\n");
496 udev_device_cleanup(udev);