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_children(sd_hwdb
*hwdb
, const struct trie_node_f
*node
) {
101 return (const struct trie_child_entry_f
*)((const char *)node
+ le64toh(hwdb
->head
->node_size
));
104 static const struct trie_value_entry_f
*trie_node_values(sd_hwdb
*hwdb
, const struct trie_node_f
*node
) {
105 const char *base
= (const char *)node
;
107 base
+= le64toh(hwdb
->head
->node_size
);
108 base
+= node
->children_count
* le64toh(hwdb
->head
->child_entry_size
);
109 return (const struct trie_value_entry_f
*)base
;
112 static const struct trie_node_f
*trie_node_from_off(sd_hwdb
*hwdb
, le64_t off
) {
113 return (const struct trie_node_f
*)(hwdb
->map
+ le64toh(off
));
116 static const char *trie_string(sd_hwdb
*hwdb
, le64_t off
) {
117 return hwdb
->map
+ le64toh(off
);
120 static int trie_children_cmp_f(const void *v1
, const void *v2
) {
121 const struct trie_child_entry_f
*n1
= v1
;
122 const struct trie_child_entry_f
*n2
= v2
;
124 return n1
->c
- n2
->c
;
127 static const struct trie_node_f
*node_lookup_f(sd_hwdb
*hwdb
, const struct trie_node_f
*node
, uint8_t c
) {
128 struct trie_child_entry_f
*child
;
129 struct trie_child_entry_f search
;
132 child
= bsearch(&search
, trie_node_children(hwdb
, node
), node
->children_count
,
133 le64toh(hwdb
->head
->child_entry_size
), trie_children_cmp_f
);
135 return trie_node_from_off(hwdb
, child
->child_off
);
139 static int hwdb_add_property(sd_hwdb
*hwdb
, const char *key
, const char *value
) {
147 * Silently ignore all properties which do not start with a
148 * space; future extensions might use additional prefixes.
155 r
= ordered_hashmap_ensure_allocated(&hwdb
->properties
, &string_hash_ops
);
159 r
= ordered_hashmap_replace(hwdb
->properties
, key
, (char*)value
);
163 hwdb
->properties_modified
= true;
168 static int trie_fnmatch_f(sd_hwdb
*hwdb
, const struct trie_node_f
*node
, size_t p
,
169 struct linebuf
*buf
, const char *search
) {
175 prefix
= trie_string(hwdb
, node
->prefix_off
);
176 len
= strlen(prefix
+ p
);
177 linebuf_add(buf
, prefix
+ p
, len
);
179 for (i
= 0; i
< node
->children_count
; i
++) {
180 const struct trie_child_entry_f
*child
= &trie_node_children(hwdb
, node
)[i
];
182 linebuf_add_char(buf
, child
->c
);
183 err
= trie_fnmatch_f(hwdb
, trie_node_from_off(hwdb
, child
->child_off
), 0, buf
, search
);
186 linebuf_rem_char(buf
);
189 if (le64toh(node
->values_count
) && fnmatch(linebuf_get(buf
), search
, 0) == 0)
190 for (i
= 0; i
< le64toh(node
->values_count
); i
++) {
191 err
= hwdb_add_property(hwdb
, trie_string(hwdb
, trie_node_values(hwdb
, node
)[i
].key_off
),
192 trie_string(hwdb
, trie_node_values(hwdb
, node
)[i
].value_off
));
197 linebuf_rem(buf
, len
);
201 static int trie_search_f(sd_hwdb
*hwdb
, const char *search
) {
203 const struct trie_node_f
*node
;
209 node
= trie_node_from_off(hwdb
, hwdb
->head
->nodes_root_off
);
211 const struct trie_node_f
*child
;
214 if (node
->prefix_off
) {
217 for (; (c
= trie_string(hwdb
, node
->prefix_off
)[p
]); p
++) {
218 if (c
== '*' || c
== '?' || c
== '[')
219 return trie_fnmatch_f(hwdb
, node
, p
, &buf
, search
+ i
+ p
);
220 if (c
!= search
[i
+ p
])
226 child
= node_lookup_f(hwdb
, node
, '*');
228 linebuf_add_char(&buf
, '*');
229 err
= trie_fnmatch_f(hwdb
, child
, 0, &buf
, search
+ i
);
232 linebuf_rem_char(&buf
);
235 child
= node_lookup_f(hwdb
, node
, '?');
237 linebuf_add_char(&buf
, '?');
238 err
= trie_fnmatch_f(hwdb
, child
, 0, &buf
, search
+ i
);
241 linebuf_rem_char(&buf
);
244 child
= node_lookup_f(hwdb
, node
, '[');
246 linebuf_add_char(&buf
, '[');
247 err
= trie_fnmatch_f(hwdb
, child
, 0, &buf
, search
+ i
);
250 linebuf_rem_char(&buf
);
253 if (search
[i
] == '\0') {
256 for (n
= 0; n
< le64toh(node
->values_count
); n
++) {
257 err
= hwdb_add_property(hwdb
, trie_string(hwdb
, trie_node_values(hwdb
, node
)[n
].key_off
),
258 trie_string(hwdb
, trie_node_values(hwdb
, node
)[n
].value_off
));
265 child
= node_lookup_f(hwdb
, node
, search
[i
]);
272 static const char hwdb_bin_paths
[] =
273 "/etc/systemd/hwdb/hwdb.bin\0"
274 "/etc/udev/hwdb.bin\0"
275 "/usr/lib/systemd/hwdb/hwdb.bin\0"
276 #ifdef HAVE_SPLIT_USR
277 "/lib/systemd/hwdb/hwdb.bin\0"
279 UDEVLIBEXECDIR
"/hwdb.bin\0";
281 _public_
int sd_hwdb_new(sd_hwdb
**ret
) {
282 _cleanup_(sd_hwdb_unrefp
) sd_hwdb
*hwdb
= NULL
;
283 const char *hwdb_bin_path
;
284 const char sig
[] = HWDB_SIG
;
286 assert_return(ret
, -EINVAL
);
288 hwdb
= new0(sd_hwdb
, 1);
292 hwdb
->n_ref
= REFCNT_INIT
;
294 /* find hwdb.bin in hwdb_bin_paths */
295 NULSTR_FOREACH(hwdb_bin_path
, hwdb_bin_paths
) {
296 hwdb
->f
= fopen(hwdb_bin_path
, "re");
299 else if (errno
== ENOENT
)
302 return log_debug_errno(errno
, "error reading %s: %m", hwdb_bin_path
);
306 log_debug("hwdb.bin does not exist, please run udevadm hwdb --update");
310 if (fstat(fileno(hwdb
->f
), &hwdb
->st
) < 0 ||
311 (size_t)hwdb
->st
.st_size
< offsetof(struct trie_header_f
, strings_len
) + 8)
312 return log_debug_errno(errno
, "error reading %s: %m", hwdb_bin_path
);
314 hwdb
->map
= mmap(0, hwdb
->st
.st_size
, PROT_READ
, MAP_SHARED
, fileno(hwdb
->f
), 0);
315 if (hwdb
->map
== MAP_FAILED
)
316 return log_debug_errno(errno
, "error mapping %s: %m", hwdb_bin_path
);
318 if (memcmp(hwdb
->map
, sig
, sizeof(hwdb
->head
->signature
)) != 0 ||
319 (size_t)hwdb
->st
.st_size
!= le64toh(hwdb
->head
->file_size
)) {
320 log_debug("error recognizing the format of %s", hwdb_bin_path
);
324 log_debug("=== trie on-disk ===");
325 log_debug("tool version: %"PRIu64
, le64toh(hwdb
->head
->tool_version
));
326 log_debug("file size: %8"PRIi64
" bytes", hwdb
->st
.st_size
);
327 log_debug("header size %8"PRIu64
" bytes", le64toh(hwdb
->head
->header_size
));
328 log_debug("strings %8"PRIu64
" bytes", le64toh(hwdb
->head
->strings_len
));
329 log_debug("nodes %8"PRIu64
" bytes", le64toh(hwdb
->head
->nodes_len
));
337 _public_ sd_hwdb
*sd_hwdb_ref(sd_hwdb
*hwdb
) {
338 assert_return(hwdb
, NULL
);
340 assert_se(REFCNT_INC(hwdb
->n_ref
) >= 2);
345 _public_ sd_hwdb
*sd_hwdb_unref(sd_hwdb
*hwdb
) {
346 if (hwdb
&& REFCNT_DEC(hwdb
->n_ref
) == 0) {
348 munmap((void *)hwdb
->map
, hwdb
->st
.st_size
);
349 safe_fclose(hwdb
->f
);
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
) {
455 assert_return(hwdb
, -EINVAL
);
456 assert_return(key
, -EINVAL
);
457 assert_return(value
, -EINVAL
);
459 if (hwdb
->properties_modified
)
462 ordered_hashmap_iterate(hwdb
->properties
, &hwdb
->properties_iterator
, &v
, &k
);