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