summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am3
-rw-r--r--man/hwdb.xml90
-rw-r--r--src/basic/util.c2
-rw-r--r--src/hwdb/hwdb.c169
-rw-r--r--src/libsystemd/sd-hwdb/hwdb-internal.h4
-rw-r--r--src/libsystemd/sd-hwdb/sd-hwdb.c62
-rwxr-xr-xtest/hwdb-test.sh31
-rw-r--r--test/hwdb/10-bad.hwdb26
8 files changed, 256 insertions, 131 deletions
diff --git a/Makefile.am b/Makefile.am
index a4e7e56067..fe3ef9116d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1720,7 +1720,8 @@ EXTRA_DIST += \
test/bus-policy/signals.conf \
test/bus-policy/check-own-rules.conf \
test/bus-policy/many-rules.conf \
- test/bus-policy/test.conf
+ test/bus-policy/test.conf \
+ test/hwdb/10-bad.hwdb
EXTRA_DIST += \
diff --git a/man/hwdb.xml b/man/hwdb.xml
index 2b1e60fb22..ae5ddb1a4d 100644
--- a/man/hwdb.xml
+++ b/man/hwdb.xml
@@ -50,28 +50,88 @@
system-supplied hwdb file with a local file if needed;
a symlink in <filename>/etc</filename> with the same name as a hwdb file in
<filename>/usr/lib</filename>, pointing to <filename>/dev/null</filename>,
- disables the hwdb file entirely. hwdb files must have the extension
+ disables that hwdb file entirely. hwdb files must have the extension
<filename>.hwdb</filename>; other extensions are ignored.</para>
- <para>The hwdb file contains data records consisting of matches and
- associated key-value pairs. Every record in the hwdb starts with one or
- more match strings, specifying a shell glob to compare the database
- lookup string against. Multiple match lines are specified in additional
- consecutive lines. Every match line is compared individually, and they are
- combined by OR. Every match line must start at the first character of
- the line.</para>
+ <para>Each hwdb file contains data records consisting of matches and associated
+ key-value pairs. Every record in the hwdb starts with one or more match strings,
+ specifying a shell glob to compare the lookup string against. Multiple match lines
+ are specified in consecutive lines. Every match line is compared individually, and
+ they are combined by OR. Every match line must start at the first character of the
+ line.</para>
- <para>The match lines are followed by one or more key-value pair lines, which
- are recognized by a leading space character. The key name and value are separated
- by <literal>=</literal>. An empty line signifies the end
- of a record. Lines beginning with <literal>#</literal> are ignored.</para>
+ <para>The match lines are followed by one or more key-value pair lines, which are
+ recognized by a leading space character. The key name and value are separated by
+ <literal>=</literal>. An empty line signifies the end of a record. Lines beginning
+ with <literal>#</literal> are ignored.</para>
+
+ <para>In case multiple records match a given lookup string, the key-value pairs
+ from all records are combined. If a key is specified multiple times, the value
+ from the record with the highest priority is used (each key can have only a single
+ value). The priority is higher when the record is in a file that sorts later
+ lexicographically, and in case of records in the same file, later records have
+ higher priority.</para>
<para>The content of all hwdb files is read by
<citerefentry><refentrytitle>systemd-hwdb</refentrytitle><manvolnum>8</manvolnum></citerefentry>
and compiled to a binary database located at <filename>/etc/udev/hwdb.bin</filename>,
- or alternatively <filename>/usr/lib/udev/hwdb.bin</filename> if you want ship the compiled
- database in an immutable image.
- During runtime, only the binary database is used.</para>
+ or alternatively <filename>/usr/lib/udev/hwdb.bin</filename> if you want ship the
+ compiled database in an immutable image. During runtime, only the binary database
+ is used.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <example>
+ <title>General syntax of hwdb files</title>
+
+ <programlisting># /usr/lib/udev/hwdb.d/example.hwdb
+# Comments can be placed before any records. This is a good spot
+# to describe what that file is used for, what kind of properties
+# it defines, and the ordering convention.
+
+# A record with three matches and one property
+mouse:*:name:*Trackball*:
+mouse:*:name:*trackball*:
+mouse:*:name:*TrackBall*:
+ ID_INPUT_TRACKBALL=1
+
+# A record with a single match and five properties
+mouse:usb:v046dp4041:name:Logitech MX Master:
+ MOUSE_DPI=1000@166
+ MOUSE_WHEEL_CLICK_ANGLE=15
+ MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL=26
+ MOUSE_WHEEL_CLICK_COUNT=24
+ MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL=14
+</programlisting>
+ </example>
+
+ <example>
+ <title>Overriding of properties</title>
+
+ <programlisting># /usr/lib/udev/hwdb.d/60-keyboard.hwdb
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pn*
+ KEYBOARD_KEY_a1=help
+ KEYBOARD_KEY_a2=setup
+ KEYBOARD_KEY_a3=battery
+
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pn123*
+ KEYBOARD_KEY_a2=wlan
+
+# /etc/udev/hwdb.d/70-keyboard.hwdb
+# disable wlan key on all at keyboards
+evdev:atkbd:*
+ KEYBOARD_KEY_a2=reserved</programlisting>
+
+ <para>If the hwdb consists of those two files, a keyboard with the lookup string
+ <literal>evdev:atkbd:dmi:bvnAcer:bdXXXXX:bd08/05/2010:svnAcer:pn123</literal>
+ will match all three records, and end up with the following properties:</para>
+
+ <programlisting>KEYBOARD_KEY_a1=help
+KEYBOARD_KEY_a2=reserved
+KEYBOARD_KEY_a3=battery</programlisting>
+ </example>
</refsect1>
<refsect1>
diff --git a/src/basic/util.c b/src/basic/util.c
index 8a630049d7..6204906f37 100644
--- a/src/basic/util.c
+++ b/src/basic/util.c
@@ -492,7 +492,7 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
u = nmemb;
while (l < u) {
idx = (l + u) / 2;
- p = (void *)(((const char *) base) + (idx * size));
+ p = (const char *) base + idx * size;
comparison = compar(key, p, arg);
if (comparison < 0)
u = idx;
diff --git a/src/hwdb/hwdb.c b/src/hwdb/hwdb.c
index 1d16d9f8aa..a23b614791 100644
--- a/src/hwdb/hwdb.c
+++ b/src/hwdb/hwdb.c
@@ -39,7 +39,7 @@
#include "verbs.h"
/*
- * Generic udev properties, key/value database based on modalias strings.
+ * Generic udev properties, key-value database based on modalias strings.
* Uses a Patricia/radix trie to index all matches for efficient lookup.
*/
@@ -70,7 +70,7 @@ struct trie_node {
struct trie_child_entry *children;
uint8_t children_count;
- /* sorted array of key/value pairs */
+ /* sorted array of key-value pairs */
struct trie_value_entry *values;
size_t values_count;
};
@@ -81,12 +81,13 @@ struct trie_child_entry {
struct trie_node *child;
};
-/* value array item with key/value pairs */
+/* value array item with key-value pairs */
struct trie_value_entry {
size_t key_off;
size_t value_off;
size_t filename_off;
- size_t line_number;
+ uint32_t line_number;
+ uint16_t file_priority;
};
static int trie_children_cmp(const void *v1, const void *v2) {
@@ -160,10 +161,9 @@ static int trie_values_cmp(const void *v1, const void *v2, void *arg) {
static int trie_node_add_value(struct trie *trie, struct trie_node *node,
const char *key, const char *value,
- const char *filename, size_t line_number) {
+ const char *filename, uint16_t file_priority, uint32_t line_number) {
ssize_t k, v, fn;
struct trie_value_entry *val;
- int r;
k = strbuf_add_string(trie->strings, key, strlen(key));
if (k < 0)
@@ -183,19 +183,12 @@ static int trie_node_add_value(struct trie *trie, struct trie_node *node,
val = xbsearch_r(&search, node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie);
if (val) {
- /*
- * At this point we have 2 identical properties on the same match-string. We
- * strictly order them by filename+line-number, since we know the dynamic
- * runtime lookup does the same for multiple matching nodes.
+ /* At this point we have 2 identical properties on the same match-string.
+ * Since we process files in order, we just replace the previous value.
*/
- r = strcmp(filename, trie->strings->buf + val->filename_off);
- if (r < 0 ||
- (r == 0 && line_number < val->line_number))
- return 0;
-
- /* replace existing earlier key with new value */
val->value_off = v;
val->filename_off = fn;
+ val->file_priority = file_priority;
val->line_number = line_number;
return 0;
}
@@ -210,6 +203,7 @@ static int trie_node_add_value(struct trie *trie, struct trie_node *node,
node->values[node->values_count].key_off = k;
node->values[node->values_count].value_off = v;
node->values[node->values_count].filename_off = fn;
+ node->values[node->values_count].file_priority = file_priority;
node->values[node->values_count].line_number = line_number;
node->values_count++;
qsort_r(node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie);
@@ -218,9 +212,9 @@ static int trie_node_add_value(struct trie *trie, struct trie_node *node,
static int trie_insert(struct trie *trie, struct trie_node *node, const char *search,
const char *key, const char *value,
- const char *filename, uint64_t line_number) {
+ const char *filename, uint16_t file_priority, uint32_t line_number) {
size_t i = 0;
- int err = 0;
+ int r = 0;
for (;;) {
size_t p;
@@ -261,9 +255,9 @@ static int trie_insert(struct trie *trie, struct trie_node *node, const char *se
node->children_count = 0;
node->values = NULL;
node->values_count = 0;
- err = node_add_child(trie, node, new_child, c);
- if (err < 0)
- return err;
+ r = node_add_child(trie, node, new_child, c);
+ if (r < 0)
+ return r;
new_child = NULL; /* avoid cleanup */
break;
@@ -272,7 +266,7 @@ static int trie_insert(struct trie *trie, struct trie_node *node, const char *se
c = search[i];
if (c == '\0')
- return trie_node_add_value(trie, node, key, value, filename, line_number);
+ return trie_node_add_value(trie, node, key, value, filename, file_priority, line_number);
child = node_lookup(node, c);
if (!child) {
@@ -290,13 +284,13 @@ static int trie_insert(struct trie *trie, struct trie_node *node, const char *se
}
child->prefix_off = off;
- err = node_add_child(trie, node, child, c);
- if (err < 0) {
+ r = node_add_child(trie, node, child, c);
+ if (r < 0) {
free(child);
- return err;
+ return r;
}
- return trie_node_add_value(trie, child, key, value, filename, line_number);
+ return trie_node_add_value(trie, child, key, value, filename, file_priority, line_number);
}
node = child;
@@ -335,11 +329,11 @@ 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 = NULL;
+ _cleanup_free_ struct trie_child_entry_f *children = NULL;
int64_t node_off;
if (node->children_count) {
- children = new0(struct trie_child_entry_f, node->children_count);
+ children = new(struct trie_child_entry_f, node->children_count);
if (!children)
return -ENOMEM;
}
@@ -349,12 +343,13 @@ 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) {
- free(children);
+ if (child_off < 0)
return child_off;
- }
- children[i].c = node->children[i].c;
- children[i].child_off = htole64(child_off);
+
+ children[i] = (struct trie_child_entry_f) {
+ .c = node->children[i].c,
+ .child_off = htole64(child_off),
+ };
}
/* write node */
@@ -366,7 +361,6 @@ static int64_t trie_store_nodes(struct trie_f *trie, struct trie_node *node) {
if (node->children_count) {
fwrite(children, sizeof(struct trie_child_entry_f), node->children_count, trie->f);
trie->children_count += node->children_count;
- free(children);
}
/* append values array */
@@ -375,12 +369,13 @@ static int64_t trie_store_nodes(struct trie_f *trie, struct trie_node *node) {
.key_off = htole64(trie->strings_off + node->values[i].key_off),
.value_off = htole64(trie->strings_off + node->values[i].value_off),
.filename_off = htole64(trie->strings_off + node->values[i].filename_off),
- .line_number = htole64(node->values[i].line_number),
+ .line_number = htole32(node->values[i].line_number),
+ .file_priority = htole16(node->values[i].file_priority),
};
fwrite(&v, sizeof(struct trie_value_entry2_f), 1, trie->f);
- trie->values_count++;
}
+ trie->values_count += node->values_count;
return node_off;
}
@@ -401,24 +396,21 @@ static int trie_store(struct trie *trie, const char *filename) {
.child_entry_size = htole64(sizeof(struct trie_child_entry_f)),
.value_entry_size = htole64(sizeof(struct trie_value_entry2_f)),
};
- int err;
+ int r;
/* calculate size of header, nodes, children entries, value entries */
t.strings_off = sizeof(struct trie_header_f);
trie_store_nodes_size(&t, trie->root);
- err = fopen_temporary(filename , &t.f, &filename_tmp);
- if (err < 0)
- return err;
+ r = fopen_temporary(filename , &t.f, &filename_tmp);
+ if (r < 0)
+ return r;
fchmod(fileno(t.f), 0444);
/* write nodes */
- err = fseeko(t.f, sizeof(struct trie_header_f), SEEK_SET);
- if (err < 0) {
- fclose(t.f);
- unlink_noerrno(filename_tmp);
- return -errno;
- }
+ if (fseeko(t.f, sizeof(struct trie_header_f), SEEK_SET) < 0)
+ goto error;
+
root_off = trie_store_nodes(&t, trie->root);
h.nodes_root_off = htole64(root_off);
pos = ftello(t.f);
@@ -431,20 +423,13 @@ static int trie_store(struct trie *trie, const char *filename) {
/* write header */
size = ftello(t.f);
h.file_size = htole64(size);
- err = fseeko(t.f, 0, SEEK_SET);
- if (err < 0) {
- fclose(t.f);
- unlink_noerrno(filename_tmp);
- return -errno;
- }
+ if (fseeko(t.f, 0, SEEK_SET) < 0)
+ goto error;
+
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) {
+ if (fclose(t.f) < 0 || rename(filename_tmp, filename) < 0) {
unlink_noerrno(filename_tmp);
- return err < 0 ? err : -errno;
+ return -errno;
}
log_debug("=== trie on-disk ===");
@@ -458,39 +443,46 @@ static int trie_store(struct trie *trie, const char *filename) {
t.values_count * sizeof(struct trie_value_entry2_f), t.values_count);
log_debug("string store: %8zu bytes", trie->strings->len);
log_debug("strings start: %8"PRIu64, t.strings_off);
-
return 0;
+
+ error:
+ r = -errno;
+ fclose(t.f);
+ unlink(filename_tmp);
+ return r;
}
static int insert_data(struct trie *trie, char **match_list, char *line,
- const char *filename, size_t line_number) {
+ const char *filename, uint16_t file_priority, uint32_t line_number) {
char *value, **entry;
+ assert(line[0] == ' ');
+
value = strchr(line, '=');
- if (!value) {
- log_error("Error, key/value pair expected but got '%s' in '%s':", line, filename);
- return -EINVAL;
- }
+ if (!value)
+ return log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
+ "Key-value pair expected but got \"%s\", ignoring", line);
value[0] = '\0';
value++;
- /* libudev requires properties to start with a space */
+ /* Replace multiple leading spaces by a single 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;
- }
+ if (isempty(line + 1) || isempty(value))
+ return log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
+ "Empty %s in \"%s=%s\", ignoring",
+ isempty(line + 1) ? "key" : "value",
+ line, value);
STRV_FOREACH(entry, match_list)
- trie_insert(trie, trie->root, *entry, line, value, filename, line_number);
+ trie_insert(trie, trie->root, *entry, line, value, filename, file_priority, line_number);
return 0;
}
-static int import_file(struct trie *trie, const char *filename) {
+static int import_file(struct trie *trie, const char *filename, uint16_t file_priority) {
enum {
HW_NONE,
HW_MATCH,
@@ -499,7 +491,7 @@ static int import_file(struct trie *trie, const char *filename) {
_cleanup_fclose_ FILE *f = NULL;
char line[LINE_MAX];
_cleanup_strv_free_ char **match_list = NULL;
- size_t line_number = 0;
+ uint32_t line_number = 0;
char *match = NULL;
int r;
@@ -534,7 +526,8 @@ static int import_file(struct trie *trie, const char *filename) {
break;
if (line[0] == ' ') {
- log_error("Error, MATCH expected but got '%s' in '%s':", line, filename);
+ log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
+ "Match expected but got indented property \"%s\", ignoring line", line);
break;
}
@@ -553,14 +546,16 @@ static int import_file(struct trie *trie, const char *filename) {
case HW_MATCH:
if (len == 0) {
- log_error("Error, DATA expected but got empty line in '%s':", filename);
+ log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
+ "Property expected, ignoring record with no properties");
+
state = HW_NONE;
strv_clear(match_list);
break;
}
- /* another match */
if (line[0] != ' ') {
+ /* another match */
match = strdup(line);
if (!match)
return -ENOMEM;
@@ -574,29 +569,34 @@ static int import_file(struct trie *trie, const char *filename) {
/* first data */
state = HW_DATA;
- insert_data(trie, match_list, line, filename, line_number);
+ insert_data(trie, match_list, line, filename, file_priority, line_number);
break;
case HW_DATA:
- /* end of record */
if (len == 0) {
+ /* end of record */
state = HW_NONE;
strv_clear(match_list);
break;
}
if (line[0] != ' ') {
- log_error("Error, DATA expected but got '%s' in '%s':", line, filename);
+ log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
+ "Property or empty line expected, got \"%s\", ignoring record", line);
state = HW_NONE;
strv_clear(match_list);
break;
}
- insert_data(trie, match_list, line, filename, line_number);
+ insert_data(trie, match_list, line, filename, file_priority, line_number);
break;
};
}
+ if (state == HW_MATCH)
+ log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
+ "Property expected, ignoring record with no properties");
+
return 0;
}
@@ -624,7 +624,9 @@ static int hwdb_query(int argc, char *argv[], void *userdata) {
static int hwdb_update(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *hwdb_bin = NULL;
_cleanup_(trie_freep) struct trie *trie = NULL;
- char **files, **f;
+ _cleanup_strv_free_ char **files = NULL;
+ char **f;
+ uint16_t file_priority = 1;
int r;
trie = new0(struct trie, 1);
@@ -645,13 +647,12 @@ static int hwdb_update(int argc, char *argv[], void *userdata) {
r = conf_files_list_strv(&files, ".hwdb", arg_root, conf_file_dirs);
if (r < 0)
- return log_error_errno(r, "failed to enumerate hwdb files: %m");
+ return log_error_errno(r, "Failed to enumerate hwdb files: %m");
STRV_FOREACH(f, files) {
- log_debug("reading file '%s'", *f);
- import_file(trie, *f);
+ log_debug("Reading file \"%s\"", *f);
+ import_file(trie, *f, file_priority++);
}
- strv_free(files);
strbuf_complete(trie->strings);
diff --git a/src/libsystemd/sd-hwdb/hwdb-internal.h b/src/libsystemd/sd-hwdb/hwdb-internal.h
index 4fff94ec76..2f4934ae8b 100644
--- a/src/libsystemd/sd-hwdb/hwdb-internal.h
+++ b/src/libsystemd/sd-hwdb/hwdb-internal.h
@@ -76,5 +76,7 @@ struct trie_value_entry2_f {
le64_t key_off;
le64_t value_off;
le64_t filename_off;
- le64_t line_number;
+ le32_t line_number;
+ le16_t file_priority;
+ le16_t padding;
} _packed_;
diff --git a/src/libsystemd/sd-hwdb/sd-hwdb.c b/src/libsystemd/sd-hwdb/sd-hwdb.c
index 719e3505c1..c155c70162 100644
--- a/src/libsystemd/sd-hwdb/sd-hwdb.c
+++ b/src/libsystemd/sd-hwdb/sd-hwdb.c
@@ -48,8 +48,6 @@ struct sd_hwdb {
const char *map;
};
- char *modalias;
-
OrderedHashmap *properties;
Iterator properties_iterator;
bool properties_modified;
@@ -164,10 +162,38 @@ static int hwdb_add_property(sd_hwdb *hwdb, const struct trie_value_entry_f *ent
entry2 = (const struct trie_value_entry2_f *)entry;
old = ordered_hashmap_get(hwdb->properties, key);
if (old) {
- /* on duplicates, we order by filename and line-number */
- r = strcmp(trie_string(hwdb, entry2->filename_off), trie_string(hwdb, old->filename_off));
- if (r < 0 ||
- (r == 0 && entry2->line_number < old->line_number))
+ /* On duplicates, we order by filename priority and line-number.
+ *
+ *
+ * v2 of the format had 64 bits for the line number.
+ * v3 reuses top 32 bits of line_number to store the priority.
+ * We check the top bits — if they are zero we have v2 format.
+ * This means that v2 clients will print wrong line numbers with
+ * v3 data.
+ *
+ * For v3 data: we compare the priority (of the source file)
+ * and the line number.
+ *
+ * For v2 data: we rely on the fact that the filenames in the hwdb
+ * are added in the order of priority (higher later), because they
+ * are *processed* in the order of priority. So we compare the
+ * indices to determine which file had higher priority. Comparing
+ * the strings alphabetically would be useless, because those are
+ * full paths, and e.g. /usr/lib would sort after /etc, even
+ * though it has lower priority. This is not reliable because of
+ * suffix compression, but should work for the most common case of
+ * /usr/lib/udev/hwbd.d and /etc/udev/hwdb.d, and is better than
+ * not doing the comparison at all.
+ */
+ bool lower;
+
+ if (entry2->file_priority == 0)
+ lower = entry2->filename_off < old->filename_off ||
+ (entry2->filename_off == old->filename_off && entry2->line_number < old->line_number);
+ else
+ lower = entry2->file_priority < old->file_priority ||
+ (entry2->file_priority == old->file_priority && entry2->line_number < old->line_number);
+ if (lower)
return 0;
}
}
@@ -234,7 +260,7 @@ static int trie_search_f(sd_hwdb *hwdb, const char *search) {
uint8_t c;
for (; (c = trie_string(hwdb, node->prefix_off)[p]); p++) {
- if (c == '*' || c == '?' || c == '[')
+ if (IN_SET(c, '*', '?', '['))
return trie_fnmatch_f(hwdb, node, p, &buf, search + i + p);
if (c != search[i + p])
return 0;
@@ -365,7 +391,6 @@ _public_ sd_hwdb *sd_hwdb_unref(sd_hwdb *hwdb) {
if (hwdb->map)
munmap((void *)hwdb->map, hwdb->st.st_size);
safe_fclose(hwdb->f);
- free(hwdb->modalias);
ordered_hashmap_free(hwdb->properties);
free(hwdb);
}
@@ -399,32 +424,13 @@ bool hwdb_validate(sd_hwdb *hwdb) {
}
static int properties_prepare(sd_hwdb *hwdb, const char *modalias) {
- _cleanup_free_ char *mod = NULL;
- int r;
-
assert(hwdb);
assert(modalias);
- if (streq_ptr(modalias, hwdb->modalias))
- return 0;
-
- mod = strdup(modalias);
- if (!mod)
- return -ENOMEM;
-
ordered_hashmap_clear(hwdb->properties);
-
hwdb->properties_modified = true;
- r = trie_search_f(hwdb, modalias);
- if (r < 0)
- return r;
-
- free(hwdb->modalias);
- hwdb->modalias = mod;
- mod = NULL;
-
- return 0;
+ return trie_search_f(hwdb, modalias);
}
_public_ int sd_hwdb_get(sd_hwdb *hwdb, const char *modalias, const char *key, const char **_value) {
diff --git a/test/hwdb-test.sh b/test/hwdb-test.sh
index 4638328059..5373930df4 100755
--- a/test/hwdb-test.sh
+++ b/test/hwdb-test.sh
@@ -32,11 +32,40 @@ D=$(mktemp --directory)
trap "rm -rf '$D'" EXIT INT QUIT PIPE
mkdir -p "$D/etc/udev"
ln -s "$ROOTDIR/hwdb" "$D/etc/udev/hwdb.d"
-err=$("$SYSTEMD_HWDB" update --root "$D" 2>&1 >/dev/null)
+
+# Test "good" properties" — no warnings or errors allowed
+err=$("$SYSTEMD_HWDB" update --root "$D" 2>&1 >/dev/null) && rc= || rc=$?
+if [ -n "$err" ]; then
+ echo "$err"
+ exit ${rc:-1}
+fi
+if [ -n "$rc" ]; then
+ echo "$SYSTEMD_HWDB returned $rc"
+ exit $rc
+fi
+
+if [ ! -e "$D/etc/udev/hwdb.bin" ]; then
+ echo "$D/etc/udev/hwdb.bin was not generated"
+ exit 1
+fi
+
+# Test "bad" properties" — warnings required, errors not allowed
+rm -f "$D/etc/udev/hwdb.bin" "$D/etc/udev/hwdb.d"
+
+ln -s "$ROOTDIR/test/hwdb" "$D/etc/udev/hwdb.d"
+err=$("$SYSTEMD_HWDB" update --root "$D" 2>&1 >/dev/null) && rc= || rc=$?
+if [ -n "$rc" ]; then
+ echo "$SYSTEMD_HWDB returned $rc"
+ exit $rc
+fi
if [ -n "$err" ]; then
+ echo "Expected warnings"
echo "$err"
+else
+ echo "$SYSTEMD_HWDB unexpectedly printed no warnings"
exit 1
fi
+
if [ ! -e "$D/etc/udev/hwdb.bin" ]; then
echo "$D/etc/udev/hwdb.bin was not generated"
exit 1
diff --git a/test/hwdb/10-bad.hwdb b/test/hwdb/10-bad.hwdb
new file mode 100644
index 0000000000..0e1e147323
--- /dev/null
+++ b/test/hwdb/10-bad.hwdb
@@ -0,0 +1,26 @@
+BAD:1:no properties
+
+BAD:2:no properties
+BAD:2:no properties
+
+BAD:3:no properties
+BAD:3:no properties
+BAD:3:no properties
+
+GOOD:5:bad property
+ NO_VALUE
+
+GOOD:6:bad property
+ =NO_NAME
+ NO_VALUE=
+
+BAD:7:match at wrong place
+ X=Y
+BAD:7:match at wrong place
+
+BAD:8:match at wrong place
+ X=Y
+BAD:8:match at wrong place
+ Z=z
+
+BAD:8:match at EOF