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/>.
37 #include "hwdb-util.h"
38 #include "hwdb-internal.h"
47 struct trie_header_f *head;
53 OrderedHashmap *properties;
54 Iterator properties_iterator;
55 bool properties_modified;
64 static void linebuf_init(struct linebuf *buf) {
69 static const char *linebuf_get(struct linebuf *buf) {
70 if (buf->len + 1 >= sizeof(buf->bytes))
72 buf->bytes[buf->len] = '\0';
76 static bool linebuf_add(struct linebuf *buf, const char *s, size_t len) {
77 if (buf->len + len >= sizeof(buf->bytes))
79 memcpy(buf->bytes + buf->len, s, len);
84 static bool linebuf_add_char(struct linebuf *buf, char c)
86 if (buf->len + 1 >= sizeof(buf->bytes))
88 buf->bytes[buf->len++] = c;
92 static void linebuf_rem(struct linebuf *buf, size_t count) {
93 assert(buf->len >= count);
97 static void linebuf_rem_char(struct linebuf *buf) {
101 static const struct trie_child_entry_f *trie_node_children(sd_hwdb *hwdb, const struct trie_node_f *node) {
102 return (const struct trie_child_entry_f *)((const char *)node + le64toh(hwdb->head->node_size));
105 static const struct trie_value_entry_f *trie_node_values(sd_hwdb *hwdb, const struct trie_node_f *node) {
106 const char *base = (const char *)node;
108 base += le64toh(hwdb->head->node_size);
109 base += node->children_count * le64toh(hwdb->head->child_entry_size);
110 return (const struct trie_value_entry_f *)base;
113 static const struct trie_node_f *trie_node_from_off(sd_hwdb *hwdb, le64_t off) {
114 return (const struct trie_node_f *)(hwdb->map + le64toh(off));
117 static const char *trie_string(sd_hwdb *hwdb, le64_t off) {
118 return hwdb->map + le64toh(off);
121 static int trie_children_cmp_f(const void *v1, const void *v2) {
122 const struct trie_child_entry_f *n1 = v1;
123 const struct trie_child_entry_f *n2 = v2;
125 return n1->c - n2->c;
128 static const struct trie_node_f *node_lookup_f(sd_hwdb *hwdb, const struct trie_node_f *node, uint8_t c) {
129 struct trie_child_entry_f *child;
130 struct trie_child_entry_f search;
133 child = bsearch(&search, trie_node_children(hwdb, node), node->children_count,
134 le64toh(hwdb->head->child_entry_size), trie_children_cmp_f);
136 return trie_node_from_off(hwdb, child->child_off);
140 static int hwdb_add_property(sd_hwdb *hwdb, const char *key, const char *value) {
148 * Silently ignore all properties which do not start with a
149 * space; future extensions might use additional prefixes.
156 r = ordered_hashmap_ensure_allocated(&hwdb->properties, &string_hash_ops);
160 r = ordered_hashmap_replace(hwdb->properties, key, (char*)value);
164 hwdb->properties_modified = true;
169 static int trie_fnmatch_f(sd_hwdb *hwdb, const struct trie_node_f *node, size_t p,
170 struct linebuf *buf, const char *search) {
176 prefix = trie_string(hwdb, node->prefix_off);
177 len = strlen(prefix + p);
178 linebuf_add(buf, prefix + p, len);
180 for (i = 0; i < node->children_count; i++) {
181 const struct trie_child_entry_f *child = &trie_node_children(hwdb, node)[i];
183 linebuf_add_char(buf, child->c);
184 err = trie_fnmatch_f(hwdb, trie_node_from_off(hwdb, child->child_off), 0, buf, search);
187 linebuf_rem_char(buf);
190 if (le64toh(node->values_count) && fnmatch(linebuf_get(buf), search, 0) == 0)
191 for (i = 0; i < le64toh(node->values_count); i++) {
192 err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[i].key_off),
193 trie_string(hwdb, trie_node_values(hwdb, node)[i].value_off));
198 linebuf_rem(buf, len);
202 static int trie_search_f(sd_hwdb *hwdb, const char *search) {
204 const struct trie_node_f *node;
210 node = trie_node_from_off(hwdb, hwdb->head->nodes_root_off);
212 const struct trie_node_f *child;
215 if (node->prefix_off) {
218 for (; (c = trie_string(hwdb, node->prefix_off)[p]); p++) {
219 if (c == '*' || c == '?' || c == '[')
220 return trie_fnmatch_f(hwdb, node, p, &buf, search + i + p);
221 if (c != search[i + p])
227 child = node_lookup_f(hwdb, node, '*');
229 linebuf_add_char(&buf, '*');
230 err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
233 linebuf_rem_char(&buf);
236 child = node_lookup_f(hwdb, node, '?');
238 linebuf_add_char(&buf, '?');
239 err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
242 linebuf_rem_char(&buf);
245 child = node_lookup_f(hwdb, node, '[');
247 linebuf_add_char(&buf, '[');
248 err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
251 linebuf_rem_char(&buf);
254 if (search[i] == '\0') {
257 for (n = 0; n < le64toh(node->values_count); n++) {
258 err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[n].key_off),
259 trie_string(hwdb, trie_node_values(hwdb, node)[n].value_off));
266 child = node_lookup_f(hwdb, node, search[i]);
273 static const char hwdb_bin_paths[] =
274 "/etc/systemd/hwdb/hwdb.bin\0"
275 "/etc/udev/hwdb.bin\0"
276 "/usr/lib/systemd/hwdb/hwdb.bin\0"
277 #ifdef HAVE_SPLIT_USR
278 "/lib/systemd/hwdb/hwdb.bin\0"
280 UDEVLIBEXECDIR "/hwdb.bin\0";
282 _public_ int sd_hwdb_new(sd_hwdb **ret) {
283 _cleanup_hwdb_unref_ sd_hwdb *hwdb = NULL;
284 const char *hwdb_bin_path;
285 const char sig[] = HWDB_SIG;
287 assert_return(ret, -EINVAL);
289 hwdb = new0(sd_hwdb, 1);
293 hwdb->n_ref = REFCNT_INIT;
295 /* find hwdb.bin in hwdb_bin_paths */
296 NULSTR_FOREACH(hwdb_bin_path, hwdb_bin_paths) {
297 hwdb->f = fopen(hwdb_bin_path, "re");
300 else if (errno == ENOENT)
303 return log_debug_errno(errno, "error reading %s: %m", hwdb_bin_path);
307 log_debug("hwdb.bin does not exist, please run udevadm hwdb --update");
311 if (fstat(fileno(hwdb->f), &hwdb->st) < 0 ||
312 (size_t)hwdb->st.st_size < offsetof(struct trie_header_f, strings_len) + 8)
313 return log_debug_errno(errno, "error reading %s: %m", hwdb_bin_path);
315 hwdb->map = mmap(0, hwdb->st.st_size, PROT_READ, MAP_SHARED, fileno(hwdb->f), 0);
316 if (hwdb->map == MAP_FAILED)
317 return log_debug_errno(errno, "error mapping %s: %m", hwdb_bin_path);
319 if (memcmp(hwdb->map, sig, sizeof(hwdb->head->signature)) != 0 ||
320 (size_t)hwdb->st.st_size != le64toh(hwdb->head->file_size)) {
321 log_debug("error recognizing the format of %s", hwdb_bin_path);
325 log_debug("=== trie on-disk ===");
326 log_debug("tool version: %"PRIu64, le64toh(hwdb->head->tool_version));
327 log_debug("file size: %8"PRIi64" bytes", hwdb->st.st_size);
328 log_debug("header size %8"PRIu64" bytes", le64toh(hwdb->head->header_size));
329 log_debug("strings %8"PRIu64" bytes", le64toh(hwdb->head->strings_len));
330 log_debug("nodes %8"PRIu64" bytes", le64toh(hwdb->head->nodes_len));
338 _public_ sd_hwdb *sd_hwdb_ref(sd_hwdb *hwdb) {
339 assert_return(hwdb, NULL);
341 assert_se(REFCNT_INC(hwdb->n_ref) >= 2);
346 _public_ sd_hwdb *sd_hwdb_unref(sd_hwdb *hwdb) {
347 if (hwdb && REFCNT_DEC(hwdb->n_ref) == 0) {
349 munmap((void *)hwdb->map, hwdb->st.st_size);
352 free(hwdb->modalias);
353 ordered_hashmap_free(hwdb->properties);
360 bool hwdb_validate(sd_hwdb *hwdb) {
370 /* if hwdb.bin doesn't exist anywhere, we need to update */
371 NULSTR_FOREACH(p, hwdb_bin_paths) {
372 if (stat(p, &st) >= 0) {
380 if (timespec_load(&hwdb->st.st_mtim) != timespec_load(&st.st_mtim))
385 static int properties_prepare(sd_hwdb *hwdb, const char *modalias) {
386 _cleanup_free_ char *mod = NULL;
392 if (streq_ptr(modalias, hwdb->modalias))
395 mod = strdup(modalias);
399 ordered_hashmap_clear(hwdb->properties);
401 hwdb->properties_modified = true;
403 r = trie_search_f(hwdb, modalias);
407 free(hwdb->modalias);
408 hwdb->modalias = mod;
414 _public_ int sd_hwdb_get(sd_hwdb *hwdb, const char *modalias, const char *key, const char **_value) {
418 assert_return(hwdb, -EINVAL);
419 assert_return(hwdb->f, -EINVAL);
420 assert_return(modalias, -EINVAL);
421 assert_return(_value, -EINVAL);
423 r = properties_prepare(hwdb, modalias);
427 value = ordered_hashmap_get(hwdb->properties, key);
436 _public_ int sd_hwdb_seek(sd_hwdb *hwdb, const char *modalias) {
439 assert_return(hwdb, -EINVAL);
440 assert_return(hwdb->f, -EINVAL);
441 assert_return(modalias, -EINVAL);
443 r = properties_prepare(hwdb, modalias);
447 hwdb->properties_modified = false;
448 hwdb->properties_iterator = ITERATOR_FIRST;
453 _public_ int sd_hwdb_enumerate(sd_hwdb *hwdb, const char **key, const char **value) {
456 assert_return(hwdb, -EINVAL);
457 assert_return(key, -EINVAL);
458 assert_return(value, -EINVAL);
460 if (hwdb->properties_modified)
463 v = ordered_hashmap_iterate(hwdb->properties, &hwdb->properties_iterator, &k);