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