2 This file is part of systemd.
4 Copyright 2012 Kay Sievers <kay.sievers@vrfy.org>
5 Copyright 2008 Alan Jenkins <alan.christopher.jenkins@googlemail.com>
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
32 #include "udev-hwdb.h"
40 static void linebuf_init(struct linebuf *buf) {
45 static const char *linebuf_get(struct linebuf *buf) {
46 if (buf->len + 1 >= sizeof(buf->bytes))
48 buf->bytes[buf->len] = '\0';
52 static bool linebuf_add(struct linebuf *buf, const char *s, size_t len) {
53 if (buf->len + len >= sizeof(buf->bytes))
55 memcpy(buf->bytes + buf->len, s, len);
60 static bool linebuf_add_char(struct linebuf *buf, char c)
62 if (buf->len + 1 >= sizeof(buf->bytes))
64 buf->bytes[buf->len++] = c;
68 static void linebuf_rem(struct linebuf *buf, size_t count) {
69 assert(buf->len >= count);
73 static void linebuf_rem_char(struct linebuf *buf) {
78 struct udev_device *dev;
81 uint64_t file_time_usec;
83 struct trie_header_f *head;
89 static const struct trie_child_entry_f *trie_node_children(struct trie_f *trie, const struct trie_node_f *node) {
90 return (const struct trie_child_entry_f *)((const char *)node + le64toh(trie->head->node_size));
93 static const struct trie_value_entry_f *trie_node_values(struct trie_f *trie, const struct trie_node_f *node) {
94 const char *base = (const char *)node;
96 base += le64toh(trie->head->node_size);
97 base += node->children_count * le64toh(trie->head->child_entry_size);
98 return (const struct trie_value_entry_f *)base;
101 static const struct trie_node_f *trie_node_from_off(struct trie_f *trie, le64_t off) {
102 return (const struct trie_node_f *)(trie->map + le64toh(off));
105 static const char *trie_string(struct trie_f *trie, le64_t off) {
106 return trie->map + le64toh(off);
109 static int trie_children_cmp_f(const void *v1, const void *v2) {
110 const struct trie_child_entry_f *n1 = v1;
111 const struct trie_child_entry_f *n2 = v2;
113 return n1->c - n2->c;
116 static const struct trie_node_f *node_lookup_f(struct trie_f *trie, const struct trie_node_f *node, uint8_t c) {
117 struct trie_child_entry_f *child;
118 struct trie_child_entry_f search;
121 child = bsearch(&search, trie_node_children(trie, node), node->children_count,
122 le64toh(trie->head->child_entry_size), trie_children_cmp_f);
124 return trie_node_from_off(trie, child->child_off);
128 static void trie_fnmatch_f(struct trie_f *trie, const struct trie_node_f *node, size_t p,
129 struct linebuf *buf, const char *search,
130 void (*cb)(struct trie_f *trie, const char *key, const char *value)) {
135 prefix = trie_string(trie, node->prefix_off);
136 len = strlen(prefix + p);
137 linebuf_add(buf, prefix + p, len);
139 for (i = 0; i < node->children_count; i++) {
140 const struct trie_child_entry_f *child = &trie_node_children(trie, node)[i];
142 linebuf_add_char(buf, child->c);
143 trie_fnmatch_f(trie, trie_node_from_off(trie, child->child_off), 0, buf, search, cb);
144 linebuf_rem_char(buf);
147 if (node->values_count && fnmatch(linebuf_get(buf), search, 0) == 0)
148 for (i = 0; i < node->values_count; i++)
149 cb(trie, trie_string(trie, trie_node_values(trie, node)[i].key_off),
150 trie_string(trie, trie_node_values(trie, node)[i].value_off));
152 linebuf_rem(buf, len);
155 static void trie_search_f(struct trie_f *trie, const char *search,
156 void (*cb)(struct trie_f *trie, const char *key, const char *value)) {
158 const struct trie_node_f *node;
163 node = trie_node_from_off(trie, trie->head->nodes_root_off);
165 const struct trie_node_f *child;
168 if (node->prefix_off) {
171 for (; (c = trie_string(trie, node->prefix_off)[p]); p++) {
172 if (c == '*' || c == '?' || c == '[') {
173 trie_fnmatch_f(trie, node, p, &buf, search + i + p, cb);
176 if (c != search[i + p])
182 child = node_lookup_f(trie, node, '*');
184 linebuf_add_char(&buf, '*');
185 trie_fnmatch_f(trie, child, 0, &buf, search + i, cb);
186 linebuf_rem_char(&buf);
189 child = node_lookup_f(trie, node, '?');
191 linebuf_add_char(&buf, '?');
192 trie_fnmatch_f(trie, child, 0, &buf, search + i, cb);
193 linebuf_rem_char(&buf);
196 child = node_lookup_f(trie, node, '[');
198 linebuf_add_char(&buf, '[');
199 trie_fnmatch_f(trie, child, 0, &buf, search + i, cb);
200 linebuf_rem_char(&buf);
203 if (search[i] == '\0') {
206 for (n = 0; n < node->values_count; n++)
207 cb(trie, trie_string(trie, trie_node_values(trie, node)[n].key_off),
208 trie_string(trie, trie_node_values(trie, node)[n].value_off));
212 child = node_lookup_f(trie, node, search[i]);
218 static void value_cb(struct trie_f *trie, const char *key, const char *value) {
219 /* TODO: add sub-matches (+) against DMI data */
221 udev_builtin_add_property(trie->dev, trie->test, key + 1, value);
224 static struct trie_f trie;
226 static int hwdb_lookup(struct udev_device *dev, const char *subsys) {
227 struct udev_device *d;
228 const char *modalias;
229 char str[UTIL_NAME_SIZE];
230 int rc = EXIT_SUCCESS;
232 /* search the first parent device with a modalias */
233 for (d = dev; d; d = udev_device_get_parent(d)) {
234 const char *dsubsys = udev_device_get_subsystem(d);
236 /* look only at devices of a specific subsystem */
237 if (subsys && dsubsys && !streq(dsubsys, subsys))
240 modalias = udev_device_get_property_value(d, "MODALIAS");
244 /* the usb_device does not have modalias, compose one */
245 if (dsubsys && streq(dsubsys, "usb")) {
249 v = udev_device_get_sysattr_value(d, "idVendor");
252 p = udev_device_get_sysattr_value(d, "idProduct");
255 vn = strtol(v, NULL, 16);
258 pn = strtol(p, NULL, 16);
261 snprintf(str, sizeof(str), "usb:v%04Xp%04X*", vn, pn);
269 trie_search_f(&trie, modalias, value_cb);
273 static int builtin_hwdb(struct udev_device *dev, int argc, char *argv[], bool test) {
274 static const struct option options[] = {
275 { "subsystem", required_argument, NULL, 's' },
278 const char *subsys = NULL;
283 option = getopt_long(argc, argv, "s", options, NULL);
296 if (hwdb_lookup(dev, subsys) < 0)
301 /* called at udev startup and reload */
302 static int builtin_hwdb_init(struct udev *udev)
305 const char sig[] = HWDB_SIG;
307 trie.f = fopen(SYSCONFDIR "/udev/hwdb.bin", "re");
311 if (fstat(fileno(trie.f), &st) < 0 || (size_t)st.st_size < offsetof(struct trie_header_f, strings_len) + 8) {
312 log_error("Error reading '%s'.", SYSCONFDIR "/udev/hwdb.bin: %m");
318 trie.map = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fileno(trie.f), 0);
319 if (trie.map == MAP_FAILED) {
320 log_error("Error mapping '%s'.", SYSCONFDIR "/udev/hwdb.bin: %m");
324 trie.file_time_usec = ts_usec(&st.st_mtim);
325 trie.map_size = st.st_size;
327 if (memcmp(trie.map, sig, sizeof(trie.head->signature)) != 0 || (size_t)st.st_size != le64toh(trie.head->file_size)) {
328 log_error("Unable to recognize the format of '%s'.", SYSCONFDIR "/udev/hwdb.bin");
329 log_error("Please try 'udevadm hwdb --update' to re-create it.");
330 munmap((void *)trie.map, st.st_size);
336 log_debug("=== trie on-disk ===\n");
337 log_debug("tool version: %llu", (unsigned long long)le64toh(trie.head->tool_version));
338 log_debug("file size: %8zi bytes\n", st.st_size);
339 log_debug("header size %8zu bytes\n", (size_t)le64toh(trie.head->header_size));
340 log_debug("strings %8zu bytes\n", (size_t)le64toh(trie.head->strings_len));
341 log_debug("nodes %8zu bytes\n", (size_t)le64toh(trie.head->nodes_len));
345 /* called on udev shutdown and reload request */
346 static void builtin_hwdb_exit(struct udev *udev)
350 munmap((void *)trie.map, trie.map_size);
355 /* called every couple of seconds during event activity; 'true' if config has changed */
356 static bool builtin_hwdb_validate(struct udev *udev)
360 if (fstat(fileno(trie.f), &st) < 0)
362 if (trie.file_time_usec != ts_usec(&st.st_mtim))
367 const struct udev_builtin udev_builtin_hwdb = {
370 .init = builtin_hwdb_init,
371 .exit = builtin_hwdb_exit,
372 .validate = builtin_hwdb_validate,
373 .help = "hardware database",