]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-hwdb/sd-hwdb.c
importd: fix typos
[thirdparty/systemd.git] / src / libsystemd / sd-hwdb / sd-hwdb.c
CommitLineData
23fbe14f
TG
1/***
2 This file is part of systemd.
3
4 Copyright 2012 Kay Sievers <kay@vrfy.org>
5 Copyright 2008 Alan Jenkins <alan.christopher.jenkins@googlemail.com>
6 Copyright 2014 Tom Gundersen <teg@jklm.no>
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <stdio.h>
23#include <errno.h>
24#include <string.h>
25#include <inttypes.h>
23fbe14f
TG
26#include <stdlib.h>
27#include <fnmatch.h>
23fbe14f
TG
28#include <sys/mman.h>
29
30#include "sd-hwdb.h"
31
32#include "hashmap.h"
33#include "refcnt.h"
34
35#include "hwdb-util.h"
36#include "hwdb-internal.h"
37
38struct sd_hwdb {
39 RefCount n_ref;
40 int refcount;
41
42 FILE *f;
43 struct stat st;
44 union {
45 struct trie_header_f *head;
46 const char *map;
47 };
48
49 char *modalias;
50
51 OrderedHashmap *properties;
52 Iterator properties_iterator;
53 bool properties_modified;
54};
55
56struct linebuf {
57 char bytes[LINE_MAX];
58 size_t size;
59 size_t len;
60};
61
62static void linebuf_init(struct linebuf *buf) {
63 buf->size = 0;
64 buf->len = 0;
65}
66
67static const char *linebuf_get(struct linebuf *buf) {
68 if (buf->len + 1 >= sizeof(buf->bytes))
69 return NULL;
70 buf->bytes[buf->len] = '\0';
71 return buf->bytes;
72}
73
74static bool linebuf_add(struct linebuf *buf, const char *s, size_t len) {
75 if (buf->len + len >= sizeof(buf->bytes))
76 return false;
77 memcpy(buf->bytes + buf->len, s, len);
78 buf->len += len;
79 return true;
80}
81
52efd56a 82static bool linebuf_add_char(struct linebuf *buf, char c) {
23fbe14f
TG
83 if (buf->len + 1 >= sizeof(buf->bytes))
84 return false;
85 buf->bytes[buf->len++] = c;
86 return true;
87}
88
89static void linebuf_rem(struct linebuf *buf, size_t count) {
90 assert(buf->len >= count);
91 buf->len -= count;
92}
93
94static void linebuf_rem_char(struct linebuf *buf) {
95 linebuf_rem(buf, 1);
96}
97
98static const struct trie_child_entry_f *trie_node_children(sd_hwdb *hwdb, const struct trie_node_f *node) {
99 return (const struct trie_child_entry_f *)((const char *)node + le64toh(hwdb->head->node_size));
100}
101
102static const struct trie_value_entry_f *trie_node_values(sd_hwdb *hwdb, const struct trie_node_f *node) {
103 const char *base = (const char *)node;
104
105 base += le64toh(hwdb->head->node_size);
106 base += node->children_count * le64toh(hwdb->head->child_entry_size);
107 return (const struct trie_value_entry_f *)base;
108}
109
110static const struct trie_node_f *trie_node_from_off(sd_hwdb *hwdb, le64_t off) {
111 return (const struct trie_node_f *)(hwdb->map + le64toh(off));
112}
113
114static const char *trie_string(sd_hwdb *hwdb, le64_t off) {
115 return hwdb->map + le64toh(off);
116}
117
118static int trie_children_cmp_f(const void *v1, const void *v2) {
119 const struct trie_child_entry_f *n1 = v1;
120 const struct trie_child_entry_f *n2 = v2;
121
122 return n1->c - n2->c;
123}
124
125static const struct trie_node_f *node_lookup_f(sd_hwdb *hwdb, const struct trie_node_f *node, uint8_t c) {
126 struct trie_child_entry_f *child;
127 struct trie_child_entry_f search;
128
129 search.c = c;
130 child = bsearch(&search, trie_node_children(hwdb, node), node->children_count,
131 le64toh(hwdb->head->child_entry_size), trie_children_cmp_f);
132 if (child)
133 return trie_node_from_off(hwdb, child->child_off);
134 return NULL;
135}
136
137static int hwdb_add_property(sd_hwdb *hwdb, const char *key, const char *value) {
138 int r;
139
140 assert(hwdb);
141 assert(key);
142 assert(value);
143
144 /*
145 * Silently ignore all properties which do not start with a
146 * space; future extensions might use additional prefixes.
147 */
148 if (key[0] != ' ')
149 return 0;
150
151 key++;
152
153 r = ordered_hashmap_ensure_allocated(&hwdb->properties, &string_hash_ops);
154 if (r < 0)
155 return r;
156
157 r = ordered_hashmap_replace(hwdb->properties, key, (char*)value);
158 if (r < 0)
159 return r;
160
161 hwdb->properties_modified = true;
162
163 return 0;
164}
165
166static int trie_fnmatch_f(sd_hwdb *hwdb, const struct trie_node_f *node, size_t p,
167 struct linebuf *buf, const char *search) {
168 size_t len;
169 size_t i;
170 const char *prefix;
171 int err;
172
173 prefix = trie_string(hwdb, node->prefix_off);
174 len = strlen(prefix + p);
175 linebuf_add(buf, prefix + p, len);
176
177 for (i = 0; i < node->children_count; i++) {
178 const struct trie_child_entry_f *child = &trie_node_children(hwdb, node)[i];
179
180 linebuf_add_char(buf, child->c);
181 err = trie_fnmatch_f(hwdb, trie_node_from_off(hwdb, child->child_off), 0, buf, search);
182 if (err < 0)
183 return err;
184 linebuf_rem_char(buf);
185 }
186
187 if (le64toh(node->values_count) && fnmatch(linebuf_get(buf), search, 0) == 0)
188 for (i = 0; i < le64toh(node->values_count); i++) {
189 err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[i].key_off),
190 trie_string(hwdb, trie_node_values(hwdb, node)[i].value_off));
191 if (err < 0)
192 return err;
193 }
194
195 linebuf_rem(buf, len);
196 return 0;
197}
198
199static int trie_search_f(sd_hwdb *hwdb, const char *search) {
200 struct linebuf buf;
201 const struct trie_node_f *node;
202 size_t i = 0;
203 int err;
204
205 linebuf_init(&buf);
206
207 node = trie_node_from_off(hwdb, hwdb->head->nodes_root_off);
208 while (node) {
209 const struct trie_node_f *child;
210 size_t p = 0;
211
212 if (node->prefix_off) {
213 uint8_t c;
214
215 for (; (c = trie_string(hwdb, node->prefix_off)[p]); p++) {
216 if (c == '*' || c == '?' || c == '[')
217 return trie_fnmatch_f(hwdb, node, p, &buf, search + i + p);
218 if (c != search[i + p])
219 return 0;
220 }
221 i += p;
222 }
223
224 child = node_lookup_f(hwdb, node, '*');
225 if (child) {
226 linebuf_add_char(&buf, '*');
227 err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
228 if (err < 0)
229 return err;
230 linebuf_rem_char(&buf);
231 }
232
233 child = node_lookup_f(hwdb, node, '?');
234 if (child) {
235 linebuf_add_char(&buf, '?');
236 err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
237 if (err < 0)
238 return err;
239 linebuf_rem_char(&buf);
240 }
241
242 child = node_lookup_f(hwdb, node, '[');
243 if (child) {
244 linebuf_add_char(&buf, '[');
245 err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
246 if (err < 0)
247 return err;
248 linebuf_rem_char(&buf);
249 }
250
251 if (search[i] == '\0') {
252 size_t n;
253
254 for (n = 0; n < le64toh(node->values_count); n++) {
255 err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[n].key_off),
256 trie_string(hwdb, trie_node_values(hwdb, node)[n].value_off));
257 if (err < 0)
258 return err;
259 }
260 return 0;
261 }
262
263 child = node_lookup_f(hwdb, node, search[i]);
264 node = child;
265 i++;
266 }
267 return 0;
268}
269
270static const char hwdb_bin_paths[] =
52efd56a
LP
271 "/etc/systemd/hwdb/hwdb.bin\0"
272 "/etc/udev/hwdb.bin\0"
273 "/usr/lib/systemd/hwdb/hwdb.bin\0"
23fbe14f 274#ifdef HAVE_SPLIT_USR
52efd56a 275 "/lib/systemd/hwdb/hwdb.bin\0"
23fbe14f 276#endif
52efd56a 277 UDEVLIBEXECDIR "/hwdb.bin\0";
23fbe14f
TG
278
279_public_ int sd_hwdb_new(sd_hwdb **ret) {
280 _cleanup_hwdb_unref_ sd_hwdb *hwdb = NULL;
281 const char *hwdb_bin_path;
282 const char sig[] = HWDB_SIG;
283
284 assert_return(ret, -EINVAL);
285
286 hwdb = new0(sd_hwdb, 1);
287 if (!hwdb)
288 return -ENOMEM;
289
290 hwdb->n_ref = REFCNT_INIT;
291
292 /* find hwdb.bin in hwdb_bin_paths */
293 NULSTR_FOREACH(hwdb_bin_path, hwdb_bin_paths) {
294 hwdb->f = fopen(hwdb_bin_path, "re");
295 if (hwdb->f)
296 break;
297 else if (errno == ENOENT)
298 continue;
299 else
300 return log_debug_errno(errno, "error reading %s: %m", hwdb_bin_path);
301 }
302
303 if (!hwdb->f) {
304 log_debug("hwdb.bin does not exist, please run udevadm hwdb --update");
305 return -ENOENT;
306 }
307
308 if (fstat(fileno(hwdb->f), &hwdb->st) < 0 ||
309 (size_t)hwdb->st.st_size < offsetof(struct trie_header_f, strings_len) + 8)
310 return log_debug_errno(errno, "error reading %s: %m", hwdb_bin_path);
311
312 hwdb->map = mmap(0, hwdb->st.st_size, PROT_READ, MAP_SHARED, fileno(hwdb->f), 0);
313 if (hwdb->map == MAP_FAILED)
314 return log_debug_errno(errno, "error mapping %s: %m", hwdb_bin_path);
315
316 if (memcmp(hwdb->map, sig, sizeof(hwdb->head->signature)) != 0 ||
317 (size_t)hwdb->st.st_size != le64toh(hwdb->head->file_size)) {
318 log_debug("error recognizing the format of %s", hwdb_bin_path);
7e518afa 319 return -EINVAL;
23fbe14f
TG
320 }
321
322 log_debug("=== trie on-disk ===");
323 log_debug("tool version: %"PRIu64, le64toh(hwdb->head->tool_version));
1fa2f38f 324 log_debug("file size: %8"PRIi64" bytes", hwdb->st.st_size);
23fbe14f
TG
325 log_debug("header size %8"PRIu64" bytes", le64toh(hwdb->head->header_size));
326 log_debug("strings %8"PRIu64" bytes", le64toh(hwdb->head->strings_len));
327 log_debug("nodes %8"PRIu64" bytes", le64toh(hwdb->head->nodes_len));
328
329 *ret = hwdb;
330 hwdb = NULL;
331
332 return 0;
333}
334
335_public_ sd_hwdb *sd_hwdb_ref(sd_hwdb *hwdb) {
336 assert_return(hwdb, NULL);
337
338 assert_se(REFCNT_INC(hwdb->n_ref) >= 2);
339
340 return hwdb;
341}
342
343_public_ sd_hwdb *sd_hwdb_unref(sd_hwdb *hwdb) {
f0c4b1c3 344 if (hwdb && REFCNT_DEC(hwdb->n_ref) == 0) {
23fbe14f
TG
345 if (hwdb->map)
346 munmap((void *)hwdb->map, hwdb->st.st_size);
347 if (hwdb->f)
348 fclose(hwdb->f);
349 free(hwdb->modalias);
350 ordered_hashmap_free(hwdb->properties);
351 free(hwdb);
352 }
353
354 return NULL;
355}
356
357bool hwdb_validate(sd_hwdb *hwdb) {
358 bool found = false;
359 const char* p;
360 struct stat st;
361
362 if (!hwdb)
363 return false;
364 if (!hwdb->f)
365 return false;
366
367 /* if hwdb.bin doesn't exist anywhere, we need to update */
368 NULSTR_FOREACH(p, hwdb_bin_paths) {
369 if (stat(p, &st) >= 0) {
370 found = true;
371 break;
372 }
373 }
374 if (!found)
375 return true;
376
377 if (timespec_load(&hwdb->st.st_mtim) != timespec_load(&st.st_mtim))
378 return true;
379 return false;
380}
381
382static int properties_prepare(sd_hwdb *hwdb, const char *modalias) {
383 _cleanup_free_ char *mod = NULL;
384 int r;
385
386 assert(hwdb);
387 assert(modalias);
388
389 if (streq_ptr(modalias, hwdb->modalias))
390 return 0;
391
392 mod = strdup(modalias);
393 if (!mod)
394 return -ENOMEM;
395
396 ordered_hashmap_clear(hwdb->properties);
397
398 hwdb->properties_modified = true;
399
400 r = trie_search_f(hwdb, modalias);
401 if (r < 0)
402 return r;
403
404 free(hwdb->modalias);
405 hwdb->modalias = mod;
406 mod = NULL;
407
408 return 0;
409}
410
411_public_ int sd_hwdb_get(sd_hwdb *hwdb, const char *modalias, const char *key, const char **_value) {
412 const char *value;
413 int r;
414
415 assert_return(hwdb, -EINVAL);
416 assert_return(hwdb->f, -EINVAL);
417 assert_return(modalias, -EINVAL);
418 assert_return(_value, -EINVAL);
419
420 r = properties_prepare(hwdb, modalias);
421 if (r < 0)
422 return r;
423
424 value = ordered_hashmap_get(hwdb->properties, key);
425 if (!value)
426 return -ENOENT;
427
428 *_value = value;
429
430 return 0;
431}
432
433_public_ int sd_hwdb_seek(sd_hwdb *hwdb, const char *modalias) {
434 int r;
435
436 assert_return(hwdb, -EINVAL);
437 assert_return(hwdb->f, -EINVAL);
438 assert_return(modalias, -EINVAL);
439
440 r = properties_prepare(hwdb, modalias);
441 if (r < 0)
442 return r;
443
444 hwdb->properties_modified = false;
445 hwdb->properties_iterator = ITERATOR_FIRST;
446
447 return 0;
448}
449
450_public_ int sd_hwdb_enumerate(sd_hwdb *hwdb, const char **key, const char **value) {
8927b1da
DH
451 const void *k;
452 void *v;
23fbe14f
TG
453
454 assert_return(hwdb, -EINVAL);
455 assert_return(key, -EINVAL);
456 assert_return(value, -EINVAL);
457
458 if (hwdb->properties_modified)
459 return -EAGAIN;
460
8927b1da 461 ordered_hashmap_iterate(hwdb->properties, &hwdb->properties_iterator, &v, &k);
23fbe14f
TG
462 if (!k)
463 return 0;
464
465 *key = k;
466 *value = v;
467
468 return 1;
469}