[systemd-commits] docs/libudev Makefile.am src/libudev src/test src/udev

Kay Sievers kay at kemper.freedesktop.org
Sat Oct 27 14:46:24 PDT 2012


 Makefile.am                       |   11 -
 docs/libudev/Makefile.am          |    2 
 docs/libudev/libudev-docs.xml     |    1 
 docs/libudev/libudev-sections.txt |   10 
 src/libudev/libudev-hwdb-def.h    |   74 +++++++
 src/libudev/libudev-hwdb.c        |  391 ++++++++++++++++++++++++++++++++++++++
 src/libudev/libudev-private.h     |   14 -
 src/libudev/libudev.h             |   11 +
 src/libudev/libudev.sym           |    8 
 src/test/test-libudev.c           |   16 +
 src/udev/udev-builtin-hwdb.c      |  314 +++---------------------------
 src/udev/udev-hwdb.h              |   69 ------
 src/udev/udevadm-hwdb.c           |   39 ++-
 13 files changed, 585 insertions(+), 375 deletions(-)

New commits:
commit 2001208c2ab631a69896d1f670c26846b70d1fb7
Author: Kay Sievers <kay at vrfy.org>
Date:   Sat Oct 27 20:59:01 2012 +0200

    libudev: import hwdb and export lookup interface

diff --git a/Makefile.am b/Makefile.am
index 634c67d..3d96500 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -26,9 +26,9 @@ SUBDIRS = . po
 # remove targets if the command fails
 .DELETE_ON_ERROR:
 
-LIBUDEV_CURRENT=2
-LIBUDEV_REVISION=6
-LIBUDEV_AGE=1
+LIBUDEV_CURRENT=3
+LIBUDEV_REVISION=0
+LIBUDEV_AGE=2
 
 LIBGUDEV_CURRENT=1
 LIBGUDEV_REVISION=2
@@ -1688,7 +1688,9 @@ libudev_la_SOURCES =\
 	src/libudev/libudev-device.c \
 	src/libudev/libudev-enumerate.c \
 	src/libudev/libudev-monitor.c \
-	src/libudev/libudev-queue.c
+	src/libudev/libudev-queue.c \
+	src/libudev/libudev-hwdb-def.h \
+	src/libudev/libudev-hwdb.c
 
 libudev_la_CFLAGS = \
 	$(AM_CFLAGS) \
@@ -1833,7 +1835,6 @@ noinst_LTLIBRARIES += \
 
 libudev_core_la_SOURCES = \
 	src/udev/udev.h \
-	src/udev/udev-hwdb.h \
 	src/udev/udev-event.c \
 	src/udev/udev-watch.c \
 	src/udev/udev-node.c \
diff --git a/docs/libudev/Makefile.am b/docs/libudev/Makefile.am
index 13e448a..6a346a6 100644
--- a/docs/libudev/Makefile.am
+++ b/docs/libudev/Makefile.am
@@ -57,7 +57,7 @@ EXTRA_HFILES=
 
 # Header files to ignore when scanning. Use base file name, no paths
 # e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h
-IGNORE_HFILES = libudev-private.h
+IGNORE_HFILES = libudev-private.h libudev-hwdb-def.h
 
 # Images to copy into HTML directory.
 # e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
diff --git a/docs/libudev/libudev-docs.xml b/docs/libudev/libudev-docs.xml
index d8248aa..454cd31 100644
--- a/docs/libudev/libudev-docs.xml
+++ b/docs/libudev/libudev-docs.xml
@@ -28,6 +28,7 @@
     <xi:include href="xml/libudev-monitor.xml"/>
     <xi:include href="xml/libudev-enumerate.xml"/>
     <xi:include href="xml/libudev-queue.xml"/>
+    <xi:include href="xml/libudev-hwdb.xml"/>
     <xi:include href="xml/libudev-util.xml"/>
     <xi:include href="xml/api-index-deprecated.xml"/>
   </chapter>
diff --git a/docs/libudev/libudev-sections.txt b/docs/libudev/libudev-sections.txt
index 11e8e1c..067a3f5 100644
--- a/docs/libudev/libudev-sections.txt
+++ b/docs/libudev/libudev-sections.txt
@@ -118,6 +118,16 @@ udev_queue_get_udev_seqnum
 </SECTION>
 
 <SECTION>
+<FILE>libudev-hwdb</FILE>
+<TITLE>udev_hwdb</TITLE>
+udev_hwdb
+udev_hwdb_ref
+udev_hwdb_unref
+udev_hwdb_new
+udev_hwdb_get_properties_list_entry
+</SECTION>
+
+<SECTION>
 <FILE>libudev-util</FILE>
 <TITLE>udev_util</TITLE>
 udev_util_encode_string
diff --git a/src/libudev/libudev-hwdb-def.h b/src/libudev/libudev-hwdb-def.h
new file mode 100644
index 0000000..8bc6944
--- /dev/null
+++ b/src/libudev/libudev-hwdb-def.h
@@ -0,0 +1,74 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2012 Kay Sievers <kay.sievers at vrfy.org>
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifndef _LIBUDEV_HWDB_DEF_H_
+#define _LIBUDEV_HWDB_DEF_H_
+
+#include "sparse-endian.h"
+
+#define HWDB_SIG { 'K', 'S', 'L', 'P', 'H', 'H', 'R', 'H' }
+
+/* on-disk trie objects */
+_packed_ struct trie_header_f {
+        uint8_t signature[8];
+
+        /* version of tool which created the file */
+        le64_t tool_version;
+        le64_t file_size;
+
+        /* size of structures to allow them to grow */
+        le64_t header_size;
+        le64_t node_size;
+        le64_t child_entry_size;
+        le64_t value_entry_size;
+
+        /* offset of the root trie node */
+        le64_t nodes_root_off;
+
+        /* size of the nodes and string section */
+        le64_t nodes_len;
+        le64_t strings_len;
+};
+
+_packed_ struct trie_node_f {
+        /* prefix of lookup string, shared by all children  */
+        le64_t prefix_off;
+        /* size of children entry array appended to the node */
+        uint8_t children_count;
+        uint8_t padding[7];
+        /* size of value entry array appended to the node */
+        le64_t values_count;
+};
+
+/* array of child entries, follows directly the node record */
+_packed_ struct trie_child_entry_f {
+        /* index of the child node */
+        uint8_t c;
+        uint8_t padding[7];
+        /* offset of the child node */
+        le64_t child_off;
+};
+
+/* array of value entries, follows directly the node record/child array */
+_packed_ struct trie_value_entry_f {
+        le64_t key_off;
+        le64_t value_off;
+};
+
+#endif
diff --git a/src/libudev/libudev-hwdb.c b/src/libudev/libudev-hwdb.c
new file mode 100644
index 0000000..92c58b6
--- /dev/null
+++ b/src/libudev/libudev-hwdb.c
@@ -0,0 +1,391 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2012 Kay Sievers <kay.sievers at vrfy.org>
+  Copyright 2008 Alan Jenkins <alan.christopher.jenkins at googlemail.com>
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <fnmatch.h>
+#include <getopt.h>
+#include <sys/mman.h>
+
+#include "libudev-private.h"
+#include "libudev-hwdb-def.h"
+
+/**
+ * SECTION:libudev-hwdb
+ * @short_description: retrieve properties from the hardware database
+ *
+ * Libuded hardware database interface.
+ */
+
+/**
+ * udev_hwdb:
+ *
+ * Opaque object representing the hardware database.
+ */
+struct udev_hwdb {
+        struct udev *udev;
+        int refcount;
+
+        FILE *f;
+        struct stat st;
+        union {
+                struct trie_header_f *head;
+                const char *map;
+        };
+
+        struct udev_list properties_list;
+};
+
+struct linebuf {
+        char bytes[LINE_MAX];
+        size_t size;
+        size_t len;
+};
+
+static void linebuf_init(struct linebuf *buf) {
+        buf->size = 0;
+        buf->len = 0;
+}
+
+static const char *linebuf_get(struct linebuf *buf) {
+        if (buf->len + 1 >= sizeof(buf->bytes))
+                return NULL;
+        buf->bytes[buf->len] = '\0';
+        return buf->bytes;
+}
+
+static bool linebuf_add(struct linebuf *buf, const char *s, size_t len) {
+        if (buf->len + len >= sizeof(buf->bytes))
+                return false;
+        memcpy(buf->bytes + buf->len, s, len);
+        buf->len += len;
+        return true;
+}
+
+static bool linebuf_add_char(struct linebuf *buf, char c)
+{
+        if (buf->len + 1 >= sizeof(buf->bytes))
+                return false;
+        buf->bytes[buf->len++] = c;
+        return true;
+}
+
+static void linebuf_rem(struct linebuf *buf, size_t count) {
+        assert(buf->len >= count);
+        buf->len -= count;
+}
+
+static void linebuf_rem_char(struct linebuf *buf) {
+        linebuf_rem(buf, 1);
+}
+
+static const struct trie_child_entry_f *trie_node_children(struct udev_hwdb *hwdb, const struct trie_node_f *node) {
+        return (const struct trie_child_entry_f *)((const char *)node + le64toh(hwdb->head->node_size));
+}
+
+static const struct trie_value_entry_f *trie_node_values(struct udev_hwdb *hwdb, const struct trie_node_f *node) {
+        const char *base = (const char *)node;
+
+        base += le64toh(hwdb->head->node_size);
+        base += node->children_count * le64toh(hwdb->head->child_entry_size);
+        return (const struct trie_value_entry_f *)base;
+}
+
+static const struct trie_node_f *trie_node_from_off(struct udev_hwdb *hwdb, le64_t off) {
+        return (const struct trie_node_f *)(hwdb->map + le64toh(off));
+}
+
+static const char *trie_string(struct udev_hwdb *hwdb, le64_t off) {
+        return hwdb->map + le64toh(off);
+}
+
+static int trie_children_cmp_f(const void *v1, const void *v2) {
+        const struct trie_child_entry_f *n1 = v1;
+        const struct trie_child_entry_f *n2 = v2;
+
+        return n1->c - n2->c;
+}
+
+static const struct trie_node_f *node_lookup_f(struct udev_hwdb *hwdb, const struct trie_node_f *node, uint8_t c) {
+        struct trie_child_entry_f *child;
+        struct trie_child_entry_f search;
+
+        search.c = c;
+        child = bsearch(&search, trie_node_children(hwdb, node), node->children_count,
+                        le64toh(hwdb->head->child_entry_size), trie_children_cmp_f);
+        if (child)
+                return trie_node_from_off(hwdb, child->child_off);
+        return NULL;
+}
+
+static int hwdb_add_property(struct udev_hwdb *hwdb, const char *key, const char *value) {
+        /* TODO: add sub-matches (+) against DMI data */
+        if (key[0] != ' ')
+                return 0;
+        if (udev_list_entry_add(&hwdb->properties_list, key+1, value) == NULL)
+                return -ENOMEM;
+        return 0;
+}
+
+static int trie_fnmatch_f(struct udev_hwdb *hwdb, const struct trie_node_f *node, size_t p,
+                          struct linebuf *buf, const char *search) {
+        size_t len;
+        size_t i;
+        const char *prefix;
+        int err;
+
+        prefix = trie_string(hwdb, node->prefix_off);
+        len = strlen(prefix + p);
+        linebuf_add(buf, prefix + p, len);
+
+        for (i = 0; i < node->children_count; i++) {
+                const struct trie_child_entry_f *child = &trie_node_children(hwdb, node)[i];
+
+                linebuf_add_char(buf, child->c);
+                err = trie_fnmatch_f(hwdb, trie_node_from_off(hwdb, child->child_off), 0, buf, search);
+                if (err < 0)
+                        return err;
+                linebuf_rem_char(buf);
+        }
+
+        if (node->values_count && fnmatch(linebuf_get(buf), search, 0) == 0)
+                for (i = 0; i < node->values_count; i++) {
+                        err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[i].key_off),
+                                                trie_string(hwdb, trie_node_values(hwdb, node)[i].value_off));
+                        if (err < 0)
+                                return err;
+                }
+
+        linebuf_rem(buf, len);
+        return 0;
+}
+
+static int trie_search_f(struct udev_hwdb *hwdb, const char *search) {
+        struct linebuf buf;
+        const struct trie_node_f *node;
+        size_t i = 0;
+        int err;
+
+        linebuf_init(&buf);
+
+        node = trie_node_from_off(hwdb, hwdb->head->nodes_root_off);
+        while (node) {
+                const struct trie_node_f *child;
+                size_t p = 0;
+
+                if (node->prefix_off) {
+                        uint8_t c;
+
+                        for (; (c = trie_string(hwdb, node->prefix_off)[p]); p++) {
+                                if (c == '*' || c == '?' || c == '[')
+                                        return trie_fnmatch_f(hwdb, node, p, &buf, search + i + p);
+                                if (c != search[i + p])
+                                        return 0;
+                        }
+                        i += p;
+                }
+
+                child = node_lookup_f(hwdb, node, '*');
+                if (child) {
+                        linebuf_add_char(&buf, '*');
+                        err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
+                        if (err < 0)
+                                return err;
+                        linebuf_rem_char(&buf);
+                }
+
+                child = node_lookup_f(hwdb, node, '?');
+                if (child) {
+                        linebuf_add_char(&buf, '?');
+                        err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
+                        if (err < 0)
+                                return err;
+                        linebuf_rem_char(&buf);
+                }
+
+                child = node_lookup_f(hwdb, node, '[');
+                if (child) {
+                        linebuf_add_char(&buf, '[');
+                        err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
+                        if (err < 0)
+                                return err;
+                        linebuf_rem_char(&buf);
+                }
+
+                if (search[i] == '\0') {
+                        size_t n;
+
+                        for (n = 0; n < node->values_count; n++) {
+                                err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[n].key_off),
+                                                        trie_string(hwdb, trie_node_values(hwdb, node)[n].value_off));
+                                if (err < 0)
+                                        return err;
+                        }
+                        return 0;
+                }
+
+                child = node_lookup_f(hwdb, node, search[i]);
+                node = child;
+                i++;
+        }
+        return 0;
+}
+
+/**
+ * udev_hwdb_new:
+ * @udev: udev library context
+ *
+ * Create a hardware database context to query properties for devices.
+ *
+ * Returns: a hwdb context.
+ **/
+_public_ struct udev_hwdb *udev_hwdb_new(struct udev *udev) {
+        struct udev_hwdb *hwdb;
+        const char sig[] = HWDB_SIG;
+
+        hwdb = new0(struct udev_hwdb, 1);
+        if (!hwdb)
+                return NULL;
+
+        hwdb->refcount = 1;
+        udev_list_init(udev, &hwdb->properties_list, true);
+
+        hwdb->f = fopen("/etc/udev/hwdb.bin", "re");
+        if (!hwdb->f) {
+                log_debug("error reading /etc/udev/hwdb.bin: %m");
+                udev_hwdb_unref(hwdb);
+                return NULL;
+        }
+
+        if (fstat(fileno(hwdb->f), &hwdb->st) < 0 ||
+            (size_t)hwdb->st.st_size < offsetof(struct trie_header_f, strings_len) + 8) {
+                log_debug("error reading /etc/udev/hwdb.bin: %m");
+                udev_hwdb_unref(hwdb);
+                return NULL;
+        }
+
+        hwdb->map = mmap(0, hwdb->st.st_size, PROT_READ, MAP_SHARED, fileno(hwdb->f), 0);
+        if (hwdb->map == MAP_FAILED) {
+                log_debug("error mapping /etc/udev/hwdb.bin: %m");
+                udev_hwdb_unref(hwdb);
+                return NULL;
+        }
+
+        if (memcmp(hwdb->map, sig, sizeof(hwdb->head->signature)) != 0 ||
+            (size_t)hwdb->st.st_size != le64toh(hwdb->head->file_size)) {
+                log_debug("error recognizing the format of /etc/udev/hwdb.bin");
+                udev_hwdb_unref(hwdb);
+                return NULL;
+        }
+
+        log_debug("=== trie on-disk ===\n");
+        log_debug("tool version:          %llu", (unsigned long long)le64toh(hwdb->head->tool_version));
+        log_debug("file size:        %8llu bytes\n", (unsigned long long)hwdb->st.st_size);
+        log_debug("header size       %8llu bytes\n", (unsigned long long)le64toh(hwdb->head->header_size));
+        log_debug("strings           %8llu bytes\n", (unsigned long long)le64toh(hwdb->head->strings_len));
+        log_debug("nodes             %8llu bytes\n", (unsigned long long)le64toh(hwdb->head->nodes_len));
+        return hwdb;
+}
+
+/**
+ * udev_hwdb_ref:
+ * @hwdb: context
+ *
+ * Take a reference of a hwdb context.
+ *
+ * Returns: the passed enumeration context
+ **/
+_public_ struct udev_hwdb *udev_hwdb_ref(struct udev_hwdb *hwdb) {
+        if (!hwdb)
+                return NULL;
+        hwdb->refcount++;
+        return hwdb;
+}
+
+/**
+ * udev_hwdb_unref:
+ * @hwdb: context
+ *
+ * Drop a reference of a hwdb context. If the refcount reaches zero,
+ * all resources of the hwdb context will be released.
+ *
+ * Returns: the passed hwdb context if it has still an active reference, or #NULL otherwise.
+ **/
+_public_ struct udev_hwdb *udev_hwdb_unref(struct udev_hwdb *hwdb) {
+        if (!hwdb)
+                return NULL;
+        hwdb->refcount--;
+        if (hwdb->refcount > 0)
+                return hwdb;
+        if (hwdb->f)
+                fclose(hwdb->f);
+        if (hwdb->map)
+                munmap((void *)hwdb->map, hwdb->st.st_size);
+        udev_list_cleanup(&hwdb->properties_list);
+        free(hwdb);
+        return NULL;
+}
+
+bool udev_hwdb_validate(struct udev_hwdb *hwdb) {
+        struct stat st;
+
+        if (!hwdb)
+                return false;
+        if (!hwdb->f)
+                return false;
+        if (fstat(fileno(hwdb->f), &st) < 0)
+                return true;
+        if (ts_usec(&hwdb->st.st_mtim) != ts_usec(&st.st_mtim))
+                return true;
+        return false;
+}
+
+/**
+ * udev_hwdb_get_properties_list_entry:
+ * @hwdb: context
+ * @modalias: modalias string
+ * @flags: (unused)
+ *
+ * Lookup a matching device in the hardware database. The lookup key is a
+ * modalias string, whose formats are defined for the Linux kernel modules.
+ * Examples are: pci:v00008086d00001C2D*, usb:v04F2pB221*. The first entry
+ * of a list of retrieved properties is returned.
+ *
+ * Returns: a udev_list_entry.
+ */
+_public_ struct udev_list_entry *udev_hwdb_get_properties_list_entry(struct udev_hwdb *hwdb, const char *modalias, unsigned int flags) {
+        int err;
+
+        if (!hwdb->f) {
+                errno = EINVAL;
+                return NULL;
+        }
+
+        err = trie_search_f(hwdb, modalias);
+        if (err < 0) {
+                errno = -err;
+                return NULL;
+        }
+        return udev_list_get_entry(&hwdb->properties_list);
+}
diff --git a/src/libudev/libudev-private.h b/src/libudev/libudev-private.h
index 2add8b3..d233565 100644
--- a/src/libudev/libudev-private.h
+++ b/src/libudev/libudev-private.h
@@ -140,11 +140,14 @@ void udev_queue_export_cleanup(struct udev_queue_export *udev_queue_export);
 int udev_queue_export_device_queued(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device);
 int udev_queue_export_device_finished(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device);
 
+/* libudev-hwdb.c */
+bool udev_hwdb_validate(struct udev_hwdb *hwdb);
+
 /* libudev-util.c */
-#define UTIL_PATH_SIZE                                1024
-#define UTIL_NAME_SIZE                                512
-#define UTIL_LINE_SIZE                                16384
-#define UDEV_ALLOWED_CHARS_INPUT                "/ $%?,"
+#define UTIL_PATH_SIZE                      1024
+#define UTIL_NAME_SIZE                       512
+#define UTIL_LINE_SIZE                     16384
+#define UDEV_ALLOWED_CHARS_INPUT        "/ $%?,"
 ssize_t util_get_sys_core_link_value(struct udev *udev, const char *slink, const char *syspath, char *value, size_t size);
 int util_resolve_sys_link(struct udev *udev, char *syspath, size_t size);
 int util_log_priority(const char *priority);
@@ -163,8 +166,7 @@ uint64_t util_string_bloom64(const char *str);
 int util_delete_path(struct udev *udev, const char *path);
 uid_t util_lookup_user(struct udev *udev, const char *user);
 gid_t util_lookup_group(struct udev *udev, const char *group);
-int util_resolve_subsys_kernel(struct udev *udev, const char *string,
-                                      char *result, size_t maxsize, int read_value);
+int util_resolve_subsys_kernel(struct udev *udev, const char *string, char *result, size_t maxsize, int read_value);
 unsigned long long ts_usec(const struct timespec *ts);
 unsigned long long now_usec(void);
 ssize_t print_kmsg(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
diff --git a/src/libudev/libudev.h b/src/libudev/libudev.h
index 799f470..cab2323 100644
--- a/src/libudev/libudev.h
+++ b/src/libudev/libudev.h
@@ -171,6 +171,17 @@ int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue,
 struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue);
 
 /*
+ *  udev_hwdb
+ *
+ *  access to the static hardware properties database
+ */
+struct udev_hwdb;
+struct udev_hwdb *udev_hwdb_new(struct udev *udev);
+struct udev_hwdb *udev_hwdb_ref(struct udev_hwdb *hwdb);
+struct udev_hwdb *udev_hwdb_unref(struct udev_hwdb *hwdb);
+struct udev_list_entry *udev_hwdb_get_properties_list_entry(struct udev_hwdb *hwdb, const char *modalias, unsigned int flags);
+
+/*
  * udev_util
  *
  * udev specific utilities
diff --git a/src/libudev/libudev.sym b/src/libudev/libudev.sym
index 5b2c3d3..df6a1ae 100644
--- a/src/libudev/libudev.sym
+++ b/src/libudev/libudev.sym
@@ -100,3 +100,11 @@ LIBUDEV_189 {
 global:
         udev_device_new_from_device_id;
 } LIBUDEV_183;
+
+LIBUDEV_196 {
+global:
+        udev_hwdb_new;
+        udev_hwdb_ref;
+        udev_hwdb_unref;
+        udev_hwdb_get_properties_list_entry;
+} LIBUDEV_189;
diff --git a/src/test/test-libudev.c b/src/test/test-libudev.c
index 481ce65..b89d900 100644
--- a/src/test/test-libudev.c
+++ b/src/test/test-libudev.c
@@ -421,6 +421,20 @@ static int test_enumerate(struct udev *udev, const char *subsystem)
         return 0;
 }
 
+static int test_hwdb(struct udev *udev, const char *modalias) {
+        struct udev_hwdb * hwdb;
+        struct udev_list_entry *entry;
+
+        hwdb = udev_hwdb_new(udev);
+
+        udev_list_entry_foreach(entry, udev_hwdb_get_properties_list_entry(hwdb, modalias, 0))
+                printf("'%s'='%s'\n", udev_list_entry_get_name(entry), udev_list_entry_get_value(entry));
+        printf("\n");
+
+        hwdb = udev_hwdb_unref(hwdb);
+        return 0;
+}
+
 int main(int argc, char *argv[])
 {
         struct udev *udev = NULL;
@@ -489,6 +503,8 @@ int main(int argc, char *argv[])
 
         test_queue(udev);
 
+        test_hwdb(udev, "usb:v0D50p0011*");
+
         test_monitor(udev);
 out:
         udev_unref(udev);
diff --git a/src/udev/udev-builtin-hwdb.c b/src/udev/udev-builtin-hwdb.c
index 14e9eda..60f6471 100644
--- a/src/udev/udev-builtin-hwdb.c
+++ b/src/udev/udev-builtin-hwdb.c
@@ -2,7 +2,6 @@
   This file is part of systemd.
 
   Copyright 2012 Kay Sievers <kay.sievers at vrfy.org>
-  Copyright 2008 Alan Jenkins <alan.christopher.jenkins at googlemail.com>
 
   systemd is free software; you can redistribute it and/or modify it
   under the terms of the GNU Lesser General Public License as published by
@@ -24,210 +23,39 @@
 #include <inttypes.h>
 #include <ctype.h>
 #include <stdlib.h>
-#include <fnmatch.h>
 #include <getopt.h>
-#include <sys/mman.h>
 
 #include "udev.h"
-#include "udev-hwdb.h"
 
-struct linebuf {
-        char bytes[LINE_MAX];
-        size_t size;
-        size_t len;
-};
-
-static void linebuf_init(struct linebuf *buf) {
-        buf->size = 0;
-        buf->len = 0;
-}
-
-static const char *linebuf_get(struct linebuf *buf) {
-        if (buf->len + 1 >= sizeof(buf->bytes))
-                return NULL;
-        buf->bytes[buf->len] = '\0';
-        return buf->bytes;
-}
-
-static bool linebuf_add(struct linebuf *buf, const char *s, size_t len) {
-        if (buf->len + len >= sizeof(buf->bytes))
-                return false;
-        memcpy(buf->bytes + buf->len, s, len);
-        buf->len += len;
-        return true;
-}
-
-static bool linebuf_add_char(struct linebuf *buf, char c)
-{
-        if (buf->len + 1 >= sizeof(buf->bytes))
-                return false;
-        buf->bytes[buf->len++] = c;
-        return true;
-}
-
-static void linebuf_rem(struct linebuf *buf, size_t count) {
-        assert(buf->len >= count);
-        buf->len -= count;
-}
-
-static void linebuf_rem_char(struct linebuf *buf) {
-        linebuf_rem(buf, 1);
-}
+static struct udev_hwdb *hwdb;
 
-struct trie_f {
-        struct udev_device *dev;
-        bool test;
-        FILE *f;
-        uint64_t file_time_usec;
-        union {
-                struct trie_header_f *head;
-                const char *map;
+static int builtin_hwdb(struct udev_device *dev, int argc, char *argv[], bool test) {
+        static const struct option options[] = {
+                { "subsystem", required_argument, NULL, 's' },
+                {}
         };
-        size_t map_size;
-};
-
-static const struct trie_child_entry_f *trie_node_children(struct trie_f *trie, const struct trie_node_f *node) {
-        return (const struct trie_child_entry_f *)((const char *)node + le64toh(trie->head->node_size));
-}
-
-static const struct trie_value_entry_f *trie_node_values(struct trie_f *trie, const struct trie_node_f *node) {
-        const char *base = (const char *)node;
-
-        base += le64toh(trie->head->node_size);
-        base += node->children_count * le64toh(trie->head->child_entry_size);
-        return (const struct trie_value_entry_f *)base;
-}
-
-static const struct trie_node_f *trie_node_from_off(struct trie_f *trie, le64_t off) {
-        return (const struct trie_node_f *)(trie->map + le64toh(off));
-}
-
-static const char *trie_string(struct trie_f *trie, le64_t off) {
-        return trie->map + le64toh(off);
-}
-
-static int trie_children_cmp_f(const void *v1, const void *v2) {
-        const struct trie_child_entry_f *n1 = v1;
-        const struct trie_child_entry_f *n2 = v2;
-
-        return n1->c - n2->c;
-}
-
-static const struct trie_node_f *node_lookup_f(struct trie_f *trie, const struct trie_node_f *node, uint8_t c) {
-        struct trie_child_entry_f *child;
-        struct trie_child_entry_f search;
-
-        search.c = c;
-        child = bsearch(&search, trie_node_children(trie, node), node->children_count,
-                        le64toh(trie->head->child_entry_size), trie_children_cmp_f);
-        if (child)
-                return trie_node_from_off(trie, child->child_off);
-        return NULL;
-}
-
-static void trie_fnmatch_f(struct trie_f *trie, const struct trie_node_f *node, size_t p,
-                           struct linebuf *buf, const char *search,
-                           void (*cb)(struct trie_f *trie, const char *key, const char *value)) {
-        size_t len;
-        size_t i;
-        const char *prefix;
-
-        prefix = trie_string(trie, node->prefix_off);
-        len = strlen(prefix + p);
-        linebuf_add(buf, prefix + p, len);
-
-        for (i = 0; i < node->children_count; i++) {
-                const struct trie_child_entry_f *child = &trie_node_children(trie, node)[i];
-
-                linebuf_add_char(buf, child->c);
-                trie_fnmatch_f(trie, trie_node_from_off(trie, child->child_off), 0, buf, search, cb);
-                linebuf_rem_char(buf);
-        }
-
-        if (node->values_count && fnmatch(linebuf_get(buf), search, 0) == 0)
-                for (i = 0; i < node->values_count; i++)
-                        cb(trie, trie_string(trie, trie_node_values(trie, node)[i].key_off),
-                           trie_string(trie, trie_node_values(trie, node)[i].value_off));
-
-        linebuf_rem(buf, len);
-}
-
-static void trie_search_f(struct trie_f *trie, const char *search,
-                          void (*cb)(struct trie_f *trie, const char *key, const char *value)) {
-        struct linebuf buf;
-        const struct trie_node_f *node;
-        size_t i = 0;
-
-        linebuf_init(&buf);
-
-        node = trie_node_from_off(trie, trie->head->nodes_root_off);
-        while (node) {
-                const struct trie_node_f *child;
-                size_t p = 0;
-
-                if (node->prefix_off) {
-                        uint8_t c;
-
-                        for (; (c = trie_string(trie, node->prefix_off)[p]); p++) {
-                                if (c == '*' || c == '?' || c == '[') {
-                                        trie_fnmatch_f(trie, node, p, &buf, search + i + p, cb);
-                                        return;
-                                }
-                                if (c != search[i + p])
-                                        return;
-                        }
-                        i += p;
-                }
-
-                child = node_lookup_f(trie, node, '*');
-                if (child) {
-                        linebuf_add_char(&buf, '*');
-                        trie_fnmatch_f(trie, child, 0, &buf, search + i, cb);
-                        linebuf_rem_char(&buf);
-                }
+        const char *subsys = NULL;
+        struct udev_device *d;
+        const char *modalias;
+        char str[UTIL_NAME_SIZE];
+        struct udev_list_entry *entry;
 
-                child = node_lookup_f(trie, node, '?');
-                if (child) {
-                        linebuf_add_char(&buf, '?');
-                        trie_fnmatch_f(trie, child, 0, &buf, search + i, cb);
-                        linebuf_rem_char(&buf);
-                }
+        if (!hwdb)
+                return EXIT_FAILURE;
 
-                child = node_lookup_f(trie, node, '[');
-                if (child) {
-                        linebuf_add_char(&buf, '[');
-                        trie_fnmatch_f(trie, child, 0, &buf, search + i, cb);
-                        linebuf_rem_char(&buf);
-                }
+        for (;;) {
+                int option;
 
-                if (search[i] == '\0') {
-                        size_t n;
+                option = getopt_long(argc, argv, "s", options, NULL);
+                if (option == -1)
+                        break;
 
-                        for (n = 0; n < node->values_count; n++)
-                                cb(trie, trie_string(trie, trie_node_values(trie, node)[n].key_off),
-                                   trie_string(trie, trie_node_values(trie, node)[n].value_off));
-                        return;
+                switch (option) {
+                case 's':
+                        subsys = optarg;
+                        break;
                 }
-
-                child = node_lookup_f(trie, node, search[i]);
-                node = child;
-                i++;
         }
-}
-
-static void value_cb(struct trie_f *trie, const char *key, const char *value) {
-        /* TODO: add sub-matches (+) against DMI data */
-        if (key[0] == ' ')
-                udev_builtin_add_property(trie->dev, trie->test, key + 1, value);
-}
-
-static struct trie_f trie;
-
-static int hwdb_lookup(struct udev_device *dev, const char *subsys) {
-        struct udev_device *d;
-        const char *modalias;
-        char str[UTIL_NAME_SIZE];
-        int rc = EXIT_SUCCESS;
 
         /* search the first parent device with a modalias */
         for (d = dev; d; d = udev_device_get_parent(d)) {
@@ -266,113 +94,35 @@ static int hwdb_lookup(struct udev_device *dev, const char *subsys) {
         if (!modalias)
                 return EXIT_FAILURE;
 
-        trie_search_f(&trie, modalias, value_cb);
-        return rc;
-}
-
-static int builtin_hwdb(struct udev_device *dev, int argc, char *argv[], bool test) {
-        static const struct option options[] = {
-                { "subsystem", required_argument, NULL, 's' },
-                {}
-        };
-        const char *subsys = NULL;
-
-        if (!trie.f)
-                return EXIT_SUCCESS;
-
-        for (;;) {
-                int option;
-
-                option = getopt_long(argc, argv, "s", options, NULL);
-                if (option == -1)
-                        break;
-
-                switch (option) {
-                case 's':
-                        subsys = optarg;
-                        break;
-                }
-        }
-
-        trie.dev = dev;
-        trie.test = test;
-        if (hwdb_lookup(dev, subsys) < 0)
-                return EXIT_FAILURE;
+        udev_list_entry_foreach(entry, udev_hwdb_get_properties_list_entry(hwdb, modalias, 0))
+        if (udev_builtin_add_property(dev, test,
+                                      udev_list_entry_get_name(entry),
+                                      udev_list_entry_get_value(entry)) < 0)
+                        return EXIT_FAILURE;
         return EXIT_SUCCESS;
 }
 
 /* called at udev startup and reload */
 static int builtin_hwdb_init(struct udev *udev)
 {
-        struct stat st;
-        const char sig[] = HWDB_SIG;
-
-        if (trie.f)
+        if (hwdb)
                 return 0;
-
-        trie.f = fopen(SYSCONFDIR "/udev/hwdb.bin", "re");
-        if (!trie.f) {
-                if (errno != EEXIST)
-                        log_error("Error reading " SYSCONFDIR "/udev/hwdb.bin: %m");
-                return -errno;
-        }
-
-        if (fstat(fileno(trie.f), &st) < 0 || (size_t)st.st_size < offsetof(struct trie_header_f, strings_len) + 8) {
-                log_error("Error reading " SYSCONFDIR "/udev/hwdb.bin: %m");
-                fclose(trie.f);
-                zero(trie);
-                return -EINVAL;
-        }
-
-        trie.map = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fileno(trie.f), 0);
-        if (trie.map == MAP_FAILED) {
-                log_error("Error mapping " SYSCONFDIR "/udev/hwdb.bin: %m");
-                fclose(trie.f);
-                return -EINVAL;
-        }
-        trie.file_time_usec = ts_usec(&st.st_mtim);
-        trie.map_size = st.st_size;
-
-        if (memcmp(trie.map, sig, sizeof(trie.head->signature)) != 0 || (size_t)st.st_size != le64toh(trie.head->file_size)) {
-                log_error("Unable to recognize the format of " SYSCONFDIR "/udev/hwdb.bin.");
-                log_error("Please try 'udevadm hwdb --update' to re-create it.");
-                munmap((void *)trie.map, st.st_size);
-                fclose(trie.f);
-                zero(trie);
-                return EINVAL;
-        }
-
-        log_debug("=== trie on-disk ===\n");
-        log_debug("tool version:          %llu", (unsigned long long)le64toh(trie.head->tool_version));
-        log_debug("file size:        %8zi bytes\n", st.st_size);
-        log_debug("header size       %8zu bytes\n", (size_t)le64toh(trie.head->header_size));
-        log_debug("strings           %8zu bytes\n", (size_t)le64toh(trie.head->strings_len));
-        log_debug("nodes             %8zu bytes\n", (size_t)le64toh(trie.head->nodes_len));
+        hwdb = udev_hwdb_new(udev);
+        if (!hwdb)
+                return -ENOMEM;
         return 0;
 }
 
 /* called on udev shutdown and reload request */
 static void builtin_hwdb_exit(struct udev *udev)
 {
-        if (!trie.f)
-                return;
-        munmap((void *)trie.map, trie.map_size);
-        fclose(trie.f);
-        zero(trie);
+        hwdb = udev_hwdb_unref(hwdb);
 }
 
 /* called every couple of seconds during event activity; 'true' if config has changed */
 static bool builtin_hwdb_validate(struct udev *udev)
 {
-        struct stat st;
-
-        if (!trie.f)
-                return false;
-        if (fstat(fileno(trie.f), &st) < 0)
-                return true;
-        if (trie.file_time_usec != ts_usec(&st.st_mtim))
-                return true;
-        return false;
+        return udev_hwdb_validate(hwdb);
 }
 
 const struct udev_builtin udev_builtin_hwdb = {
diff --git a/src/udev/udev-hwdb.h b/src/udev/udev-hwdb.h
deleted file mode 100644
index 8a9955e..0000000
--- a/src/udev/udev-hwdb.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/***
-  This file is part of systemd.
-
-  Copyright 2012 Kay Sievers <kay.sievers at vrfy.org>
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "sparse-endian.h"
-
-#define HWDB_SIG { 'K', 'S', 'L', 'P', 'H', 'H', 'R', 'H' }
-
-/* on-disk trie objects */
-_packed_ struct trie_header_f {
-        uint8_t signature[8];
-
-        /* version of tool which created the file */
-        le64_t tool_version;
-        le64_t file_size;
-
-        /* size of structures to allow them to grow */
-        le64_t header_size;
-        le64_t node_size;
-        le64_t child_entry_size;
-        le64_t value_entry_size;
-
-        /* offset of the root trie node */
-        le64_t nodes_root_off;
-
-        /* size of the nodes and string section */
-        le64_t nodes_len;
-        le64_t strings_len;
-};
-
-_packed_ struct trie_node_f {
-        /* prefix of lookup string, shared by all children  */
-        le64_t prefix_off;
-        /* size of children entry array appended to the node */
-        uint8_t children_count;
-        uint8_t padding[7];
-        /* size of value entry array appended to the node */
-        le64_t values_count;
-};
-
-/* array of child entries, follows directly the node record */
-_packed_ struct trie_child_entry_f {
-        /* index of the child node */
-        uint8_t c;
-        uint8_t padding[7];
-        /* offset of the child node */
-        le64_t child_off;
-};
-
-/* array of value entries, follows directly the node record/child array */
-_packed_ struct trie_value_entry_f {
-        le64_t key_off;
-        le64_t value_off;
-};
diff --git a/src/udev/udevadm-hwdb.c b/src/udev/udevadm-hwdb.c
index ce8eff4..5587094 100644
--- a/src/udev/udevadm-hwdb.c
+++ b/src/udev/udevadm-hwdb.c
@@ -27,7 +27,7 @@
 #include "conf-files.h"
 
 #include "udev.h"
-#include "udev-hwdb.h"
+#include "libudev-hwdb-def.h"
 
 /*
  * Generic udev properties, key/value database based on modalias strings.
@@ -35,7 +35,7 @@
  */
 
 static const char * const conf_file_dirs[] = {
-        SYSCONFDIR "/udev/hwdb.d",
+        "/etc/udev/hwdb.d",
         UDEVLIBEXECDIR "/hwdb.d",
         NULL
 };
@@ -291,7 +291,7 @@ static int64_t trie_store_nodes(struct trie_f *trie, struct trie_node *node) {
                 .children_count = node->children_count,
                 .values_count = htole64(node->values_count),
         };
-        struct trie_child_entry_f *children;
+        struct trie_child_entry_f *children = NULL;
         int64_t node_off;
 
         if (node->children_count) {
@@ -390,12 +390,15 @@ static int trie_store(struct trie *trie, const char *filename) {
         }
 
         log_debug("=== trie on-disk ===\n");
-        log_debug("size:             %8zi bytes\n", size);
+        log_debug("size:             %8llu bytes\n", (unsigned long long)size);
         log_debug("header:           %8zu bytes\n", sizeof(struct trie_header_f));
-        log_debug("nodes:            %8zu bytes (%8zi)\n", t.nodes_count * sizeof(struct trie_node_f), t.nodes_count);
-        log_debug("child pointers:   %8zu bytes (%8zi)\n", t.children_count * sizeof(struct trie_child_entry_f), t.children_count);
-        log_debug("value pointers:   %8zu bytes (%8zi)\n", t.values_count * sizeof(struct trie_value_entry_f), t.values_count);
-        log_debug("string store:     %8zu bytes\n", trie->strings->len);
+        log_debug("nodes:            %8llu bytes (%8llu)\n",
+                  (unsigned long long)t.nodes_count * sizeof(struct trie_node_f), (unsigned long long)t.nodes_count);
+        log_debug("child pointers:   %8llu bytes (%8llu)\n",
+                  (unsigned long long)t.children_count * sizeof(struct trie_child_entry_f), (unsigned long long)t.children_count);
+        log_debug("value pointers:   %8llu bytes (%8llu)\n",
+                  (unsigned long long)t.values_count * sizeof(struct trie_value_entry_f), (unsigned long long)t.values_count);
+        log_debug("string store:     %8llu bytes\n", (unsigned long long)trie->strings->len);
         log_debug("strings start:    %8llu\n", (unsigned long long) t.strings_off);
 out:
         free(filename_tmp);
@@ -406,12 +409,14 @@ static int import_file(struct trie *trie, const char *filename) {
         FILE *f;
         char line[LINE_MAX];
         char match[LINE_MAX];
+        char cond[LINE_MAX];
 
         f = fopen(filename, "re");
         if (f == NULL)
                 return -errno;
 
         match[0] = '\0';
+        cond[0] = '\0';
         while (fgets(line, sizeof(line), f)) {
                 size_t len;
 
@@ -421,6 +426,7 @@ static int import_file(struct trie *trie, const char *filename) {
                 /* new line, new record */
                 if (line[0] == '\n') {
                         match[0] = '\0';
+                        cond[0] = '\0';
                         continue;
                 }
 
@@ -433,9 +439,19 @@ static int import_file(struct trie *trie, const char *filename) {
                 /* start of new record */
                 if (match[0] == '\0') {
                         strcpy(match, line);
+                        cond[0] = '\0';
                         continue;
                 }
 
+                if (line[0] == '+') {
+                        strcpy(cond, line);
+                        continue;
+                }
+
+                /* TODO: support +; skip the entire record until we support it */
+                if (cond[0] != '\0')
+                        continue;
+
                 /* value lines */
                 if (line[0] == ' ') {
                         char *value;
@@ -535,13 +551,12 @@ static int adm_hwdb(struct udev *udev, int argc, char *argv[]) {
         log_debug("strings incoming: %8zu bytes (%8zu)\n", trie->strings->in_len, trie->strings->in_count);
         log_debug("strings dedup'ed: %8zu bytes (%8zu)\n", trie->strings->dedup_len, trie->strings->dedup_count);
 
-        mkdir_parents(SYSCONFDIR "/udev/hwdb.bin", 0755);
-        err = trie_store(trie, SYSCONFDIR "/udev/hwdb.bin");
+        mkdir_parents("/etc/udev/hwdb.bin", 0755);
+        err = trie_store(trie, "/etc/udev/hwdb.bin");
         if (err < 0) {
-                log_error("Failure writing hardware database '%s': %s", SYSCONFDIR "/udev/hwdb.bin", strerror(-err));
+                log_error("Failure writing hardware database '%s': %s", "/etc/udev/hwdb.bin", strerror(-err));
                 rc = EXIT_FAILURE;
         }
-
 out:
         if (trie->root)
                 trie_node_cleanup(trie->root);



More information about the systemd-commits mailing list