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_PATH "/var/lib/systemd/catalog"
273 #define CATALOG_DATABASE CATALOG_PATH "/database"
275 int catalog_update(void) {
276 _cleanup_strv_free_ char **files = NULL;
277 _cleanup_fclose_ FILE *w = NULL;
278 _cleanup_free_ char *p = NULL;
281 struct strbuf *sb = NULL;
282 _cleanup_free_ CatalogItem *items = NULL;
284 CatalogHeader header;
290 h = hashmap_new(catalog_hash_func, catalog_compare_func);
300 r = conf_files_list_strv(&files, ".catalog", (const char **) conf_file_dirs);
302 log_error("Failed to get catalog files: %s", strerror(-r));
306 STRV_FOREACH(f, files) {
307 log_debug("reading file '%s'", *f);
308 import_file(h, sb, *f);
311 if (hashmap_size(h) <= 0) {
312 log_info("No items in catalog.");
316 log_debug("Found %u items in catalog.", hashmap_size(h));
320 items = new(CatalogItem, hashmap_size(h));
327 HASHMAP_FOREACH(i, h, j) {
328 log_debug("Found " SD_ID128_FORMAT_STR ", language %s", SD_ID128_FORMAT_VAL(i->id), isempty(i->language) ? "C" : i->language);
332 assert(n == hashmap_size(h));
333 qsort(items, n, sizeof(CatalogItem), catalog_compare_func);
335 r = mkdir_p(CATALOG_PATH, 0775);
337 log_error("Recursive mkdir %s: %s", CATALOG_PATH, strerror(-r));
341 r = fopen_temporary(CATALOG_DATABASE, &w, &p);
343 log_error("Failed to open database for writing: %s: %s",
344 CATALOG_DATABASE, strerror(-r));
349 memcpy(header.signature, CATALOG_SIGNATURE, sizeof(header.signature));
350 header.header_size = htole64(ALIGN_TO(sizeof(CatalogHeader), 8));
351 header.catalog_item_size = htole64(sizeof(CatalogItem));
352 header.n_items = htole64(hashmap_size(h));
354 k = fwrite(&header, 1, sizeof(header), w);
355 if (k != sizeof(header)) {
356 log_error("%s: failed to write header.", p);
360 k = fwrite(items, 1, n * sizeof(CatalogItem), w);
361 if (k != n * sizeof(CatalogItem)) {
362 log_error("%s: failed to write database.", p);
366 k = fwrite(sb->buf, 1, sb->len, w);
368 log_error("%s: failed to write strings.", p);
375 log_error("%s: failed to write database.", p);
379 fchmod(fileno(w), 0644);
381 if (rename(p, CATALOG_DATABASE) < 0) {
382 log_error("rename (%s -> %s) failed: %m", p, CATALOG_DATABASE);
387 log_info("%s: wrote %u items, with %zu bytes of strings, %zu total size.",
388 CATALOG_DATABASE, n, sb->len, ftell(w));
396 hashmap_free_free(h);
407 static int open_mmap(int *_fd, struct stat *_st, void **_p) {
408 const CatalogHeader *h;
417 fd = open("/var/lib/systemd/catalog/database", O_RDONLY|O_CLOEXEC);
421 if (fstat(fd, &st) < 0) {
422 close_nointr_nofail(fd);
426 if (st.st_size < (off_t) sizeof(CatalogHeader)) {
427 close_nointr_nofail(fd);
431 p = mmap(NULL, PAGE_ALIGN(st.st_size), PROT_READ, MAP_SHARED, fd, 0);
432 if (p == MAP_FAILED) {
433 close_nointr_nofail(fd);
438 if (memcmp(h->signature, CATALOG_SIGNATURE, sizeof(h->signature)) != 0 ||
439 le64toh(h->header_size) < sizeof(CatalogHeader) ||
440 le64toh(h->catalog_item_size) < sizeof(CatalogItem) ||
441 h->incompatible_flags != 0 ||
442 le64toh(h->n_items) <= 0 ||
443 st.st_size < (off_t) (le64toh(h->header_size) + le64toh(h->catalog_item_size) * le64toh(h->n_items))) {
444 close_nointr_nofail(fd);
445 munmap(p, st.st_size);
456 static const char *find_id(void *p, sd_id128_t id) {
457 CatalogItem key, *f = NULL;
458 const CatalogHeader *h = p;
464 loc = setlocale(LC_MESSAGES, NULL);
465 if (loc && loc[0] && !streq(loc, "C") && !streq(loc, "POSIX")) {
466 strncpy(key.language, loc, sizeof(key.language));
467 key.language[strcspn(key.language, ".@")] = 0;
469 f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
473 e = strchr(key.language, '_');
476 f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
483 f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
489 return (const char*) p +
490 le64toh(h->header_size) +
491 le64toh(h->n_items) * le64toh(h->catalog_item_size) +
495 int catalog_get(sd_id128_t id, char **_text) {
496 _cleanup_close_ int fd = -1;
505 r = open_mmap(&fd, &st, &p);
526 munmap(p, st.st_size);
531 static char *find_header(const char *s, const char *header) {
536 v = startswith(s, header);
538 v += strspn(v, WHITESPACE);
539 return strndup(v, strcspn(v, NEWLINE));
555 int catalog_list(FILE *f) {
556 _cleanup_close_ int fd = -1;
559 const CatalogHeader *h;
560 const CatalogItem *items;
564 bool last_id_set = false;
566 r = open_mmap(&fd, &st, &p);
571 items = (const CatalogItem*) ((const uint8_t*) p + le64toh(h->header_size));
573 for (n = 0; n < le64toh(h->n_items); n++) {
575 _cleanup_free_ char *subject = NULL, *defined_by = NULL;
577 if (last_id_set && sd_id128_equal(last_id, items[n].id))
580 assert_se(s = find_id(p, items[n].id));
582 subject = find_header(s, "Subject:");
583 defined_by = find_header(s, "Defined-By:");
585 fprintf(f, SD_ID128_FORMAT_STR " %s: %s\n", SD_ID128_FORMAT_VAL(items[n].id), strna(defined_by), strna(subject));
588 last_id = items[n].id;
591 munmap(p, st.st_size);