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) {
144 * Silently ignore all properties which do not start with a
145 * space; future extensions might use additional prefixes.
150 if (udev_list_entry_add(&hwdb->properties_list, key+1, value) == NULL)
155 static int trie_fnmatch_f(struct udev_hwdb *hwdb, const struct trie_node_f *node, size_t p,
156 struct linebuf *buf, const char *search) {
162 prefix = trie_string(hwdb, node->prefix_off);
163 len = strlen(prefix + p);
164 linebuf_add(buf, prefix + p, len);
166 for (i = 0; i < node->children_count; i++) {
167 const struct trie_child_entry_f *child = &trie_node_children(hwdb, node)[i];
169 linebuf_add_char(buf, child->c);
170 err = trie_fnmatch_f(hwdb, trie_node_from_off(hwdb, child->child_off), 0, buf, search);
173 linebuf_rem_char(buf);
176 if (le64toh(node->values_count) && fnmatch(linebuf_get(buf), search, 0) == 0)
177 for (i = 0; i < le64toh(node->values_count); i++) {
178 err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[i].key_off),
179 trie_string(hwdb, trie_node_values(hwdb, node)[i].value_off));
184 linebuf_rem(buf, len);
188 static int trie_search_f(struct udev_hwdb *hwdb, const char *search) {
190 const struct trie_node_f *node;
196 node = trie_node_from_off(hwdb, hwdb->head->nodes_root_off);
198 const struct trie_node_f *child;
201 if (node->prefix_off) {
204 for (; (c = trie_string(hwdb, node->prefix_off)[p]); p++) {
205 if (c == '*' || c == '?' || c == '[')
206 return trie_fnmatch_f(hwdb, node, p, &buf, search + i + p);
207 if (c != search[i + p])
213 child = node_lookup_f(hwdb, node, '*');
215 linebuf_add_char(&buf, '*');
216 err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
219 linebuf_rem_char(&buf);
222 child = node_lookup_f(hwdb, node, '?');
224 linebuf_add_char(&buf, '?');
225 err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
228 linebuf_rem_char(&buf);
231 child = node_lookup_f(hwdb, node, '[');
233 linebuf_add_char(&buf, '[');
234 err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
237 linebuf_rem_char(&buf);
240 if (search[i] == '\0') {
243 for (n = 0; n < le64toh(node->values_count); n++) {
244 err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[n].key_off),
245 trie_string(hwdb, trie_node_values(hwdb, node)[n].value_off));
252 child = node_lookup_f(hwdb, node, search[i]);
261 * @udev: udev library context
263 * Create a hardware database context to query properties for devices.
265 * Returns: a hwdb context.
267 _public_ struct udev_hwdb *udev_hwdb_new(struct udev *udev) {
268 struct udev_hwdb *hwdb;
269 const char sig[] = HWDB_SIG;
271 hwdb = new0(struct udev_hwdb, 1);
276 udev_list_init(udev, &hwdb->properties_list, true);
278 hwdb->f = fopen("/etc/udev/hwdb.bin", "re");
280 udev_dbg(udev, "error reading /etc/udev/hwdb.bin: %m");
281 udev_hwdb_unref(hwdb);
285 if (fstat(fileno(hwdb->f), &hwdb->st) < 0 ||
286 (size_t)hwdb->st.st_size < offsetof(struct trie_header_f, strings_len) + 8) {
287 udev_dbg(udev, "error reading /etc/udev/hwdb.bin: %m");
288 udev_hwdb_unref(hwdb);
292 hwdb->map = mmap(0, hwdb->st.st_size, PROT_READ, MAP_SHARED, fileno(hwdb->f), 0);
293 if (hwdb->map == MAP_FAILED) {
294 udev_dbg(udev, "error mapping /etc/udev/hwdb.bin: %m");
295 udev_hwdb_unref(hwdb);
299 if (memcmp(hwdb->map, sig, sizeof(hwdb->head->signature)) != 0 ||
300 (size_t)hwdb->st.st_size != le64toh(hwdb->head->file_size)) {
301 udev_dbg(udev, "error recognizing the format of /etc/udev/hwdb.bin");
302 udev_hwdb_unref(hwdb);
306 udev_dbg(udev, "=== trie on-disk ===\n");
307 udev_dbg(udev, "tool version: %"PRIu64, le64toh(hwdb->head->tool_version));
308 udev_dbg(udev, "file size: %8llu bytes\n", (unsigned long long) hwdb->st.st_size);
309 udev_dbg(udev, "header size %8"PRIu64" bytes\n", le64toh(hwdb->head->header_size));
310 udev_dbg(udev, "strings %8"PRIu64" bytes\n", le64toh(hwdb->head->strings_len));
311 udev_dbg(udev, "nodes %8"PRIu64" bytes\n", le64toh(hwdb->head->nodes_len));
319 * Take a reference of a hwdb context.
321 * Returns: the passed enumeration context
323 _public_ struct udev_hwdb *udev_hwdb_ref(struct udev_hwdb *hwdb) {
334 * Drop a reference of a hwdb context. If the refcount reaches zero,
335 * all resources of the hwdb context will be released.
339 _public_ struct udev_hwdb *udev_hwdb_unref(struct udev_hwdb *hwdb) {
343 if (hwdb->refcount > 0)
346 munmap((void *)hwdb->map, hwdb->st.st_size);
349 udev_list_cleanup(&hwdb->properties_list);
354 bool udev_hwdb_validate(struct udev_hwdb *hwdb) {
361 if (stat("/etc/udev/hwdb.bin", &st) < 0)
363 if (timespec_load(&hwdb->st.st_mtim) != timespec_load(&st.st_mtim))
369 * udev_hwdb_get_properties_list_entry:
371 * @modalias: modalias string
374 * Lookup a matching device in the hardware database. The lookup key is a
375 * modalias string, whose formats are defined for the Linux kernel modules.
376 * Examples are: pci:v00008086d00001C2D*, usb:v04F2pB221*. The first entry
377 * of a list of retrieved properties is returned.
379 * Returns: a udev_list_entry.
381 _public_ struct udev_list_entry *udev_hwdb_get_properties_list_entry(struct udev_hwdb *hwdb, const char *modalias, unsigned int flags) {
384 if (!hwdb || !hwdb->f) {
389 udev_list_cleanup(&hwdb->properties_list);
390 err = trie_search_f(hwdb, modalias);
395 return udev_list_get_entry(&hwdb->properties_list);