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/>.
33 #include "hwdb-internal.h"
34 #include "hwdb-util.h"
36 #include "string-util.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
) {
83 if (buf
->len
+ 1 >= sizeof(buf
->bytes
))
85 buf
->bytes
[buf
->len
++] = c
;
89 static void linebuf_rem(struct linebuf
*buf
, size_t count
) {
90 assert(buf
->len
>= count
);
94 static void linebuf_rem_char(struct linebuf
*buf
) {
98 static const struct trie_child_entry_f
*trie_node_children(sd_hwdb
*hwdb
, const struct trie_node_f
*node
) {
99 return (const struct trie_child_entry_f
*)((const char *)node
+ le64toh(hwdb
->head
->node_size
));
102 static const struct trie_value_entry_f
*trie_node_values(sd_hwdb
*hwdb
, const struct trie_node_f
*node
) {
103 const char *base
= (const char *)node
;
105 base
+= le64toh(hwdb
->head
->node_size
);
106 base
+= node
->children_count
* le64toh(hwdb
->head
->child_entry_size
);
107 return (const struct trie_value_entry_f
*)base
;
110 static const struct trie_node_f
*trie_node_from_off(sd_hwdb
*hwdb
, le64_t off
) {
111 return (const struct trie_node_f
*)(hwdb
->map
+ le64toh(off
));
114 static const char *trie_string(sd_hwdb
*hwdb
, le64_t off
) {
115 return hwdb
->map
+ le64toh(off
);
118 static int trie_children_cmp_f(const void *v1
, const void *v2
) {
119 const struct trie_child_entry_f
*n1
= v1
;
120 const struct trie_child_entry_f
*n2
= v2
;
122 return n1
->c
- n2
->c
;
125 static const struct trie_node_f
*node_lookup_f(sd_hwdb
*hwdb
, const struct trie_node_f
*node
, uint8_t c
) {
126 struct trie_child_entry_f
*child
;
127 struct trie_child_entry_f search
;
130 child
= bsearch(&search
, trie_node_children(hwdb
, node
), node
->children_count
,
131 le64toh(hwdb
->head
->child_entry_size
), trie_children_cmp_f
);
133 return trie_node_from_off(hwdb
, child
->child_off
);
137 static int hwdb_add_property(sd_hwdb
*hwdb
, const char *key
, const char *value
) {
145 * Silently ignore all properties which do not start with a
146 * space; future extensions might use additional prefixes.
153 r
= ordered_hashmap_ensure_allocated(&hwdb
->properties
, &string_hash_ops
);
157 r
= ordered_hashmap_replace(hwdb
->properties
, key
, (char*)value
);
161 hwdb
->properties_modified
= true;
166 static int trie_fnmatch_f(sd_hwdb
*hwdb
, const struct trie_node_f
*node
, size_t p
,
167 struct linebuf
*buf
, const char *search
) {
173 prefix
= trie_string(hwdb
, node
->prefix_off
);
174 len
= strlen(prefix
+ p
);
175 linebuf_add(buf
, prefix
+ p
, len
);
177 for (i
= 0; i
< node
->children_count
; i
++) {
178 const struct trie_child_entry_f
*child
= &trie_node_children(hwdb
, node
)[i
];
180 linebuf_add_char(buf
, child
->c
);
181 err
= trie_fnmatch_f(hwdb
, trie_node_from_off(hwdb
, child
->child_off
), 0, buf
, search
);
184 linebuf_rem_char(buf
);
187 if (le64toh(node
->values_count
) && fnmatch(linebuf_get(buf
), search
, 0) == 0)
188 for (i
= 0; i
< le64toh(node
->values_count
); i
++) {
189 err
= hwdb_add_property(hwdb
, trie_string(hwdb
, trie_node_values(hwdb
, node
)[i
].key_off
),
190 trie_string(hwdb
, trie_node_values(hwdb
, node
)[i
].value_off
));
195 linebuf_rem(buf
, len
);
199 static int trie_search_f(sd_hwdb
*hwdb
, const char *search
) {
201 const struct trie_node_f
*node
;
207 node
= trie_node_from_off(hwdb
, hwdb
->head
->nodes_root_off
);
209 const struct trie_node_f
*child
;
212 if (node
->prefix_off
) {
215 for (; (c
= trie_string(hwdb
, node
->prefix_off
)[p
]); p
++) {
216 if (c
== '*' || c
== '?' || c
== '[')
217 return trie_fnmatch_f(hwdb
, node
, p
, &buf
, search
+ i
+ p
);
218 if (c
!= search
[i
+ p
])
224 child
= node_lookup_f(hwdb
, node
, '*');
226 linebuf_add_char(&buf
, '*');
227 err
= trie_fnmatch_f(hwdb
, child
, 0, &buf
, search
+ i
);
230 linebuf_rem_char(&buf
);
233 child
= node_lookup_f(hwdb
, node
, '?');
235 linebuf_add_char(&buf
, '?');
236 err
= trie_fnmatch_f(hwdb
, child
, 0, &buf
, search
+ i
);
239 linebuf_rem_char(&buf
);
242 child
= node_lookup_f(hwdb
, node
, '[');
244 linebuf_add_char(&buf
, '[');
245 err
= trie_fnmatch_f(hwdb
, child
, 0, &buf
, search
+ i
);
248 linebuf_rem_char(&buf
);
251 if (search
[i
] == '\0') {
254 for (n
= 0; n
< le64toh(node
->values_count
); n
++) {
255 err
= hwdb_add_property(hwdb
, trie_string(hwdb
, trie_node_values(hwdb
, node
)[n
].key_off
),
256 trie_string(hwdb
, trie_node_values(hwdb
, node
)[n
].value_off
));
263 child
= node_lookup_f(hwdb
, node
, search
[i
]);
270 static const char hwdb_bin_paths
[] =
271 "/etc/systemd/hwdb/hwdb.bin\0"
272 "/etc/udev/hwdb.bin\0"
273 "/usr/lib/systemd/hwdb/hwdb.bin\0"
274 #ifdef HAVE_SPLIT_USR
275 "/lib/systemd/hwdb/hwdb.bin\0"
277 UDEVLIBEXECDIR
"/hwdb.bin\0";
279 _public_
int sd_hwdb_new(sd_hwdb
**ret
) {
280 _cleanup_hwdb_unref_ sd_hwdb
*hwdb
= NULL
;
281 const char *hwdb_bin_path
;
282 const char sig
[] = HWDB_SIG
;
284 assert_return(ret
, -EINVAL
);
286 hwdb
= new0(sd_hwdb
, 1);
290 hwdb
->n_ref
= REFCNT_INIT
;
292 /* find hwdb.bin in hwdb_bin_paths */
293 NULSTR_FOREACH(hwdb_bin_path
, hwdb_bin_paths
) {
294 hwdb
->f
= fopen(hwdb_bin_path
, "re");
297 else if (errno
== ENOENT
)
300 return log_debug_errno(errno
, "error reading %s: %m", hwdb_bin_path
);
304 log_debug("hwdb.bin does not exist, please run udevadm hwdb --update");
308 if (fstat(fileno(hwdb
->f
), &hwdb
->st
) < 0 ||
309 (size_t)hwdb
->st
.st_size
< offsetof(struct trie_header_f
, strings_len
) + 8)
310 return log_debug_errno(errno
, "error reading %s: %m", hwdb_bin_path
);
312 hwdb
->map
= mmap(0, hwdb
->st
.st_size
, PROT_READ
, MAP_SHARED
, fileno(hwdb
->f
), 0);
313 if (hwdb
->map
== MAP_FAILED
)
314 return log_debug_errno(errno
, "error mapping %s: %m", hwdb_bin_path
);
316 if (memcmp(hwdb
->map
, sig
, sizeof(hwdb
->head
->signature
)) != 0 ||
317 (size_t)hwdb
->st
.st_size
!= le64toh(hwdb
->head
->file_size
)) {
318 log_debug("error recognizing the format of %s", hwdb_bin_path
);
322 log_debug("=== trie on-disk ===");
323 log_debug("tool version: %"PRIu64
, le64toh(hwdb
->head
->tool_version
));
324 log_debug("file size: %8"PRIi64
" bytes", hwdb
->st
.st_size
);
325 log_debug("header size %8"PRIu64
" bytes", le64toh(hwdb
->head
->header_size
));
326 log_debug("strings %8"PRIu64
" bytes", le64toh(hwdb
->head
->strings_len
));
327 log_debug("nodes %8"PRIu64
" bytes", le64toh(hwdb
->head
->nodes_len
));
335 _public_ sd_hwdb
*sd_hwdb_ref(sd_hwdb
*hwdb
) {
336 assert_return(hwdb
, NULL
);
338 assert_se(REFCNT_INC(hwdb
->n_ref
) >= 2);
343 _public_ sd_hwdb
*sd_hwdb_unref(sd_hwdb
*hwdb
) {
344 if (hwdb
&& REFCNT_DEC(hwdb
->n_ref
) == 0) {
346 munmap((void *)hwdb
->map
, hwdb
->st
.st_size
);
347 safe_fclose(hwdb
->f
);
348 free(hwdb
->modalias
);
349 ordered_hashmap_free(hwdb
->properties
);
356 bool hwdb_validate(sd_hwdb
*hwdb
) {
366 /* if hwdb.bin doesn't exist anywhere, we need to update */
367 NULSTR_FOREACH(p
, hwdb_bin_paths
) {
368 if (stat(p
, &st
) >= 0) {
376 if (timespec_load(&hwdb
->st
.st_mtim
) != timespec_load(&st
.st_mtim
))
381 static int properties_prepare(sd_hwdb
*hwdb
, const char *modalias
) {
382 _cleanup_free_
char *mod
= NULL
;
388 if (streq_ptr(modalias
, hwdb
->modalias
))
391 mod
= strdup(modalias
);
395 ordered_hashmap_clear(hwdb
->properties
);
397 hwdb
->properties_modified
= true;
399 r
= trie_search_f(hwdb
, modalias
);
403 free(hwdb
->modalias
);
404 hwdb
->modalias
= mod
;
410 _public_
int sd_hwdb_get(sd_hwdb
*hwdb
, const char *modalias
, const char *key
, const char **_value
) {
414 assert_return(hwdb
, -EINVAL
);
415 assert_return(hwdb
->f
, -EINVAL
);
416 assert_return(modalias
, -EINVAL
);
417 assert_return(_value
, -EINVAL
);
419 r
= properties_prepare(hwdb
, modalias
);
423 value
= ordered_hashmap_get(hwdb
->properties
, key
);
432 _public_
int sd_hwdb_seek(sd_hwdb
*hwdb
, const char *modalias
) {
435 assert_return(hwdb
, -EINVAL
);
436 assert_return(hwdb
->f
, -EINVAL
);
437 assert_return(modalias
, -EINVAL
);
439 r
= properties_prepare(hwdb
, modalias
);
443 hwdb
->properties_modified
= false;
444 hwdb
->properties_iterator
= ITERATOR_FIRST
;
449 _public_
int sd_hwdb_enumerate(sd_hwdb
*hwdb
, const char **key
, const char **value
) {
453 assert_return(hwdb
, -EINVAL
);
454 assert_return(key
, -EINVAL
);
455 assert_return(value
, -EINVAL
);
457 if (hwdb
->properties_modified
)
460 ordered_hashmap_iterate(hwdb
->properties
, &hwdb
->properties_iterator
, &v
, &k
);