chiark / gitweb /
a0d6cac401629d76fee701b4f6aa9f9486c8e3a6
[elogind.git] / udevinfo.c
1 /*
2  * Copyright (C) 2004-2006 Kay Sievers <kay.sievers@vrfy.org>
3  *
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.
7  * 
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.
12  * 
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.
16  *
17  */
18
19 #include <stdlib.h>
20 #include <string.h>
21 #include <stdio.h>
22 #include <stddef.h>
23 #include <ctype.h>
24 #include <stdarg.h>
25 #include <unistd.h>
26 #include <dirent.h>
27 #include <errno.h>
28 #include <getopt.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31
32 #include "udev.h"
33
34 static void print_all_attributes(const char *devpath, const char *key)
35 {
36         char path[PATH_SIZE];
37         DIR *dir;
38         struct dirent *dent;
39
40         strlcpy(path, sysfs_path, sizeof(path));
41         strlcat(path, devpath, sizeof(path));
42
43         dir = opendir(path);
44         if (dir != NULL) {
45                 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
46                         struct stat statbuf;
47                         char filename[PATH_SIZE];
48                         char *attr_value;
49                         char value[NAME_SIZE];
50                         size_t len;
51
52                         if (dent->d_name[0] == '.')
53                                 continue;
54
55                         if (strcmp(dent->d_name, "uevent") == 0)
56                                 continue;
57
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)
62                                 continue;
63                         if (S_ISLNK(statbuf.st_mode))
64                                 continue;
65
66                         attr_value = sysfs_attr_get_value(devpath, dent->d_name);
67                         if (attr_value == NULL)
68                                 continue;
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);
73
74                         /* remove trailing newlines */
75                         while (len && value[len-1] == '\n')
76                                 value[--len] = '\0';
77
78                         /* skip nonprintable attributes */
79                         while (len && isprint(value[len-1]))
80                                 len--;
81                         if (len) {
82                                 dbg("attribute value of '%s' non-printable, skip\n", dent->d_name);
83                                 continue;
84                         }
85
86                         printf("    %s{%s}==\"%s\"\n", key, dent->d_name, value);
87                 }
88         }
89         printf("\n");
90 }
91
92 static int print_device_chain(const char *devpath)
93 {
94         struct sysfs_device *dev;
95
96         dev = sysfs_device_get(devpath);
97         if (dev == NULL)
98                 return -1;
99
100         printf("\n"
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"
106                "\n");
107
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");
113
114         /* walk up the chain of devices */
115         while (1) {
116                 dev = sysfs_device_get_parent(dev);
117                 if (dev == NULL)
118                         break;
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);
123
124                 print_all_attributes(dev->devpath, "ATTRS");
125         }
126
127         return 0;
128 }
129
130 static void print_record(struct udevice *udev)
131 {
132         struct name_entry *name_loop;
133
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);
146 }
147
148 static void export_db(void) {
149         LIST_HEAD(name_list);
150         struct name_entry *name_loop;
151
152         udev_db_get_all_entries(&name_list);
153         list_for_each_entry(name_loop, &name_list, node) {
154                 struct udevice *udev_db;
155
156                 udev_db = udev_device_init(NULL);
157                 if (udev_db == NULL)
158                         continue;
159                 if (udev_db_get_device(udev_db, name_loop->name) == 0)
160                         print_record(udev_db);
161                         printf("\n");
162                 udev_device_cleanup(udev_db);
163         }
164         name_list_cleanup(&name_list);
165 }
166
167 static int lookup_device_by_name(struct udevice *udev, const char *name)
168 {
169         LIST_HEAD(name_list);
170         int count;
171         struct name_entry *device;
172         int rc  = -1;
173
174         count = udev_db_get_devices_by_name(name, &name_list);
175         if (count <= 0)
176                 goto out;
177
178         info("found %i devices for '%s'\n", count, name);
179
180         /* select the device that seems to match */
181         list_for_each_entry(device, &name_list, node) {
182                 char filename[PATH_SIZE];
183                 struct stat statbuf;
184
185                 udev_device_init(udev);
186                 if (udev_db_get_device(udev, device->name) != 0)
187                         continue;
188                 info("found db entry '%s'\n", device->name);
189
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)
195                         continue;
196                 if (major(udev->devt) > 0 && udev->devt != statbuf.st_rdev) {
197                         info("skip '%s', dev_t doesn't match\n", udev->name);
198                         continue;
199                 }
200                 rc = 0;
201                 break;
202         }
203 out:
204         name_list_cleanup(&name_list);
205         return rc;
206 }
207
208 static int stat_device(const char *name)
209 {
210         struct stat statbuf;
211
212         if (stat(name, &statbuf) != 0)
213                 return -1;
214
215         printf("%d %d\n", major(statbuf.st_dev), minor(statbuf.st_dev));
216         return 0;
217 }
218
219 int udevinfo(int argc, char *argv[], char *envp[])
220 {
221         int option;
222         struct udevice *udev;
223         int root = 0;
224
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' },
235                 {}
236         };
237
238         enum action_type {
239                 ACTION_NONE,
240                 ACTION_QUERY,
241                 ACTION_ATTRIBUTE_WALK,
242                 ACTION_ROOT,
243                 ACTION_DEVICE_ID_FILE,
244         } action = ACTION_NONE;
245
246         enum query_type {
247                 QUERY_NONE,
248                 QUERY_NAME,
249                 QUERY_PATH,
250                 QUERY_SYMLINK,
251                 QUERY_ENV,
252                 QUERY_ALL,
253         } query = QUERY_NONE;
254
255         char path[PATH_SIZE] = "";
256         char name[PATH_SIZE] = "";
257         struct name_entry *name_loop;
258         int rc = 0;
259
260         logging_init("udevinfo");
261         udev_config_init();
262         sysfs_init();
263
264         udev = udev_device_init(NULL);
265         if (udev == NULL) {
266                 rc = 1;
267                 goto exit;
268         }
269
270         while (1) {
271                 option = getopt_long(argc, argv, "aed:n:p:q:rVh", options, NULL);
272                 if (option == -1)
273                         break;
274
275                 dbg("option '%c'\n", option);
276                 switch (option) {
277                 case 'n':
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));
281                         else
282                                 strlcpy(name, optarg, sizeof(name));
283                         remove_trailing_chars(name, '/');
284                         dbg("name: %s\n", name);
285                         break;
286                 case 'p':
287                         /* remove /sys if given */
288                         if (strncmp(optarg, sysfs_path, strlen(sysfs_path)) == 0)
289                                 strlcpy(path, &optarg[strlen(sysfs_path)], sizeof(path));
290                         else
291                                 strlcpy(path, optarg, sizeof(path));
292                         remove_trailing_chars(path, '/');
293
294                         /* possibly resolve to real devpath */
295                         if (sysfs_resolve_link(path, sizeof(path)) != 0) {
296                                 char temp[PATH_SIZE];
297                                 char *pos;
298
299                                 /* also check if the parent is a link */
300                                 strlcpy(temp, path, sizeof(temp));
301                                 pos = strrchr(temp, '/');
302                                 if (pos != 0) {
303                                         char tail[PATH_SIZE];
304
305                                         strlcpy(tail, pos, sizeof(tail));
306                                         pos[0] = '\0';
307                                         if (sysfs_resolve_link(temp, sizeof(temp)) == 0) {
308                                                 strlcpy(path, temp, sizeof(path));
309                                                 strlcat(path, tail, sizeof(path));
310                                         }
311                                 }
312                         }
313                         dbg("path: %s\n", path);
314                         break;
315                 case 'q':
316                         action = ACTION_QUERY;
317                         if (strcmp(optarg, "name") == 0) {
318                                 query = QUERY_NAME;
319                                 break;
320                         }
321                         if (strcmp(optarg, "symlink") == 0) {
322                                 query = QUERY_SYMLINK;
323                                 break;
324                         }
325                         if (strcmp(optarg, "path") == 0) {
326                                 query = QUERY_PATH;
327                                 break;
328                         }
329                         if (strcmp(optarg, "env") == 0) {
330                                 query = QUERY_ENV;
331                                 break;
332                         }
333                         if (strcmp(optarg, "all") == 0) {
334                                 query = QUERY_ALL;
335                                 break;
336                         }
337                         fprintf(stderr, "unknown query type\n");
338                         rc = 2;
339                         goto exit;
340                 case 'r':
341                         if (action == ACTION_NONE)
342                                 action = ACTION_ROOT;
343                         root = 1;
344                         break;
345                 case 'd':
346                         action = ACTION_DEVICE_ID_FILE;
347                         strlcpy(name, optarg, sizeof(name));
348                         break;
349                 case 'a':
350                         action = ACTION_ATTRIBUTE_WALK;
351                         break;
352                 case 'e':
353                         export_db();
354                         goto exit;
355                 case 1:
356                         printf("%s\n", UDEV_VERSION);
357                         goto exit;
358                 case 'V':
359                         printf("udevinfo, version %s\n", UDEV_VERSION);
360                         goto exit;
361                 case 'h':
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"
368                                "      all                      all values\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"
377                                "\n");
378                         goto exit;
379                 default:
380                         goto exit;
381                 }
382         }
383
384         /* run action */
385         switch (action) {
386         case ACTION_QUERY:
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);
391                                 rc = 3;
392                                 goto exit;
393                         }
394                 } else if (name[0] != '\0') {
395                         if (lookup_device_by_name(udev, name) != 0) {
396                                 fprintf(stderr, "node name not found\n");
397                                 rc = 4;
398                                 goto exit;
399                         }
400                 } else {
401                         fprintf(stderr, "query needs --path or node --name specified\n");
402                         rc = 4;
403                         goto exit;
404                 }
405
406                 switch(query) {
407                 case QUERY_NAME:
408                         if (root)
409                                 printf("%s/%s\n", udev_root, udev->name);
410                         else
411                                 printf("%s\n", udev->name);
412                         break;
413                 case QUERY_SYMLINK:
414                         list_for_each_entry(name_loop, &udev->symlink_list, node) {
415                                 char c = name_loop->node.next != &udev->symlink_list ? ' ' : '\n';
416
417                                 if (root)
418                                         printf("%s/%s%c", udev_root, name_loop->name, c);
419                                 else
420                                         printf("%s%c", name_loop->name, c);
421                         }
422                         break;
423                 case QUERY_PATH:
424                         printf("%s\n", udev->dev->devpath);
425                         goto exit;
426                 case QUERY_ENV:
427                         list_for_each_entry(name_loop, &udev->env_list, node)
428                                 printf("%s\n", name_loop->name);
429                         break;
430                 case QUERY_ALL:
431                         print_record(udev);
432                         break;
433                 default:
434                         fprintf(stderr, "unknown query type\n");
435                         break;
436                 }
437                 break;
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");
442                                 rc = 4;
443                                 goto exit;
444                         }
445                 } else if (name[0] != '\0') {
446                         if (lookup_device_by_name(udev, name) != 0) {
447                                 fprintf(stderr, "node name not found\n");
448                                 rc = 4;
449                                 goto exit;
450                         }
451                         if (print_device_chain(udev->dev->devpath) != 0) {
452                                 fprintf(stderr, "no valid sysfs device found\n");
453                                 rc = 4;
454                                 goto exit;
455                         }
456                 } else {
457                         fprintf(stderr, "attribute walk needs --path or node --name specified\n");
458                         rc = 5;
459                         goto exit;
460                 }
461                 break;
462         case ACTION_DEVICE_ID_FILE:
463                 if (stat_device(name) != 0)
464                         rc = 6;
465                 break;
466         case ACTION_ROOT:
467                 printf("%s\n", udev_root);
468                 break;
469         default:
470                 fprintf(stderr, "missing option\n");
471                 rc = 1;
472                 break;
473         }
474
475 exit:
476         udev_device_cleanup(udev);
477         sysfs_cleanup();
478         logging_close();
479         return rc;
480 }