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