libudev-hwdb.c revision 3cf7b686e6b29f78de0af5929602cae4482f6d49
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen/***
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen This file is part of systemd.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen Copyright 2012 Kay Sievers <kay@vrfy.org>
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen Copyright 2008 Alan Jenkins <alan.christopher.jenkins@googlemail.com>
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
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
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
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***/
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include <stdio.h>
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include <errno.h>
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include <string.h>
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include <inttypes.h>
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include <ctype.h>
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include <stdlib.h>
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include <fnmatch.h>
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include <getopt.h>
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include <sys/mman.h>
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "libudev-private.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen#include "libudev-hwdb-def.h"
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen/**
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * SECTION:libudev-hwdb
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * @short_description: retrieve properties from the hardware database
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen *
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * Libudev hardware database interface.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen/**
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * udev_hwdb:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen *
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * Opaque object representing the hardware database.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstruct udev_hwdb {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen struct udev *udev;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int refcount;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen FILE *f;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen struct stat st;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen union {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen struct trie_header_f *head;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen const char *map;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen };
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen struct udev_list properties_list;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen};
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstruct linebuf {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen char bytes[LINE_MAX];
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen size_t size;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen size_t len;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen};
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic void linebuf_init(struct linebuf *buf) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen buf->size = 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen buf->len = 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic const char *linebuf_get(struct linebuf *buf) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (buf->len + 1 >= sizeof(buf->bytes))
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen buf->bytes[buf->len] = '\0';
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return buf->bytes;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic bool linebuf_add(struct linebuf *buf, const char *s, size_t len) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (buf->len + len >= sizeof(buf->bytes))
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return false;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen memcpy(buf->bytes + buf->len, s, len);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen buf->len += len;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return true;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic bool linebuf_add_char(struct linebuf *buf, char c)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen{
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (buf->len + 1 >= sizeof(buf->bytes))
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return false;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen buf->bytes[buf->len++] = c;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return true;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic void linebuf_rem(struct linebuf *buf, size_t count) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen assert(buf->len >= count);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen buf->len -= count;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic void linebuf_rem_char(struct linebuf *buf) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen linebuf_rem(buf, 1);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
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 Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic const struct trie_value_entry_f *trie_node_values(struct udev_hwdb *hwdb, const struct trie_node_f *node) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen const char *base = (const char *)node;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen base += le64toh(hwdb->head->node_size);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen base += node->children_count * le64toh(hwdb->head->child_entry_size);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return (const struct trie_value_entry_f *)base;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
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 Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic const char *trie_string(struct udev_hwdb *hwdb, le64_t off) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return hwdb->map + le64toh(off);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int trie_children_cmp_f(const void *v1, const void *v2) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen const struct trie_child_entry_f *n1 = v1;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen const struct trie_child_entry_f *n2 = v2;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return n1->c - n2->c;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
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 struct trie_child_entry_f *child;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen struct trie_child_entry_f search;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen search.c = 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 if (child)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return trie_node_from_off(hwdb, child->child_off);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int hwdb_add_property(struct udev_hwdb *hwdb, const char *key, const char *value) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /*
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * Silently ignore all properties which do not start with a
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * space; future extensions might use additional prefixes.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (key[0] != ' ')
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (udev_list_entry_add(&hwdb->properties_list, key+1, value) == NULL)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return -ENOMEM;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int trie_fnmatch_f(struct udev_hwdb *hwdb, const struct trie_node_f *node, size_t p,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen struct linebuf *buf, const char *search) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen size_t len;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen size_t i;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen const char *prefix;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int err;
08232a020bd2571088d3ee06dda07732c5e963d1Tom Gundersen
08232a020bd2571088d3ee06dda07732c5e963d1Tom Gundersen prefix = trie_string(hwdb, node->prefix_off);
08232a020bd2571088d3ee06dda07732c5e963d1Tom Gundersen len = strlen(prefix + p);
08232a020bd2571088d3ee06dda07732c5e963d1Tom Gundersen linebuf_add(buf, prefix + p, len);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen for (i = 0; i < node->children_count; i++) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen const struct trie_child_entry_f *child = &trie_node_children(hwdb, node)[i];
08232a020bd2571088d3ee06dda07732c5e963d1Tom Gundersen
08232a020bd2571088d3ee06dda07732c5e963d1Tom Gundersen linebuf_add_char(buf, child->c);
08232a020bd2571088d3ee06dda07732c5e963d1Tom Gundersen err = trie_fnmatch_f(hwdb, trie_node_from_off(hwdb, child->child_off), 0, buf, search);
08232a020bd2571088d3ee06dda07732c5e963d1Tom Gundersen if (err < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return err;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen linebuf_rem_char(buf);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
08232a020bd2571088d3ee06dda07732c5e963d1Tom Gundersen
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));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (err < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return err;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen linebuf_rem(buf, len);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
52d629010db73a9466c359201916494bd55186d1Tom Gundersen
52d629010db73a9466c359201916494bd55186d1Tom Gundersenstatic int trie_search_f(struct udev_hwdb *hwdb, const char *search) {
52d629010db73a9466c359201916494bd55186d1Tom Gundersen struct linebuf buf;
52d629010db73a9466c359201916494bd55186d1Tom Gundersen const struct trie_node_f *node;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen size_t i = 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int err;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen linebuf_init(&buf);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
52d629010db73a9466c359201916494bd55186d1Tom Gundersen node = trie_node_from_off(hwdb, hwdb->head->nodes_root_off);
52d629010db73a9466c359201916494bd55186d1Tom Gundersen while (node) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen const struct trie_node_f *child;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen size_t p = 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (node->prefix_off) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen uint8_t c;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen for (; (c = trie_string(hwdb, node->prefix_off)[p]); p++) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (c == '*' || c == '?' || c == '[')
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return trie_fnmatch_f(hwdb, node, p, &buf, search + i + p);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (c != search[i + p])
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen i += p;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen child = node_lookup_f(hwdb, node, '*');
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (child) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen linebuf_add_char(&buf, '*');
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (err < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return err;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen linebuf_rem_char(&buf);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen child = node_lookup_f(hwdb, node, '?');
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (child) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen linebuf_add_char(&buf, '?');
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (err < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return err;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen linebuf_rem_char(&buf);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen child = node_lookup_f(hwdb, node, '[');
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (child) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen linebuf_add_char(&buf, '[');
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (err < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return err;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen linebuf_rem_char(&buf);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (search[i] == '\0') {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen size_t n;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
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 if (err < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return err;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen child = node_lookup_f(hwdb, node, search[i]);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen node = child;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen i++;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return 0;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen/**
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * udev_hwdb_new:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * @udev: udev library context
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen *
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * Create a hardware database context to query properties for devices.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen *
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * Returns: a hwdb context.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen **/
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen_public_ struct udev_hwdb *udev_hwdb_new(struct udev *udev) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen struct udev_hwdb *hwdb;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen const char sig[] = HWDB_SIG;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen hwdb = new0(struct udev_hwdb, 1);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (!hwdb)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen hwdb->refcount = 1;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen udev_list_init(udev, &hwdb->properties_list, true);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen hwdb->f = fopen("/etc/udev/hwdb.bin", "re");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (!hwdb->f) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_debug("error reading /etc/udev/hwdb.bin: %m");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen udev_hwdb_unref(hwdb);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (fstat(fileno(hwdb->f), &hwdb->st) < 0 ||
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 udev_hwdb_unref(hwdb);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen hwdb->map = mmap(0, hwdb->st.st_size, PROT_READ, MAP_SHARED, fileno(hwdb->f), 0);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (hwdb->map == MAP_FAILED) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_debug("error mapping /etc/udev/hwdb.bin: %m");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen udev_hwdb_unref(hwdb);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
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 udev_hwdb_unref(hwdb);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_debug("=== trie on-disk ===\n");
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));
08232a020bd2571088d3ee06dda07732c5e963d1Tom Gundersen return hwdb;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen/**
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * udev_hwdb_ref:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * @hwdb: context
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen *
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * Take a reference of a hwdb context.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen *
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * Returns: the passed enumeration context
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen **/
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen_public_ struct udev_hwdb *udev_hwdb_ref(struct udev_hwdb *hwdb) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (!hwdb)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen hwdb->refcount++;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return hwdb;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen/**
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * udev_hwdb_unref:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * @hwdb: context
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen *
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 *
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * Returns: the passed hwdb context if it has still an active reference, or #NULL otherwise.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen **/
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen_public_ struct udev_hwdb *udev_hwdb_unref(struct udev_hwdb *hwdb) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (!hwdb)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen hwdb->refcount--;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (hwdb->refcount > 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return hwdb;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (hwdb->map)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen munmap((void *)hwdb->map, hwdb->st.st_size);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (hwdb->f)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen fclose(hwdb->f);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen udev_list_cleanup(&hwdb->properties_list);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen free(hwdb);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenbool udev_hwdb_validate(struct udev_hwdb *hwdb) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen struct stat st;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (!hwdb)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return false;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (!hwdb->f)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return false;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (fstat(fileno(hwdb->f), &st) < 0)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return true;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (timespec_load(&hwdb->st.st_mtim) != timespec_load(&st.st_mtim))
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return true;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return false;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen/**
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * udev_hwdb_get_properties_list_entry:
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * @hwdb: context
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * @modalias: modalias string
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * @flags: (unused)
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen *
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 *
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * Returns: a udev_list_entry.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen_public_ struct udev_list_entry *udev_hwdb_get_properties_list_entry(struct udev_hwdb *hwdb, const char *modalias, unsigned int flags) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen int err;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (!hwdb || !hwdb->f) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen errno = EINVAL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen udev_list_cleanup(&hwdb->properties_list);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen err = trie_search_f(hwdb, modalias);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (err < 0) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen errno = -err;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen }
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return udev_list_get_entry(&hwdb->properties_list);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen}
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen