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/>.
32 #include "alloc-util.h"
35 #include "hwdb-internal.h"
36 #include "hwdb-util.h"
38 #include "string-util.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
) {
85 if (buf
->len
+ 1 >= sizeof(buf
->bytes
))
87 buf
->bytes
[buf
->len
++] = c
;
91 static void linebuf_rem(struct linebuf
*buf
, size_t count
) {
92 assert(buf
->len
>= count
);
96 static void linebuf_rem_char(struct linebuf
*buf
) {
100 static const struct trie_child_entry_f
*trie_node_child(sd_hwdb
*hwdb
, const struct trie_node_f
*node
, size_t idx
) {
101 const char *base
= (const char *)node
;
103 base
+= le64toh(hwdb
->head
->node_size
);
104 base
+= idx
* le64toh(hwdb
->head
->child_entry_size
);
105 return (const struct trie_child_entry_f
*)base
;
108 static const struct trie_value_entry_f
*trie_node_value(sd_hwdb
*hwdb
, const struct trie_node_f
*node
, size_t idx
) {
109 const char *base
= (const char *)node
;
111 base
+= le64toh(hwdb
->head
->node_size
);
112 base
+= node
->children_count
* le64toh(hwdb
->head
->child_entry_size
);
113 base
+= idx
* le64toh(hwdb
->head
->value_entry_size
);
114 return (const struct trie_value_entry_f
*)base
;
117 static const struct trie_node_f
*trie_node_from_off(sd_hwdb
*hwdb
, le64_t off
) {
118 return (const struct trie_node_f
*)(hwdb
->map
+ le64toh(off
));
121 static const char *trie_string(sd_hwdb
*hwdb
, le64_t off
) {
122 return hwdb
->map
+ le64toh(off
);
125 static int trie_children_cmp_f(const void *v1
, const void *v2
) {
126 const struct trie_child_entry_f
*n1
= v1
;
127 const struct trie_child_entry_f
*n2
= v2
;
129 return n1
->c
- n2
->c
;
132 static const struct trie_node_f
*node_lookup_f(sd_hwdb
*hwdb
, const struct trie_node_f
*node
, uint8_t c
) {
133 struct trie_child_entry_f
*child
;
134 struct trie_child_entry_f search
;
137 child
= bsearch(&search
, (const char *)node
+ le64toh(hwdb
->head
->node_size
), node
->children_count
,
138 le64toh(hwdb
->head
->child_entry_size
), trie_children_cmp_f
);
140 return trie_node_from_off(hwdb
, child
->child_off
);
144 static int hwdb_add_property(sd_hwdb
*hwdb
, const struct trie_value_entry_f
*entry
) {
150 key
= trie_string(hwdb
, entry
->key_off
);
153 * Silently ignore all properties which do not start with a
154 * space; future extensions might use additional prefixes.
161 if (le64toh(hwdb
->head
->value_entry_size
) >= sizeof(struct trie_value_entry2_f
)) {
162 const struct trie_value_entry2_f
*old
, *entry2
;
164 entry2
= (const struct trie_value_entry2_f
*)entry
;
165 old
= ordered_hashmap_get(hwdb
->properties
, key
);
167 /* on duplicates, we order by filename and line-number */
168 r
= strcmp(trie_string(hwdb
, entry2
->filename_off
), trie_string(hwdb
, old
->filename_off
));
170 (r
== 0 && entry2
->line_number
< old
->line_number
))
175 r
= ordered_hashmap_ensure_allocated(&hwdb
->properties
, &string_hash_ops
);
179 r
= ordered_hashmap_replace(hwdb
->properties
, key
, (void *)entry
);
183 hwdb
->properties_modified
= true;
188 static int trie_fnmatch_f(sd_hwdb
*hwdb
, const struct trie_node_f
*node
, size_t p
,
189 struct linebuf
*buf
, const char *search
) {
195 prefix
= trie_string(hwdb
, node
->prefix_off
);
196 len
= strlen(prefix
+ p
);
197 linebuf_add(buf
, prefix
+ p
, len
);
199 for (i
= 0; i
< node
->children_count
; i
++) {
200 const struct trie_child_entry_f
*child
= trie_node_child(hwdb
, node
, i
);
202 linebuf_add_char(buf
, child
->c
);
203 err
= trie_fnmatch_f(hwdb
, trie_node_from_off(hwdb
, child
->child_off
), 0, buf
, search
);
206 linebuf_rem_char(buf
);
209 if (le64toh(node
->values_count
) && fnmatch(linebuf_get(buf
), search
, 0) == 0)
210 for (i
= 0; i
< le64toh(node
->values_count
); i
++) {
211 err
= hwdb_add_property(hwdb
, trie_node_value(hwdb
, node
, i
));
216 linebuf_rem(buf
, len
);
220 static int trie_search_f(sd_hwdb
*hwdb
, const char *search
) {
222 const struct trie_node_f
*node
;
228 node
= trie_node_from_off(hwdb
, hwdb
->head
->nodes_root_off
);
230 const struct trie_node_f
*child
;
233 if (node
->prefix_off
) {
236 for (; (c
= trie_string(hwdb
, node
->prefix_off
)[p
]); p
++) {
237 if (c
== '*' || c
== '?' || c
== '[')
238 return trie_fnmatch_f(hwdb
, node
, p
, &buf
, search
+ i
+ p
);
239 if (c
!= search
[i
+ p
])
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 child
= node_lookup_f(hwdb
, node
, '?');
256 linebuf_add_char(&buf
, '?');
257 err
= trie_fnmatch_f(hwdb
, child
, 0, &buf
, search
+ i
);
260 linebuf_rem_char(&buf
);
263 child
= node_lookup_f(hwdb
, node
, '[');
265 linebuf_add_char(&buf
, '[');
266 err
= trie_fnmatch_f(hwdb
, child
, 0, &buf
, search
+ i
);
269 linebuf_rem_char(&buf
);
272 if (search
[i
] == '\0') {
275 for (n
= 0; n
< le64toh(node
->values_count
); n
++) {
276 err
= hwdb_add_property(hwdb
, trie_node_value(hwdb
, node
, n
));
283 child
= node_lookup_f(hwdb
, node
, search
[i
]);
290 static const char hwdb_bin_paths
[] =
291 "/etc/systemd/hwdb/hwdb.bin\0"
292 "/etc/udev/hwdb.bin\0"
293 "/usr/lib/systemd/hwdb/hwdb.bin\0"
294 #ifdef HAVE_SPLIT_USR
295 "/lib/systemd/hwdb/hwdb.bin\0"
297 UDEVLIBEXECDIR
"/hwdb.bin\0";
299 _public_
int sd_hwdb_new(sd_hwdb
**ret
) {
300 _cleanup_(sd_hwdb_unrefp
) sd_hwdb
*hwdb
= NULL
;
301 const char *hwdb_bin_path
;
302 const char sig
[] = HWDB_SIG
;
304 assert_return(ret
, -EINVAL
);
306 hwdb
= new0(sd_hwdb
, 1);
310 hwdb
->n_ref
= REFCNT_INIT
;
312 /* find hwdb.bin in hwdb_bin_paths */
313 NULSTR_FOREACH(hwdb_bin_path
, hwdb_bin_paths
) {
314 hwdb
->f
= fopen(hwdb_bin_path
, "re");
317 else if (errno
== ENOENT
)
320 return log_debug_errno(errno
, "error reading %s: %m", hwdb_bin_path
);
324 log_debug("hwdb.bin does not exist, please run systemd-hwdb update");
328 if (fstat(fileno(hwdb
->f
), &hwdb
->st
) < 0 ||
329 (size_t)hwdb
->st
.st_size
< offsetof(struct trie_header_f
, strings_len
) + 8)
330 return log_debug_errno(errno
, "error reading %s: %m", hwdb_bin_path
);
332 hwdb
->map
= mmap(0, hwdb
->st
.st_size
, PROT_READ
, MAP_SHARED
, fileno(hwdb
->f
), 0);
333 if (hwdb
->map
== MAP_FAILED
)
334 return log_debug_errno(errno
, "error mapping %s: %m", hwdb_bin_path
);
336 if (memcmp(hwdb
->map
, sig
, sizeof(hwdb
->head
->signature
)) != 0 ||
337 (size_t)hwdb
->st
.st_size
!= le64toh(hwdb
->head
->file_size
)) {
338 log_debug("error recognizing the format of %s", hwdb_bin_path
);
342 log_debug("=== trie on-disk ===");
343 log_debug("tool version: %"PRIu64
, le64toh(hwdb
->head
->tool_version
));
344 log_debug("file size: %8"PRIi64
" bytes", hwdb
->st
.st_size
);
345 log_debug("header size %8"PRIu64
" bytes", le64toh(hwdb
->head
->header_size
));
346 log_debug("strings %8"PRIu64
" bytes", le64toh(hwdb
->head
->strings_len
));
347 log_debug("nodes %8"PRIu64
" bytes", le64toh(hwdb
->head
->nodes_len
));
355 _public_ sd_hwdb
*sd_hwdb_ref(sd_hwdb
*hwdb
) {
356 assert_return(hwdb
, NULL
);
358 assert_se(REFCNT_INC(hwdb
->n_ref
) >= 2);
363 _public_ sd_hwdb
*sd_hwdb_unref(sd_hwdb
*hwdb
) {
364 if (hwdb
&& REFCNT_DEC(hwdb
->n_ref
) == 0) {
366 munmap((void *)hwdb
->map
, hwdb
->st
.st_size
);
367 safe_fclose(hwdb
->f
);
368 free(hwdb
->modalias
);
369 ordered_hashmap_free(hwdb
->properties
);
376 bool hwdb_validate(sd_hwdb
*hwdb
) {
386 /* if hwdb.bin doesn't exist anywhere, we need to update */
387 NULSTR_FOREACH(p
, hwdb_bin_paths
) {
388 if (stat(p
, &st
) >= 0) {
396 if (timespec_load(&hwdb
->st
.st_mtim
) != timespec_load(&st
.st_mtim
))
401 static int properties_prepare(sd_hwdb
*hwdb
, const char *modalias
) {
402 _cleanup_free_
char *mod
= NULL
;
408 if (streq_ptr(modalias
, hwdb
->modalias
))
411 mod
= strdup(modalias
);
415 ordered_hashmap_clear(hwdb
->properties
);
417 hwdb
->properties_modified
= true;
419 r
= trie_search_f(hwdb
, modalias
);
423 free(hwdb
->modalias
);
424 hwdb
->modalias
= mod
;
430 _public_
int sd_hwdb_get(sd_hwdb
*hwdb
, const char *modalias
, const char *key
, const char **_value
) {
431 const struct trie_value_entry_f
*entry
;
434 assert_return(hwdb
, -EINVAL
);
435 assert_return(hwdb
->f
, -EINVAL
);
436 assert_return(modalias
, -EINVAL
);
437 assert_return(_value
, -EINVAL
);
439 r
= properties_prepare(hwdb
, modalias
);
443 entry
= ordered_hashmap_get(hwdb
->properties
, key
);
447 *_value
= trie_string(hwdb
, entry
->value_off
);
452 _public_
int sd_hwdb_seek(sd_hwdb
*hwdb
, const char *modalias
) {
455 assert_return(hwdb
, -EINVAL
);
456 assert_return(hwdb
->f
, -EINVAL
);
457 assert_return(modalias
, -EINVAL
);
459 r
= properties_prepare(hwdb
, modalias
);
463 hwdb
->properties_modified
= false;
464 hwdb
->properties_iterator
= ITERATOR_FIRST
;
469 _public_
int sd_hwdb_enumerate(sd_hwdb
*hwdb
, const char **key
, const char **value
) {
470 const struct trie_value_entry_f
*entry
;
473 assert_return(hwdb
, -EINVAL
);
474 assert_return(key
, -EINVAL
);
475 assert_return(value
, -EINVAL
);
477 if (hwdb
->properties_modified
)
480 ordered_hashmap_iterate(hwdb
->properties
, &hwdb
->properties_iterator
, (void **)&entry
, &k
);
485 *value
= trie_string(hwdb
, entry
->value_off
);