2014-12-03 17:02:34 +01:00
|
|
|
/***
|
|
|
|
This file is part of systemd.
|
|
|
|
|
|
|
|
Copyright 2012 Kay Sievers <kay@vrfy.org>
|
|
|
|
Copyright 2008 Alan Jenkins <alan.christopher.jenkins@googlemail.com>
|
|
|
|
Copyright 2014 Tom Gundersen <teg@jklm.no>
|
|
|
|
|
|
|
|
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 <errno.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <fnmatch.h>
|
2014-12-03 17:02:34 +01:00
|
|
|
#include <inttypes.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <stdio.h>
|
2014-12-03 17:02:34 +01:00
|
|
|
#include <stdlib.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <string.h>
|
2014-12-03 17:02:34 +01:00
|
|
|
#include <sys/mman.h>
|
|
|
|
|
|
|
|
#include "sd-hwdb.h"
|
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "alloc-util.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "fd-util.h"
|
2014-12-03 17:02:34 +01:00
|
|
|
#include "hashmap.h"
|
|
|
|
#include "hwdb-internal.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "hwdb-util.h"
|
|
|
|
#include "refcnt.h"
|
|
|
|
#include "string-util.h"
|
2014-12-03 17:02:34 +01:00
|
|
|
|
|
|
|
struct sd_hwdb {
|
|
|
|
RefCount n_ref;
|
|
|
|
int refcount;
|
|
|
|
|
|
|
|
FILE *f;
|
|
|
|
struct stat st;
|
|
|
|
union {
|
|
|
|
struct trie_header_f *head;
|
|
|
|
const char *map;
|
|
|
|
};
|
|
|
|
|
|
|
|
OrderedHashmap *properties;
|
|
|
|
Iterator properties_iterator;
|
|
|
|
bool properties_modified;
|
|
|
|
};
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-08-27 17:48:24 +02:00
|
|
|
static bool linebuf_add_char(struct linebuf *buf, char c) {
|
2014-12-03 17:02:34 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2016-09-21 14:56:04 +02:00
|
|
|
static const struct trie_child_entry_f *trie_node_child(sd_hwdb *hwdb, const struct trie_node_f *node, size_t idx) {
|
|
|
|
const char *base = (const char *)node;
|
|
|
|
|
|
|
|
base += le64toh(hwdb->head->node_size);
|
|
|
|
base += idx * le64toh(hwdb->head->child_entry_size);
|
|
|
|
return (const struct trie_child_entry_f *)base;
|
2014-12-03 17:02:34 +01:00
|
|
|
}
|
|
|
|
|
2016-09-21 14:56:04 +02:00
|
|
|
static const struct trie_value_entry_f *trie_node_value(sd_hwdb *hwdb, const struct trie_node_f *node, size_t idx) {
|
2014-12-03 17:02:34 +01:00
|
|
|
const char *base = (const char *)node;
|
|
|
|
|
|
|
|
base += le64toh(hwdb->head->node_size);
|
|
|
|
base += node->children_count * le64toh(hwdb->head->child_entry_size);
|
2016-09-21 14:56:04 +02:00
|
|
|
base += idx * le64toh(hwdb->head->value_entry_size);
|
2014-12-03 17:02:34 +01:00
|
|
|
return (const struct trie_value_entry_f *)base;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct trie_node_f *trie_node_from_off(sd_hwdb *hwdb, le64_t off) {
|
|
|
|
return (const struct trie_node_f *)(hwdb->map + le64toh(off));
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *trie_string(sd_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(sd_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;
|
2016-09-21 14:56:04 +02:00
|
|
|
child = bsearch(&search, (const char *)node + le64toh(hwdb->head->node_size), node->children_count,
|
2014-12-03 17:02:34 +01:00
|
|
|
le64toh(hwdb->head->child_entry_size), trie_children_cmp_f);
|
|
|
|
if (child)
|
|
|
|
return trie_node_from_off(hwdb, child->child_off);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-09-21 15:16:00 +02:00
|
|
|
static int hwdb_add_property(sd_hwdb *hwdb, const struct trie_value_entry_f *entry) {
|
|
|
|
const char *key;
|
2014-12-03 17:02:34 +01:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(hwdb);
|
2016-09-21 15:16:00 +02:00
|
|
|
|
|
|
|
key = trie_string(hwdb, entry->key_off);
|
2014-12-03 17:02:34 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Silently ignore all properties which do not start with a
|
|
|
|
* space; future extensions might use additional prefixes.
|
|
|
|
*/
|
|
|
|
if (key[0] != ' ')
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
key++;
|
|
|
|
|
2016-09-21 15:16:00 +02:00
|
|
|
if (le64toh(hwdb->head->value_entry_size) >= sizeof(struct trie_value_entry2_f)) {
|
|
|
|
const struct trie_value_entry2_f *old, *entry2;
|
|
|
|
|
|
|
|
entry2 = (const struct trie_value_entry2_f *)entry;
|
|
|
|
old = ordered_hashmap_get(hwdb->properties, key);
|
|
|
|
if (old) {
|
hwdb, sd-hwdb: rework priority comparison when loading properties
We cannot compare filenames directly, because paths are not sortable
lexicographically, e.g. /etc/udev is "later" (has higher priority)
than /usr/lib/udev.
The on-disk format is changed to have a separate field for "file priority",
which is stored when writing the binary file, and then loaded and used in
comparisons. For data in the previous format (as generated by systemd 232),
this information is not available, and we use a trick where the offset into the
string table is used as a proxy for priority. Most of the time strings are
stored in the order in which the files were processed. This is not entirely
reliable, but is good enough to properly order /usr/lib and /etc/, which are
the two most common cases. This hack is included because it allows proper
parsing of files until the binary hwdb is regenerated.
Instead of adding a new field, I reduced the size of line_number from 64 to 32
bits, and added a 16 bit priority field, and 16 bits of padding. Adding a new
field of 16 bytes would significantly screw up alignment and increase file
size, and line number realistically don't need more than ~20 bits.
Fixes #4750.
2016-11-30 02:26:35 +01:00
|
|
|
/* 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)
|
2016-09-21 15:16:00 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-03 17:02:34 +01:00
|
|
|
r = ordered_hashmap_ensure_allocated(&hwdb->properties, &string_hash_ops);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2016-09-21 15:16:00 +02:00
|
|
|
r = ordered_hashmap_replace(hwdb->properties, key, (void *)entry);
|
2014-12-03 17:02:34 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
hwdb->properties_modified = true;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int trie_fnmatch_f(sd_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++) {
|
2016-09-21 14:56:04 +02:00
|
|
|
const struct trie_child_entry_f *child = trie_node_child(hwdb, node, i);
|
2014-12-03 17:02:34 +01:00
|
|
|
|
|
|
|
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 (le64toh(node->values_count) && fnmatch(linebuf_get(buf), search, 0) == 0)
|
|
|
|
for (i = 0; i < le64toh(node->values_count); i++) {
|
2016-09-21 15:16:00 +02:00
|
|
|
err = hwdb_add_property(hwdb, trie_node_value(hwdb, node, i));
|
2014-12-03 17:02:34 +01:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
linebuf_rem(buf, len);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int trie_search_f(sd_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++) {
|
2016-11-29 20:42:57 +01:00
|
|
|
if (IN_SET(c, '*', '?', '['))
|
2014-12-03 17:02:34 +01:00
|
|
|
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 < le64toh(node->values_count); n++) {
|
2016-09-21 15:16:00 +02:00
|
|
|
err = hwdb_add_property(hwdb, trie_node_value(hwdb, node, n));
|
2014-12-03 17:02:34 +01:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
child = node_lookup_f(hwdb, node, search[i]);
|
|
|
|
node = child;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char hwdb_bin_paths[] =
|
2015-08-27 17:48:24 +02:00
|
|
|
"/etc/systemd/hwdb/hwdb.bin\0"
|
|
|
|
"/etc/udev/hwdb.bin\0"
|
|
|
|
"/usr/lib/systemd/hwdb/hwdb.bin\0"
|
2017-10-03 10:41:51 +02:00
|
|
|
#if HAVE_SPLIT_USR
|
2015-08-27 17:48:24 +02:00
|
|
|
"/lib/systemd/hwdb/hwdb.bin\0"
|
2014-12-03 17:02:34 +01:00
|
|
|
#endif
|
2015-08-27 17:48:24 +02:00
|
|
|
UDEVLIBEXECDIR "/hwdb.bin\0";
|
2014-12-03 17:02:34 +01:00
|
|
|
|
|
|
|
_public_ int sd_hwdb_new(sd_hwdb **ret) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
|
2014-12-03 17:02:34 +01:00
|
|
|
const char *hwdb_bin_path;
|
|
|
|
const char sig[] = HWDB_SIG;
|
|
|
|
|
|
|
|
assert_return(ret, -EINVAL);
|
|
|
|
|
|
|
|
hwdb = new0(sd_hwdb, 1);
|
|
|
|
if (!hwdb)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
hwdb->n_ref = REFCNT_INIT;
|
|
|
|
|
|
|
|
/* find hwdb.bin in hwdb_bin_paths */
|
|
|
|
NULSTR_FOREACH(hwdb_bin_path, hwdb_bin_paths) {
|
|
|
|
hwdb->f = fopen(hwdb_bin_path, "re");
|
|
|
|
if (hwdb->f)
|
|
|
|
break;
|
|
|
|
else if (errno == ENOENT)
|
|
|
|
continue;
|
|
|
|
else
|
|
|
|
return log_debug_errno(errno, "error reading %s: %m", hwdb_bin_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!hwdb->f) {
|
2016-11-23 19:21:56 +01:00
|
|
|
log_debug("hwdb.bin does not exist, please run systemd-hwdb update");
|
2014-12-03 17:02:34 +01:00
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fstat(fileno(hwdb->f), &hwdb->st) < 0 ||
|
|
|
|
(size_t)hwdb->st.st_size < offsetof(struct trie_header_f, strings_len) + 8)
|
|
|
|
return log_debug_errno(errno, "error reading %s: %m", hwdb_bin_path);
|
|
|
|
|
|
|
|
hwdb->map = mmap(0, hwdb->st.st_size, PROT_READ, MAP_SHARED, fileno(hwdb->f), 0);
|
|
|
|
if (hwdb->map == MAP_FAILED)
|
|
|
|
return log_debug_errno(errno, "error mapping %s: %m", hwdb_bin_path);
|
|
|
|
|
|
|
|
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 %s", hwdb_bin_path);
|
2015-06-02 23:20:15 +02:00
|
|
|
return -EINVAL;
|
2014-12-03 17:02:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
log_debug("=== trie on-disk ===");
|
|
|
|
log_debug("tool version: %"PRIu64, le64toh(hwdb->head->tool_version));
|
2015-01-21 04:22:15 +01:00
|
|
|
log_debug("file size: %8"PRIi64" bytes", hwdb->st.st_size);
|
2014-12-03 17:02:34 +01:00
|
|
|
log_debug("header size %8"PRIu64" bytes", le64toh(hwdb->head->header_size));
|
|
|
|
log_debug("strings %8"PRIu64" bytes", le64toh(hwdb->head->strings_len));
|
|
|
|
log_debug("nodes %8"PRIu64" bytes", le64toh(hwdb->head->nodes_len));
|
|
|
|
|
|
|
|
*ret = hwdb;
|
|
|
|
hwdb = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
_public_ sd_hwdb *sd_hwdb_ref(sd_hwdb *hwdb) {
|
|
|
|
assert_return(hwdb, NULL);
|
|
|
|
|
|
|
|
assert_se(REFCNT_INC(hwdb->n_ref) >= 2);
|
|
|
|
|
|
|
|
return hwdb;
|
|
|
|
}
|
|
|
|
|
|
|
|
_public_ sd_hwdb *sd_hwdb_unref(sd_hwdb *hwdb) {
|
2015-01-13 23:03:11 +01:00
|
|
|
if (hwdb && REFCNT_DEC(hwdb->n_ref) == 0) {
|
2014-12-03 17:02:34 +01:00
|
|
|
if (hwdb->map)
|
|
|
|
munmap((void *)hwdb->map, hwdb->st.st_size);
|
2015-09-09 15:20:10 +02:00
|
|
|
safe_fclose(hwdb->f);
|
2014-12-03 17:02:34 +01:00
|
|
|
ordered_hashmap_free(hwdb->properties);
|
|
|
|
free(hwdb);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool hwdb_validate(sd_hwdb *hwdb) {
|
|
|
|
bool found = false;
|
|
|
|
const char* p;
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
if (!hwdb)
|
|
|
|
return false;
|
|
|
|
if (!hwdb->f)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* if hwdb.bin doesn't exist anywhere, we need to update */
|
|
|
|
NULSTR_FOREACH(p, hwdb_bin_paths) {
|
|
|
|
if (stat(p, &st) >= 0) {
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (timespec_load(&hwdb->st.st_mtim) != timespec_load(&st.st_mtim))
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int properties_prepare(sd_hwdb *hwdb, const char *modalias) {
|
|
|
|
assert(hwdb);
|
|
|
|
assert(modalias);
|
|
|
|
|
|
|
|
ordered_hashmap_clear(hwdb->properties);
|
|
|
|
hwdb->properties_modified = true;
|
|
|
|
|
2016-11-29 21:17:19 +01:00
|
|
|
return trie_search_f(hwdb, modalias);
|
2014-12-03 17:02:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
_public_ int sd_hwdb_get(sd_hwdb *hwdb, const char *modalias, const char *key, const char **_value) {
|
2016-09-21 15:16:00 +02:00
|
|
|
const struct trie_value_entry_f *entry;
|
2014-12-03 17:02:34 +01:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert_return(hwdb, -EINVAL);
|
|
|
|
assert_return(hwdb->f, -EINVAL);
|
|
|
|
assert_return(modalias, -EINVAL);
|
|
|
|
assert_return(_value, -EINVAL);
|
|
|
|
|
|
|
|
r = properties_prepare(hwdb, modalias);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2016-09-21 15:16:00 +02:00
|
|
|
entry = ordered_hashmap_get(hwdb->properties, key);
|
|
|
|
if (!entry)
|
2014-12-03 17:02:34 +01:00
|
|
|
return -ENOENT;
|
|
|
|
|
2016-09-21 15:16:00 +02:00
|
|
|
*_value = trie_string(hwdb, entry->value_off);
|
2014-12-03 17:02:34 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
_public_ int sd_hwdb_seek(sd_hwdb *hwdb, const char *modalias) {
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert_return(hwdb, -EINVAL);
|
|
|
|
assert_return(hwdb->f, -EINVAL);
|
|
|
|
assert_return(modalias, -EINVAL);
|
|
|
|
|
|
|
|
r = properties_prepare(hwdb, modalias);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
hwdb->properties_modified = false;
|
|
|
|
hwdb->properties_iterator = ITERATOR_FIRST;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
_public_ int sd_hwdb_enumerate(sd_hwdb *hwdb, const char **key, const char **value) {
|
2016-09-21 15:16:00 +02:00
|
|
|
const struct trie_value_entry_f *entry;
|
2015-06-14 16:51:35 +02:00
|
|
|
const void *k;
|
2014-12-03 17:02:34 +01:00
|
|
|
|
|
|
|
assert_return(hwdb, -EINVAL);
|
|
|
|
assert_return(key, -EINVAL);
|
|
|
|
assert_return(value, -EINVAL);
|
|
|
|
|
|
|
|
if (hwdb->properties_modified)
|
|
|
|
return -EAGAIN;
|
|
|
|
|
2016-09-21 15:16:00 +02:00
|
|
|
ordered_hashmap_iterate(hwdb->properties, &hwdb->properties_iterator, (void **)&entry, &k);
|
2014-12-03 17:02:34 +01:00
|
|
|
if (!k)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
*key = k;
|
2016-09-21 15:16:00 +02:00
|
|
|
*value = trie_string(hwdb, entry->value_off);
|
2014-12-03 17:02:34 +01:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|