diff --git a/Makefile.am b/Makefile.am index 634c67d13..3d96500c8 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 13e448ace..6a346a653 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 d8248aaaf..454cd3164 100644 --- a/docs/libudev/libudev-docs.xml +++ b/docs/libudev/libudev-docs.xml @@ -28,6 +28,7 @@ + diff --git a/docs/libudev/libudev-sections.txt b/docs/libudev/libudev-sections.txt index 11e8e1cdf..067a3f5b9 100644 --- a/docs/libudev/libudev-sections.txt +++ b/docs/libudev/libudev-sections.txt @@ -117,6 +117,16 @@ udev_queue_get_kernel_seqnum udev_queue_get_udev_seqnum +
+libudev-hwdb +udev_hwdb +udev_hwdb +udev_hwdb_ref +udev_hwdb_unref +udev_hwdb_new +udev_hwdb_get_properties_list_entry +
+
libudev-util udev_util diff --git a/src/udev/udev-hwdb.h b/src/libudev/libudev-hwdb-def.h similarity index 96% rename from src/udev/udev-hwdb.h rename to src/libudev/libudev-hwdb-def.h index 8a9955eb6..8bc694457 100644 --- a/src/udev/udev-hwdb.h +++ b/src/libudev/libudev-hwdb-def.h @@ -17,6 +17,9 @@ along with systemd; If not, see . ***/ +#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' } @@ -67,3 +70,5 @@ _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 000000000..92c58b61a --- /dev/null +++ b/src/libudev/libudev-hwdb.c @@ -0,0 +1,391 @@ +/*** + This file is part of systemd. + + Copyright 2012 Kay Sievers + Copyright 2008 Alan Jenkins + + 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 . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 2add8b357..d233565fb 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 799f47096..cab2323de 100644 --- a/src/libudev/libudev.h +++ b/src/libudev/libudev.h @@ -170,6 +170,17 @@ int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue, unsigned long long int start, unsigned long long int end); 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 * diff --git a/src/libudev/libudev.sym b/src/libudev/libudev.sym index 5b2c3d32c..df6a1aedd 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 481ce65db..b89d90012 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 14e9edab8..60f647183 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 - Copyright 2008 Alan Jenkins 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 #include #include -#include #include -#include #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/udevadm-hwdb.c b/src/udev/udevadm-hwdb.c index ce8eff481..5587094e6 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);