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)
58 strlcpy(filename, path, sizeof(filename));
59 strlcat(filename, "/", sizeof(filename));
60 strlcat(filename, dent->d_name, sizeof(filename));
61 if (lstat(filename, &statbuf) != 0)
63 if (S_ISLNK(statbuf.st_mode))
66 attr_value = sysfs_attr_get_value(devpath, dent->d_name);
67 if (attr_value == NULL)
69 len = strlcpy(value, attr_value, sizeof(value));
70 if(len >= sizeof(value))
71 len = sizeof(value) - 1;
72 dbg("attr '%s'='%s'(%zi)\n", dent->d_name, value, len);
74 /* remove trailing newlines */
75 while (len && value[len-1] == '\n')
78 /* skip nonprintable attributes */
79 while (len && isprint(value[len-1]))
82 dbg("attribute value of '%s' non-printable, skip\n", dent->d_name);
86 printf(" %s{%s}==\"%s\"\n", key, dent->d_name, value);
92 static int print_device_chain(const char *devpath)
94 struct sysfs_device *dev;
96 dev = sysfs_device_get(devpath);
101 "Udevinfo starts with the device specified by the devpath and then\n"
102 "walks up the chain of parent devices. It prints for every device\n"
103 "found, all possible attributes in the udev rules key format.\n"
104 "A rule to match, can be composed by the attributes of the device\n"
105 "and the attributes from one single parent device.\n"
108 printf(" looking at device '%s':\n", dev->devpath);
109 printf(" KERNEL==\"%s\"\n", dev->kernel);
110 printf(" SUBSYSTEM==\"%s\"\n", dev->subsystem);
111 printf(" DRIVER==\"%s\"\n", dev->driver);
112 print_all_attributes(dev->devpath, "ATTR");
114 /* walk up the chain of devices */
116 dev = sysfs_device_get_parent(dev);
119 printf(" looking at parent device '%s':\n", dev->devpath);
120 printf(" KERNELS==\"%s\"\n", dev->kernel);
121 printf(" SUBSYSTEMS==\"%s\"\n", dev->subsystem);
122 printf(" DRIVERS==\"%s\"\n", dev->driver);
124 print_all_attributes(dev->devpath, "ATTRS");
130 static void print_record(struct udevice *udev)
132 struct name_entry *name_loop;
134 printf("P: %s\n", udev->dev->devpath);
135 printf("N: %s\n", udev->name);
136 list_for_each_entry(name_loop, &udev->symlink_list, node)
137 printf("S: %s\n", name_loop->name);
138 if (udev->link_priority != 0)
139 printf("L: %i\n", udev->link_priority);
140 if (udev->partitions != 0)
141 printf("A:%u\n", udev->partitions);
142 if (udev->ignore_remove)
143 printf("R:%u\n", udev->ignore_remove);
144 list_for_each_entry(name_loop, &udev->env_list, node)
145 printf("E: %s\n", name_loop->name);
148 static void export_db(void) {
149 LIST_HEAD(name_list);
150 struct name_entry *name_loop;
152 udev_db_get_all_entries(&name_list);
153 list_for_each_entry(name_loop, &name_list, node) {
154 struct udevice *udev_db;
156 udev_db = udev_device_init(NULL);
159 if (udev_db_get_device(udev_db, name_loop->name) == 0)
160 print_record(udev_db);
162 udev_device_cleanup(udev_db);
164 name_list_cleanup(&name_list);
167 static int lookup_device_by_name(struct udevice *udev, const char *name)
169 LIST_HEAD(name_list);
171 struct name_entry *device;
174 count = udev_db_get_devices_by_name(name, &name_list);
178 info("found %i devices for '%s'\n", count, name);
180 /* select the device that seems to match */
181 list_for_each_entry(device, &name_list, node) {
182 char filename[PATH_SIZE];
185 udev_device_init(udev);
186 if (udev_db_get_device(udev, device->name) != 0)
188 info("found db entry '%s'\n", device->name);
190 /* make sure, we don't get a link of a differnt device */
191 strlcpy(filename, udev_root, sizeof(filename));
192 strlcat(filename, "/", sizeof(filename));
193 strlcat(filename, name, sizeof(filename));
194 if (stat(filename, &statbuf) != 0)
196 if (major(udev->devt) > 0 && udev->devt != statbuf.st_rdev) {
197 info("skip '%s', dev_t doesn't match\n", udev->name);
204 name_list_cleanup(&name_list);
208 static int stat_device(const char *name)
212 if (stat(name, &statbuf) != 0)
215 printf("%d %d\n", major(statbuf.st_dev), minor(statbuf.st_dev));
219 int udevinfo(int argc, char *argv[], char *envp[])
222 struct udevice *udev;
225 static const struct option options[] = {
226 { "name", 1, NULL, 'n' },
227 { "path", 1, NULL, 'p' },
228 { "query", 1, NULL, 'q' },
229 { "attribute-walk", 0, NULL, 'a' },
230 { "export-db", 0, NULL, 'e' },
231 { "root", 0, NULL, 'r' },
232 { "device-id-of-file", 1, NULL, 'd' },
233 { "version", 0, NULL, 1 }, /* -V outputs braindead format */
234 { "help", 0, NULL, 'h' },
241 ACTION_ATTRIBUTE_WALK,
243 ACTION_DEVICE_ID_FILE,
244 } action = ACTION_NONE;
253 } query = QUERY_NONE;
255 char path[PATH_SIZE] = "";
256 char name[PATH_SIZE] = "";
257 struct name_entry *name_loop;
260 logging_init("udevinfo");
264 udev = udev_device_init(NULL);
271 option = getopt_long(argc, argv, "aed:n:p:q:rVh", options, NULL);
275 dbg("option '%c'\n", option);
278 /* remove /dev if given */
279 if (strncmp(optarg, udev_root, strlen(udev_root)) == 0)
280 strlcpy(name, &optarg[strlen(udev_root)+1], sizeof(name));
282 strlcpy(name, optarg, sizeof(name));
283 remove_trailing_chars(name, '/');
284 dbg("name: %s\n", name);
287 /* remove /sys if given */
288 if (strncmp(optarg, sysfs_path, strlen(sysfs_path)) == 0)
289 strlcpy(path, &optarg[strlen(sysfs_path)], sizeof(path));
291 strlcpy(path, optarg, sizeof(path));
292 remove_trailing_chars(path, '/');
294 /* possibly resolve to real devpath */
295 if (sysfs_resolve_link(path, sizeof(path)) != 0) {
296 char temp[PATH_SIZE];
299 /* also check if the parent is a link */
300 strlcpy(temp, path, sizeof(temp));
301 pos = strrchr(temp, '/');
303 char tail[PATH_SIZE];
305 strlcpy(tail, pos, sizeof(tail));
307 if (sysfs_resolve_link(temp, sizeof(temp)) == 0) {
308 strlcpy(path, temp, sizeof(path));
309 strlcat(path, tail, sizeof(path));
313 dbg("path: %s\n", path);
316 action = ACTION_QUERY;
317 if (strcmp(optarg, "name") == 0) {
321 if (strcmp(optarg, "symlink") == 0) {
322 query = QUERY_SYMLINK;
325 if (strcmp(optarg, "path") == 0) {
329 if (strcmp(optarg, "env") == 0) {
333 if (strcmp(optarg, "all") == 0) {
337 fprintf(stderr, "unknown query type\n");
341 if (action == ACTION_NONE)
342 action = ACTION_ROOT;
346 action = ACTION_DEVICE_ID_FILE;
347 strlcpy(name, optarg, sizeof(name));
350 action = ACTION_ATTRIBUTE_WALK;
356 printf("%s\n", UDEV_VERSION);
359 printf("udevinfo, version %s\n", UDEV_VERSION);
362 printf("Usage: udevadm info OPTIONS\n"
363 " --query=<type> query database for the specified value:\n"
364 " name name of device node\n"
365 " symlink pointing to node\n"
366 " path sysfs device path\n"
367 " env the device related imported environment\n"
369 " --path=<devpath> sysfs device path used for query or chain\n"
370 " --name=<name> node or symlink name used for query\n"
371 " --root prepend to query result or print udev_root\n"
372 " --attribute-walk print all key matches while walking along chain\n"
373 " of parent devices\n"
374 " --device-id-of-file=<file> print major/minor of underlying device\n"
375 " --export-db export the content of the udev database\n"
376 " --help print this text\n"
387 /* needs devpath or node/symlink name for query */
388 if (path[0] != '\0') {
389 if (udev_db_get_device(udev, path) != 0) {
390 fprintf(stderr, "no record for '%s' in database\n", path);
394 } else if (name[0] != '\0') {
395 if (lookup_device_by_name(udev, name) != 0) {
396 fprintf(stderr, "node name not found\n");
401 fprintf(stderr, "query needs --path or node --name specified\n");
409 printf("%s/%s\n", udev_root, udev->name);
411 printf("%s\n", udev->name);
414 list_for_each_entry(name_loop, &udev->symlink_list, node) {
415 char c = name_loop->node.next != &udev->symlink_list ? ' ' : '\n';
418 printf("%s/%s%c", udev_root, name_loop->name, c);
420 printf("%s%c", name_loop->name, c);
424 printf("%s\n", udev->dev->devpath);
427 list_for_each_entry(name_loop, &udev->env_list, node)
428 printf("%s\n", name_loop->name);
434 fprintf(stderr, "unknown query type\n");
438 case ACTION_ATTRIBUTE_WALK:
439 if (path[0] != '\0') {
440 if (print_device_chain(path) != 0) {
441 fprintf(stderr, "no valid sysfs device found\n");
445 } else if (name[0] != '\0') {
446 if (lookup_device_by_name(udev, name) != 0) {
447 fprintf(stderr, "node name not found\n");
451 if (print_device_chain(udev->dev->devpath) != 0) {
452 fprintf(stderr, "no valid sysfs device found\n");
457 fprintf(stderr, "attribute walk needs --path or node --name specified\n");
462 case ACTION_DEVICE_ID_FILE:
463 if (stat_device(name) != 0)
467 printf("%s\n", udev_root);
470 fprintf(stderr, "missing option\n");
476 udev_device_cleanup(udev);