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>
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
31 #include "libudev-private.h"
32 #include "libudev-hwdb-def.h"
35 * SECTION:libudev-hwdb
36 * @short_description: retrieve properties from the hardware database
38 * Libudev hardware database interface.
44 * Opaque object representing the hardware database.
53 struct trie_header_f *head;
57 struct udev_list properties_list;
66 static void linebuf_init(struct linebuf *buf) {
71 static const char *linebuf_get(struct linebuf *buf) {
72 if (buf->len + 1 >= sizeof(buf->bytes))
74 buf->bytes[buf->len] = '\0';
78 static bool linebuf_add(struct linebuf *buf, const char *s, size_t len) {
79 if (buf->len + len >= sizeof(buf->bytes))
81 memcpy(buf->bytes + buf->len, s, len);
86 static bool linebuf_add_char(struct linebuf *buf, char c)
88 if (buf->len + 1 >= sizeof(buf->bytes))
90 buf->bytes[buf->len++] = c;
94 static void linebuf_rem(struct linebuf *buf, size_t count) {
95 assert(buf->len >= count);
99 static void linebuf_rem_char(struct linebuf *buf) {
103 static const struct trie_child_entry_f *trie_node_children(struct udev_hwdb *hwdb, const struct trie_node_f *node) {
104 return (const struct trie_child_entry_f *)((const char *)node + le64toh(hwdb->head->node_size));
107 static const struct trie_value_entry_f *trie_node_values(struct udev_hwdb *hwdb, const struct trie_node_f *node) {
108 const char *base = (const char *)node;
110 base += le64toh(hwdb->head->node_size);
111 base += node->children_count * le64toh(hwdb->head->child_entry_size);
112 return (const struct trie_value_entry_f *)base;
115 static const struct trie_node_f *trie_node_from_off(struct udev_hwdb *hwdb, le64_t off) {
116 return (const struct trie_node_f *)(hwdb->map + le64toh(off));
119 static const char *trie_string(struct udev_hwdb *hwdb, le64_t off) {
120 return hwdb->map + le64toh(off);
123 static int trie_children_cmp_f(const void *v1, const void *v2) {
124 const struct trie_child_entry_f *n1 = v1;
125 const struct trie_child_entry_f *n2 = v2;
127 return n1->c - n2->c;
130 static const struct trie_node_f *node_lookup_f(struct udev_hwdb *hwdb, const struct trie_node_f *node, uint8_t c) {
131 struct trie_child_entry_f *child;
132 struct trie_child_entry_f search;
135 child = bsearch(&search, trie_node_children(hwdb, node), node->children_count,
136 le64toh(hwdb->head->child_entry_size), trie_children_cmp_f);
138 return trie_node_from_off(hwdb, child->child_off);
142 static int hwdb_add_property(struct udev_hwdb *hwdb, const char *key, const char *value) {
143 /* TODO: add sub-matches (+) against DMI data */
146 if (udev_list_entry_add(&hwdb->properties_list, key+1, value) == NULL)
151 static int trie_fnmatch_f(struct udev_hwdb *hwdb, const struct trie_node_f *node, size_t p,
152 struct linebuf *buf, const char *search) {
158 prefix = trie_string(hwdb, node->prefix_off);
159 len = strlen(prefix + p);
160 linebuf_add(buf, prefix + p, len);
162 for (i = 0; i < node->children_count; i++) {
163 const struct trie_child_entry_f *child = &trie_node_children(hwdb, node)[i];
165 linebuf_add_char(buf, child->c);
166 err = trie_fnmatch_f(hwdb, trie_node_from_off(hwdb, child->child_off), 0, buf, search);
169 linebuf_rem_char(buf);
172 if (le64toh(node->values_count) && fnmatch(linebuf_get(buf), search, 0) == 0)
173 for (i = 0; i < le64toh(node->values_count); i++) {
174 err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[i].key_off),
175 trie_string(hwdb, trie_node_values(hwdb, node)[i].value_off));
180 linebuf_rem(buf, len);
184 static int trie_search_f(struct udev_hwdb *hwdb, const char *search) {
186 const struct trie_node_f *node;
192 node = trie_node_from_off(hwdb, hwdb->head->nodes_root_off);
194 const struct trie_node_f *child;
197 if (node->prefix_off) {
200 for (; (c = trie_string(hwdb, node->prefix_off)[p]); p++) {
201 if (c == '*' || c == '?' || c == '[')
202 return trie_fnmatch_f(hwdb, node, p, &buf, search + i + p);
203 if (c != search[i + p])
209 child = node_lookup_f(hwdb, node, '*');
211 linebuf_add_char(&buf, '*');
212 err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
215 linebuf_rem_char(&buf);
218 child = node_lookup_f(hwdb, node, '?');
220 linebuf_add_char(&buf, '?');
221 err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
224 linebuf_rem_char(&buf);
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 if (search[i] == '\0') {
239 for (n = 0; n < le64toh(node->values_count); n++) {
240 err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[n].key_off),
241 trie_string(hwdb, trie_node_values(hwdb, node)[n].value_off));
248 child = node_lookup_f(hwdb, node, search[i]);
257 * @udev: udev library context
259 * Create a hardware database context to query properties for devices.
261 * Returns: a hwdb context.
263 _public_ struct udev_hwdb *udev_hwdb_new(struct udev *udev) {
264 struct udev_hwdb *hwdb;
265 const char sig[] = HWDB_SIG;
267 hwdb = new0(struct udev_hwdb, 1);
272 udev_list_init(udev, &hwdb->properties_list, true);
274 hwdb->f = fopen("/etc/udev/hwdb.bin", "re");
276 log_debug("error reading /etc/udev/hwdb.bin: %m");
277 udev_hwdb_unref(hwdb);
281 if (fstat(fileno(hwdb->f), &hwdb->st) < 0 ||
282 (size_t)hwdb->st.st_size < offsetof(struct trie_header_f, strings_len) + 8) {
283 log_debug("error reading /etc/udev/hwdb.bin: %m");
284 udev_hwdb_unref(hwdb);
288 hwdb->map = mmap(0, hwdb->st.st_size, PROT_READ, MAP_SHARED, fileno(hwdb->f), 0);
289 if (hwdb->map == MAP_FAILED) {
290 log_debug("error mapping /etc/udev/hwdb.bin: %m");
291 udev_hwdb_unref(hwdb);
295 if (memcmp(hwdb->map, sig, sizeof(hwdb->head->signature)) != 0 ||
296 (size_t)hwdb->st.st_size != le64toh(hwdb->head->file_size)) {
297 log_debug("error recognizing the format of /etc/udev/hwdb.bin");
298 udev_hwdb_unref(hwdb);
302 log_debug("=== trie on-disk ===\n");
303 log_debug("tool version: %llu", (unsigned long long)le64toh(hwdb->head->tool_version));
304 log_debug("file size: %8llu bytes\n", (unsigned long long)hwdb->st.st_size);
305 log_debug("header size %8llu bytes\n", (unsigned long long)le64toh(hwdb->head->header_size));
306 log_debug("strings %8llu bytes\n", (unsigned long long)le64toh(hwdb->head->strings_len));
307 log_debug("nodes %8llu bytes\n", (unsigned long long)le64toh(hwdb->head->nodes_len));
315 * Take a reference of a hwdb context.
317 * Returns: the passed enumeration context
319 _public_ struct udev_hwdb *udev_hwdb_ref(struct udev_hwdb *hwdb) {
330 * Drop a reference of a hwdb context. If the refcount reaches zero,
331 * all resources of the hwdb context will be released.
333 * Returns: the passed hwdb context if it has still an active reference, or #NULL otherwise.
335 _public_ struct udev_hwdb *udev_hwdb_unref(struct udev_hwdb *hwdb) {
339 if (hwdb->refcount > 0)
342 munmap((void *)hwdb->map, hwdb->st.st_size);
345 udev_list_cleanup(&hwdb->properties_list);
350 bool udev_hwdb_validate(struct udev_hwdb *hwdb) {
357 if (fstat(fileno(hwdb->f), &st) < 0)
359 if (timespec_load(&hwdb->st.st_mtim) != timespec_load(&st.st_mtim))
365 * udev_hwdb_get_properties_list_entry:
367 * @modalias: modalias string
370 * Lookup a matching device in the hardware database. The lookup key is a
371 * modalias string, whose formats are defined for the Linux kernel modules.
372 * Examples are: pci:v00008086d00001C2D*, usb:v04F2pB221*. The first entry
373 * of a list of retrieved properties is returned.
375 * Returns: a udev_list_entry.
377 _public_ struct udev_list_entry *udev_hwdb_get_properties_list_entry(struct udev_hwdb *hwdb, const char *modalias, unsigned int flags) {
380 if (!hwdb || !hwdb->f) {
385 udev_list_cleanup(&hwdb->properties_list);
386 err = trie_search_f(hwdb, modalias);
391 return udev_list_get_entry(&hwdb->properties_list);