chiark / gitweb /
udevd: write full database file for (unsupported) renamed device nodes
[elogind.git] / extras / usb-db / usb-db.c
1 /*
2  * Copyright (C) 2009 Lennart Poettering <lennart@poettering.net>
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details:
13  */
14
15 #include <stdio.h>
16 #include <errno.h>
17 #include <string.h>
18 #include <inttypes.h>
19 #include <ctype.h>
20 #include <stdlib.h>
21
22 #include <libudev.h>
23
24 #if defined(BUILD_FOR_USB)
25 # define DATABASE USB_DATABASE
26 # define SUBSYSTEM "usb"
27 # define DEVTYPE "usb_device"
28 # define VENDOR_ATTR "idVendor"
29 # define PRODUCT_ATTR "idProduct"
30 #elif defined(BUILD_FOR_PCI)
31 # define DATABASE PCI_DATABASE
32 # define SUBSYSTEM "pci"
33 # define DEVTYPE NULL
34 # define VENDOR_ATTR "vendor"
35 # define PRODUCT_ATTR "device"
36 #else
37 # error "Are you havin' a laugh?"
38 #endif
39
40 static int get_id_attr(
41         struct udev_device *parent,
42         const char *name,
43         uint16_t *value) {
44
45         const char *t;
46         unsigned u;
47
48         if (!(t = udev_device_get_sysattr_value(parent, name))) {
49                 fprintf(stderr, "%s lacks %s.\n", udev_device_get_syspath(parent), name);
50                 return -1;
51         }
52
53         if (!strncmp(t, "0x", 2))
54                 t += 2;
55
56         if (sscanf(t, "%04x", &u) != 1 || u > 0xFFFFU) {
57                 fprintf(stderr, "Failed to parse %s on %s.\n", name, udev_device_get_syspath(parent));
58                 return -1;
59         }
60
61         *value = (uint16_t) u;
62         return 0;
63 }
64
65 static int get_vid_pid(
66         struct udev_device *parent,
67         uint16_t *vid,
68         uint16_t *pid) {
69
70         if (get_id_attr(parent, VENDOR_ATTR, vid) < 0)
71                 return -1;
72         else if (*vid <= 0) {
73                 fprintf(stderr, "Invalid vendor id.\n");
74                 return -1;
75         }
76
77         if (get_id_attr(parent, PRODUCT_ATTR, pid) < 0)
78                 return -1;
79
80         return 0;
81 }
82
83 static void rstrip(char *n) {
84         size_t i;
85
86         for (i = strlen(n); i > 0 && isspace(n[i-1]); i--)
87                 n[i-1] = 0;
88 }
89
90 #define HEXCHARS "0123456789abcdefABCDEF"
91 #define WHITESPACE " \t\n\r"
92
93 static int lookup_vid_pid(
94         uint16_t vid,
95         uint16_t pid,
96         char **vendor,
97         char **product) {
98
99         FILE *f;
100         int ret = -1;
101         int found_vendor = 0;
102         char *line = NULL;
103
104         *vendor = *product = NULL;
105
106         if (!(f = fopen(DATABASE, "r"))) {
107                 fprintf(stderr, "Failed to open database file "DATABASE": %s\n", strerror(errno));
108                 return -1;
109         }
110
111         for (;;) {
112                 size_t n;
113
114                 if (line) {
115                         free(line);
116                         line = NULL;
117                 }
118
119                 if (getline(&line, &n, f) < 0)
120                         break;
121
122                 rstrip(line);
123
124                 if (line[0] == '#' || line[0] == 0)
125                         continue;
126
127                 if (strspn(line, HEXCHARS) == 4) {
128                         unsigned u;
129
130                         if (found_vendor)
131                                 break;
132
133                         if (sscanf(line, "%04x", &u) == 1 && u == vid) {
134                                 char *t;
135
136                                 t = line+4;
137                                 t += strspn(t, WHITESPACE);
138
139                                 if (!(*vendor = strdup(t))) {
140                                         fprintf(stderr, "Out of memory.\n");
141                                         goto finish;
142                                 }
143
144                                 found_vendor = 1;
145                         }
146
147                         continue;
148                 }
149
150                 if (found_vendor && line[0] == '\t' && strspn(line+1, HEXCHARS) == 4) {
151                         unsigned u;
152
153                         if (sscanf(line+1, "%04x", &u) == 1 && u == pid) {
154                                 char *t;
155
156                                 t = line+5;
157                                 t += strspn(t, WHITESPACE);
158
159                                 if (!(*product = strdup(t))) {
160                                         fprintf(stderr, "Out of memory.\n");
161                                         goto finish;
162                                 }
163
164                                 break;
165                         }
166                 }
167         }
168
169         ret = 0;
170
171 finish:
172         free(line);
173         fclose(f);
174
175         if (ret < 0) {
176                 free(*product);
177                 free(*vendor);
178
179                 *product = *vendor = NULL;
180         }
181
182         return ret;
183 }
184
185 static struct udev_device *find_device(struct udev_device *dev, const char *subsys, const char *devtype)
186 {
187         const char *str;
188
189         str = udev_device_get_subsystem(dev);
190         if (str == NULL)
191                 goto try_parent;
192         if (strcmp(str, subsys) != 0)
193                 goto try_parent;
194
195         if (devtype != NULL) {
196                 str = udev_device_get_devtype(dev);
197                 if (str == NULL)
198                         goto try_parent;
199                 if (strcmp(str, devtype) != 0)
200                         goto try_parent;
201         }
202         return dev;
203 try_parent:
204         return udev_device_get_parent_with_subsystem_devtype(dev, SUBSYSTEM, DEVTYPE);
205 }
206
207 int main(int argc, char*argv[]) {
208
209         struct udev *udev = NULL;
210         int ret = 1;
211         char *sp;
212         struct udev_device *dev = NULL, *parent = NULL;
213         uint16_t vid = 0, pid = 0;
214         char *vendor = NULL, *product = NULL;
215
216         if (argc < 2) {
217                 fprintf(stderr, "Need to pass sysfs path.\n");
218                 goto finish;
219         }
220
221         if (!(udev = udev_new()))
222                 goto finish;
223
224         if (asprintf(&sp, "%s%s", udev_get_sys_path(udev), argv[1]) < 0) {
225                 fprintf(stderr, "Failed to allocate sysfs path.\n");
226                 goto finish;
227         }
228
229         dev = udev_device_new_from_syspath(udev, sp);
230         free(sp);
231
232         if (!dev) {
233                 fprintf(stderr, "Failed to access %s.\n", argv[1]);
234                 goto finish;
235         }
236
237         parent = find_device(dev, SUBSYSTEM, DEVTYPE);
238         if (!parent) {
239                 fprintf(stderr, "Failed to find device.\n");
240                 goto finish;
241         }
242
243         if (get_vid_pid(parent, &vid, &pid) < 0)
244                 goto finish;
245
246         if (lookup_vid_pid(vid, pid, &vendor, &product) < 0)
247                 goto finish;
248
249         if (vendor)
250                 printf("ID_VENDOR_FROM_DATABASE=%s\n", vendor);
251
252         if (product)
253                 printf("ID_MODEL_FROM_DATABASE=%s\n", product);
254
255         ret = 0;
256
257 finish:
258         udev_device_unref(dev);
259         udev_unref(udev);
260         free(vendor);
261         free(product);
262
263         return ret;
264 }