udevadm-hwdb.c revision e32a4e1ef4c61561b08f50f73f82587bdc946b40
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering This file is part of systemd.
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering Copyright 2012 Kay Sievers <kay@vrfy.org>
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering systemd is free software; you can redistribute it and/or modify it
2f6a59070559786428d9eaf199ae3d61772b2225Kay Sievers under the terms of the GNU Lesser General Public License as published by
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering the Free Software Foundation; either version 2.1 of the License, or
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering (at your option) any later version.
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering systemd is distributed in the hope that it will be useful, but
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering Lesser General Public License for more details.
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering You should have received a copy of the GNU Lesser General Public License
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen * Generic udev properties, key/value database based on modalias strings.
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering * Uses a Patricia/radix trie to index all matches for efficient lookup.
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringstatic const char * const conf_file_dirs[] = {
e1636421f46db6d06fbd028ef20a3113fa3e11f8Lennart Poettering/* in-memory trie objects */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* prefix, common part for all children of this node */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* sorted array of pointers to children nodes */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* sorted array of key/value pairs */
ffc06c3513d9a0693c7f810d03b20705127ba55aKay Sievers/* children array item with char (0-255) index */
2f6a59070559786428d9eaf199ae3d61772b2225Kay Sievers/* value array item with key/value pairs */
2311eb2ff0c3ff80ec3645b02c97170c9a565454Kay Sieversstatic int trie_children_cmp(const void *v1, const void *v2) {
2311eb2ff0c3ff80ec3645b02c97170c9a565454Kay Sieversstatic int node_add_child(struct trie *trie, struct trie_node *node, struct trie_node *node_child, uint8_t c) {
2311eb2ff0c3ff80ec3645b02c97170c9a565454Kay Sievers /* extend array, add new entry, sort for bisection */
2311eb2ff0c3ff80ec3645b02c97170c9a565454Kay Sievers child = realloc(node->children, (node->children_count + 1) * sizeof(struct trie_child_entry));
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering node->children[node->children_count].c = c;
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers node->children[node->children_count].child = node_child;
2311eb2ff0c3ff80ec3645b02c97170c9a565454Kay Sievers qsort(node->children, node->children_count, sizeof(struct trie_child_entry), trie_children_cmp);
2f6a59070559786428d9eaf199ae3d61772b2225Kay Sieversstatic struct trie_node *node_lookup(const struct trie_node *node, uint8_t c) {
2311eb2ff0c3ff80ec3645b02c97170c9a565454Kay Sievers child = bsearch(&search, node->children, node->children_count, sizeof(struct trie_child_entry), trie_children_cmp);
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sieversstatic void trie_node_cleanup(struct trie_node *node) {
2af32104c47dadf426f2e7697cd7382520476fc5Lennart Poettering for (i = 0; i < node->children_count; i++)
2f6a59070559786428d9eaf199ae3d61772b2225Kay Sieversstatic int trie_values_cmp(const void *v1, const void *v2, void *arg) {
2f6a59070559786428d9eaf199ae3d61772b2225Kay Sievers return strcmp(trie->strings->buf + val1->key_off,
bd5ce8e9fc10a593822344c098ccbe8c47fe34e9Lennart Poetteringstatic int trie_node_add_value(struct trie *trie, struct trie_node *node,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering k = strbuf_add_string(trie->strings, key, strlen(key));
2f6a59070559786428d9eaf199ae3d61772b2225Kay Sievers v = strbuf_add_string(trie->strings, value, strlen(value));
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers val = xbsearch_r(&search, node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie);
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers /* replace existing earlier key with new value */
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers /* extend array, add new entry, sort for bisection */
3062c15117ab6eac5e8b3a3ceb5351ec22ea4481Lennart Poettering val = realloc(node->values, (node->values_count + 1) * sizeof(struct trie_value_entry));
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers qsort_r(node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie);
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sieversstatic int trie_insert(struct trie *trie, struct trie_node *node, const char *search,
2f6a59070559786428d9eaf199ae3d61772b2225Kay Sievers for (p = 0; (c = trie->strings->buf[node->prefix_off + p]); p++) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (c == search[i + p])
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* split node */
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen child = calloc(sizeof(struct trie_node), 1);
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sievers /* move values from parent to child */
ffc06c3513d9a0693c7f810d03b20705127ba55aKay Sievers /* update parent; use strdup() because the source gets realloc()d */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering s = strndup(trie->strings->buf + node->prefix_off, p);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering err = node_add_child(trie, node, child, c);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (c == '\0')
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return trie_node_add_value(trie, node, key, value);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* new child */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering child = calloc(sizeof(struct trie_node), 1);
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen off = strbuf_add_string(trie->strings, search + i+1, strlen(search + i+1));
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen err = node_add_child(trie, node, child, c);
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen return trie_node_add_value(trie, child, key, value);
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen/* calculate the storage space for the nodes, children arrays, value arrays */
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersenstatic void trie_store_nodes_size(struct trie_f *trie, struct trie_node *node) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering for (i = 0; i < node->children_count; i++)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering trie_store_nodes_size(trie, node->children[i].child);
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen trie->strings_off += sizeof(struct trie_node_f);
e5609878d8802e2469c433be418bcbcf55fbe63bLennart Poettering for (i = 0; i < node->children_count; i++)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering trie->strings_off += sizeof(struct trie_child_entry_f);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering trie->strings_off += sizeof(struct trie_value_entry_f);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringstatic int64_t trie_store_nodes(struct trie_f *trie, struct trie_node *node) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering .prefix_off = htole64(trie->strings_off + node->prefix_off),
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering .values_count = htole64(node->values_count),
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen struct trie_child_entry_f *children = NULL;
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen children = new0(struct trie_child_entry_f, node->children_count);
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen /* post-order recursion */
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen for (i = 0; i < node->children_count; i++) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering child_off = trie_store_nodes(trie, node->children[i].child);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering children[i].child_off = htole64(child_off);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* write node */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering fwrite(&n, sizeof(struct trie_node_f), 1, trie->f);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* append children array */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering fwrite(children, sizeof(struct trie_child_entry_f), node->children_count, trie->f);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering trie->children_count += node->children_count;
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen /* append values array */
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen for (i = 0; i < node->values_count; i++) {
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen struct trie_value_entry_f v = {
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen .key_off = htole64(trie->strings_off + node->values[i].key_off),
e5609878d8802e2469c433be418bcbcf55fbe63bLennart Poettering .value_off = htole64(trie->strings_off + node->values[i].value_off),
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen fwrite(&v, sizeof(struct trie_value_entry_f), 1, trie->f);
f75cb30bf97f623417cc7ee4b1bcc5c36cdbeb20Dave Reisnerstatic int trie_store(struct trie *trie, const char *filename) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering .header_size = htole64(sizeof(struct trie_header_f)),
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering .node_size = htole64(sizeof(struct trie_node_f)),
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering .child_entry_size = htole64(sizeof(struct trie_child_entry_f)),
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering .value_entry_size = htole64(sizeof(struct trie_value_entry_f)),
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* calculate size of header, nodes, children entries, value entries */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering t.strings_off = sizeof(struct trie_header_f);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering err = fopen_temporary(filename , &t.f, &filename_tmp);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* write nodes */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering fseeko(t.f, sizeof(struct trie_header_f), SEEK_SET);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering root_off = trie_store_nodes(&t, trie->root);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering h.nodes_len = htole64(pos - sizeof(struct trie_header_f));
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* write string buffer */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering fwrite(trie->strings->buf, trie->strings->len, 1, t.f);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering h.strings_len = htole64(trie->strings->len);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* write header */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering fwrite(&h, sizeof(struct trie_header_f), 1, t.f);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (err < 0 || rename(filename_tmp, filename) < 0) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_debug("size: %8llu bytes\n", (unsigned long long)size);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_debug("header: %8zu bytes\n", sizeof(struct trie_header_f));
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_debug("nodes: %8llu bytes (%8llu)\n",
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering (unsigned long long)t.nodes_count * sizeof(struct trie_node_f), (unsigned long long)t.nodes_count);
857a493d55f94731394e4d9f61ffce661858e9a0Lennart Poettering log_debug("child pointers: %8llu bytes (%8llu)\n",
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering (unsigned long long)t.children_count * sizeof(struct trie_child_entry_f), (unsigned long long)t.children_count);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_debug("value pointers: %8llu bytes (%8llu)\n",
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering (unsigned long long)t.values_count * sizeof(struct trie_value_entry_f), (unsigned long long)t.values_count);
857a493d55f94731394e4d9f61ffce661858e9a0Lennart Poettering log_debug("string store: %8llu bytes\n", (unsigned long long)trie->strings->len);
7c2d80944afb4196f2eff614e8da1450dffcbeaaThomas Hindoe Paaboel Andersen log_debug("strings start: %8llu\n", (unsigned long long) t.strings_off);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringstatic int import_file(struct trie *trie, const char *filename) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* new line, new record */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* remove newline */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* start of new record */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* TODO: support +; skip the entire record until we support it */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* value lines */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering trie_insert(trie, trie->root, match, line, value);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringstatic void help(void) {
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen " --update update the hardware database\n"
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen " --test <modalias> query database and print result\n"
546158bc6f46f8004cc11e81d19d223e0da56730Jan Janssenstatic int adm_hwdb(struct udev *udev, int argc, char *argv[]) {
eb9da376d76b48585b3b63b4f91903b54f7abd36Lennart Poettering option = getopt_long(argc, argv, "ut:h", options, NULL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* string store */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering trie->root = calloc(sizeof(struct trie_node), 1);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering err = conf_files_list_strv(&files, ".hwdb", (const char **)conf_file_dirs);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_error("failed to enumerate hwdb files: %s\n", strerror(-err));
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering trie->nodes_count * sizeof(struct trie_node), trie->nodes_count);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_debug("children arrays: %8zu bytes (%8zu)\n",
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering trie->children_count * sizeof(struct trie_child_entry), trie->children_count);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_debug("values arrays: %8zu bytes (%8zu)\n",
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering trie->values_count * sizeof(struct trie_value_entry), trie->values_count);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_debug("strings incoming: %8zu bytes (%8zu)\n",
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering trie->strings->in_len, trie->strings->in_count);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_debug("strings dedup'ed: %8zu bytes (%8zu)\n",
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering trie->strings->dedup_len, trie->strings->dedup_count);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_error("Failure writing hardware database '%s': %s",
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering struct udev_hwdb *hwdb = udev_hwdb_new(udev);
84f6181c2ac99a0514ca5e0c8fc8c8e284caf789Lennart Poettering udev_list_entry_foreach(entry, udev_hwdb_get_properties_list_entry(hwdb, test, 0))
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering printf("%s=%s\n", udev_list_entry_get_name(entry), udev_list_entry_get_value(entry));
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen .help = "maintain the hardware database index",