libudev-hwdb.c revision 3cf7b686e6b29f78de0af5929602cae4482f6d49
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen This file is part of systemd.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen Copyright 2012 Kay Sievers <kay@vrfy.org>
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen Copyright 2008 Alan Jenkins <alan.christopher.jenkins@googlemail.com>
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen systemd is free software; you can redistribute it and/or modify it
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen under the terms of the GNU Lesser General Public License as published by
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen the Free Software Foundation; either version 2.1 of the License, or
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen (at your option) any later version.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen systemd is distributed in the hope that it will be useful, but
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen Lesser General Public License for more details.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen You should have received a copy of the GNU Lesser General Public License
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * SECTION:libudev-hwdb
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * @short_description: retrieve properties from the hardware database
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * Libudev hardware database interface.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * Opaque object representing the hardware database.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen const char *map;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic void linebuf_init(struct linebuf *buf) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic const char *linebuf_get(struct linebuf *buf) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic bool linebuf_add(struct linebuf *buf, const char *s, size_t len) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return false;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic bool linebuf_add_char(struct linebuf *buf, char c)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return false;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic void linebuf_rem(struct linebuf *buf, size_t count) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic void linebuf_rem_char(struct linebuf *buf) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic const struct trie_child_entry_f *trie_node_children(struct udev_hwdb *hwdb, const struct trie_node_f *node) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return (const struct trie_child_entry_f *)((const char *)node + le64toh(hwdb->head->node_size));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic const struct trie_value_entry_f *trie_node_values(struct udev_hwdb *hwdb, const struct trie_node_f *node) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen base += node->children_count * le64toh(hwdb->head->child_entry_size);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return (const struct trie_value_entry_f *)base;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic const struct trie_node_f *trie_node_from_off(struct udev_hwdb *hwdb, le64_t off) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return (const struct trie_node_f *)(hwdb->map + le64toh(off));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic const char *trie_string(struct udev_hwdb *hwdb, le64_t off) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int trie_children_cmp_f(const void *v1, const void *v2) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic const struct trie_node_f *node_lookup_f(struct udev_hwdb *hwdb, const struct trie_node_f *node, uint8_t c) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen child = bsearch(&search, trie_node_children(hwdb, node), node->children_count,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen le64toh(hwdb->head->child_entry_size), trie_children_cmp_f);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return trie_node_from_off(hwdb, child->child_off);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int hwdb_add_property(struct udev_hwdb *hwdb, const char *key, const char *value) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * Silently ignore all properties which do not start with a
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * space; future extensions might use additional prefixes.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (udev_list_entry_add(&hwdb->properties_list, key+1, value) == NULL)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int trie_fnmatch_f(struct udev_hwdb *hwdb, const struct trie_node_f *node, size_t p,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen const struct trie_child_entry_f *child = &trie_node_children(hwdb, node)[i];
08232a020bd2571088d3ee06dda07732c5e963d1Tom Gundersen err = trie_fnmatch_f(hwdb, trie_node_from_off(hwdb, child->child_off), 0, buf, search);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (le64toh(node->values_count) && fnmatch(linebuf_get(buf), search, 0) == 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen for (i = 0; i < le64toh(node->values_count); i++) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[i].key_off),
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen trie_string(hwdb, trie_node_values(hwdb, node)[i].value_off));
52d629010db73a9466c359201916494bd55186d1Tom Gundersenstatic int trie_search_f(struct udev_hwdb *hwdb, const char *search) {
52d629010db73a9466c359201916494bd55186d1Tom Gundersen node = trie_node_from_off(hwdb, hwdb->head->nodes_root_off);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen for (; (c = trie_string(hwdb, node->prefix_off)[p]); p++) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return trie_fnmatch_f(hwdb, node, p, &buf, search + i + p);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (c != search[i + p])
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen for (n = 0; n < le64toh(node->values_count); n++) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[n].key_off),
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen trie_string(hwdb, trie_node_values(hwdb, node)[n].value_off));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * udev_hwdb_new:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * @udev: udev library context
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * Create a hardware database context to query properties for devices.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * Returns: a hwdb context.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen_public_ struct udev_hwdb *udev_hwdb_new(struct udev *udev) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen udev_list_init(udev, &hwdb->properties_list, true);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_debug("error reading /etc/udev/hwdb.bin: %m");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen (size_t)hwdb->st.st_size < offsetof(struct trie_header_f, strings_len) + 8) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_debug("error reading /etc/udev/hwdb.bin: %m");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen hwdb->map = mmap(0, hwdb->st.st_size, PROT_READ, MAP_SHARED, fileno(hwdb->f), 0);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_debug("error mapping /etc/udev/hwdb.bin: %m");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (memcmp(hwdb->map, sig, sizeof(hwdb->head->signature)) != 0 ||
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen (size_t)hwdb->st.st_size != le64toh(hwdb->head->file_size)) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_debug("error recognizing the format of /etc/udev/hwdb.bin");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_debug("tool version: %"PRIu64, le64toh(hwdb->head->tool_version));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_debug("file size: %8llu bytes\n", (unsigned long long) hwdb->st.st_size);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_debug("header size %8"PRIu64" bytes\n", le64toh(hwdb->head->header_size));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_debug("strings %8"PRIu64" bytes\n", le64toh(hwdb->head->strings_len));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_debug("nodes %8"PRIu64" bytes\n", le64toh(hwdb->head->nodes_len));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * udev_hwdb_ref:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * @hwdb: context
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * Take a reference of a hwdb context.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * Returns: the passed enumeration context
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen_public_ struct udev_hwdb *udev_hwdb_ref(struct udev_hwdb *hwdb) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * udev_hwdb_unref:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * @hwdb: context
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * Drop a reference of a hwdb context. If the refcount reaches zero,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * all resources of the hwdb context will be released.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * Returns: the passed hwdb context if it has still an active reference, or #NULL otherwise.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen_public_ struct udev_hwdb *udev_hwdb_unref(struct udev_hwdb *hwdb) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenbool udev_hwdb_validate(struct udev_hwdb *hwdb) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return false;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return false;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (timespec_load(&hwdb->st.st_mtim) != timespec_load(&st.st_mtim))
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return false;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * udev_hwdb_get_properties_list_entry:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * @hwdb: context
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * @modalias: modalias string
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * @flags: (unused)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * Lookup a matching device in the hardware database. The lookup key is a
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * modalias string, whose formats are defined for the Linux kernel modules.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * Examples are: pci:v00008086d00001C2D*, usb:v04F2pB221*. The first entry
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * of a list of retrieved properties is returned.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * Returns: a udev_list_entry.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen_public_ struct udev_list_entry *udev_hwdb_get_properties_list_entry(struct udev_hwdb *hwdb, const char *modalias, unsigned int flags) {