chiark / gitweb /
3fc40faf143231952f4ae8ac4d57d9346660e542
[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)", 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", dent->d_name);
83                                 continue;
84                         }
85
86                         replace_chars(value, ALLOWED_CHARS_INPUT);
87                         printf("    %s{%s}==\"%s\"\n", key, dent->d_name, value);
88                 }
89         }
90         printf("\n");
91 }
92
93 static int print_device_chain(const char *devpath)
94 {
95         struct sysfs_device *dev;
96
97         dev = sysfs_device_get(devpath);
98         if (dev == NULL)
99                 return -1;
100
101         printf("\n"
102                "Udevinfo starts with the device specified by the devpath and then\n"
103                "walks up the chain of parent devices. It prints for every device\n"
104                "found, all possible attributes in the udev rules key format.\n"
105                "A rule to match, can be composed by the attributes of the device\n"
106                "and the attributes from one single parent device.\n"
107                "\n");
108
109         printf("  looking at device '%s':\n", dev->devpath);
110         printf("    KERNEL==\"%s\"\n", dev->kernel);
111         printf("    SUBSYSTEM==\"%s\"\n", dev->subsystem);
112         printf("    DRIVER==\"%s\"\n", dev->driver);
113         print_all_attributes(dev->devpath, "ATTR");
114
115         /* walk up the chain of devices */
116         while (1) {
117                 dev = sysfs_device_get_parent(dev);
118                 if (dev == NULL)
119                         break;
120                 printf("  looking at parent device '%s':\n", dev->devpath);
121                 printf("    KERNELS==\"%s\"\n", dev->kernel);
122                 printf("    SUBSYSTEMS==\"%s\"\n", dev->subsystem);
123                 printf("    DRIVERS==\"%s\"\n", dev->driver);
124
125                 print_all_attributes(dev->devpath, "ATTRS");
126         }
127
128         return 0;
129 }
130
131 static void print_record(struct udevice *udev)
132 {
133         struct name_entry *name_loop;
134
135         printf("P: %s\n", udev->dev->devpath);
136         printf("N: %s\n", udev->name);
137         list_for_each_entry(name_loop, &udev->symlink_list, node)
138                 printf("S: %s\n", name_loop->name);
139         if (udev->link_priority != 0)
140                 printf("L: %i\n", udev->link_priority);
141         if (udev->partitions != 0)
142                 printf("A:%u\n", udev->partitions);
143         if (udev->ignore_remove)
144                 printf("R:%u\n", udev->ignore_remove);
145         list_for_each_entry(name_loop, &udev->env_list, node)
146                 printf("E: %s\n", name_loop->name);
147 }
148
149 static void export_db(void) {
150         LIST_HEAD(name_list);
151         struct name_entry *name_loop;
152
153         udev_db_get_all_entries(&name_list);
154         list_for_each_entry(name_loop, &name_list, node) {
155                 struct udevice *udev_db;
156
157                 udev_db = udev_device_init(NULL);
158                 if (udev_db == NULL)
159                         continue;
160                 if (udev_db_get_device(udev_db, name_loop->name) == 0)
161                         print_record(udev_db);
162                         printf("\n");
163                 udev_device_cleanup(udev_db);
164         }
165         name_list_cleanup(&name_list);
166 }
167
168 static int lookup_device_by_name(struct udevice *udev, const char *name)
169 {
170         LIST_HEAD(name_list);
171         int count;
172         struct name_entry *device;
173         int rc  = -1;
174
175         count = udev_db_get_devices_by_name(name, &name_list);
176         if (count <= 0)
177                 goto out;
178
179         info("found %i devices for '%s'", count, name);
180
181         /* select the device that seems to match */
182         list_for_each_entry(device, &name_list, node) {
183                 char filename[PATH_SIZE];
184                 struct stat statbuf;
185
186                 udev_device_init(udev);
187                 if (udev_db_get_device(udev, device->name) != 0)
188                         continue;
189                 info("found db entry '%s'", device->name);
190
191                 /* make sure, we don't get a link of a differnt device */
192                 strlcpy(filename, udev_root, sizeof(filename));
193                 strlcat(filename, "/", sizeof(filename));
194                 strlcat(filename, name, sizeof(filename));
195                 if (stat(filename, &statbuf) != 0)
196                         continue;
197                 if (major(udev->devt) > 0 && udev->devt != statbuf.st_rdev) {
198                         info("skip '%s', dev_t doesn't match", udev->name);
199                         continue;
200                 }
201                 rc = 0;
202                 break;
203         }
204 out:
205         name_list_cleanup(&name_list);
206         return rc;
207 }
208
209 static int stat_device(const char *name)
210 {
211         struct stat statbuf;
212
213         if (stat(name, &statbuf) != 0)
214                 return -1;
215
216         if (major(statbuf.st_dev) == 0)
217                 return -1;
218
219         printf("%d %d\n", major(statbuf.st_dev), minor(statbuf.st_dev));
220         return 0;
221 }
222
223 int udevinfo(int argc, char *argv[], char *envp[])
224 {
225         int option;
226         struct udevice *udev;
227         int root = 0;
228
229         static const struct option options[] = {
230                 { "name", 1, NULL, 'n' },
231                 { "path", 1, NULL, 'p' },
232                 { "query", 1, NULL, 'q' },
233                 { "attribute-walk", 0, NULL, 'a' },
234                 { "export-db", 0, NULL, 'e' },
235                 { "root", 0, NULL, 'r' },
236                 { "device-id-of-file", 1, NULL, 'd' },
237                 { "version", 0, NULL, 1 }, /* -V outputs braindead format */
238                 { "help", 0, NULL, 'h' },
239                 {}
240         };
241
242         enum action_type {
243                 ACTION_NONE,
244                 ACTION_QUERY,
245                 ACTION_ATTRIBUTE_WALK,
246                 ACTION_ROOT,
247                 ACTION_DEVICE_ID_FILE,
248         } action = ACTION_NONE;
249
250         enum query_type {
251                 QUERY_NONE,
252                 QUERY_NAME,
253                 QUERY_PATH,
254                 QUERY_SYMLINK,
255                 QUERY_ENV,
256                 QUERY_ALL,
257         } query = QUERY_NONE;
258
259         char path[PATH_SIZE] = "";
260         char name[PATH_SIZE] = "";
261         struct name_entry *name_loop;
262         int rc = 0;
263
264         logging_init("udevinfo");
265         udev_config_init();
266         sysfs_init();
267
268         udev = udev_device_init(NULL);
269         if (udev == NULL) {
270                 rc = 1;
271                 goto exit;
272         }
273
274         while (1) {
275                 option = getopt_long(argc, argv, "aed:n:p:q:rVh", options, NULL);
276                 if (option == -1)
277                         break;
278
279                 dbg("option '%c'", option);
280                 switch (option) {
281                 case 'n':
282                         /* remove /dev if given */
283                         if (strncmp(optarg, udev_root, strlen(udev_root)) == 0)
284                                 strlcpy(name, &optarg[strlen(udev_root)+1], sizeof(name));
285                         else
286                                 strlcpy(name, optarg, sizeof(name));
287                         dbg("name: %s", name);
288                         break;
289                 case 'p':
290                         /* remove /sys if given */
291                         if (strncmp(optarg, sysfs_path, strlen(sysfs_path)) == 0)
292                                 strlcpy(path, &optarg[strlen(sysfs_path)], sizeof(path));
293                         else
294                                 strlcpy(path, optarg, sizeof(path));
295                         dbg("path: %s", path);
296                         break;
297                 case 'q':
298                         action = ACTION_QUERY;
299                         if (strcmp(optarg, "name") == 0) {
300                                 query = QUERY_NAME;
301                                 break;
302                         }
303                         if (strcmp(optarg, "symlink") == 0) {
304                                 query = QUERY_SYMLINK;
305                                 break;
306                         }
307                         if (strcmp(optarg, "path") == 0) {
308                                 query = QUERY_PATH;
309                                 break;
310                         }
311                         if (strcmp(optarg, "env") == 0) {
312                                 query = QUERY_ENV;
313                                 break;
314                         }
315                         if (strcmp(optarg, "all") == 0) {
316                                 query = QUERY_ALL;
317                                 break;
318                         }
319                         fprintf(stderr, "unknown query type\n");
320                         rc = 2;
321                         goto exit;
322                 case 'r':
323                         if (action == ACTION_NONE)
324                                 action = ACTION_ROOT;
325                         root = 1;
326                         break;
327                 case 'd':
328                         action = ACTION_DEVICE_ID_FILE;
329                         strlcpy(name, optarg, sizeof(name));
330                         break;
331                 case 'a':
332                         action = ACTION_ATTRIBUTE_WALK;
333                         break;
334                 case 'e':
335                         export_db();
336                         goto exit;
337                 case 1:
338                         printf("%s\n", UDEV_VERSION);
339                         goto exit;
340                 case 'V':
341                         printf("udevinfo, version %s\n", UDEV_VERSION);
342                         goto exit;
343                 case 'h':
344                         printf("Usage: udevadm info OPTIONS\n"
345                                "  --query=<type>             query database for the specified value:\n"
346                                "      name                     name of device node\n"
347                                "      symlink                  pointing to node\n"
348                                "      path                     sysfs device path\n"
349                                "      env                      the device related imported environment\n"
350                                "      all                      all values\n"
351                                "  --path=<devpath>           sysfs device path used for query or chain\n"
352                                "  --name=<name>              node or symlink name used for query\n"
353                                "  --root                     prepend to query result or print udev_root\n"
354                                "  --attribute-walk           print all key matches while walking along chain\n"
355                                "                             of parent devices\n"
356                                "  --device-id-of-file=<file> print major/minor of underlying device\n"
357                                "  --export-db                export the content of the udev database\n"
358                                "  --help                     print this text\n"
359                                "\n");
360                         goto exit;
361                 default:
362                         goto exit;
363                 }
364         }
365
366         /* run action */
367         switch (action) {
368         case ACTION_QUERY:
369                 /* needs devpath or node/symlink name for query */
370                 if (path[0] != '\0') {
371                         if (udev_db_get_device(udev, path) != 0) {
372                                 fprintf(stderr, "no record for '%s' in database\n", path);
373                                 rc = 3;
374                                 goto exit;
375                         }
376                 } else if (name[0] != '\0') {
377                         if (lookup_device_by_name(udev, name) != 0) {
378                                 fprintf(stderr, "node name not found\n");
379                                 rc = 4;
380                                 goto exit;
381                         }
382                 } else {
383                         fprintf(stderr, "query needs --path or node --name specified\n");
384                         rc = 4;
385                         goto exit;
386                 }
387
388                 switch(query) {
389                 case QUERY_NAME:
390                         if (root)
391                                 printf("%s/%s\n", udev_root, udev->name);
392                         else
393                                 printf("%s\n", udev->name);
394                         break;
395                 case QUERY_SYMLINK:
396                         list_for_each_entry(name_loop, &udev->symlink_list, node) {
397                                 char c = name_loop->node.next != &udev->symlink_list ? ' ' : '\n';
398
399                                 if (root)
400                                         printf("%s/%s%c", udev_root, name_loop->name, c);
401                                 else
402                                         printf("%s%c", name_loop->name, c);
403                         }
404                         break;
405                 case QUERY_PATH:
406                         printf("%s\n", udev->dev->devpath);
407                         goto exit;
408                 case QUERY_ENV:
409                         list_for_each_entry(name_loop, &udev->env_list, node)
410                                 printf("%s\n", name_loop->name);
411                         break;
412                 case QUERY_ALL:
413                         print_record(udev);
414                         break;
415                 default:
416                         fprintf(stderr, "unknown query type\n");
417                         break;
418                 }
419                 break;
420         case ACTION_ATTRIBUTE_WALK:
421                 if (path[0] != '\0') {
422                         if (print_device_chain(path) != 0) {
423                                 fprintf(stderr, "no valid sysfs device found\n");
424                                 rc = 4;
425                                 goto exit;
426                         }
427                 } else if (name[0] != '\0') {
428                         if (lookup_device_by_name(udev, name) != 0) {
429                                 fprintf(stderr, "node name not found\n");
430                                 rc = 4;
431                                 goto exit;
432                         }
433                         if (print_device_chain(udev->dev->devpath) != 0) {
434                                 fprintf(stderr, "no valid sysfs device found\n");
435                                 rc = 4;
436                                 goto exit;
437                         }
438                 } else {
439                         fprintf(stderr, "attribute walk needs --path or node --name specified\n");
440                         rc = 5;
441                         goto exit;
442                 }
443                 break;
444         case ACTION_DEVICE_ID_FILE:
445                 if (stat_device(name) != 0)
446                         rc = 6;
447                 break;
448         case ACTION_ROOT:
449                 printf("%s\n", udev_root);
450                 break;
451         default:
452                 fprintf(stderr, "missing option\n");
453                 rc = 1;
454                 break;
455         }
456
457 exit:
458         udev_device_cleanup(udev);
459         sysfs_cleanup();
460         logging_close();
461         return rc;
462 }