Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 :
3 : #include <ctype.h>
4 : #include <stdio.h>
5 : #include <sys/stat.h>
6 :
7 : #include "alloc-util.h"
8 : #include "conf-files.h"
9 : #include "fd-util.h"
10 : #include "fileio.h"
11 : #include "fs-util.h"
12 : #include "hwdb-internal.h"
13 : #include "hwdb-util.h"
14 : #include "label.h"
15 : #include "mkdir.h"
16 : #include "path-util.h"
17 : #include "sort-util.h"
18 : #include "strbuf.h"
19 : #include "string-util.h"
20 : #include "strv.h"
21 : #include "tmpfile-util.h"
22 :
23 : static const char *default_hwdb_bin_dir = "/etc/udev";
24 : static const char * const conf_file_dirs[] = {
25 : "/etc/udev/hwdb.d",
26 : UDEVLIBEXECDIR "/hwdb.d",
27 : NULL
28 : };
29 :
30 : /*
31 : * Generic udev properties, key-value database based on modalias strings.
32 : * Uses a Patricia/radix trie to index all matches for efficient lookup.
33 : */
34 :
35 : /* in-memory trie objects */
36 : struct trie {
37 : struct trie_node *root;
38 : struct strbuf *strings;
39 :
40 : size_t nodes_count;
41 : size_t children_count;
42 : size_t values_count;
43 : };
44 :
45 : struct trie_node {
46 : /* prefix, common part for all children of this node */
47 : size_t prefix_off;
48 :
49 : /* sorted array of pointers to children nodes */
50 : struct trie_child_entry *children;
51 : uint8_t children_count;
52 :
53 : /* sorted array of key-value pairs */
54 : struct trie_value_entry *values;
55 : size_t values_count;
56 : };
57 :
58 : /* children array item with char (0-255) index */
59 : struct trie_child_entry {
60 : uint8_t c;
61 : struct trie_node *child;
62 : };
63 :
64 : /* value array item with key-value pairs */
65 : struct trie_value_entry {
66 : size_t key_off;
67 : size_t value_off;
68 : size_t filename_off;
69 : uint32_t line_number;
70 : uint16_t file_priority;
71 : };
72 :
73 1805074 : static int trie_children_cmp(const struct trie_child_entry *a, const struct trie_child_entry *b) {
74 1805074 : return CMP(a->c, b->c);
75 : }
76 :
77 118035 : static int node_add_child(struct trie *trie, struct trie_node *node, struct trie_node *node_child, uint8_t c) {
78 : struct trie_child_entry *child;
79 :
80 : /* extend array, add new entry, sort for bisection */
81 118035 : child = reallocarray(node->children, node->children_count + 1, sizeof(struct trie_child_entry));
82 118035 : if (!child)
83 0 : return -ENOMEM;
84 :
85 118035 : node->children = child;
86 118035 : trie->children_count++;
87 118035 : node->children[node->children_count].c = c;
88 118035 : node->children[node->children_count].child = node_child;
89 118035 : node->children_count++;
90 118035 : typesafe_qsort(node->children, node->children_count, trie_children_cmp);
91 118035 : trie->nodes_count++;
92 :
93 118035 : return 0;
94 : }
95 :
96 623227 : static struct trie_node *node_lookup(const struct trie_node *node, uint8_t c) {
97 : struct trie_child_entry *child;
98 : struct trie_child_entry search;
99 :
100 623227 : search.c = c;
101 623227 : child = typesafe_bsearch(&search, node->children, node->children_count, trie_children_cmp);
102 623227 : if (child)
103 534647 : return child->child;
104 88580 : return NULL;
105 : }
106 :
107 118037 : static void trie_node_cleanup(struct trie_node *node) {
108 : size_t i;
109 :
110 118037 : if (!node)
111 0 : return;
112 :
113 236072 : for (i = 0; i < node->children_count; i++)
114 118035 : trie_node_cleanup(node->children[i].child);
115 118037 : free(node->children);
116 118037 : free(node->values);
117 118037 : free(node);
118 : }
119 :
120 2 : static void trie_free(struct trie *trie) {
121 2 : if (!trie)
122 0 : return;
123 :
124 2 : trie_node_cleanup(trie->root);
125 2 : strbuf_cleanup(trie->strings);
126 2 : free(trie);
127 : }
128 :
129 2 : DEFINE_TRIVIAL_CLEANUP_FUNC(struct trie*, trie_free);
130 :
131 22226 : static int trie_values_cmp(const struct trie_value_entry *a, const struct trie_value_entry *b, struct trie *trie) {
132 44452 : return strcmp(trie->strings->buf + a->key_off,
133 22226 : trie->strings->buf + b->key_off);
134 : }
135 :
136 89818 : static int trie_node_add_value(struct trie *trie, struct trie_node *node,
137 : const char *key, const char *value,
138 : const char *filename, uint16_t file_priority, uint32_t line_number, bool compat) {
139 89818 : ssize_t k, v, fn = 0;
140 : struct trie_value_entry *val;
141 :
142 89818 : k = strbuf_add_string(trie->strings, key, strlen(key));
143 89818 : if (k < 0)
144 0 : return k;
145 89818 : v = strbuf_add_string(trie->strings, value, strlen(value));
146 89818 : if (v < 0)
147 0 : return v;
148 :
149 89818 : if (!compat) {
150 89818 : fn = strbuf_add_string(trie->strings, filename, strlen(filename));
151 89818 : if (fn < 0)
152 0 : return fn;
153 : }
154 :
155 89818 : if (node->values_count) {
156 1238 : struct trie_value_entry search = {
157 : .key_off = k,
158 : .value_off = v,
159 : };
160 :
161 1238 : val = typesafe_bsearch_r(&search, node->values, node->values_count, trie_values_cmp, trie);
162 1238 : if (val) {
163 : /* At this point we have 2 identical properties on the same match-string.
164 : * Since we process files in order, we just replace the previous value. */
165 2 : val->value_off = v;
166 2 : val->filename_off = fn;
167 2 : val->file_priority = file_priority;
168 2 : val->line_number = line_number;
169 2 : return 0;
170 : }
171 : }
172 :
173 : /* extend array, add new entry, sort for bisection */
174 89816 : val = reallocarray(node->values, node->values_count + 1, sizeof(struct trie_value_entry));
175 89816 : if (!val)
176 0 : return -ENOMEM;
177 89816 : trie->values_count++;
178 89816 : node->values = val;
179 89816 : node->values[node->values_count] = (struct trie_value_entry) {
180 : .key_off = k,
181 : .value_off = v,
182 : .filename_off = fn,
183 : .file_priority = file_priority,
184 : .line_number = line_number,
185 : };
186 89816 : node->values_count++;
187 89816 : typesafe_qsort_r(node->values, node->values_count, trie_values_cmp, trie);
188 89816 : return 0;
189 : }
190 :
191 89818 : static int trie_insert(struct trie *trie, struct trie_node *node, const char *search,
192 : const char *key, const char *value,
193 : const char *filename, uint16_t file_priority, uint32_t line_number, bool compat) {
194 89818 : size_t i = 0;
195 89818 : int r = 0;
196 :
197 534647 : for (;;) {
198 : size_t p;
199 : uint8_t c;
200 : struct trie_node *child;
201 :
202 1520727 : for (p = 0; (c = trie->strings->buf[node->prefix_off + p]); p++) {
203 925717 : _cleanup_free_ struct trie_node *new_child = NULL;
204 925717 : _cleanup_free_ char *s = NULL;
205 : ssize_t off;
206 :
207 925717 : if (c == search[i + p])
208 896262 : continue;
209 :
210 : /* split node */
211 29455 : new_child = new(struct trie_node, 1);
212 29455 : if (!new_child)
213 0 : return -ENOMEM;
214 :
215 : /* move values from parent to child */
216 58910 : *new_child = (struct trie_node) {
217 29455 : .prefix_off = node->prefix_off + p+1,
218 29455 : .children = node->children,
219 29455 : .children_count = node->children_count,
220 29455 : .values = node->values,
221 29455 : .values_count = node->values_count,
222 : };
223 :
224 : /* update parent; use strdup() because the source gets realloc()d */
225 29455 : s = strndup(trie->strings->buf + node->prefix_off, p);
226 29455 : if (!s)
227 0 : return -ENOMEM;
228 :
229 29455 : off = strbuf_add_string(trie->strings, s, p);
230 29455 : if (off < 0)
231 0 : return off;
232 :
233 29455 : *node = (struct trie_node) {
234 : .prefix_off = off,
235 : };
236 29455 : r = node_add_child(trie, node, new_child, c);
237 29455 : if (r < 0)
238 0 : return r;
239 :
240 29455 : new_child = NULL; /* avoid cleanup */
241 29455 : break;
242 : }
243 624465 : i += p;
244 :
245 624465 : c = search[i];
246 624465 : if (c == '\0')
247 1238 : return trie_node_add_value(trie, node, key, value, filename, file_priority, line_number, compat);
248 :
249 623227 : child = node_lookup(node, c);
250 623227 : if (!child) {
251 88580 : _cleanup_free_ struct trie_node *new_child = NULL;
252 : ssize_t off;
253 :
254 : /* new child */
255 88580 : new_child = new(struct trie_node, 1);
256 88580 : if (!new_child)
257 0 : return -ENOMEM;
258 :
259 88580 : off = strbuf_add_string(trie->strings, search + i+1, strlen(search + i+1));
260 88580 : if (off < 0)
261 0 : return off;
262 :
263 88580 : *new_child = (struct trie_node) {
264 : .prefix_off = off,
265 : };
266 :
267 88580 : r = node_add_child(trie, node, new_child, c);
268 88580 : if (r < 0)
269 0 : return r;
270 :
271 88580 : child = TAKE_PTR(new_child);
272 88580 : return trie_node_add_value(trie, child, key, value, filename, file_priority, line_number, compat);
273 : }
274 :
275 534647 : node = child;
276 534647 : i++;
277 : }
278 : }
279 :
280 : struct trie_f {
281 : FILE *f;
282 : struct trie *trie;
283 : uint64_t strings_off;
284 :
285 : uint64_t nodes_count;
286 : uint64_t children_count;
287 : uint64_t values_count;
288 : };
289 :
290 : /* calculate the storage space for the nodes, children arrays, value arrays */
291 118037 : static void trie_store_nodes_size(struct trie_f *trie, struct trie_node *node, bool compat) {
292 : uint64_t i;
293 :
294 236072 : for (i = 0; i < node->children_count; i++)
295 118035 : trie_store_nodes_size(trie, node->children[i].child, compat);
296 :
297 118037 : trie->strings_off += sizeof(struct trie_node_f);
298 236072 : for (i = 0; i < node->children_count; i++)
299 118035 : trie->strings_off += sizeof(struct trie_child_entry_f);
300 207853 : for (i = 0; i < node->values_count; i++)
301 89816 : trie->strings_off += compat ? sizeof(struct trie_value_entry_f) : sizeof(struct trie_value_entry2_f);
302 118037 : }
303 :
304 118037 : static int64_t trie_store_nodes(struct trie_f *trie, struct trie_node *node, bool compat) {
305 : uint64_t i;
306 472148 : struct trie_node_f n = {
307 118037 : .prefix_off = htole64(trie->strings_off + node->prefix_off),
308 118037 : .children_count = node->children_count,
309 118037 : .values_count = htole64(node->values_count),
310 : };
311 118037 : _cleanup_free_ struct trie_child_entry_f *children = NULL;
312 : int64_t node_off;
313 :
314 118037 : if (node->children_count) {
315 29465 : children = new(struct trie_child_entry_f, node->children_count);
316 29465 : if (!children)
317 0 : return -ENOMEM;
318 : }
319 :
320 : /* post-order recursion */
321 236072 : for (i = 0; i < node->children_count; i++) {
322 : int64_t child_off;
323 :
324 118035 : child_off = trie_store_nodes(trie, node->children[i].child, compat);
325 118035 : if (child_off < 0)
326 0 : return child_off;
327 :
328 118035 : children[i] = (struct trie_child_entry_f) {
329 118035 : .c = node->children[i].c,
330 118035 : .child_off = htole64(child_off),
331 : };
332 : }
333 :
334 : /* write node */
335 118037 : node_off = ftello(trie->f);
336 118037 : fwrite(&n, sizeof(struct trie_node_f), 1, trie->f);
337 118037 : trie->nodes_count++;
338 :
339 : /* append children array */
340 118037 : if (node->children_count) {
341 29465 : fwrite(children, sizeof(struct trie_child_entry_f), node->children_count, trie->f);
342 29465 : trie->children_count += node->children_count;
343 : }
344 :
345 : /* append values array */
346 207853 : for (i = 0; i < node->values_count; i++) {
347 538896 : struct trie_value_entry2_f v = {
348 89816 : .key_off = htole64(trie->strings_off + node->values[i].key_off),
349 89816 : .value_off = htole64(trie->strings_off + node->values[i].value_off),
350 89816 : .filename_off = htole64(trie->strings_off + node->values[i].filename_off),
351 89816 : .line_number = htole32(node->values[i].line_number),
352 89816 : .file_priority = htole16(node->values[i].file_priority),
353 : };
354 :
355 89816 : fwrite(&v, compat ? sizeof(struct trie_value_entry_f) : sizeof(struct trie_value_entry2_f), 1, trie->f);
356 : }
357 118037 : trie->values_count += node->values_count;
358 :
359 118037 : return node_off;
360 : }
361 :
362 2 : static int trie_store(struct trie *trie, const char *filename, bool compat) {
363 2 : struct trie_f t = {
364 : .trie = trie,
365 : };
366 2 : _cleanup_free_ char *filename_tmp = NULL;
367 : int64_t pos;
368 : int64_t root_off;
369 : int64_t size;
370 12 : struct trie_header_f h = {
371 : .signature = HWDB_SIG,
372 2 : .tool_version = htole64(PROJECT_VERSION),
373 2 : .header_size = htole64(sizeof(struct trie_header_f)),
374 2 : .node_size = htole64(sizeof(struct trie_node_f)),
375 2 : .child_entry_size = htole64(sizeof(struct trie_child_entry_f)),
376 2 : .value_entry_size = htole64(compat ? sizeof(struct trie_value_entry_f) : sizeof(struct trie_value_entry2_f)),
377 : };
378 : int r;
379 :
380 : /* calculate size of header, nodes, children entries, value entries */
381 2 : t.strings_off = sizeof(struct trie_header_f);
382 2 : trie_store_nodes_size(&t, trie->root, compat);
383 :
384 2 : r = fopen_temporary(filename, &t.f, &filename_tmp);
385 2 : if (r < 0)
386 0 : return r;
387 2 : fchmod(fileno(t.f), 0444);
388 :
389 : /* write nodes */
390 2 : if (fseeko(t.f, sizeof(struct trie_header_f), SEEK_SET) < 0)
391 0 : goto error_fclose;
392 :
393 2 : root_off = trie_store_nodes(&t, trie->root, compat);
394 2 : h.nodes_root_off = htole64(root_off);
395 2 : pos = ftello(t.f);
396 2 : h.nodes_len = htole64(pos - sizeof(struct trie_header_f));
397 :
398 : /* write string buffer */
399 2 : fwrite(trie->strings->buf, trie->strings->len, 1, t.f);
400 2 : h.strings_len = htole64(trie->strings->len);
401 :
402 : /* write header */
403 2 : size = ftello(t.f);
404 2 : h.file_size = htole64(size);
405 2 : if (fseeko(t.f, 0, SEEK_SET) < 0)
406 0 : goto error_fclose;
407 2 : fwrite(&h, sizeof(struct trie_header_f), 1, t.f);
408 :
409 2 : if (ferror(t.f))
410 0 : goto error_fclose;
411 2 : if (fflush(t.f) < 0)
412 0 : goto error_fclose;
413 2 : if (fsync(fileno(t.f)) < 0)
414 0 : goto error_fclose;
415 2 : if (rename(filename_tmp, filename) < 0)
416 0 : goto error_fclose;
417 :
418 : /* write succeeded */
419 2 : fclose(t.f);
420 :
421 2 : log_debug("=== trie on-disk ===");
422 2 : log_debug("size: %8"PRIi64" bytes", size);
423 2 : log_debug("header: %8zu bytes", sizeof(struct trie_header_f));
424 2 : log_debug("nodes: %8"PRIu64" bytes (%8"PRIu64")",
425 : t.nodes_count * sizeof(struct trie_node_f), t.nodes_count);
426 2 : log_debug("child pointers: %8"PRIu64" bytes (%8"PRIu64")",
427 : t.children_count * sizeof(struct trie_child_entry_f), t.children_count);
428 2 : log_debug("value pointers: %8"PRIu64" bytes (%8"PRIu64")",
429 : t.values_count * (compat ? sizeof(struct trie_value_entry_f) : sizeof(struct trie_value_entry2_f)), t.values_count);
430 2 : log_debug("string store: %8zu bytes", trie->strings->len);
431 2 : log_debug("strings start: %8"PRIu64, t.strings_off);
432 2 : return 0;
433 :
434 0 : error_fclose:
435 0 : r = -errno;
436 0 : fclose(t.f);
437 0 : (void) unlink(filename_tmp);
438 0 : return r;
439 : }
440 :
441 89339 : static int insert_data(struct trie *trie, char **match_list, char *line, const char *filename,
442 : uint16_t file_priority, uint32_t line_number, bool compat) {
443 : char *value, **entry;
444 :
445 89339 : assert(line[0] == ' ');
446 :
447 89339 : value = strchr(line, '=');
448 89339 : if (!value)
449 1 : return log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
450 : "Key-value pair expected but got \"%s\", ignoring", line);
451 :
452 89338 : value[0] = '\0';
453 89338 : value++;
454 :
455 : /* Replace multiple leading spaces by a single space */
456 89339 : while (isblank(line[0]) && isblank(line[1]))
457 1 : line++;
458 :
459 89338 : if (isempty(line + 1) || isempty(value))
460 2 : return log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
461 : "Empty %s in \"%s=%s\", ignoring",
462 : isempty(line + 1) ? "key" : "value",
463 : line, value);
464 :
465 179154 : STRV_FOREACH(entry, match_list)
466 89818 : trie_insert(trie, trie->root, *entry, line, value, filename, file_priority, line_number, compat);
467 :
468 89336 : return 0;
469 : }
470 :
471 20 : static int import_file(struct trie *trie, const char *filename, uint16_t file_priority, bool compat) {
472 : enum {
473 : HW_NONE,
474 : HW_MATCH,
475 : HW_DATA,
476 20 : } state = HW_NONE;
477 20 : _cleanup_fclose_ FILE *f = NULL;
478 20 : _cleanup_strv_free_ char **match_list = NULL;
479 20 : uint32_t line_number = 0;
480 20 : char *match = NULL;
481 20 : int r = 0, err;
482 :
483 20 : f = fopen(filename, "re");
484 20 : if (!f)
485 0 : return -errno;
486 :
487 267912 : for (;;) {
488 267932 : _cleanup_free_ char *line = NULL;
489 : size_t len;
490 : char *pos;
491 :
492 267932 : r = read_line(f, LONG_LINE_MAX, &line);
493 267932 : if (r < 0)
494 0 : return r;
495 267932 : if (r == 0)
496 20 : break;
497 :
498 267912 : ++line_number;
499 :
500 : /* comment line */
501 267912 : if (line[0] == '#')
502 1424 : continue;
503 :
504 : /* strip trailing comment */
505 266488 : pos = strchr(line, '#');
506 266488 : if (pos)
507 1637 : pos[0] = '\0';
508 :
509 : /* strip trailing whitespace */
510 266488 : len = strlen(line);
511 280168 : while (len > 0 && isspace(line[len-1]))
512 13680 : len--;
513 266488 : line[len] = '\0';
514 :
515 266488 : switch (state) {
516 88578 : case HW_NONE:
517 88578 : if (len == 0)
518 139 : break;
519 :
520 88439 : if (line[0] == ' ') {
521 1 : log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
522 : "Match expected but got indented property \"%s\", ignoring line", line);
523 1 : r = -EINVAL;
524 1 : break;
525 : }
526 :
527 : /* start of record, first match */
528 88438 : state = HW_MATCH;
529 :
530 88438 : match = strdup(line);
531 88438 : if (!match)
532 0 : return -ENOMEM;
533 :
534 88438 : err = strv_consume(&match_list, match);
535 88438 : if (err < 0)
536 0 : return err;
537 :
538 88438 : break;
539 :
540 88590 : case HW_MATCH:
541 88590 : if (len == 0) {
542 3 : log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
543 : "Property expected, ignoring record with no properties");
544 3 : r = -EINVAL;
545 3 : state = HW_NONE;
546 3 : strv_clear(match_list);
547 3 : break;
548 : }
549 :
550 88587 : if (line[0] != ' ') {
551 : /* another match */
552 153 : match = strdup(line);
553 153 : if (!match)
554 0 : return -ENOMEM;
555 :
556 153 : err = strv_consume(&match_list, match);
557 153 : if (err < 0)
558 0 : return err;
559 :
560 153 : break;
561 : }
562 :
563 : /* first data */
564 88434 : state = HW_DATA;
565 88434 : err = insert_data(trie, match_list, line, filename, file_priority, line_number, compat);
566 88434 : if (err < 0)
567 2 : r = err;
568 88434 : break;
569 :
570 89320 : case HW_DATA:
571 89320 : if (len == 0) {
572 : /* end of record */
573 88413 : state = HW_NONE;
574 88413 : strv_clear(match_list);
575 88413 : break;
576 : }
577 :
578 907 : if (line[0] != ' ') {
579 2 : log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
580 : "Property or empty line expected, got \"%s\", ignoring record", line);
581 2 : r = -EINVAL;
582 2 : state = HW_NONE;
583 2 : strv_clear(match_list);
584 2 : break;
585 : }
586 :
587 905 : err = insert_data(trie, match_list, line, filename, file_priority, line_number, compat);
588 905 : if (err < 0)
589 1 : r = err;
590 905 : break;
591 266488 : };
592 : }
593 :
594 20 : if (state == HW_MATCH)
595 1 : log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
596 : "Property expected, ignoring record with no properties");
597 :
598 20 : return r;
599 : }
600 :
601 2 : int hwdb_update(const char *root, const char *hwdb_bin_dir, bool strict, bool compat) {
602 2 : _cleanup_free_ char *hwdb_bin = NULL;
603 2 : _cleanup_(trie_freep) struct trie *trie = NULL;
604 2 : _cleanup_strv_free_ char **files = NULL;
605 : char **f;
606 2 : uint16_t file_priority = 1;
607 2 : int r = 0, err;
608 :
609 : /* The argument 'compat' controls the format version of database. If false, then hwdb.bin will be created with
610 : * additional information such that priority, line number, and filename of database source. If true, then hwdb.bin
611 : * will be created without the information. systemd-hwdb command should set the argument false, and 'udevadm hwdb'
612 : * command should set it true. */
613 :
614 2 : trie = new0(struct trie, 1);
615 2 : if (!trie)
616 0 : return -ENOMEM;
617 :
618 : /* string store */
619 2 : trie->strings = strbuf_new();
620 2 : if (!trie->strings)
621 0 : return -ENOMEM;
622 :
623 : /* index */
624 2 : trie->root = new0(struct trie_node, 1);
625 2 : if (!trie->root)
626 0 : return -ENOMEM;
627 :
628 2 : trie->nodes_count++;
629 :
630 2 : err = conf_files_list_strv(&files, ".hwdb", root, 0, conf_file_dirs);
631 2 : if (err < 0)
632 0 : return log_error_errno(err, "Failed to enumerate hwdb files: %m");
633 :
634 22 : STRV_FOREACH(f, files) {
635 20 : log_debug("Reading file \"%s\"", *f);
636 20 : err = import_file(trie, *f, file_priority++, compat);
637 20 : if (err < 0 && strict)
638 0 : r = err;
639 : }
640 :
641 2 : strbuf_complete(trie->strings);
642 :
643 2 : log_debug("=== trie in-memory ===");
644 2 : log_debug("nodes: %8zu bytes (%8zu)",
645 : trie->nodes_count * sizeof(struct trie_node), trie->nodes_count);
646 2 : log_debug("children arrays: %8zu bytes (%8zu)",
647 : trie->children_count * sizeof(struct trie_child_entry), trie->children_count);
648 2 : log_debug("values arrays: %8zu bytes (%8zu)",
649 : trie->values_count * sizeof(struct trie_value_entry), trie->values_count);
650 2 : log_debug("strings: %8zu bytes",
651 : trie->strings->len);
652 2 : log_debug("strings incoming: %8zu bytes (%8zu)",
653 : trie->strings->in_len, trie->strings->in_count);
654 2 : log_debug("strings dedup'ed: %8zu bytes (%8zu)",
655 : trie->strings->dedup_len, trie->strings->dedup_count);
656 :
657 2 : hwdb_bin = path_join(root, hwdb_bin_dir ?: default_hwdb_bin_dir, "hwdb.bin");
658 2 : if (!hwdb_bin)
659 0 : return -ENOMEM;
660 :
661 2 : mkdir_parents_label(hwdb_bin, 0755);
662 2 : err = trie_store(trie, hwdb_bin, compat);
663 2 : if (err < 0)
664 0 : return log_error_errno(err, "Failed to write database %s: %m", hwdb_bin);
665 :
666 2 : err = label_fix(hwdb_bin, 0);
667 2 : if (err < 0)
668 0 : return err;
669 :
670 2 : return r;
671 : }
672 :
673 0 : int hwdb_query(const char *modalias) {
674 0 : _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
675 : const char *key, *value;
676 : int r;
677 :
678 0 : assert(modalias);
679 :
680 0 : r = sd_hwdb_new(&hwdb);
681 0 : if (r < 0)
682 0 : return r;
683 :
684 0 : SD_HWDB_FOREACH_PROPERTY(hwdb, modalias, key, value)
685 0 : printf("%s=%s\n", key, value);
686 :
687 0 : return 0;
688 : }
|