2 This file is part of systemd.
4 Copyright 2012 Kay Sievers <kay@vrfy.org>
5 Copyright 2008 Alan Jenkins <alan.christopher.jenkins@googlemail.com>
6 Copyright 2014 Tom Gundersen <teg@jklm.no>
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
35 #include "hwdb-util.h"
36 #include "hwdb-internal.h"
45 struct trie_header_f *head;
51 OrderedHashmap *properties;
52 Iterator properties_iterator;
53 bool properties_modified;
62 static void linebuf_init(struct linebuf *buf) {
67 static const char *linebuf_get(struct linebuf *buf) {
68 if (buf->len + 1 >= sizeof(buf->bytes))
70 buf->bytes[buf->len] = '\0';
74 static bool linebuf_add(struct linebuf *buf, const char *s, size_t len) {
75 if (buf->len + len >= sizeof(buf->bytes))
77 memcpy(buf->bytes + buf->len, s, len);
82 static bool linebuf_add_char(struct linebuf *buf, char c)
84 if (buf->len + 1 >= sizeof(buf->bytes))
86 buf->bytes[buf->len++] = c;
90 static void linebuf_rem(struct linebuf *buf, size_t count) {
91 assert(buf->len >= count);
95 static void linebuf_rem_char(struct linebuf *buf) {
99 static const struct trie_child_entry_f *trie_node_children(sd_hwdb *hwdb, const struct trie_node_f *node) {
100 return (const struct trie_child_entry_f *)((const char *)node + le64toh(hwdb->head->node_size));
103 static const struct trie_value_entry_f *trie_node_values(sd_hwdb *hwdb, const struct trie_node_f *node) {
104 const char *base = (const char *)node;
106 base += le64toh(hwdb->head->node_size);
107 base += node->children_count * le64toh(hwdb->head->child_entry_size);
108 return (const struct trie_value_entry_f *)base;
111 static const struct trie_node_f *trie_node_from_off(sd_hwdb *hwdb, le64_t off) {
112 return (const struct trie_node_f *)(hwdb->map + le64toh(off));
115 static const char *trie_string(sd_hwdb *hwdb, le64_t off) {
116 return hwdb->map + le64toh(off);
119 static int trie_children_cmp_f(const void *v1, const void *v2) {
120 const struct trie_child_entry_f *n1 = v1;
121 const struct trie_child_entry_f *n2 = v2;
123 return n1->c - n2->c;
126 static const struct trie_node_f *node_lookup_f(sd_hwdb *hwdb, const struct trie_node_f *node, uint8_t c) {
127 struct trie_child_entry_f *child;
128 struct trie_child_entry_f search;
131 child = bsearch(&search, trie_node_children(hwdb, node), node->children_count,
132 le64toh(hwdb->head->child_entry_size), trie_children_cmp_f);
134 return trie_node_from_off(hwdb, child->child_off);
138 static int hwdb_add_property(sd_hwdb *hwdb, const char *key, const char *value) {
146 * Silently ignore all properties which do not start with a
147 * space; future extensions might use additional prefixes.
154 r = ordered_hashmap_ensure_allocated(&hwdb->properties, &string_hash_ops);
158 r = ordered_hashmap_replace(hwdb->properties, key, (char*)value);
162 hwdb->properties_modified = true;
167 static int trie_fnmatch_f(sd_hwdb *hwdb, const struct trie_node_f *node, size_t p,
168 struct linebuf *buf, const char *search) {
174 prefix = trie_string(hwdb, node->prefix_off);
175 len = strlen(prefix + p);
176 linebuf_add(buf, prefix + p, len);
178 for (i = 0; i < node->children_count; i++) {
179 const struct trie_child_entry_f *child = &trie_node_children(hwdb, node)[i];
181 linebuf_add_char(buf, child->c);
182 err = trie_fnmatch_f(hwdb, trie_node_from_off(hwdb, child->child_off), 0, buf, search);
185 linebuf_rem_char(buf);
188 if (le64toh(node->values_count) && fnmatch(linebuf_get(buf), search, 0) == 0)
189 for (i = 0; i < le64toh(node->values_count); i++) {
190 err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[i].key_off),
191 trie_string(hwdb, trie_node_values(hwdb, node)[i].value_off));
196 linebuf_rem(buf, len);
200 static int trie_search_f(sd_hwdb *hwdb, const char *search) {
202 const struct trie_node_f *node;
208 node = trie_node_from_off(hwdb, hwdb->head->nodes_root_off);
210 const struct trie_node_f *child;
213 if (node->prefix_off) {
216 for (; (c = trie_string(hwdb, node->prefix_off)[p]); p++) {
217 if (c == '*' || c == '?' || c == '[')
218 return trie_fnmatch_f(hwdb, node, p, &buf, search + i + p);
219 if (c != search[i + p])
225 child = node_lookup_f(hwdb, node, '*');
227 linebuf_add_char(&buf, '*');
228 err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
231 linebuf_rem_char(&buf);
234 child = node_lookup_f(hwdb, node, '?');
236 linebuf_add_char(&buf, '?');
237 err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
240 linebuf_rem_char(&buf);
243 child = node_lookup_f(hwdb, node, '[');
245 linebuf_add_char(&buf, '[');
246 err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
249 linebuf_rem_char(&buf);
252 if (search[i] == '\0') {
255 for (n = 0; n < le64toh(node->values_count); n++) {
256 err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[n].key_off),
257 trie_string(hwdb, trie_node_values(hwdb, node)[n].value_off));
264 child = node_lookup_f(hwdb, node, search[i]);
271 static const char hwdb_bin_paths[] =
272 "/etc/systemd/hwdb/hwdb.bin\0"
273 "/etc/udev/hwdb.bin\0"
274 "/usr/lib/systemd/hwdb/hwdb.bin\0"
275 #ifdef HAVE_SPLIT_USR
276 "/lib/systemd/hwdb/hwdb.bin\0"
278 UDEVLIBEXECDIR "/hwdb.bin\0";
280 _public_ int sd_hwdb_new(sd_hwdb **ret) {
281 _cleanup_hwdb_unref_ sd_hwdb *hwdb = NULL;
282 const char *hwdb_bin_path;
283 const char sig[] = HWDB_SIG;
285 assert_return(ret, -EINVAL);
287 hwdb = new0(sd_hwdb, 1);
291 hwdb->n_ref = REFCNT_INIT;
293 /* find hwdb.bin in hwdb_bin_paths */
294 NULSTR_FOREACH(hwdb_bin_path, hwdb_bin_paths) {
295 hwdb->f = fopen(hwdb_bin_path, "re");
298 else if (errno == ENOENT)
301 return log_debug_errno(errno, "error reading %s: %m", hwdb_bin_path);
305 log_debug("hwdb.bin does not exist, please run udevadm hwdb --update");
309 if (fstat(fileno(hwdb->f), &hwdb->st) < 0 ||
310 (size_t)hwdb->st.st_size < offsetof(struct trie_header_f, strings_len) + 8)
311 return log_debug_errno(errno, "error reading %s: %m", hwdb_bin_path);
313 hwdb->map = mmap(0, hwdb->st.st_size, PROT_READ, MAP_SHARED, fileno(hwdb->f), 0);
314 if (hwdb->map == MAP_FAILED)
315 return log_debug_errno(errno, "error mapping %s: %m", hwdb_bin_path);
317 if (memcmp(hwdb->map, sig, sizeof(hwdb->head->signature)) != 0 ||
318 (size_t)hwdb->st.st_size != le64toh(hwdb->head->file_size)) {
319 log_debug("error recognizing the format of %s", hwdb_bin_path);
323 log_debug("=== trie on-disk ===");
324 log_debug("tool version: %"PRIu64, le64toh(hwdb->head->tool_version));
325 log_debug("file size: %8"PRIi64" bytes", hwdb->st.st_size);
326 log_debug("header size %8"PRIu64" bytes", le64toh(hwdb->head->header_size));
327 log_debug("strings %8"PRIu64" bytes", le64toh(hwdb->head->strings_len));
328 log_debug("nodes %8"PRIu64" bytes", le64toh(hwdb->head->nodes_len));
336 _public_ sd_hwdb *sd_hwdb_ref(sd_hwdb *hwdb) {
337 assert_return(hwdb, NULL);
339 assert_se(REFCNT_INC(hwdb->n_ref) >= 2);
344 _public_ sd_hwdb *sd_hwdb_unref(sd_hwdb *hwdb) {
345 if (hwdb && REFCNT_DEC(hwdb->n_ref) == 0) {
347 munmap((void *)hwdb->map, hwdb->st.st_size);
350 free(hwdb->modalias);
351 ordered_hashmap_free(hwdb->properties);
358 bool hwdb_validate(sd_hwdb *hwdb) {
368 /* if hwdb.bin doesn't exist anywhere, we need to update */
369 NULSTR_FOREACH(p, hwdb_bin_paths) {
370 if (stat(p, &st) >= 0) {
378 if (timespec_load(&hwdb->st.st_mtim) != timespec_load(&st.st_mtim))
383 static int properties_prepare(sd_hwdb *hwdb, const char *modalias) {
384 _cleanup_free_ char *mod = NULL;
390 if (streq_ptr(modalias, hwdb->modalias))
393 mod = strdup(modalias);
397 ordered_hashmap_clear(hwdb->properties);
399 hwdb->properties_modified = true;
401 r = trie_search_f(hwdb, modalias);
405 free(hwdb->modalias);
406 hwdb->modalias = mod;
412 _public_ int sd_hwdb_get(sd_hwdb *hwdb, const char *modalias, const char *key, const char **_value) {
416 assert_return(hwdb, -EINVAL);
417 assert_return(hwdb->f, -EINVAL);
418 assert_return(modalias, -EINVAL);
419 assert_return(_value, -EINVAL);
421 r = properties_prepare(hwdb, modalias);
425 value = ordered_hashmap_get(hwdb->properties, key);
434 _public_ int sd_hwdb_seek(sd_hwdb *hwdb, const char *modalias) {
437 assert_return(hwdb, -EINVAL);
438 assert_return(hwdb->f, -EINVAL);
439 assert_return(modalias, -EINVAL);
441 r = properties_prepare(hwdb, modalias);
445 hwdb->properties_modified = false;
446 hwdb->properties_iterator = ITERATOR_FIRST;
451 _public_ int sd_hwdb_enumerate(sd_hwdb *hwdb, const char **key, const char **value) {
454 assert_return(hwdb, -EINVAL);
455 assert_return(key, -EINVAL);
456 assert_return(value, -EINVAL);
458 if (hwdb->properties_modified)
461 v = ordered_hashmap_iterate(hwdb->properties, &hwdb->properties_iterator, &k);