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