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;
57 typedef struct CatalogItem {
63 static unsigned catalog_hash_func(const void *p) {
64 const CatalogItem *i = p;
66 assert_cc(sizeof(unsigned) == sizeof(uint8_t)*4);
68 return (((unsigned) i->id.bytes[0] << 24) |
69 ((unsigned) i->id.bytes[1] << 16) |
70 ((unsigned) i->id.bytes[2] << 8) |
71 ((unsigned) i->id.bytes[3])) ^
72 (((unsigned) i->id.bytes[4] << 24) |
73 ((unsigned) i->id.bytes[5] << 16) |
74 ((unsigned) i->id.bytes[6] << 8) |
75 ((unsigned) i->id.bytes[7])) ^
76 (((unsigned) i->id.bytes[8] << 24) |
77 ((unsigned) i->id.bytes[9] << 16) |
78 ((unsigned) i->id.bytes[10] << 8) |
79 ((unsigned) i->id.bytes[11])) ^
80 (((unsigned) i->id.bytes[12] << 24) |
81 ((unsigned) i->id.bytes[13] << 16) |
82 ((unsigned) i->id.bytes[14] << 8) |
83 ((unsigned) i->id.bytes[15])) ^
84 string_hash_func(i->language);
87 static int catalog_compare_func(const void *a, const void *b) {
88 const CatalogItem *i = a, *j = b;
91 for (k = 0; k < ELEMENTSOF(j->id.bytes); k++) {
92 if (i->id.bytes[k] < j->id.bytes[k])
94 if (i->id.bytes[k] > j->id.bytes[k])
98 return strncmp(i->language, j->language, sizeof(i->language));
101 static int finish_item(
105 const char *language,
106 const char *payload) {
116 offset = strbuf_add_string(sb, payload, strlen(payload));
120 if (offset > 0xFFFFFFFF) {
121 log_error("Too many catalog entries.");
125 i = new0(CatalogItem, 1);
130 strncpy(i->language, language, sizeof(i->language));
131 i->offset = htole32((uint32_t) offset);
133 r = hashmap_put(h, i, i);
135 log_warning("Duplicate entry for " SD_ID128_FORMAT_STR ".%s, ignoring.", SD_ID128_FORMAT_VAL(id), language ? language : "C");
143 static int import_file(Hashmap *h, struct strbuf *sb, const char *path) {
144 _cleanup_fclose_ FILE *f = NULL;
145 _cleanup_free_ char *payload = NULL;
149 bool got_id = false, empty_line = true;
156 f = fopen(path, "re");
158 log_error("Failed to open file %s: %m", path);
167 if (!fgets(line, sizeof(line), f)) {
171 log_error("Failed to read file %s: %m", path);
184 if (strchr(COMMENTS, line[0]))
188 strlen(line) >= 2+1+32 &&
192 (line[2+1+32] == ' ' || line[2+1+32] == 0)) {
199 with_language = line[2+1+32] != 0;
202 if (sd_id128_from_string(line + 2 + 1, &jd) >= 0) {
205 r = finish_item(h, sb, id, language, payload);
211 t = strstrip(line + 2 + 1 + 32 + 1);
215 log_error("[%s:%u] Language too short.", path, n);
218 if (c > sizeof(language)) {
219 log_error("[%s:%u] language too long.", path, n);
223 strncpy(language, t, sizeof(language));
240 log_error("[%s:%u] Got payload before ID.", path, n);
244 a = payload ? strlen(payload) : 0;
247 c = a + (empty_line ? 1 : 0) + b + 1 + 1;
248 t = realloc(payload, c);
254 memcpy(t + a + 1, line, b);
258 memcpy(t + a, line, b);
268 r = finish_item(h, sb, id, language, payload);
276 int catalog_update(void) {
277 _cleanup_strv_free_ char **files = NULL;
278 _cleanup_fclose_ FILE *w = NULL;
279 _cleanup_free_ char *p = NULL;
282 struct strbuf *sb = NULL;
283 _cleanup_free_ CatalogItem *items = NULL;
285 CatalogHeader header;
291 h = hashmap_new(catalog_hash_func, catalog_compare_func);
301 r = conf_files_list_strv(&files, ".catalog", (const char **) conf_file_dirs);
303 log_error("Failed to get catalog files: %s", strerror(-r));
307 STRV_FOREACH(f, files) {
308 log_debug("reading file '%s'", *f);
309 import_file(h, sb, *f);
312 if (hashmap_size(h) <= 0) {
313 log_info("No items in catalog.");
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 mkdir_p("/var/lib/systemd/catalog", 0775);
337 r = fopen_temporary("/var/lib/systemd/catalog/database", &w, &p);
339 log_error("Failed to open database for writing: %s", strerror(-r));
344 memcpy(header.signature, CATALOG_SIGNATURE, sizeof(header.signature));
345 header.header_size = htole32(ALIGN_TO(sizeof(CatalogHeader), 8));
346 header.n_items = htole32(hashmap_size(h));
348 k = fwrite(&header, 1, sizeof(header), w);
349 if (k != sizeof(header)) {
350 log_error("Failed to write header.");
354 k = fwrite(items, 1, n * sizeof(CatalogItem), w);
355 if (k != n * sizeof(CatalogItem)) {
356 log_error("Failed to write database.");
360 k = fwrite(sb->buf, 1, sb->len, w);
362 log_error("Failed to write strings.");
369 log_error("Failed to write database.");
373 fchmod(fileno(w), 0644);
375 if (rename(p, "/var/lib/systemd/catalog/database") < 0) {
376 log_error("rename() failed: %m");
387 hashmap_free_free(h);
398 static int open_mmap(int *_fd, struct stat *_st, void **_p) {
399 const CatalogHeader *h;
408 fd = open("/var/lib/systemd/catalog/database", O_RDONLY|O_CLOEXEC);
412 if (fstat(fd, &st) < 0) {
413 close_nointr_nofail(fd);
417 if (st.st_size < (off_t) sizeof(CatalogHeader)) {
418 close_nointr_nofail(fd);
422 p = mmap(NULL, PAGE_ALIGN(st.st_size), PROT_READ, MAP_SHARED, fd, 0);
423 if (p == MAP_FAILED) {
424 close_nointr_nofail(fd);
429 if (memcmp(h->signature, CATALOG_SIGNATURE, sizeof(h->signature)) != 0 ||
430 le32toh(h->header_size) < sizeof(CatalogHeader) ||
431 h->incompatible_flags != 0 ||
432 le32toh(h->n_items) <= 0 ||
433 st.st_size < (off_t) (le32toh(h->header_size) + sizeof(CatalogItem) * le32toh(h->n_items))) {
434 close_nointr_nofail(fd);
435 munmap(p, st.st_size);
446 static const char *find_id(void *p, sd_id128_t id) {
447 CatalogItem key, *f = NULL;
448 const CatalogHeader *h = p;
454 loc = setlocale(LC_MESSAGES, NULL);
455 if (loc && loc[0] && !streq(loc, "C") && !streq(loc, "POSIX")) {
456 strncpy(key.language, loc, sizeof(key.language));
457 key.language[strcspn(key.language, ".@")] = 0;
459 f = bsearch(&key, (const uint8_t*) p + le32toh(h->header_size), le32toh(h->n_items), sizeof(CatalogItem), catalog_compare_func);
463 e = strchr(key.language, '_');
466 f = bsearch(&key, (const uint8_t*) p + le32toh(h->header_size), le32toh(h->n_items), sizeof(CatalogItem), catalog_compare_func);
473 f = bsearch(&key, (const uint8_t*) p + le32toh(h->header_size), le32toh(h->n_items), sizeof(CatalogItem), catalog_compare_func);
479 return (const char*) p +
480 le32toh(h->header_size) +
481 le32toh(h->n_items) * sizeof(CatalogItem) +
485 int catalog_get(sd_id128_t id, char **_text) {
486 _cleanup_close_ int fd = -1;
495 r = open_mmap(&fd, &st, &p);
516 munmap(p, st.st_size);
521 static char *find_header(const char *s, const char *header) {
526 v = startswith(s, header);
528 v += strspn(v, WHITESPACE);
529 return strndup(v, strcspn(v, NEWLINE));
545 int catalog_list(FILE *f) {
546 _cleanup_close_ int fd = -1;
549 const CatalogHeader *h;
550 const CatalogItem *items;
554 bool last_id_set = false;
556 r = open_mmap(&fd, &st, &p);
561 items = (const CatalogItem*) ((const uint8_t*) p + le32toh(h->header_size));
563 for (n = 0; n < le32toh(h->n_items); n++) {
565 _cleanup_free_ char *subject = NULL, *defined_by = NULL;
567 if (last_id_set && sd_id128_equal(last_id, items[n].id))
570 assert_se(s = find_id(p, items[n].id));
572 subject = find_header(s, "Subject:");
573 defined_by = find_header(s, "Defined-By:");
575 fprintf(f, SD_ID128_FORMAT_STR " %s: %s\n", SD_ID128_FORMAT_VAL(items[n].id), strna(defined_by), strna(subject));
578 last_id = items[n].id;
581 munmap(p, st.st_size);