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)
214 if (stat(name, &statbuf) != 0)
217 printf("%d %d\n", major(statbuf.st_dev), minor(statbuf.st_dev));
221 int udevinfo(int argc, char *argv[], char *envp[])
224 struct udevice *udev;
227 static const struct option options[] = {
228 { "name", 1, NULL, 'n' },
229 { "path", 1, NULL, 'p' },
230 { "query", 1, NULL, 'q' },
231 { "attribute-walk", 0, NULL, 'a' },
232 { "export-db", 0, NULL, 'e' },
233 { "root", 0, NULL, 'r' },
234 { "device-id-of-file", 1, NULL, 'd' },
235 { "version", 0, NULL, 1 }, /* -V outputs braindead format */
236 { "help", 0, NULL, 'h' },
243 ACTION_ATTRIBUTE_WALK,
245 ACTION_DEVICE_ID_FILE,
246 } action = ACTION_NONE;
255 } query = QUERY_NONE;
257 char path[PATH_SIZE] = "";
258 char name[PATH_SIZE] = "";
259 struct name_entry *name_loop;
262 logging_init("udevinfo");
266 udev = udev_device_init(NULL);
273 option = getopt_long(argc, argv, "aed:n:p:q:rVh", options, NULL);
277 dbg("option '%c'\n", option);
280 /* remove /dev if given */
281 if (strncmp(optarg, udev_root, strlen(udev_root)) == 0)
282 strlcpy(name, &optarg[strlen(udev_root)+1], sizeof(name));
284 strlcpy(name, optarg, sizeof(name));
285 remove_trailing_chars(name, '/');
286 dbg("name: %s\n", name);
289 /* remove /sys if given */
290 if (strncmp(optarg, sysfs_path, strlen(sysfs_path)) == 0)
291 strlcpy(path, &optarg[strlen(sysfs_path)], sizeof(path));
293 strlcpy(path, optarg, sizeof(path));
294 remove_trailing_chars(path, '/');
296 /* possibly resolve to real devpath */
297 if (sysfs_resolve_link(path, sizeof(path)) != 0) {
298 char temp[PATH_SIZE];
301 /* also check if the parent is a link */
302 strlcpy(temp, path, sizeof(temp));
303 pos = strrchr(temp, '/');
305 char tail[PATH_SIZE];
307 strlcpy(tail, pos, sizeof(tail));
309 if (sysfs_resolve_link(temp, sizeof(temp)) == 0) {
310 strlcpy(path, temp, sizeof(path));
311 strlcat(path, tail, sizeof(path));
315 dbg("path: %s\n", path);
318 action = ACTION_QUERY;
319 if (strcmp(optarg, "name") == 0) {
323 if (strcmp(optarg, "symlink") == 0) {
324 query = QUERY_SYMLINK;
327 if (strcmp(optarg, "path") == 0) {
331 if (strcmp(optarg, "env") == 0) {
335 if (strcmp(optarg, "all") == 0) {
339 fprintf(stderr, "unknown query type\n");
343 if (action == ACTION_NONE)
344 action = ACTION_ROOT;
348 action = ACTION_DEVICE_ID_FILE;
349 strlcpy(name, optarg, sizeof(name));
352 action = ACTION_ATTRIBUTE_WALK;
358 printf("%s\n", UDEV_VERSION);
361 printf("udevinfo, version %s\n", UDEV_VERSION);
364 printf("Usage: udevadm info OPTIONS\n"
365 " --query=<type> query database for the specified value:\n"
366 " name name of device node\n"
367 " symlink pointing to node\n"
368 " path sysfs device path\n"
369 " env the device related imported environment\n"
371 " --path=<devpath> sysfs device path used for query or chain\n"
372 " --name=<name> node or symlink name used for query\n"
373 " --root prepend to query result or print udev_root\n"
374 " --attribute-walk print all key matches while walking along chain\n"
375 " of parent devices\n"
376 " --device-id-of-file=<file> print major/minor of underlying device\n"
377 " --export-db export the content of the udev database\n"
378 " --help print this text\n"
389 /* needs devpath or node/symlink name for query */
390 if (path[0] != '\0') {
391 if (udev_db_get_device(udev, path) != 0) {
392 fprintf(stderr, "no record for '%s' in database\n", path);
396 } else if (name[0] != '\0') {
397 if (lookup_device_by_name(udev, name) != 0) {
398 fprintf(stderr, "node name not found\n");
403 fprintf(stderr, "query needs --path or node --name specified\n");
411 printf("%s/%s\n", udev_root, udev->name);
413 printf("%s\n", udev->name);
416 list_for_each_entry(name_loop, &udev->symlink_list, node) {
417 char c = name_loop->node.next != &udev->symlink_list ? ' ' : '\n';
420 printf("%s/%s%c", udev_root, name_loop->name, c);
422 printf("%s%c", name_loop->name, c);
426 printf("%s\n", udev->dev->devpath);
429 list_for_each_entry(name_loop, &udev->env_list, node)
430 printf("%s\n", name_loop->name);
436 fprintf(stderr, "unknown query type\n");
440 case ACTION_ATTRIBUTE_WALK:
441 if (path[0] != '\0') {
442 if (print_device_chain(path) != 0) {
443 fprintf(stderr, "no valid sysfs device found\n");
447 } else if (name[0] != '\0') {
448 if (lookup_device_by_name(udev, name) != 0) {
449 fprintf(stderr, "node name not found\n");
453 if (print_device_chain(udev->dev->devpath) != 0) {
454 fprintf(stderr, "no valid sysfs device found\n");
459 fprintf(stderr, "attribute walk needs --path or node --name specified\n");
464 case ACTION_DEVICE_ID_FILE:
465 if (stat_device(name) != 0)
469 printf("%s\n", udev_root);
472 fprintf(stderr, "missing option\n");
478 udev_device_cleanup(udev);