chiark / gitweb /
fc45f7565d192e778173b15bc9d293c69ce62dd8
[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 int udevinfo(int argc, char *argv[], char *envp[])
210 {
211         int option;
212         struct udevice *udev;
213         int root = 0;
214
215         static const struct option options[] = {
216                 { "name", 1, NULL, 'n' },
217                 { "path", 1, NULL, 'p' },
218                 { "query", 1, NULL, 'q' },
219                 { "attribute-walk", 0, NULL, 'a' },
220                 { "export-db", 0, NULL, 'e' },
221                 { "root", 0, NULL, 'r' },
222                 { "version", 0, NULL, 1 }, /* -V outputs braindead format */
223                 { "help", 0, NULL, 'h' },
224                 {}
225         };
226
227         enum action_type {
228                 ACTION_NONE,
229                 ACTION_QUERY,
230                 ACTION_ATTRIBUTE_WALK,
231                 ACTION_ROOT,
232         } action = ACTION_NONE;
233
234         enum query_type {
235                 QUERY_NONE,
236                 QUERY_NAME,
237                 QUERY_PATH,
238                 QUERY_SYMLINK,
239                 QUERY_ENV,
240                 QUERY_ALL,
241         } query = QUERY_NONE;
242
243         char path[PATH_SIZE] = "";
244         char name[PATH_SIZE] = "";
245         struct name_entry *name_loop;
246         int rc = 0;
247
248         logging_init("udevinfo");
249         udev_config_init();
250         sysfs_init();
251
252         udev = udev_device_init(NULL);
253         if (udev == NULL) {
254                 rc = 1;
255                 goto exit;
256         }
257
258         while (1) {
259                 option = getopt_long(argc, argv, "aen:p:q:rVh", options, NULL);
260                 if (option == -1)
261                         break;
262
263                 dbg("option '%c'", option);
264                 switch (option) {
265                 case 'n':
266                         /* remove /dev if given */
267                         if (strncmp(optarg, udev_root, strlen(udev_root)) == 0)
268                                 strlcpy(name, &optarg[strlen(udev_root)+1], sizeof(name));
269                         else
270                                 strlcpy(name, optarg, sizeof(name));
271                         dbg("name: %s", name);
272                         break;
273                 case 'p':
274                         /* remove /sys if given */
275                         if (strncmp(optarg, sysfs_path, strlen(sysfs_path)) == 0)
276                                 strlcpy(path, &optarg[strlen(sysfs_path)], sizeof(path));
277                         else
278                                 strlcpy(path, optarg, sizeof(path));
279                         dbg("path: %s", path);
280                         break;
281                 case 'q':
282                         action = ACTION_QUERY;
283                         if (strcmp(optarg, "name") == 0) {
284                                 query = QUERY_NAME;
285                                 break;
286                         }
287                         if (strcmp(optarg, "symlink") == 0) {
288                                 query = QUERY_SYMLINK;
289                                 break;
290                         }
291                         if (strcmp(optarg, "path") == 0) {
292                                 query = QUERY_PATH;
293                                 break;
294                         }
295                         if (strcmp(optarg, "env") == 0) {
296                                 query = QUERY_ENV;
297                                 break;
298                         }
299                         if (strcmp(optarg, "all") == 0) {
300                                 query = QUERY_ALL;
301                                 break;
302                         }
303                         fprintf(stderr, "unknown query type\n");
304                         rc = 2;
305                         goto exit;
306                 case 'r':
307                         if (action == ACTION_NONE)
308                                 action = ACTION_ROOT;
309                         root = 1;
310                         break;
311                 case 'a':
312                         action = ACTION_ATTRIBUTE_WALK;
313                         break;
314                 case 'e':
315                         export_db();
316                         goto exit;
317                 case 1:
318                         printf("%s\n", UDEV_VERSION);
319                         goto exit;
320                 case 'V':
321                         printf("udevinfo, version %s\n", UDEV_VERSION);
322                         goto exit;
323                 case 'h':
324                         printf("Usage: udevadm info OPTIONS\n"
325                                "  --query=<type>    query database for the specified value:\n"
326                                "    name            name of device node\n"
327                                "    symlink         pointing to node\n"
328                                "    path            sysfs device path\n"
329                                "    env             the device related imported environment\n"
330                                "    all             all values\n"
331                                "\n"
332                                "  --path=<devpath>  sysfs device path used for query or chain\n"
333                                "  --name=<name>     node or symlink name used for query\n"
334                                "\n"
335                                "  --root            prepend to query result or print udev_root\n"
336                                "  --attribute-walk  print all SYSFS_attributes along the device chain\n"
337                                "  --export-db       export the content of the udev database\n"
338                                "  --help            print this text\n"
339                                "\n");
340                         goto exit;
341                 default:
342                         goto exit;
343                 }
344         }
345
346         /* run action */
347         switch (action) {
348         case ACTION_QUERY:
349                 /* needs devpath or node/symlink name for query */
350                 if (path[0] != '\0') {
351                         if (udev_db_get_device(udev, path) != 0) {
352                                 fprintf(stderr, "no record for '%s' in database\n", path);
353                                 rc = 3;
354                                 goto exit;
355                         }
356                 } else if (name[0] != '\0') {
357                         if (lookup_device_by_name(udev, name) != 0) {
358                                 fprintf(stderr, "node name not found\n");
359                                 rc = 4;
360                                 goto exit;
361                         }
362                 } else {
363                         fprintf(stderr, "query needs --path or node --name specified\n");
364                         rc = 4;
365                         goto exit;
366                 }
367
368                 switch(query) {
369                 case QUERY_NAME:
370                         if (root)
371                                 printf("%s/%s\n", udev_root, udev->name);
372                         else
373                                 printf("%s\n", udev->name);
374                         break;
375                 case QUERY_SYMLINK:
376                         list_for_each_entry(name_loop, &udev->symlink_list, node) {
377                                 char c = name_loop->node.next != &udev->symlink_list ? ' ' : '\n';
378
379                                 if (root)
380                                         printf("%s/%s%c", udev_root, name_loop->name, c);
381                                 else
382                                         printf("%s%c", name_loop->name, c);
383                         }
384                         break;
385                 case QUERY_PATH:
386                         printf("%s\n", udev->dev->devpath);
387                         goto exit;
388                 case QUERY_ENV:
389                         list_for_each_entry(name_loop, &udev->env_list, node)
390                                 printf("%s\n", name_loop->name);
391                         break;
392                 case QUERY_ALL:
393                         print_record(udev);
394                         break;
395                 default:
396                         fprintf(stderr, "unknown query type\n");
397                         break;
398                 }
399                 break;
400         case ACTION_ATTRIBUTE_WALK:
401                 if (path[0] != '\0') {
402                         if (print_device_chain(path) != 0) {
403                                 fprintf(stderr, "no valid sysfs device found\n");
404                                 rc = 4;
405                                 goto exit;
406                         }
407                 } else if (name[0] != '\0') {
408                         if (lookup_device_by_name(udev, name) != 0) {
409                                 fprintf(stderr, "node name not found\n");
410                                 rc = 4;
411                                 goto exit;
412                         }
413                         if (print_device_chain(udev->dev->devpath) != 0) {
414                                 fprintf(stderr, "no valid sysfs device found\n");
415                                 rc = 4;
416                                 goto exit;
417                         }
418                 } else {
419                         fprintf(stderr, "attribute walk needs --path or node --name specified\n");
420                         rc = 5;
421                         goto exit;
422                 }
423                 break;
424         case ACTION_ROOT:
425                 printf("%s\n", udev_root);
426                 break;
427         default:
428                 fprintf(stderr, "missing option\n");
429                 rc = 1;
430                 break;
431         }
432
433 exit:
434         udev_device_cleanup(udev);
435         sysfs_cleanup();
436         logging_close();
437         return rc;
438 }