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