1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <errno.h>
4 #include <fnmatch.h>
5 #include <getopt.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 
9 #include "sd-hwdb.h"
10 
11 #include "alloc-util.h"
12 #include "device-util.h"
13 #include "hwdb-util.h"
14 #include "parse-util.h"
15 #include "string-util.h"
16 #include "udev-builtin.h"
17 
18 static sd_hwdb *hwdb;
19 
udev_builtin_hwdb_lookup(sd_device * dev,const char * prefix,const char * modalias,const char * filter,bool test)20 int udev_builtin_hwdb_lookup(sd_device *dev,
21                              const char *prefix, const char *modalias,
22                              const char *filter, bool test) {
23         _cleanup_free_ char *lookup = NULL;
24         const char *key, *value;
25         int n = 0, r;
26 
27         if (!hwdb)
28                 return -ENOENT;
29 
30         if (prefix) {
31                 lookup = strjoin(prefix, modalias);
32                 if (!lookup)
33                         return -ENOMEM;
34                 modalias = lookup;
35         }
36 
37         SD_HWDB_FOREACH_PROPERTY(hwdb, modalias, key, value) {
38                 if (filter && fnmatch(filter, key, FNM_NOESCAPE) != 0)
39                         continue;
40 
41                 r = udev_builtin_add_property(dev, test, key, value);
42                 if (r < 0)
43                         return r;
44                 n++;
45         }
46         return n;
47 }
48 
modalias_usb(sd_device * dev,char * s,size_t size)49 static const char *modalias_usb(sd_device *dev, char *s, size_t size) {
50         const char *v, *p, *n = NULL;
51         uint16_t vn, pn;
52 
53         if (sd_device_get_sysattr_value(dev, "idVendor", &v) < 0)
54                 return NULL;
55         if (sd_device_get_sysattr_value(dev, "idProduct", &p) < 0)
56                 return NULL;
57         if (safe_atoux16(v, &vn) < 0)
58                 return NULL;
59         if (safe_atoux16(p, &pn) < 0)
60                 return NULL;
61         (void) sd_device_get_sysattr_value(dev, "product", &n);
62 
63         (void) snprintf(s, size, "usb:v%04Xp%04X:%s", vn, pn, strempty(n));
64         return s;
65 }
66 
udev_builtin_hwdb_search(sd_device * dev,sd_device * srcdev,const char * subsystem,const char * prefix,const char * filter,bool test)67 static int udev_builtin_hwdb_search(sd_device *dev, sd_device *srcdev,
68                                     const char *subsystem, const char *prefix,
69                                     const char *filter, bool test) {
70         char s[LINE_MAX];
71         bool last = false;
72         int r = 0;
73 
74         assert(dev);
75 
76         if (!srcdev)
77                 srcdev = dev;
78 
79         for (sd_device *d = srcdev; d; ) {
80                 const char *dsubsys, *devtype, *modalias = NULL;
81 
82                 if (sd_device_get_subsystem(d, &dsubsys) < 0)
83                         goto next;
84 
85                 /* look only at devices of a specific subsystem */
86                 if (subsystem && !streq(dsubsys, subsystem))
87                         goto next;
88 
89                 (void) sd_device_get_property_value(d, "MODALIAS", &modalias);
90 
91                 if (streq(dsubsys, "usb") &&
92                     sd_device_get_devtype(d, &devtype) >= 0 &&
93                     streq(devtype, "usb_device")) {
94                         /* if the usb_device does not have a modalias, compose one */
95                         if (!modalias)
96                                 modalias = modalias_usb(d, s, sizeof(s));
97 
98                         /* avoid looking at any parent device, they are usually just a USB hub */
99                         last = true;
100                 }
101 
102                 if (!modalias)
103                         goto next;
104 
105                 log_device_debug(dev, "hwdb modalias key: \"%s\"", modalias);
106 
107                 r = udev_builtin_hwdb_lookup(dev, prefix, modalias, filter, test);
108                 if (r > 0)
109                         break;
110 
111                 if (last)
112                         break;
113 next:
114                 if (sd_device_get_parent(d, &d) < 0)
115                         break;
116         }
117 
118         return r;
119 }
120 
builtin_hwdb(sd_device * dev,sd_netlink ** rtnl,int argc,char * argv[],bool test)121 static int builtin_hwdb(sd_device *dev, sd_netlink **rtnl, int argc, char *argv[], bool test) {
122         static const struct option options[] = {
123                 { "filter", required_argument, NULL, 'f' },
124                 { "device", required_argument, NULL, 'd' },
125                 { "subsystem", required_argument, NULL, 's' },
126                 { "lookup-prefix", required_argument, NULL, 'p' },
127                 {}
128         };
129         const char *filter = NULL;
130         const char *device = NULL;
131         const char *subsystem = NULL;
132         const char *prefix = NULL;
133         _cleanup_(sd_device_unrefp) sd_device *srcdev = NULL;
134         int r;
135 
136         if (!hwdb)
137                 return -EINVAL;
138 
139         for (;;) {
140                 int option;
141 
142                 option = getopt_long(argc, argv, "f:d:s:p:", options, NULL);
143                 if (option == -1)
144                         break;
145 
146                 switch (option) {
147                 case 'f':
148                         filter = optarg;
149                         break;
150 
151                 case 'd':
152                         device = optarg;
153                         break;
154 
155                 case 's':
156                         subsystem = optarg;
157                         break;
158 
159                 case 'p':
160                         prefix = optarg;
161                         break;
162                 }
163         }
164 
165         /* query a specific key given as argument */
166         if (argv[optind]) {
167                 r = udev_builtin_hwdb_lookup(dev, prefix, argv[optind], filter, test);
168                 if (r < 0)
169                         return log_device_debug_errno(dev, r, "Failed to look up hwdb: %m");
170                 if (r == 0)
171                         return log_device_debug_errno(dev, SYNTHETIC_ERRNO(ENODATA), "No entry found from hwdb.");
172                 return r;
173         }
174 
175         /* read data from another device than the device we will store the data */
176         if (device) {
177                 r = sd_device_new_from_device_id(&srcdev, device);
178                 if (r < 0)
179                         return log_device_debug_errno(dev, r, "Failed to create sd_device object '%s': %m", device);
180         }
181 
182         r = udev_builtin_hwdb_search(dev, srcdev, subsystem, prefix, filter, test);
183         if (r < 0)
184                 return log_device_debug_errno(dev, r, "Failed to look up hwdb: %m");
185         if (r == 0)
186                 return log_device_debug_errno(dev, SYNTHETIC_ERRNO(ENODATA), "No entry found from hwdb.");
187         return r;
188 }
189 
190 /* called at udev startup and reload */
builtin_hwdb_init(void)191 static int builtin_hwdb_init(void) {
192         int r;
193 
194         if (hwdb)
195                 return 0;
196 
197         r = sd_hwdb_new(&hwdb);
198         if (r < 0)
199                 return r;
200 
201         return 0;
202 }
203 
204 /* called on udev shutdown and reload request */
builtin_hwdb_exit(void)205 static void builtin_hwdb_exit(void) {
206         hwdb = sd_hwdb_unref(hwdb);
207 }
208 
209 /* called every couple of seconds during event activity; 'true' if config has changed */
builtin_hwdb_validate(void)210 static bool builtin_hwdb_validate(void) {
211         return hwdb_validate(hwdb);
212 }
213 
214 const UdevBuiltin udev_builtin_hwdb = {
215         .name = "hwdb",
216         .cmd = builtin_hwdb,
217         .init = builtin_hwdb_init,
218         .exit = builtin_hwdb_exit,
219         .validate = builtin_hwdb_validate,
220         .help = "Hardware database",
221 };
222