chiark / gitweb /
update Gentoo rules
[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
30 #include "udev.h"
31
32
33 #ifdef USE_LOG
34 void log_message (int priority, const char *format, ...)
35 {
36         va_list args;
37
38         if (priority > udev_log_priority)
39                 return;
40
41         va_start(args, format);
42         vsyslog(priority, format, args);
43         va_end(args);
44 }
45 #endif
46
47 static void print_all_attributes(const char *devpath, const char *key)
48 {
49         char path[PATH_SIZE];
50         DIR *dir;
51         struct dirent *dent;
52
53         strlcpy(path, sysfs_path, sizeof(path));
54         strlcat(path, devpath, sizeof(path));
55
56         dir = opendir(path);
57         if (dir != NULL) {
58                 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
59                         char *attr_value;
60                         char value[NAME_SIZE];
61                         size_t len;
62
63                         attr_value = sysfs_attr_get_value(devpath, dent->d_name);
64                         if (attr_value == NULL)
65                                 continue;
66                         len = strlcpy(value, attr_value, sizeof(value));
67                         dbg("attr '%s'='%s'(%zi)", dent->d_name, value, len);
68
69                         /* remove trailing newlines */
70                         while (len && value[len-1] == '\n')
71                                 value[--len] = '\0';
72
73                         /* skip nonprintable attributes */
74                         while (len && isprint(value[len-1]))
75                                 len--;
76                         if (len) {
77                                 dbg("attribute value of '%s' non-printable, skip", dent->d_name);
78                                 continue;
79                         }
80
81                         replace_untrusted_chars(value);
82                         printf("    %s{%s}==\"%s\"\n", key, dent->d_name, value);
83                 }
84         }
85         printf("\n");
86 }
87
88 static int print_device_chain(const char *devpath)
89 {
90         struct sysfs_device *dev;
91
92         printf("\n"
93                "Udevinfo starts with the device specified by the devpath and then\n"
94                "walks up the chain of parent devices. It prints for every device\n"
95                "found, all possible attributes in the udev rules key format.\n"
96                "A rule to match, can be composed by the attributes of the device\n"
97                "and the attributes from one single parent device.\n"
98                "\n");
99
100         dev = sysfs_device_get(devpath);
101         if (dev == NULL)
102                 return -1;
103
104         printf("  looking at device '%s':\n", dev->devpath);
105         printf("    KERNEL==\"%s\"\n", dev->kernel);
106         printf("    SUBSYSTEM==\"%s\"\n", dev->subsystem);
107         printf("    DRIVER==\"%s\"\n", dev->driver);
108         print_all_attributes(dev->devpath, "ATTR");
109
110         /* walk up the chain of devices */
111         while (1) {
112                 dev = sysfs_device_get_parent(dev);
113                 if (dev == NULL)
114                         break;
115                 printf("  looking at parent device '%s':\n", dev->devpath);
116                 printf("    KERNELS==\"%s\"\n", dev->kernel);
117                 printf("    SUBSYTEMS==\"%s\"\n", dev->subsystem);
118                 printf("    DRIVERS==\"%s\"\n", dev->driver);
119
120                 print_all_attributes(dev->devpath, "ATTRS");
121         }
122
123         return 0;
124 }
125
126 static void print_record(struct udevice *udev)
127 {
128         struct name_entry *name_loop;
129
130         printf("P: %s\n", udev->dev->devpath);
131         printf("N: %s\n", udev->name);
132         list_for_each_entry(name_loop, &udev->symlink_list, node)
133                 printf("S: %s\n", name_loop->name);
134         list_for_each_entry(name_loop, &udev->env_list, node)
135                 printf("E: %s\n", name_loop->name);
136 }
137
138 static void export_name_devpath(struct udevice *udev) {
139         printf("%s=%s/%s\n", udev->dev->devpath, udev_root, udev->name);
140 }
141
142 static void export_record(struct udevice *udev) {
143         print_record(udev);
144         printf("\n");
145 }
146
147 static void export_db(void fnct(struct udevice *udev)) {
148         LIST_HEAD(name_list);
149         struct name_entry *name_loop;
150
151         udev_db_get_all_entries(&name_list);
152         list_for_each_entry(name_loop, &name_list, node) {
153                 struct udevice *udev_db;
154
155                 udev_db = udev_device_init();
156                 if (udev_db == NULL)
157                         continue;
158                 if (udev_db_get_device(udev_db, name_loop->name) == 0)
159                         fnct(udev_db);
160                 udev_device_cleanup(udev_db);
161         }
162         name_list_cleanup(&name_list);
163 }
164
165 int main(int argc, char *argv[], char *envp[])
166 {
167         int option;
168         struct udevice *udev;
169         int root = 0;
170
171         struct option options[] = {
172                 { "name", 1, NULL, 'n' },
173                 { "path", 1, NULL, 'p' },
174                 { "query", 1, NULL, 'q' },
175                 { "attribute-walk", 0, NULL, 'a' },
176                 { "export-db", 0, NULL, 'e' },
177                 { "root", 0, NULL, 'r' },
178                 { "version", 0, NULL, 'V' },
179                 { "help", 0, NULL, 'h' },
180                 {}
181         };
182
183         enum action_type {
184                 ACTION_NONE,
185                 ACTION_QUERY,
186                 ACTION_ATTRIBUTE_WALK,
187                 ACTION_ROOT,
188         } action = ACTION_NONE;
189
190         enum query_type {
191                 QUERY_NONE,
192                 QUERY_NAME,
193                 QUERY_PATH,
194                 QUERY_SYMLINK,
195                 QUERY_ENV,
196                 QUERY_ALL,
197         } query = QUERY_NONE;
198
199         char path[PATH_SIZE] = "";
200         char name[PATH_SIZE] = "";
201         struct name_entry *name_loop;
202         int rc = 0;
203
204         logging_init("udevinfo");
205         udev_config_init();
206         sysfs_init();
207
208         udev = udev_device_init();
209         if (udev == NULL) {
210                 rc = 1;
211                 goto exit;
212         }
213
214         /* get command line options */
215         while (1) {
216                 option = getopt_long(argc, argv, "aden:p:q:rVh", options, NULL);
217                 if (option == -1)
218                         break;
219
220                 dbg("option '%c'", option);
221                 switch (option) {
222                 case 'n':
223                         /* remove /dev if given */
224                         if (strncmp(optarg, udev_root, strlen(udev_root)) == 0)
225                                 strlcpy(name, &optarg[strlen(udev_root)+1], sizeof(name));
226                         else
227                                 strlcpy(name, optarg, sizeof(name));
228                         dbg("name: %s\n", name);
229                         break;
230                 case 'p':
231                         /* remove /sys if given */
232                         if (strncmp(optarg, sysfs_path, strlen(sysfs_path)) == 0)
233                                 strlcpy(path, &optarg[strlen(sysfs_path)], sizeof(path));
234                         else
235                                 strlcpy(path, optarg, sizeof(path));
236                         dbg("path: %s\n", path);
237                         break;
238                 case 'q':
239                         dbg("udev query: %s\n", optarg);
240                         action = ACTION_QUERY;
241                         if (strcmp(optarg, "name") == 0) {
242                                 query = QUERY_NAME;
243                                 break;
244                         }
245                         if (strcmp(optarg, "symlink") == 0) {
246                                 query = QUERY_SYMLINK;
247                                 break;
248                         }
249                         if (strcmp(optarg, "path") == 0) {
250                                 query = QUERY_PATH;
251                                 break;
252                         }
253                         if (strcmp(optarg, "env") == 0) {
254                                 query = QUERY_ENV;
255                                 break;
256                         }
257                         if (strcmp(optarg, "all") == 0) {
258                                 query = QUERY_ALL;
259                                 break;
260                         }
261                         fprintf(stderr, "unknown query type\n");
262                         rc = 2;
263                         goto exit;
264                 case 'r':
265                         if (action == ACTION_NONE)
266                                 action = ACTION_ROOT;
267                         root = 1;
268                         break;
269                 case 'a':
270                         action = ACTION_ATTRIBUTE_WALK;
271                         break;
272                 case 'd':
273                         export_db(export_name_devpath);
274                         goto exit;
275                 case 'e':
276                         export_db(export_record);
277                         goto exit;
278                 case 'V':
279                         printf("udevinfo, version %s\n", UDEV_VERSION);
280                         goto exit;
281                 case 'h':
282                         printf("Usage: udevinfo OPTIONS\n"
283                                "  --query=<type>    query database for the specified value:\n"
284                                "    name            name of device node\n"
285                                "    symlink         pointing to node\n"
286                                "    path            sysfs device path\n"
287                                "    env             the device related imported environment\n"
288                                "    all             all values\n"
289                                "\n"
290                                "  --path=<devpath>  sysfs device path used for query or chain\n"
291                                "  --name=<name>     node or symlink name used for query\n"
292                                "\n"
293                                "  --root            prepend to query result or print udev_root\n"
294                                "  --attribute-walk  print all SYSFS_attributes along the device chain\n"
295                                "  --export-db       export the content of the udev database\n"
296                                "  --verision        print udev version\n"
297                                "  --help            print this text\n"
298                                "\n");
299                         goto exit;
300                 default:
301                         goto exit;
302                 }
303         }
304
305         /* run action */
306         switch (action) {
307         case ACTION_QUERY:
308                 /* needs devpath or node/symlink name for query */
309                 if (path[0] != '\0') {
310                         if (udev_db_get_device(udev, path) != 0) {
311                                 fprintf(stderr, "no record for '%s' in database\n", path);
312                                 rc = 3;
313                                 goto exit;
314                         }
315                 } else if (name[0] != '\0') {
316                         char devpath[PATH_SIZE];
317
318                         if (udev_db_lookup_name(name, devpath, sizeof(devpath)) != 0) {
319                                 fprintf(stderr, "node name not found\n");
320                                 rc = 4;
321                                 goto exit;
322                         }
323                         udev_db_get_device(udev, devpath);
324                 } else {
325                         fprintf(stderr, "query needs --path or node --name specified\n");
326                         rc = 4;
327                         goto exit;
328                 }
329
330                 switch(query) {
331                 case QUERY_NAME:
332                         if (root)
333                                 printf("%s/%s\n", udev_root, udev->name);
334                         else
335                                 printf("%s\n", udev->name);
336                         break;
337                 case QUERY_SYMLINK:
338                         if (list_empty(&udev->symlink_list))
339                                 goto exit;
340                         if (root)
341                                 list_for_each_entry(name_loop, &udev->symlink_list, node)
342                                         printf("%s/%s ", udev_root, name_loop->name);
343                         else
344                                 list_for_each_entry(name_loop, &udev->symlink_list, node)
345                                         printf("%s ", name_loop->name);
346                         printf("\n");
347                         break;
348                 case QUERY_PATH:
349                         printf("%s\n", udev->dev->devpath);
350                         goto exit;
351                 case QUERY_ENV:
352                         list_for_each_entry(name_loop, &udev->env_list, node)
353                                 printf("%s\n", name_loop->name);
354                         break;
355                 case QUERY_ALL:
356                         print_record(udev);
357                         break;
358                 default:
359                         fprintf(stderr, "unknown query type\n");
360                         break;
361                 }
362                 break;
363         case ACTION_ATTRIBUTE_WALK:
364                 if (path[0] != '\0') {
365                         print_device_chain(path);
366                 } else if (name[0] != '\0') {
367                         char devpath[PATH_SIZE];
368
369                         if (udev_db_lookup_name(name, devpath, sizeof(devpath)) != 0) {
370                                 fprintf(stderr, "node name not found\n");
371                                 rc = 4;
372                                 goto exit;
373                         }
374                         print_device_chain(devpath);
375                 } else {
376                         fprintf(stderr, "attribute walk needs --path or node --name specified\n");
377                         rc = 5;
378                         goto exit;
379                 }
380                 break;
381         case ACTION_ROOT:
382                 printf("%s\n", udev_root);
383                 break;
384         default:
385                 fprintf(stderr, "missing option\n");
386                 rc = 1;
387                 break;
388         }
389
390 exit:
391         udev_device_cleanup(udev);
392         sysfs_cleanup();
393         logging_close();
394         return rc;
395 }