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 int catalog_update(void) {
273 _cleanup_strv_free_ char **files = NULL;
274 _cleanup_fclose_ FILE *w = NULL;
275 _cleanup_free_ char *p = NULL;
278 struct strbuf *sb = NULL;
279 _cleanup_free_ CatalogItem *items = NULL;
281 CatalogHeader header;
287 h = hashmap_new(catalog_hash_func, catalog_compare_func);
297 r = conf_files_list_strv(&files, ".catalog", (const char **) conf_file_dirs);
299 log_error("Failed to get catalog files: %s", strerror(-r));
303 STRV_FOREACH(f, files) {
304 log_debug("reading file '%s'", *f);
305 import_file(h, sb, *f);
308 if (hashmap_size(h) <= 0) {
309 log_info("No items in catalog.");
316 items = new(CatalogItem, hashmap_size(h));
323 HASHMAP_FOREACH(i, h, j) {
324 log_debug("Found " SD_ID128_FORMAT_STR ", language %s", SD_ID128_FORMAT_VAL(i->id), isempty(i->language) ? "C" : i->language);
328 assert(n == hashmap_size(h));
329 qsort(items, n, sizeof(CatalogItem), catalog_compare_func);
331 mkdir_p("/var/lib/systemd/catalog", 0775);
333 r = fopen_temporary("/var/lib/systemd/catalog/database", &w, &p);
335 log_error("Failed to open database for writing: %s", strerror(-r));
340 memcpy(header.signature, CATALOG_SIGNATURE, sizeof(header.signature));
341 header.header_size = htole64(ALIGN_TO(sizeof(CatalogHeader), 8));
342 header.catalog_item_size = htole64(sizeof(CatalogItem));
343 header.n_items = htole64(hashmap_size(h));
345 k = fwrite(&header, 1, sizeof(header), w);
346 if (k != sizeof(header)) {
347 log_error("Failed to write header.");
351 k = fwrite(items, 1, n * sizeof(CatalogItem), w);
352 if (k != n * sizeof(CatalogItem)) {
353 log_error("Failed to write database.");
357 k = fwrite(sb->buf, 1, sb->len, w);
359 log_error("Failed to write strings.");
366 log_error("Failed to write database.");
370 fchmod(fileno(w), 0644);
372 if (rename(p, "/var/lib/systemd/catalog/database") < 0) {
373 log_error("rename() failed: %m");
384 hashmap_free_free(h);
395 static int open_mmap(int *_fd, struct stat *_st, void **_p) {
396 const CatalogHeader *h;
405 fd = open("/var/lib/systemd/catalog/database", O_RDONLY|O_CLOEXEC);
409 if (fstat(fd, &st) < 0) {
410 close_nointr_nofail(fd);
414 if (st.st_size < (off_t) sizeof(CatalogHeader)) {
415 close_nointr_nofail(fd);
419 p = mmap(NULL, PAGE_ALIGN(st.st_size), PROT_READ, MAP_SHARED, fd, 0);
420 if (p == MAP_FAILED) {
421 close_nointr_nofail(fd);
426 if (memcmp(h->signature, CATALOG_SIGNATURE, sizeof(h->signature)) != 0 ||
427 le64toh(h->header_size) < sizeof(CatalogHeader) ||
428 le64toh(h->catalog_item_size) < sizeof(CatalogItem) ||
429 h->incompatible_flags != 0 ||
430 le64toh(h->n_items) <= 0 ||
431 st.st_size < (off_t) (le64toh(h->header_size) + le64toh(h->catalog_item_size) * le64toh(h->n_items))) {
432 close_nointr_nofail(fd);
433 munmap(p, st.st_size);
444 static const char *find_id(void *p, sd_id128_t id) {
445 CatalogItem key, *f = NULL;
446 const CatalogHeader *h = p;
452 loc = setlocale(LC_MESSAGES, NULL);
453 if (loc && loc[0] && !streq(loc, "C") && !streq(loc, "POSIX")) {
454 strncpy(key.language, loc, sizeof(key.language));
455 key.language[strcspn(key.language, ".@")] = 0;
457 f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
461 e = strchr(key.language, '_');
464 f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
471 f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
477 return (const char*) p +
478 le64toh(h->header_size) +
479 le64toh(h->n_items) * le64toh(h->catalog_item_size) +
483 int catalog_get(sd_id128_t id, char **_text) {
484 _cleanup_close_ int fd = -1;
493 r = open_mmap(&fd, &st, &p);
514 munmap(p, st.st_size);
519 static char *find_header(const char *s, const char *header) {
524 v = startswith(s, header);
526 v += strspn(v, WHITESPACE);
527 return strndup(v, strcspn(v, NEWLINE));
543 int catalog_list(FILE *f) {
544 _cleanup_close_ int fd = -1;
547 const CatalogHeader *h;
548 const CatalogItem *items;
552 bool last_id_set = false;
554 r = open_mmap(&fd, &st, &p);
559 items = (const CatalogItem*) ((const uint8_t*) p + le64toh(h->header_size));
561 for (n = 0; n < le64toh(h->n_items); n++) {
563 _cleanup_free_ char *subject = NULL, *defined_by = NULL;
565 if (last_id_set && sd_id128_equal(last_id, items[n].id))
568 assert_se(s = find_id(p, items[n].id));
570 subject = find_header(s, "Subject:");
571 defined_by = find_header(s, "Defined-By:");
573 fprintf(f, SD_ID128_FORMAT_STR " %s: %s\n", SD_ID128_FORMAT_VAL(items[n].id), strna(defined_by), strna(subject));
576 last_id = items[n].id;
579 munmap(p, st.st_size);