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