1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2012 Lennart Poettering
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.
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.
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/>.
32 #include "sparse-endian.h"
37 #include "conf-files.h"
41 static const char * const conf_file_dirs[] = {
42 "/usr/local/lib/systemd/catalog/",
43 "/usr/lib/systemd/catalog/",
47 #define CATALOG_SIGNATURE (uint8_t[]) { 'R', 'H', 'H', 'H', 'K', 'S', 'L', 'P' }
49 typedef struct CatalogHeader {
50 uint8_t signature[8]; /* "RHHHKSLP" */
51 le32_t compatible_flags;
52 le32_t incompatible_flags;
55 le64_t catalog_item_size;
58 typedef struct CatalogItem {
64 static unsigned catalog_hash_func(const void *p) {
65 const CatalogItem *i = p;
67 assert_cc(sizeof(unsigned) == sizeof(uint8_t)*4);
69 return (((unsigned) i->id.bytes[0] << 24) |
70 ((unsigned) i->id.bytes[1] << 16) |
71 ((unsigned) i->id.bytes[2] << 8) |
72 ((unsigned) i->id.bytes[3])) ^
73 (((unsigned) i->id.bytes[4] << 24) |
74 ((unsigned) i->id.bytes[5] << 16) |
75 ((unsigned) i->id.bytes[6] << 8) |
76 ((unsigned) i->id.bytes[7])) ^
77 (((unsigned) i->id.bytes[8] << 24) |
78 ((unsigned) i->id.bytes[9] << 16) |
79 ((unsigned) i->id.bytes[10] << 8) |
80 ((unsigned) i->id.bytes[11])) ^
81 (((unsigned) i->id.bytes[12] << 24) |
82 ((unsigned) i->id.bytes[13] << 16) |
83 ((unsigned) i->id.bytes[14] << 8) |
84 ((unsigned) i->id.bytes[15])) ^
85 string_hash_func(i->language);
88 static int catalog_compare_func(const void *a, const void *b) {
89 const CatalogItem *i = a, *j = b;
92 for (k = 0; k < ELEMENTSOF(j->id.bytes); k++) {
93 if (i->id.bytes[k] < j->id.bytes[k])
95 if (i->id.bytes[k] > j->id.bytes[k])
99 return strncmp(i->language, j->language, sizeof(i->language));
102 static int finish_item(
106 const char *language,
107 const char *payload) {
117 offset = strbuf_add_string(sb, payload, strlen(payload));
121 i = new0(CatalogItem, 1);
126 strncpy(i->language, language, sizeof(i->language));
127 i->offset = htole64((uint64_t) offset);
129 r = hashmap_put(h, i, i);
131 log_warning("Duplicate entry for " SD_ID128_FORMAT_STR ".%s, ignoring.", SD_ID128_FORMAT_VAL(id), language ? language : "C");
139 static int import_file(Hashmap *h, struct strbuf *sb, const char *path) {
140 _cleanup_fclose_ FILE *f = NULL;
141 _cleanup_free_ char *payload = NULL;
145 bool got_id = false, empty_line = true;
152 f = fopen(path, "re");
154 log_error("Failed to open file %s: %m", path);
163 if (!fgets(line, sizeof(line), f)) {
167 log_error("Failed to read file %s: %m", path);
180 if (strchr(COMMENTS, line[0]))
184 strlen(line) >= 2+1+32 &&
188 (line[2+1+32] == ' ' || line[2+1+32] == 0)) {
195 with_language = line[2+1+32] != 0;
198 if (sd_id128_from_string(line + 2 + 1, &jd) >= 0) {
201 r = finish_item(h, sb, id, language, payload);
207 t = strstrip(line + 2 + 1 + 32 + 1);
211 log_error("[%s:%u] Language too short.", path, n);
214 if (c > sizeof(language)) {
215 log_error("[%s:%u] language too long.", path, n);
219 strncpy(language, t, sizeof(language));
236 log_error("[%s:%u] Got payload before ID.", path, n);
240 a = payload ? strlen(payload) : 0;
243 c = a + (empty_line ? 1 : 0) + b + 1 + 1;
244 t = realloc(payload, c);
250 memcpy(t + a + 1, line, b);
254 memcpy(t + a, line, b);
264 r = finish_item(h, sb, id, language, payload);
272 #define CATALOG_DATABASE CATALOG_PATH "/database"
274 int catalog_update(void) {
275 _cleanup_strv_free_ char **files = NULL;
276 _cleanup_fclose_ FILE *w = NULL;
277 _cleanup_free_ char *p = NULL;
280 struct strbuf *sb = NULL;
281 _cleanup_free_ CatalogItem *items = NULL;
283 CatalogHeader header;
289 h = hashmap_new(catalog_hash_func, catalog_compare_func);
299 r = conf_files_list_strv(&files, ".catalog", NULL, (const char **) conf_file_dirs);
301 log_error("Failed to get catalog files: %s", strerror(-r));
305 STRV_FOREACH(f, files) {
306 log_debug("reading file '%s'", *f);
307 import_file(h, sb, *f);
310 if (hashmap_size(h) <= 0) {
311 log_info("No items in catalog.");
315 log_debug("Found %u items in catalog.", hashmap_size(h));
319 items = new(CatalogItem, hashmap_size(h));
326 HASHMAP_FOREACH(i, h, j) {
327 log_debug("Found " SD_ID128_FORMAT_STR ", language %s", SD_ID128_FORMAT_VAL(i->id), isempty(i->language) ? "C" : i->language);
331 assert(n == hashmap_size(h));
332 qsort(items, n, sizeof(CatalogItem), catalog_compare_func);
334 r = mkdir_p(CATALOG_PATH, 0775);
336 log_error("Recursive mkdir %s: %s", CATALOG_PATH, strerror(-r));
340 r = fopen_temporary(CATALOG_DATABASE, &w, &p);
342 log_error("Failed to open database for writing: %s: %s",
343 CATALOG_DATABASE, strerror(-r));
348 memcpy(header.signature, CATALOG_SIGNATURE, sizeof(header.signature));
349 header.header_size = htole64(ALIGN_TO(sizeof(CatalogHeader), 8));
350 header.catalog_item_size = htole64(sizeof(CatalogItem));
351 header.n_items = htole64(hashmap_size(h));
353 k = fwrite(&header, 1, sizeof(header), w);
354 if (k != sizeof(header)) {
355 log_error("%s: failed to write header.", p);
359 k = fwrite(items, 1, n * sizeof(CatalogItem), w);
360 if (k != n * sizeof(CatalogItem)) {
361 log_error("%s: failed to write database.", p);
365 k = fwrite(sb->buf, 1, sb->len, w);
367 log_error("%s: failed to write strings.", p);
374 log_error("%s: failed to write database.", p);
378 fchmod(fileno(w), 0644);
380 if (rename(p, CATALOG_DATABASE) < 0) {
381 log_error("rename (%s -> %s) failed: %m", p, CATALOG_DATABASE);
386 log_debug("%s: wrote %u items, with %zu bytes of strings, %ld total size.",
387 CATALOG_DATABASE, n, sb->len, ftell(w));
395 hashmap_free_free(h);
406 static int open_mmap(int *_fd, struct stat *_st, void **_p) {
407 const CatalogHeader *h;
416 fd = open(CATALOG_DATABASE, O_RDONLY|O_CLOEXEC);
420 if (fstat(fd, &st) < 0) {
421 close_nointr_nofail(fd);
425 if (st.st_size < (off_t) sizeof(CatalogHeader)) {
426 close_nointr_nofail(fd);
430 p = mmap(NULL, PAGE_ALIGN(st.st_size), PROT_READ, MAP_SHARED, fd, 0);
431 if (p == MAP_FAILED) {
432 close_nointr_nofail(fd);
437 if (memcmp(h->signature, CATALOG_SIGNATURE, sizeof(h->signature)) != 0 ||
438 le64toh(h->header_size) < sizeof(CatalogHeader) ||
439 le64toh(h->catalog_item_size) < sizeof(CatalogItem) ||
440 h->incompatible_flags != 0 ||
441 le64toh(h->n_items) <= 0 ||
442 st.st_size < (off_t) (le64toh(h->header_size) + le64toh(h->catalog_item_size) * le64toh(h->n_items))) {
443 close_nointr_nofail(fd);
444 munmap(p, st.st_size);
455 static const char *find_id(void *p, sd_id128_t id) {
456 CatalogItem key, *f = NULL;
457 const CatalogHeader *h = p;
463 loc = setlocale(LC_MESSAGES, NULL);
464 if (loc && loc[0] && !streq(loc, "C") && !streq(loc, "POSIX")) {
465 strncpy(key.language, loc, sizeof(key.language));
466 key.language[strcspn(key.language, ".@")] = 0;
468 f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
472 e = strchr(key.language, '_');
475 f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
482 f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
488 return (const char*) p +
489 le64toh(h->header_size) +
490 le64toh(h->n_items) * le64toh(h->catalog_item_size) +
494 int catalog_get(sd_id128_t id, char **_text) {
495 _cleanup_close_ int fd = -1;
504 r = open_mmap(&fd, &st, &p);
525 munmap(p, st.st_size);
530 static char *find_header(const char *s, const char *header) {
535 v = startswith(s, header);
537 v += strspn(v, WHITESPACE);
538 return strndup(v, strcspn(v, NEWLINE));
554 int catalog_list(FILE *f) {
555 _cleanup_close_ int fd = -1;
558 const CatalogHeader *h;
559 const CatalogItem *items;
563 bool last_id_set = false;
565 r = open_mmap(&fd, &st, &p);
570 items = (const CatalogItem*) ((const uint8_t*) p + le64toh(h->header_size));
572 for (n = 0; n < le64toh(h->n_items); n++) {
574 _cleanup_free_ char *subject = NULL, *defined_by = NULL;
576 if (last_id_set && sd_id128_equal(last_id, items[n].id))
579 assert_se(s = find_id(p, items[n].id));
581 subject = find_header(s, "Subject:");
582 defined_by = find_header(s, "Defined-By:");
584 fprintf(f, SD_ID128_FORMAT_STR " %s: %s\n", SD_ID128_FORMAT_VAL(items[n].id), strna(defined_by), strna(subject));
587 last_id = items[n].id;
590 munmap(p, st.st_size);