]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/hwdb/hwdb.c
tree-wide: beautify remaining copyright statements
[thirdparty/systemd.git] / src / hwdb / hwdb.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
65eb4378 2/***
96b2fb93 3 Copyright © 2012 Kay Sievers <kay@vrfy.org>
65eb4378
TG
4***/
5
3f6fd1ba 6#include <ctype.h>
65eb4378 7#include <getopt.h>
3f6fd1ba 8#include <stdlib.h>
65eb4378 9#include <string.h>
65eb4378 10
b5efdb8a 11#include "alloc-util.h"
65eb4378 12#include "conf-files.h"
0d39fa9c
LP
13#include "fd-util.h"
14#include "fileio.h"
f4f15635 15#include "fs-util.h"
65eb4378
TG
16#include "hwdb-internal.h"
17#include "hwdb-util.h"
ea683512 18#include "label.h"
3f6fd1ba 19#include "mkdir.h"
0aac506b 20#include "path-util.h"
ea683512 21#include "selinux-util.h"
3f6fd1ba 22#include "strbuf.h"
07630cea 23#include "string-util.h"
3f6fd1ba
LP
24#include "strv.h"
25#include "util.h"
26#include "verbs.h"
65eb4378
TG
27
28/*
49141e0c 29 * Generic udev properties, key-value database based on modalias strings.
65eb4378
TG
30 * Uses a Patricia/radix trie to index all matches for efficient lookup.
31 */
32
33static const char *arg_hwdb_bin_dir = "/etc/udev";
34static const char *arg_root = "";
aacbcab6 35static bool arg_strict;
65eb4378
TG
36
37static const char * const conf_file_dirs[] = {
38 "/etc/udev/hwdb.d",
39 UDEVLIBEXECDIR "/hwdb.d",
40 NULL
41};
42
43/* in-memory trie objects */
44struct trie {
45 struct trie_node *root;
46 struct strbuf *strings;
47
48 size_t nodes_count;
49 size_t children_count;
50 size_t values_count;
51};
52
53struct trie_node {
54 /* prefix, common part for all children of this node */
55 size_t prefix_off;
56
57 /* sorted array of pointers to children nodes */
58 struct trie_child_entry *children;
59 uint8_t children_count;
60
49141e0c 61 /* sorted array of key-value pairs */
65eb4378
TG
62 struct trie_value_entry *values;
63 size_t values_count;
64};
65
66/* children array item with char (0-255) index */
67struct trie_child_entry {
68 uint8_t c;
69 struct trie_node *child;
70};
71
49141e0c 72/* value array item with key-value pairs */
65eb4378
TG
73struct trie_value_entry {
74 size_t key_off;
75 size_t value_off;
698c5a17 76 size_t filename_off;
d8646d05
ZJS
77 uint32_t line_number;
78 uint16_t file_priority;
65eb4378
TG
79};
80
81static int trie_children_cmp(const void *v1, const void *v2) {
82 const struct trie_child_entry *n1 = v1;
83 const struct trie_child_entry *n2 = v2;
84
85 return n1->c - n2->c;
86}
87
88static int node_add_child(struct trie *trie, struct trie_node *node, struct trie_node *node_child, uint8_t c) {
89 struct trie_child_entry *child;
90
91 /* extend array, add new entry, sort for bisection */
62d74c78 92 child = reallocarray(node->children, node->children_count + 1, sizeof(struct trie_child_entry));
65eb4378
TG
93 if (!child)
94 return -ENOMEM;
95
96 node->children = child;
97 trie->children_count++;
98 node->children[node->children_count].c = c;
99 node->children[node->children_count].child = node_child;
100 node->children_count++;
101 qsort(node->children, node->children_count, sizeof(struct trie_child_entry), trie_children_cmp);
102 trie->nodes_count++;
103
104 return 0;
105}
106
107static struct trie_node *node_lookup(const struct trie_node *node, uint8_t c) {
108 struct trie_child_entry *child;
109 struct trie_child_entry search;
110
111 search.c = c;
d6c5d19b 112 child = bsearch_safe(&search, node->children, node->children_count, sizeof(struct trie_child_entry), trie_children_cmp);
65eb4378
TG
113 if (child)
114 return child->child;
115 return NULL;
116}
117
118static void trie_node_cleanup(struct trie_node *node) {
119 size_t i;
120
121 for (i = 0; i < node->children_count; i++)
122 trie_node_cleanup(node->children[i].child);
123 free(node->children);
124 free(node->values);
125 free(node);
126}
127
128static void trie_free(struct trie *trie) {
129 if (!trie)
130 return;
131
132 if (trie->root)
133 trie_node_cleanup(trie->root);
134
135 strbuf_cleanup(trie->strings);
136 free(trie);
137}
138
139DEFINE_TRIVIAL_CLEANUP_FUNC(struct trie*, trie_free);
140
141static int trie_values_cmp(const void *v1, const void *v2, void *arg) {
142 const struct trie_value_entry *val1 = v1;
143 const struct trie_value_entry *val2 = v2;
144 struct trie *trie = arg;
145
146 return strcmp(trie->strings->buf + val1->key_off,
147 trie->strings->buf + val2->key_off);
148}
149
150static int trie_node_add_value(struct trie *trie, struct trie_node *node,
698c5a17 151 const char *key, const char *value,
d8646d05 152 const char *filename, uint16_t file_priority, uint32_t line_number) {
698c5a17 153 ssize_t k, v, fn;
65eb4378
TG
154 struct trie_value_entry *val;
155
156 k = strbuf_add_string(trie->strings, key, strlen(key));
157 if (k < 0)
158 return k;
159 v = strbuf_add_string(trie->strings, value, strlen(value));
698c5a17
DH
160 if (v < 0)
161 return v;
162 fn = strbuf_add_string(trie->strings, filename, strlen(filename));
342d3ac1
TA
163 if (fn < 0)
164 return fn;
65eb4378
TG
165
166 if (node->values_count) {
167 struct trie_value_entry search = {
168 .key_off = k,
169 .value_off = v,
170 };
171
172 val = xbsearch_r(&search, node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie);
173 if (val) {
389be927
ZJS
174 /* At this point we have 2 identical properties on the same match-string.
175 * Since we process files in order, we just replace the previous value.
698c5a17 176 */
65eb4378 177 val->value_off = v;
698c5a17 178 val->filename_off = fn;
d8646d05 179 val->file_priority = file_priority;
698c5a17 180 val->line_number = line_number;
65eb4378
TG
181 return 0;
182 }
183 }
184
185 /* extend array, add new entry, sort for bisection */
62d74c78 186 val = reallocarray(node->values, node->values_count + 1, sizeof(struct trie_value_entry));
65eb4378
TG
187 if (!val)
188 return -ENOMEM;
189 trie->values_count++;
190 node->values = val;
191 node->values[node->values_count].key_off = k;
192 node->values[node->values_count].value_off = v;
698c5a17 193 node->values[node->values_count].filename_off = fn;
d8646d05 194 node->values[node->values_count].file_priority = file_priority;
698c5a17 195 node->values[node->values_count].line_number = line_number;
65eb4378
TG
196 node->values_count++;
197 qsort_r(node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie);
198 return 0;
199}
200
201static int trie_insert(struct trie *trie, struct trie_node *node, const char *search,
698c5a17 202 const char *key, const char *value,
d8646d05 203 const char *filename, uint16_t file_priority, uint32_t line_number) {
65eb4378 204 size_t i = 0;
ca0e88ef 205 int r = 0;
65eb4378
TG
206
207 for (;;) {
208 size_t p;
209 uint8_t c;
210 struct trie_node *child;
211
212 for (p = 0; (c = trie->strings->buf[node->prefix_off + p]); p++) {
213 _cleanup_free_ char *s = NULL;
214 ssize_t off;
215 _cleanup_free_ struct trie_node *new_child = NULL;
216
217 if (c == search[i + p])
218 continue;
219
220 /* split node */
221 new_child = new0(struct trie_node, 1);
222 if (!new_child)
223 return -ENOMEM;
224
225 /* move values from parent to child */
226 new_child->prefix_off = node->prefix_off + p+1;
227 new_child->children = node->children;
228 new_child->children_count = node->children_count;
229 new_child->values = node->values;
230 new_child->values_count = node->values_count;
231
232 /* update parent; use strdup() because the source gets realloc()d */
233 s = strndup(trie->strings->buf + node->prefix_off, p);
234 if (!s)
235 return -ENOMEM;
236
237 off = strbuf_add_string(trie->strings, s, p);
238 if (off < 0)
239 return off;
240
241 node->prefix_off = off;
242 node->children = NULL;
243 node->children_count = 0;
244 node->values = NULL;
245 node->values_count = 0;
ca0e88ef
ZJS
246 r = node_add_child(trie, node, new_child, c);
247 if (r < 0)
248 return r;
65eb4378
TG
249
250 new_child = NULL; /* avoid cleanup */
251 break;
252 }
253 i += p;
254
255 c = search[i];
256 if (c == '\0')
d8646d05 257 return trie_node_add_value(trie, node, key, value, filename, file_priority, line_number);
65eb4378
TG
258
259 child = node_lookup(node, c);
260 if (!child) {
261 ssize_t off;
262
263 /* new child */
264 child = new0(struct trie_node, 1);
265 if (!child)
266 return -ENOMEM;
267
268 off = strbuf_add_string(trie->strings, search + i+1, strlen(search + i+1));
269 if (off < 0) {
270 free(child);
271 return off;
272 }
273
274 child->prefix_off = off;
ca0e88ef
ZJS
275 r = node_add_child(trie, node, child, c);
276 if (r < 0) {
65eb4378 277 free(child);
ca0e88ef 278 return r;
65eb4378
TG
279 }
280
d8646d05 281 return trie_node_add_value(trie, child, key, value, filename, file_priority, line_number);
65eb4378
TG
282 }
283
284 node = child;
285 i++;
286 }
287}
288
289struct trie_f {
290 FILE *f;
291 struct trie *trie;
292 uint64_t strings_off;
293
294 uint64_t nodes_count;
295 uint64_t children_count;
296 uint64_t values_count;
297};
298
299/* calculate the storage space for the nodes, children arrays, value arrays */
300static void trie_store_nodes_size(struct trie_f *trie, struct trie_node *node) {
301 uint64_t i;
302
303 for (i = 0; i < node->children_count; i++)
304 trie_store_nodes_size(trie, node->children[i].child);
305
306 trie->strings_off += sizeof(struct trie_node_f);
307 for (i = 0; i < node->children_count; i++)
308 trie->strings_off += sizeof(struct trie_child_entry_f);
309 for (i = 0; i < node->values_count; i++)
698c5a17 310 trie->strings_off += sizeof(struct trie_value_entry2_f);
65eb4378
TG
311}
312
313static int64_t trie_store_nodes(struct trie_f *trie, struct trie_node *node) {
314 uint64_t i;
315 struct trie_node_f n = {
316 .prefix_off = htole64(trie->strings_off + node->prefix_off),
317 .children_count = node->children_count,
318 .values_count = htole64(node->values_count),
319 };
ca0e88ef 320 _cleanup_free_ struct trie_child_entry_f *children = NULL;
65eb4378
TG
321 int64_t node_off;
322
323 if (node->children_count) {
ca0e88ef 324 children = new(struct trie_child_entry_f, node->children_count);
65eb4378
TG
325 if (!children)
326 return -ENOMEM;
327 }
328
329 /* post-order recursion */
330 for (i = 0; i < node->children_count; i++) {
331 int64_t child_off;
332
333 child_off = trie_store_nodes(trie, node->children[i].child);
ca0e88ef 334 if (child_off < 0)
65eb4378 335 return child_off;
ca0e88ef
ZJS
336
337 children[i] = (struct trie_child_entry_f) {
338 .c = node->children[i].c,
339 .child_off = htole64(child_off),
340 };
65eb4378
TG
341 }
342
343 /* write node */
344 node_off = ftello(trie->f);
345 fwrite(&n, sizeof(struct trie_node_f), 1, trie->f);
346 trie->nodes_count++;
347
348 /* append children array */
349 if (node->children_count) {
350 fwrite(children, sizeof(struct trie_child_entry_f), node->children_count, trie->f);
351 trie->children_count += node->children_count;
65eb4378
TG
352 }
353
354 /* append values array */
355 for (i = 0; i < node->values_count; i++) {
698c5a17 356 struct trie_value_entry2_f v = {
65eb4378
TG
357 .key_off = htole64(trie->strings_off + node->values[i].key_off),
358 .value_off = htole64(trie->strings_off + node->values[i].value_off),
698c5a17 359 .filename_off = htole64(trie->strings_off + node->values[i].filename_off),
d8646d05
ZJS
360 .line_number = htole32(node->values[i].line_number),
361 .file_priority = htole16(node->values[i].file_priority),
65eb4378
TG
362 };
363
698c5a17 364 fwrite(&v, sizeof(struct trie_value_entry2_f), 1, trie->f);
65eb4378 365 }
ca0e88ef 366 trie->values_count += node->values_count;
65eb4378
TG
367
368 return node_off;
369}
370
371static int trie_store(struct trie *trie, const char *filename) {
372 struct trie_f t = {
373 .trie = trie,
374 };
375 _cleanup_free_ char *filename_tmp = NULL;
376 int64_t pos;
377 int64_t root_off;
378 int64_t size;
379 struct trie_header_f h = {
380 .signature = HWDB_SIG,
948aaa7c 381 .tool_version = htole64(atoi(PACKAGE_VERSION)),
65eb4378
TG
382 .header_size = htole64(sizeof(struct trie_header_f)),
383 .node_size = htole64(sizeof(struct trie_node_f)),
384 .child_entry_size = htole64(sizeof(struct trie_child_entry_f)),
698c5a17 385 .value_entry_size = htole64(sizeof(struct trie_value_entry2_f)),
65eb4378 386 };
d702dcdf 387 int r;
65eb4378
TG
388
389 /* calculate size of header, nodes, children entries, value entries */
390 t.strings_off = sizeof(struct trie_header_f);
391 trie_store_nodes_size(&t, trie->root);
392
0675e94a 393 r = fopen_temporary(filename, &t.f, &filename_tmp);
d702dcdf
ZJS
394 if (r < 0)
395 return r;
65eb4378
TG
396 fchmod(fileno(t.f), 0444);
397
398 /* write nodes */
d702dcdf 399 if (fseeko(t.f, sizeof(struct trie_header_f), SEEK_SET) < 0)
0675e94a 400 goto error_fclose;
d702dcdf 401
65eb4378
TG
402 root_off = trie_store_nodes(&t, trie->root);
403 h.nodes_root_off = htole64(root_off);
404 pos = ftello(t.f);
405 h.nodes_len = htole64(pos - sizeof(struct trie_header_f));
406
407 /* write string buffer */
408 fwrite(trie->strings->buf, trie->strings->len, 1, t.f);
409 h.strings_len = htole64(trie->strings->len);
410
411 /* write header */
412 size = ftello(t.f);
413 h.file_size = htole64(size);
d702dcdf 414 if (fseeko(t.f, 0, SEEK_SET) < 0)
0675e94a 415 goto error_fclose;
65eb4378 416 fwrite(&h, sizeof(struct trie_header_f), 1, t.f);
0675e94a
AJ
417
418 if (ferror(t.f))
419 goto error_fclose;
420 if (fflush(t.f) < 0)
421 goto error_fclose;
422 if (fsync(fileno(t.f)) < 0)
423 goto error_fclose;
424 if (rename(filename_tmp, filename) < 0)
425 goto error_fclose;
426
427 /* write succeeded */
428 fclose(t.f);
65eb4378
TG
429
430 log_debug("=== trie on-disk ===");
1fa2f38f 431 log_debug("size: %8"PRIi64" bytes", size);
65eb4378
TG
432 log_debug("header: %8zu bytes", sizeof(struct trie_header_f));
433 log_debug("nodes: %8"PRIu64" bytes (%8"PRIu64")",
434 t.nodes_count * sizeof(struct trie_node_f), t.nodes_count);
435 log_debug("child pointers: %8"PRIu64" bytes (%8"PRIu64")",
436 t.children_count * sizeof(struct trie_child_entry_f), t.children_count);
437 log_debug("value pointers: %8"PRIu64" bytes (%8"PRIu64")",
698c5a17 438 t.values_count * sizeof(struct trie_value_entry2_f), t.values_count);
65eb4378
TG
439 log_debug("string store: %8zu bytes", trie->strings->len);
440 log_debug("strings start: %8"PRIu64, t.strings_off);
65eb4378 441 return 0;
d702dcdf 442
0675e94a 443 error_fclose:
d702dcdf
ZJS
444 r = -errno;
445 fclose(t.f);
446 unlink(filename_tmp);
447 return r;
65eb4378
TG
448}
449
698c5a17 450static int insert_data(struct trie *trie, char **match_list, char *line,
d8646d05 451 const char *filename, uint16_t file_priority, uint32_t line_number) {
65eb4378
TG
452 char *value, **entry;
453
a6a2f018
ZJS
454 assert(line[0] == ' ');
455
65eb4378 456 value = strchr(line, '=');
49141e0c
ZJS
457 if (!value)
458 return log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
459 "Key-value pair expected but got \"%s\", ignoring", line);
65eb4378
TG
460
461 value[0] = '\0';
462 value++;
463
a6a2f018 464 /* Replace multiple leading spaces by a single space */
65eb4378
TG
465 while (isblank(line[0]) && isblank(line[1]))
466 line++;
467
a6a2f018 468 if (isempty(line + 1) || isempty(value))
49141e0c 469 return log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
a6a2f018
ZJS
470 "Empty %s in \"%s=%s\", ignoring",
471 isempty(line + 1) ? "key" : "value",
472 line, value);
65eb4378
TG
473
474 STRV_FOREACH(entry, match_list)
d8646d05 475 trie_insert(trie, trie->root, *entry, line, value, filename, file_priority, line_number);
65eb4378
TG
476
477 return 0;
478}
479
d8646d05 480static int import_file(struct trie *trie, const char *filename, uint16_t file_priority) {
65eb4378
TG
481 enum {
482 HW_NONE,
483 HW_MATCH,
484 HW_DATA,
485 } state = HW_NONE;
486 _cleanup_fclose_ FILE *f = NULL;
487 char line[LINE_MAX];
488 _cleanup_strv_free_ char **match_list = NULL;
d8646d05 489 uint32_t line_number = 0;
65eb4378 490 char *match = NULL;
aacbcab6 491 int r = 0, err;
65eb4378
TG
492
493 f = fopen(filename, "re");
494 if (!f)
495 return -errno;
496
497 while (fgets(line, sizeof(line), f)) {
498 size_t len;
499 char *pos;
500
698c5a17
DH
501 ++line_number;
502
65eb4378
TG
503 /* comment line */
504 if (line[0] == '#')
505 continue;
506
507 /* strip trailing comment */
508 pos = strchr(line, '#');
509 if (pos)
510 pos[0] = '\0';
511
512 /* strip trailing whitespace */
513 len = strlen(line);
514 while (len > 0 && isspace(line[len-1]))
515 len--;
516 line[len] = '\0';
517
518 switch (state) {
519 case HW_NONE:
520 if (len == 0)
521 break;
522
523 if (line[0] == ' ') {
49141e0c
ZJS
524 log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
525 "Match expected but got indented property \"%s\", ignoring line", line);
aacbcab6 526 r = -EINVAL;
65eb4378
TG
527 break;
528 }
529
530 /* start of record, first match */
531 state = HW_MATCH;
532
533 match = strdup(line);
534 if (!match)
535 return -ENOMEM;
536
aacbcab6
NB
537 err = strv_consume(&match_list, match);
538 if (err < 0)
539 return err;
65eb4378
TG
540
541 break;
542
543 case HW_MATCH:
544 if (len == 0) {
49141e0c
ZJS
545 log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
546 "Property expected, ignoring record with no properties");
aacbcab6 547 r = -EINVAL;
65eb4378
TG
548 state = HW_NONE;
549 strv_clear(match_list);
550 break;
551 }
552
65eb4378 553 if (line[0] != ' ') {
7a100dce 554 /* another match */
65eb4378
TG
555 match = strdup(line);
556 if (!match)
557 return -ENOMEM;
558
aacbcab6
NB
559 err = strv_consume(&match_list, match);
560 if (err < 0)
561 return err;
65eb4378
TG
562
563 break;
564 }
565
566 /* first data */
567 state = HW_DATA;
aacbcab6
NB
568 err = insert_data(trie, match_list, line, filename, file_priority, line_number);
569 if (err < 0)
570 r = err;
65eb4378
TG
571 break;
572
573 case HW_DATA:
65eb4378 574 if (len == 0) {
7a100dce 575 /* end of record */
65eb4378
TG
576 state = HW_NONE;
577 strv_clear(match_list);
578 break;
579 }
580
581 if (line[0] != ' ') {
49141e0c
ZJS
582 log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
583 "Property or empty line expected, got \"%s\", ignoring record", line);
aacbcab6 584 r = -EINVAL;
65eb4378
TG
585 state = HW_NONE;
586 strv_clear(match_list);
587 break;
588 }
589
aacbcab6
NB
590 err = insert_data(trie, match_list, line, filename, file_priority, line_number);
591 if (err < 0)
592 r = err;
65eb4378
TG
593 break;
594 };
595 }
596
7a100dce
ZJS
597 if (state == HW_MATCH)
598 log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
599 "Property expected, ignoring record with no properties");
600
aacbcab6 601 return r;
65eb4378
TG
602}
603
caa8dab2 604static int hwdb_query(int argc, char *argv[], void *userdata) {
4afd3348 605 _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
65eb4378
TG
606 const char *key, *value;
607 const char *modalias;
608 int r;
609
caa8dab2
TG
610 assert(argc >= 2);
611 assert(argv);
65eb4378 612
caa8dab2 613 modalias = argv[1];
65eb4378
TG
614
615 r = sd_hwdb_new(&hwdb);
616 if (r < 0)
617 return r;
618
619 SD_HWDB_FOREACH_PROPERTY(hwdb, modalias, key, value)
620 printf("%s=%s\n", key, value);
621
622 return 0;
623}
624
caa8dab2 625static int hwdb_update(int argc, char *argv[], void *userdata) {
65eb4378
TG
626 _cleanup_free_ char *hwdb_bin = NULL;
627 _cleanup_(trie_freep) struct trie *trie = NULL;
ca0e88ef
ZJS
628 _cleanup_strv_free_ char **files = NULL;
629 char **f;
d8646d05 630 uint16_t file_priority = 1;
aacbcab6 631 int r = 0, err;
65eb4378
TG
632
633 trie = new0(struct trie, 1);
634 if (!trie)
635 return -ENOMEM;
636
637 /* string store */
638 trie->strings = strbuf_new();
639 if (!trie->strings)
640 return -ENOMEM;
641
642 /* index */
643 trie->root = new0(struct trie_node, 1);
644 if (!trie->root)
645 return -ENOMEM;
646
647 trie->nodes_count++;
648
aacbcab6
NB
649 err = conf_files_list_strv(&files, ".hwdb", arg_root, 0, conf_file_dirs);
650 if (err < 0)
651 return log_error_errno(err, "Failed to enumerate hwdb files: %m");
65eb4378
TG
652
653 STRV_FOREACH(f, files) {
ca0e88ef 654 log_debug("Reading file \"%s\"", *f);
aacbcab6
NB
655 err = import_file(trie, *f, file_priority++);
656 if (err < 0 && arg_strict)
657 r = err;
65eb4378 658 }
65eb4378
TG
659
660 strbuf_complete(trie->strings);
661
662 log_debug("=== trie in-memory ===");
663 log_debug("nodes: %8zu bytes (%8zu)",
664 trie->nodes_count * sizeof(struct trie_node), trie->nodes_count);
665 log_debug("children arrays: %8zu bytes (%8zu)",
666 trie->children_count * sizeof(struct trie_child_entry), trie->children_count);
667 log_debug("values arrays: %8zu bytes (%8zu)",
668 trie->values_count * sizeof(struct trie_value_entry), trie->values_count);
669 log_debug("strings: %8zu bytes",
670 trie->strings->len);
671 log_debug("strings incoming: %8zu bytes (%8zu)",
672 trie->strings->in_len, trie->strings->in_count);
673 log_debug("strings dedup'ed: %8zu bytes (%8zu)",
674 trie->strings->dedup_len, trie->strings->dedup_count);
675
0aac506b 676 hwdb_bin = path_join(arg_root, arg_hwdb_bin_dir, "hwdb.bin");
65eb4378
TG
677 if (!hwdb_bin)
678 return -ENOMEM;
679
ea683512 680 mkdir_parents_label(hwdb_bin, 0755);
aacbcab6
NB
681 err = trie_store(trie, hwdb_bin);
682 if (err < 0)
683 return log_error_errno(err, "Failure writing database %s: %m", hwdb_bin);
684
685 err = label_fix(hwdb_bin, 0);
686 if (err < 0)
687 return err;
65eb4378 688
aacbcab6 689 return r;
65eb4378
TG
690}
691
692static void help(void) {
693 printf("Usage: %s OPTIONS COMMAND\n\n"
424d110a 694 "Update or query the hardware database.\n\n"
7ccf7603
ZJS
695 " -h --help Show this help\n"
696 " --version Show package version\n"
697 " -s --strict When updating, return non-zero exit value on any parsing error\n"
698 " --usr Generate in " UDEVLIBEXECDIR " instead of /etc/udev\n"
699 " -r --root=PATH Alternative root path in the filesystem\n\n"
65eb4378 700 "Commands:\n"
7ccf7603
ZJS
701 " update Update the hwdb database\n"
702 " query MODALIAS Query database and print result\n",
65eb4378
TG
703 program_invocation_short_name);
704}
705
706static int parse_argv(int argc, char *argv[]) {
707 enum {
424d110a
LP
708 ARG_VERSION = 0x100,
709 ARG_USR,
65eb4378
TG
710 };
711
712 static const struct option options[] = {
424d110a
LP
713 { "help", no_argument, NULL, 'h' },
714 { "version", no_argument, NULL, ARG_VERSION },
715 { "usr", no_argument, NULL, ARG_USR },
aacbcab6 716 { "strict", no_argument, NULL, 's' },
424d110a 717 { "root", required_argument, NULL, 'r' },
65eb4378
TG
718 {}
719 };
720
721 int c;
722
723 assert(argc >= 0);
724 assert(argv);
725
aacbcab6 726 while ((c = getopt_long(argc, argv, "ust:r:h", options, NULL)) >= 0) {
65eb4378 727 switch(c) {
424d110a
LP
728
729 case 'h':
730 help();
731 return 0;
732
733 case ARG_VERSION:
3f6fd1ba 734 return version();
424d110a 735
65eb4378
TG
736 case ARG_USR:
737 arg_hwdb_bin_dir = UDEVLIBEXECDIR;
738 break;
424d110a 739
aacbcab6
NB
740 case 's':
741 arg_strict = true;
742 break;
743
65eb4378
TG
744 case 'r':
745 arg_root = optarg;
746 break;
424d110a 747
65eb4378
TG
748 case '?':
749 return -EINVAL;
424d110a 750
65eb4378
TG
751 default:
752 assert_not_reached("Unknown option");
753 }
754 }
755
756 return 1;
757}
758
759static int hwdb_main(int argc, char *argv[]) {
15c3626e 760 static const Verb verbs[] = {
caa8dab2 761 { "update", 1, 1, 0, hwdb_update },
424d110a 762 { "query", 2, 2, 0, hwdb_query },
caa8dab2 763 {},
65eb4378
TG
764 };
765
caa8dab2 766 return dispatch_verb(argc, argv, verbs, NULL);
65eb4378
TG
767}
768
769int main (int argc, char *argv[]) {
770 int r;
771
772 log_parse_environment();
773 log_open();
774
775 r = parse_argv(argc, argv);
776 if (r <= 0)
777 goto finish;
778
ea683512
EV
779 mac_selinux_init();
780
65eb4378
TG
781 r = hwdb_main(argc, argv);
782
783finish:
784 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
785}