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