X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fudev%2Fudevadm-hwdb.c;h=00609e31b52202d4af5ef76d5c32a07fc1c473de;hp=3e849aaed6c7eda6e44f68ae7d4529ace5d06484;hb=2eec67acbb00593e414549a7e5b35eb7dd776b1b;hpb=7fd1b19bc9e9f5574f2877936b8ac267c7706947 diff --git a/src/udev/udevadm-hwdb.c b/src/udev/udevadm-hwdb.c index 3e849aaed..00609e31b 100644 --- a/src/udev/udevadm-hwdb.c +++ b/src/udev/udevadm-hwdb.c @@ -18,16 +18,17 @@ ***/ #include -#include #include #include +#include #include "util.h" #include "strbuf.h" #include "conf-files.h" #include "udev.h" -#include "libudev-hwdb-def.h" +#include "hwdb-internal.h" +#include "hwdb-util.h" /* * Generic udev properties, key/value database based on modalias strings. @@ -189,7 +190,7 @@ static int trie_insert(struct trie *trie, struct trie_node *node, const char *se continue; /* split node */ - new_child = calloc(sizeof(struct trie_node), 1); + new_child = new0(struct trie_node, 1); if (!new_child) return -ENOMEM; @@ -232,7 +233,7 @@ static int trie_insert(struct trie *trie, struct trie_node *node, const char *se ssize_t off; /* new child */ - child = calloc(sizeof(struct trie_node), 1); + child = new0(struct trie_node, 1); if (!child) return -ENOMEM; @@ -302,8 +303,10 @@ static int64_t trie_store_nodes(struct trie_f *trie, struct trie_node *node) { int64_t child_off; child_off = trie_store_nodes(trie, node->children[i].child); - if (child_off < 0) + if (child_off < 0) { + free(children); return child_off; + } children[i].c = node->children[i].c; children[i].child_off = htole64(child_off); } @@ -338,7 +341,7 @@ static int trie_store(struct trie *trie, const char *filename) { struct trie_f t = { .trie = trie, }; - char *filename_tmp; + _cleanup_free_ char *filename_tmp = NULL; int64_t pos; int64_t root_off; int64_t size; @@ -362,7 +365,12 @@ static int trie_store(struct trie *trie, const char *filename) { fchmod(fileno(t.f), 0444); /* write nodes */ - fseeko(t.f, sizeof(struct trie_header_f), SEEK_SET); + err = fseeko(t.f, sizeof(struct trie_header_f), SEEK_SET); + if (err < 0) { + fclose(t.f); + unlink_noerrno(filename_tmp); + return -errno; + } root_off = trie_store_nodes(&t, trie->root); h.nodes_root_off = htole64(root_off); pos = ftello(t.f); @@ -375,130 +383,198 @@ static int trie_store(struct trie *trie, const char *filename) { /* write header */ size = ftello(t.f); h.file_size = htole64(size); - fseeko(t.f, 0, SEEK_SET); + err = fseeko(t.f, 0, SEEK_SET); + if (err < 0) { + fclose(t.f); + unlink_noerrno(filename_tmp); + return -errno; + } fwrite(&h, sizeof(struct trie_header_f), 1, t.f); err = ferror(t.f); if (err) err = -errno; fclose(t.f); if (err < 0 || rename(filename_tmp, filename) < 0) { - unlink(filename_tmp); - goto out; + unlink_noerrno(filename_tmp); + return err < 0 ? err : -errno; } - log_debug("=== trie on-disk ===\n"); - log_debug("size: %8llu bytes\n", (unsigned long long)size); - log_debug("header: %8zu bytes\n", sizeof(struct trie_header_f)); - 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); - return err; + log_debug("=== trie on-disk ==="); + log_debug("size: %8"PRIi64" bytes", size); + log_debug("header: %8zu bytes", sizeof(struct trie_header_f)); + log_debug("nodes: %8"PRIu64" bytes (%8"PRIu64")", + t.nodes_count * sizeof(struct trie_node_f), t.nodes_count); + log_debug("child pointers: %8"PRIu64" bytes (%8"PRIu64")", + t.children_count * sizeof(struct trie_child_entry_f), t.children_count); + log_debug("value pointers: %8"PRIu64" bytes (%8"PRIu64")", + t.values_count * sizeof(struct trie_value_entry_f), t.values_count); + log_debug("string store: %8zu bytes", trie->strings->len); + log_debug("strings start: %8"PRIu64, t.strings_off); + + return 0; +} + +static int insert_data(struct trie *trie, struct udev_list *match_list, + char *line, const char *filename) { + char *value; + struct udev_list_entry *entry; + + value = strchr(line, '='); + if (!value) { + log_error("Error, key/value pair expected but got '%s' in '%s':", line, filename); + return -EINVAL; + } + + value[0] = '\0'; + value++; + + /* libudev requires properties to start with a space */ + while (isblank(line[0]) && isblank(line[1])) + line++; + + if (line[0] == '\0' || value[0] == '\0') { + log_error("Error, empty key or value '%s' in '%s':", line, filename); + return -EINVAL; + } + + udev_list_entry_foreach(entry, udev_list_get_entry(match_list)) + trie_insert(trie, trie->root, udev_list_entry_get_name(entry), line, value); + + return 0; } -static int import_file(struct trie *trie, const char *filename) { +static int import_file(struct udev *udev, struct trie *trie, const char *filename) { + enum { + HW_MATCH, + HW_DATA, + HW_NONE, + } state = HW_NONE; FILE *f; char line[LINE_MAX]; - char match[LINE_MAX]; - char cond[LINE_MAX]; + struct udev_list match_list; + + udev_list_init(udev, &match_list, false); f = fopen(filename, "re"); if (f == NULL) return -errno; - match[0] = '\0'; - cond[0] = '\0'; while (fgets(line, sizeof(line), f)) { size_t len; + char *pos; + /* comment line */ if (line[0] == '#') continue; - /* new line, new record */ - if (line[0] == '\n') { - match[0] = '\0'; - cond[0] = '\0'; - continue; - } + /* strip trailing comment */ + pos = strchr(line, '#'); + if (pos) + pos[0] = '\0'; - /* remove newline */ + /* strip trailing whitespace */ len = strlen(line); - if (len < 2) - continue; - line[len-1] = '\0'; + while (len > 0 && isspace(line[len-1])) + len--; + line[len] = '\0'; + + switch (state) { + case HW_NONE: + if (len == 0) + break; + + if (line[0] == ' ') { + log_error("Error, MATCH expected but got '%s' in '%s':", line, filename); + break; + } - /* start of new record */ - if (match[0] == '\0') { - strcpy(match, line); - cond[0] = '\0'; - continue; - } + /* start of record, first match */ + state = HW_MATCH; + udev_list_entry_add(&match_list, line, NULL); + break; - if (line[0] == '+') { - strcpy(cond, line); - continue; - } + case HW_MATCH: + if (len == 0) { + log_error("Error, DATA expected but got empty line in '%s':", filename); + state = HW_NONE; + udev_list_cleanup(&match_list); + break; + } - /* TODO: support +; skip the entire record until we support it */ - if (cond[0] != '\0') - continue; + /* another match */ + if (line[0] != ' ') { + udev_list_entry_add(&match_list, line, NULL); + break; + } - /* value lines */ - if (line[0] == ' ') { - char *value; + /* first data */ + state = HW_DATA; + insert_data(trie, &match_list, line, filename); + break; - value = strchr(line, '='); - if (!value) - continue; - value[0] = '\0'; - value++; - trie_insert(trie, trie->root, match, line, value); - } + case HW_DATA: + /* end of record */ + if (len == 0) { + state = HW_NONE; + udev_list_cleanup(&match_list); + break; + } + + if (line[0] != ' ') { + log_error("Error, DATA expected but got '%s' in '%s':", line, filename); + state = HW_NONE; + udev_list_cleanup(&match_list); + break; + } + + insert_data(trie, &match_list, line, filename); + break; + }; } + fclose(f); + udev_list_cleanup(&match_list); return 0; } static void help(void) { printf("Usage: udevadm hwdb OPTIONS\n" - " --update update the hardware database\n" - " --test= query database and print result\n" - " --root= alternative root path in the filesystem\n" - " --help\n\n"); + " -u,--update update the hardware database\n" + " --usr generate in " UDEVLIBEXECDIR " instead of /etc/udev\n" + " -t,--test=MODALIAS query database and print result\n" + " -r,--root=PATH alternative root path in the filesystem\n" + " -h,--help\n\n"); } static int adm_hwdb(struct udev *udev, int argc, char *argv[]) { + enum { + ARG_USR = 0x100, + }; + static const struct option options[] = { - { "update", no_argument, NULL, 'u' }, - { "root", required_argument, NULL, 'r' }, - { "test", required_argument, NULL, 't' }, - { "help", no_argument, NULL, 'h' }, + { "update", no_argument, NULL, 'u' }, + { "usr", no_argument, NULL, ARG_USR }, + { "test", required_argument, NULL, 't' }, + { "root", required_argument, NULL, 'r' }, + { "help", no_argument, NULL, 'h' }, {} }; const char *test = NULL; const char *root = ""; + const char *hwdb_bin_dir = "/etc/udev"; bool update = false; struct trie *trie = NULL; - int err; + int err, c; int rc = EXIT_SUCCESS; - for (;;) { - int option; - - option = getopt_long(argc, argv, "ut:r:h", options, NULL); - if (option == -1) - break; - - switch (option) { + while ((c = getopt_long(argc, argv, "ut:r:h", options, NULL)) >= 0) + switch(c) { case 'u': update = true; break; + case ARG_USR: + hwdb_bin_dir = UDEVLIBEXECDIR; + break; case 't': test = optarg; break; @@ -508,19 +584,22 @@ static int adm_hwdb(struct udev *udev, int argc, char *argv[]) { case 'h': help(); return EXIT_SUCCESS; + case '?': + return EXIT_FAILURE; + default: + assert_not_reached("Unknown option"); } - } if (!update && !test) { - help(); - return EXIT_SUCCESS; + log_error("Either --update or --test must be used"); + return EXIT_FAILURE; } if (update) { char **files, **f; _cleanup_free_ char *hwdb_bin = NULL; - trie = calloc(sizeof(struct trie), 1); + trie = new0(struct trie, 1); if (!trie) { rc = EXIT_FAILURE; goto out; @@ -534,7 +613,7 @@ static int adm_hwdb(struct udev *udev, int argc, char *argv[]) { } /* index */ - trie->root = calloc(sizeof(struct trie_node), 1); + trie->root = new0(struct trie_node, 1); if (!trie->root) { rc = EXIT_FAILURE; goto out; @@ -543,53 +622,55 @@ static int adm_hwdb(struct udev *udev, int argc, char *argv[]) { err = conf_files_list_strv(&files, ".hwdb", root, conf_file_dirs); if (err < 0) { - log_error("failed to enumerate hwdb files: %s\n", strerror(-err)); + log_error_errno(err, "failed to enumerate hwdb files: %m"); rc = EXIT_FAILURE; goto out; } STRV_FOREACH(f, files) { log_debug("reading file '%s'", *f); - import_file(trie, *f); + import_file(udev, trie, *f); } strv_free(files); strbuf_complete(trie->strings); - log_debug("=== trie in-memory ===\n"); - log_debug("nodes: %8zu bytes (%8zu)\n", + log_debug("=== trie in-memory ==="); + log_debug("nodes: %8zu bytes (%8zu)", trie->nodes_count * sizeof(struct trie_node), trie->nodes_count); - log_debug("children arrays: %8zu bytes (%8zu)\n", + log_debug("children arrays: %8zu bytes (%8zu)", trie->children_count * sizeof(struct trie_child_entry), trie->children_count); - log_debug("values arrays: %8zu bytes (%8zu)\n", + log_debug("values arrays: %8zu bytes (%8zu)", trie->values_count * sizeof(struct trie_value_entry), trie->values_count); - log_debug("strings: %8zu bytes\n", + log_debug("strings: %8zu bytes", trie->strings->len); - log_debug("strings incoming: %8zu bytes (%8zu)\n", + log_debug("strings incoming: %8zu bytes (%8zu)", trie->strings->in_len, trie->strings->in_count); - log_debug("strings dedup'ed: %8zu bytes (%8zu)\n", + log_debug("strings dedup'ed: %8zu bytes (%8zu)", trie->strings->dedup_len, trie->strings->dedup_count); - if (asprintf(&hwdb_bin, "%s/etc/udev/hwdb.bin", root) < 0) { + hwdb_bin = strjoin(root, "/", hwdb_bin_dir, "/hwdb.bin", NULL); + if (!hwdb_bin) { rc = EXIT_FAILURE; goto out; } mkdir_parents(hwdb_bin, 0755); err = trie_store(trie, hwdb_bin); if (err < 0) { - log_error("Failure writing database %s: %s", hwdb_bin, strerror(-err)); + log_error_errno(err, "Failure writing database %s: %m", hwdb_bin); rc = EXIT_FAILURE; } } if (test) { - struct udev_hwdb *hwdb = udev_hwdb_new(udev); + _cleanup_hwdb_unref_ sd_hwdb *hwdb = NULL; + int r; - if (hwdb) { - struct udev_list_entry *entry; + r = sd_hwdb_new(&hwdb); + if (r >= 0) { + const char *key, *value; - udev_list_entry_foreach(entry, udev_hwdb_get_properties_list_entry(hwdb, test, 0)) - printf("%s=%s\n", udev_list_entry_get_name(entry), udev_list_entry_get_value(entry)); - hwdb = udev_hwdb_unref(hwdb); + SD_HWDB_FOREACH_PROPERTY(hwdb, test, key, value) + printf("%s=%s\n", key, value); } } out: @@ -605,5 +686,4 @@ out: const struct udevadm_cmd udevadm_hwdb = { .name = "hwdb", .cmd = adm_hwdb, - .help = "maintain the hardware database index", };