chiark / gitweb /
udevinfo: add database export
[elogind.git] / udevinfo.c
1 /*
2  * udevinfo.c - fetches stored device information or sysfs attributes
3  *
4  * Copyright (C) 2004-2005 Kay Sievers <kay.sievers@vrfy.org>
5  *
6  *      This program is free software; you can redistribute it and/or modify it
7  *      under the terms of the GNU General Public License as published by the
8  *      Free Software Foundation version 2 of the License.
9  * 
10  *      This program is distributed in the hope that it will be useful, but
11  *      WITHOUT ANY WARRANTY; without even the implied warranty of
12  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *      General Public License for more details.
14  * 
15  *      You should have received a copy of the GNU General Public License along
16  *      with this program; if not, write to the Free Software Foundation, Inc.,
17  *      675 Mass Ave, Cambridge, MA 02139, USA.
18  *
19  */
20
21 #include <stdlib.h>
22 #include <string.h>
23 #include <stdio.h>
24 #include <ctype.h>
25 #include <stdarg.h>
26 #include <unistd.h>
27 #include <errno.h>
28
29 #include "libsysfs/sysfs/libsysfs.h"
30 #include "udev_libc_wrapper.h"
31 #include "udev.h"
32 #include "udev_utils.h"
33 #include "udev_version.h"
34 #include "udev_db.h"
35 #include "logging.h"
36
37
38 #ifdef USE_LOG
39 void log_message (int priority, const char *format, ...)
40 {
41         va_list args;
42
43         if (priority > udev_log_priority)
44                 return;
45
46         va_start(args, format);
47         vsyslog(priority, format, args);
48         va_end(args);
49 }
50 #endif
51
52 static void print_all_attributes(struct dlist *attr_list)
53 {
54         struct sysfs_attribute *attr;
55         char value[VALUE_SIZE];
56         size_t len;
57
58         dlist_for_each_data(attr_list, attr, struct sysfs_attribute) {
59                 if (attr->value == NULL)
60                         continue;
61                 len = strlcpy(value, attr->value, sizeof(value));
62                 if (len >= sizeof(value)) {
63                         dbg("attribute value of '%s' too long, skip", attr->name);
64                         continue;
65                 }
66
67                 /* remove trailing newlines */
68                 while (len && value[len-1] == '\n')
69                         value[--len] = '\0';
70                 /* skip nonprintable attributes */
71                 while (len && isprint(value[len-1]))
72                         len--;
73                 if (len) {
74                         dbg("attribute value of '%s' non-printable, skip", attr->name);
75                         continue;
76                 }
77                 replace_untrusted_chars(value);
78                 printf("    SYSFS{%s}==\"%s\"\n", attr->name, value);
79         }
80         printf("\n");
81 }
82
83 static void print_record(struct udevice *udev)
84 {
85         struct name_entry *name_loop;
86
87         printf("P: %s\n", udev->devpath);
88         printf("N: %s\n", udev->name);
89         list_for_each_entry(name_loop, &udev->symlink_list, node)
90                 printf("S: %s\n", name_loop->name);
91         list_for_each_entry(name_loop, &udev->env_list, node)
92                 printf("E: %s\n", name_loop->name);
93 }
94
95 static int print_device_chain(const char *path)
96 {
97         struct sysfs_class_device *class_dev;
98         struct sysfs_class_device *class_dev_parent;
99         struct sysfs_attribute *attr;
100         struct sysfs_device *sysfs_dev;
101         struct dlist *attr_list;
102         int retval = 0;
103
104         /*  get the class dev */
105         class_dev = sysfs_open_class_device_path(path);
106         if (class_dev == NULL) {
107                 fprintf(stderr, "couldn't get the class device\n");
108                 return -1;
109         }
110
111         printf("\nudevinfo starts with the device the node belongs to and then walks up the\n"
112                "device chain, to print for every device found, all possibly useful attributes\n"
113                "in the udev key format.\n"
114                "Only attributes within one device section may be used together in one rule,\n"
115                "to match the device for which the node will be created.\n"
116                "\n");
117
118         /* look for the 'dev' file */
119         attr = sysfs_get_classdev_attr(class_dev, "dev");
120         if (attr != NULL)
121                 printf("device '%s' has major:minor %s", class_dev->path, attr->value);
122
123         /* open sysfs class device directory and print all attributes */
124         printf("  looking at class device '%s':\n", class_dev->path);
125         printf("    KERNEL==\"%s\"\n", class_dev->name);
126         printf("    SUBSYSTEM==\"%s\"\n", class_dev->classname);
127
128         attr_list = sysfs_get_classdev_attributes(class_dev);
129         if (attr_list == NULL) {
130                 fprintf(stderr, "couldn't open class device directory\n");
131                 retval = -1;
132                 goto exit;
133         }
134         print_all_attributes(attr_list);
135
136         /* get the device link (if parent exists look here) */
137         class_dev_parent = sysfs_get_classdev_parent(class_dev);
138         if (class_dev_parent != NULL)
139                 sysfs_dev = sysfs_get_classdev_device(class_dev_parent);
140         else 
141                 sysfs_dev = sysfs_get_classdev_device(class_dev);
142         
143         if (sysfs_dev != NULL)
144                 printf("follow the \"device\"-link to the physical device:\n");
145
146         /* look the device chain upwards */
147         while (sysfs_dev != NULL) {
148                 printf("  looking at the device chain at '%s':\n", sysfs_dev->path);
149                 printf("    BUS==\"%s\"\n", sysfs_dev->bus);
150                 printf("    ID==\"%s\"\n", sysfs_dev->bus_id);
151                 printf("    DRIVER==\"%s\"\n", sysfs_dev->driver_name);
152
153                 attr_list = sysfs_get_device_attributes(sysfs_dev);
154                 if (attr_list != NULL)
155                         print_all_attributes(attr_list);
156                 else
157                         printf("\n");
158
159                 sysfs_dev = sysfs_get_device_parent(sysfs_dev);
160                 if (sysfs_dev == NULL)
161                         break;
162         }
163
164 exit:
165         sysfs_close_class_device(class_dev);
166         return retval;
167 }
168
169 static void dump_name_devpath(struct udevice *udev) {
170         printf("%s=%s/%s\n", udev->devpath, udev_root, udev->name);
171 }
172
173 static void dump_record(struct udevice *udev) {
174         print_record(udev);
175         printf("\n");
176 }
177
178 static void dump_db(void fnct(struct udevice *udev)) {
179         LIST_HEAD(name_list);
180         struct name_entry *name_loop;
181
182         udev_db_get_all_entries(&name_list);
183         list_for_each_entry(name_loop, &name_list, node) {
184                 struct udevice udev_db;
185
186                 udev_init_device(&udev_db, NULL, NULL, NULL);
187                 if (udev_db_get_device(&udev_db, name_loop->name) == 0)
188                         fnct(&udev_db);
189                 udev_cleanup_device(&udev_db);
190         }
191         name_list_cleanup(&name_list);
192 }
193
194 static void print_help(void)
195 {
196         fprintf(stderr, "Usage: udevinfo [-anpqrVh]\n"
197                "  -q TYPE  query database for the specified value:\n"
198                "             'name'    name of device node\n"
199                "             'symlink' pointing to node\n"
200                "             'path'    sysfs device path\n"
201                "             'env'     the device related imported environment\n"
202                "             'all'     all values\n"
203                "\n"
204                "  -p PATH  sysfs device path used for query or chain\n"
205                "  -n NAME  node/symlink name used for query\n"
206                "\n"
207                "  -r       print udev root\n"
208                "  -a       print all SYSFS_attributes along the device chain\n"
209                "  -d       print the relationship of devpath and the node name for all\n"
210                "  -e       print the content of the udev database\n"
211                "  -V       print udev version\n"
212                "  -h       print this help text\n"
213                "\n");
214 }
215
216 int main(int argc, char *argv[], char *envp[])
217 {
218         static const char short_options[] = "aden:p:q:rVh";
219         int option;
220         struct udevice udev;
221         int root = 0;
222
223         enum action_type {
224                 ACTION_NONE,
225                 ACTION_QUERY,
226                 ACTION_ATTRIBUTE_WALK,
227                 ACTION_ROOT,
228         } action = ACTION_NONE;
229
230         enum query_type {
231                 QUERY_NONE,
232                 QUERY_NAME,
233                 QUERY_PATH,
234                 QUERY_SYMLINK,
235                 QUERY_ENV,
236                 QUERY_ALL,
237         } query = QUERY_NONE;
238
239         char path[PATH_SIZE] = "";
240         char name[PATH_SIZE] = "";
241         char temp[PATH_SIZE];
242         struct name_entry *name_loop;
243         char *pos;
244         int retval = 0;
245
246         logging_init("udevinfo");
247
248         udev_init_config();
249         udev_init_device(&udev, NULL, NULL, NULL);
250
251         /* get command line options */
252         while (1) {
253                 option = getopt(argc, argv, short_options);
254                 if (option == -1)
255                         break;
256
257                 dbg("option '%c'", option);
258                 switch (option) {
259                 case 'n':
260                         dbg("udev name: %s\n", optarg);
261                         strlcpy(name, optarg, sizeof(name));
262                         break;
263                 case 'p':
264                         dbg("udev path: %s\n", optarg);
265                         strlcpy(path, optarg, sizeof(path));
266                         break;
267                 case 'q':
268                         dbg("udev query: %s\n", optarg);
269                         action = ACTION_QUERY;
270                         if (strcmp(optarg, "name") == 0) {
271                                 query = QUERY_NAME;
272                                 break;
273                         }
274                         if (strcmp(optarg, "symlink") == 0) {
275                                 query = QUERY_SYMLINK;
276                                 break;
277                         }
278                         if (strcmp(optarg, "path") == 0) {
279                                 query = QUERY_PATH;
280                                 break;
281                         }
282                         if (strcmp(optarg, "env") == 0) {
283                                 query = QUERY_ENV;
284                                 break;
285                         }
286                         if (strcmp(optarg, "all") == 0) {
287                                 query = QUERY_ALL;
288                                 break;
289                         }
290                         fprintf(stderr, "unknown query type\n");
291                         retval = 2;
292                         goto exit;
293                 case 'r':
294                         if (action == ACTION_NONE)
295                                 action = ACTION_ROOT;
296                         root = 1;
297                         break;
298                 case 'a':
299                         action = ACTION_ATTRIBUTE_WALK;
300                         break;
301                 case 'd':
302                         dump_db(dump_name_devpath);
303                         goto exit;
304                 case 'e':
305                         dump_db(dump_record);
306                         goto exit;
307                 case 'V':
308                         printf("udevinfo, version %s\n", UDEV_VERSION);
309                         goto exit;
310                 case 'h':
311                 case '?':
312                 default:
313                         print_help();
314                         goto exit;
315                 }
316         }
317
318         /* run action */
319         switch (action) {
320         case ACTION_QUERY:
321                 /* need devpath or node/symlink name for query */
322                 if (path[0] != '\0') {
323                         /* remove sysfs_path if given */
324                         if (strncmp(path, sysfs_path, strlen(sysfs_path)) == 0) {
325                                 pos = path + strlen(sysfs_path);
326                         } else {
327                                 if (path[0] != '/') {
328                                         /* prepend '/' if missing */
329                                         strcpy(temp, "/");
330                                         strlcpy(temp, path, sizeof(temp));
331                                         pos = temp;
332                                 } else {
333                                         pos = path;
334                                 }
335                         }
336                         retval = udev_db_get_device(&udev, pos);
337                         if (retval != 0) {
338                                 fprintf(stderr, "no record for '%s' in database\n", pos);
339                                 retval = 3;
340                                 goto exit;
341                         }
342                 } else if (name[0] != '\0') {
343                         char devpath[PATH_SIZE];
344                         int len;
345
346                         /* remove udev_root if given */
347                         len = strlen(udev_root);
348                         if (strncmp(name, udev_root, len) == 0) {
349                                 pos = &name[len+1];
350                         } else
351                                 pos = name;
352
353                         retval = udev_db_lookup_name(pos, devpath, sizeof(devpath));
354                         if (retval != 0) {
355                                 fprintf(stderr, "no record for '%s' in database\n", pos);
356                                 retval = 3;
357                                 goto exit;
358                         }
359                         udev_db_get_device(&udev, devpath);
360                 } else {
361                         fprintf(stderr, "query needs device path(-p) or node name(-n) specified\n");
362                         retval = 4;
363                         goto exit;
364                 }
365
366                 switch(query) {
367                 case QUERY_NAME:
368                         if (root)
369                                 printf("%s/%s\n", udev_root, udev.name);
370                         else
371                                 printf("%s\n", udev.name);
372                         break;
373                 case QUERY_SYMLINK:
374                         if (list_empty(&udev.symlink_list))
375                                 goto exit;
376                         if (root)
377                                 list_for_each_entry(name_loop, &udev.symlink_list, node)
378                                         printf("%s/%s ", udev_root, name_loop->name);
379                         else
380                                 list_for_each_entry(name_loop, &udev.symlink_list, node)
381                                         printf("%s ", name_loop->name);
382                         printf("\n");
383                         break;
384                 case QUERY_PATH:
385                         printf("%s\n", udev.devpath);
386                         goto exit;
387                 case QUERY_ENV:
388                         list_for_each_entry(name_loop, &udev.env_list, node)
389                                 printf("%s\n", name_loop->name);
390                         break;
391                 case QUERY_ALL:
392                         print_record(&udev);
393                         break;
394                 default:
395                         print_help();
396                         break;
397                 }
398                 break;
399         case ACTION_ATTRIBUTE_WALK:
400                 if (path[0] == '\0') {
401                         fprintf(stderr, "attribute walk on device chain needs path(-p) specified\n");
402                         retval = 4;
403                         goto exit;
404                 } else {
405                         if (strncmp(path, sysfs_path, strlen(sysfs_path)) != 0) {
406                                 /* prepend sysfs mountpoint if not given */
407                                 snprintf(temp, sizeof(temp), "%s%s", sysfs_path, path);
408                                 temp[sizeof(temp)-1] = '\0';
409                                 strlcpy(path, temp, sizeof(temp));
410                         }
411                         print_device_chain(path);
412                 }
413                 break;
414         case ACTION_ROOT:
415                 printf("%s\n", udev_root);
416                 break;
417         default:
418                 print_help();
419                 retval = 1;
420                 break;
421         }
422
423 exit:
424         udev_cleanup_device(&udev);
425         logging_close();
426         return retval;
427 }