]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/catalog.c
systemd: treat reload failure as failure
[thirdparty/systemd.git] / src / journal / catalog.c
CommitLineData
d4205751
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2012 Lennart Poettering
7
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.
12
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.
17
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/>.
20***/
21
22#include <fcntl.h>
23#include <stdio.h>
24#include <unistd.h>
25#include <errno.h>
26#include <string.h>
27#include <sys/mman.h>
28#include <locale.h>
844ec79b 29#include <libgen.h>
d4205751
LP
30
31#include "util.h"
32#include "log.h"
33#include "sparse-endian.h"
34#include "sd-id128.h"
35#include "hashmap.h"
36#include "strv.h"
37#include "strbuf.h"
18cd5fe9 38#include "strxcpyx.h"
d4205751
LP
39#include "conf-files.h"
40#include "mkdir.h"
41#include "catalog.h"
42
844ec79b 43const char * const catalog_file_dirs[] = {
d4205751
LP
44 "/usr/local/lib/systemd/catalog/",
45 "/usr/lib/systemd/catalog/",
46 NULL
47};
48
49#define CATALOG_SIGNATURE (uint8_t[]) { 'R', 'H', 'H', 'H', 'K', 'S', 'L', 'P' }
50
51typedef struct CatalogHeader {
52 uint8_t signature[8]; /* "RHHHKSLP" */
53 le32_t compatible_flags;
54 le32_t incompatible_flags;
83f6936a
LP
55 le64_t header_size;
56 le64_t n_items;
57 le64_t catalog_item_size;
d4205751
LP
58} CatalogHeader;
59
60typedef struct CatalogItem {
61 sd_id128_t id;
62 char language[32];
83f6936a 63 le64_t offset;
d4205751
LP
64} CatalogItem;
65
844ec79b 66unsigned catalog_hash_func(const void *p) {
d4205751
LP
67 const CatalogItem *i = p;
68
69 assert_cc(sizeof(unsigned) == sizeof(uint8_t)*4);
70
71 return (((unsigned) i->id.bytes[0] << 24) |
72 ((unsigned) i->id.bytes[1] << 16) |
73 ((unsigned) i->id.bytes[2] << 8) |
74 ((unsigned) i->id.bytes[3])) ^
75 (((unsigned) i->id.bytes[4] << 24) |
76 ((unsigned) i->id.bytes[5] << 16) |
77 ((unsigned) i->id.bytes[6] << 8) |
78 ((unsigned) i->id.bytes[7])) ^
79 (((unsigned) i->id.bytes[8] << 24) |
80 ((unsigned) i->id.bytes[9] << 16) |
81 ((unsigned) i->id.bytes[10] << 8) |
82 ((unsigned) i->id.bytes[11])) ^
83 (((unsigned) i->id.bytes[12] << 24) |
84 ((unsigned) i->id.bytes[13] << 16) |
85 ((unsigned) i->id.bytes[14] << 8) |
86 ((unsigned) i->id.bytes[15])) ^
87 string_hash_func(i->language);
88}
89
844ec79b 90int catalog_compare_func(const void *a, const void *b) {
d4205751
LP
91 const CatalogItem *i = a, *j = b;
92 unsigned k;
93
94 for (k = 0; k < ELEMENTSOF(j->id.bytes); k++) {
95 if (i->id.bytes[k] < j->id.bytes[k])
96 return -1;
97 if (i->id.bytes[k] > j->id.bytes[k])
98 return 1;
99 }
100
18cd5fe9 101 return strcmp(i->language, j->language);
d4205751
LP
102}
103
104static int finish_item(
105 Hashmap *h,
106 struct strbuf *sb,
107 sd_id128_t id,
108 const char *language,
109 const char *payload) {
110
111 ssize_t offset;
112 CatalogItem *i;
113 int r;
114
115 assert(h);
116 assert(sb);
117 assert(payload);
118
119 offset = strbuf_add_string(sb, payload, strlen(payload));
120 if (offset < 0)
121 return log_oom();
122
d4205751
LP
123 i = new0(CatalogItem, 1);
124 if (!i)
125 return log_oom();
126
127 i->id = id;
18cd5fe9 128 strscpy(i->language, sizeof(i->language), language);
83f6936a 129 i->offset = htole64((uint64_t) offset);
d4205751
LP
130
131 r = hashmap_put(h, i, i);
132 if (r == EEXIST) {
18cd5fe9
ZJS
133 log_warning("Duplicate entry for " SD_ID128_FORMAT_STR ".%s, ignoring.",
134 SD_ID128_FORMAT_VAL(id), language ? language : "C");
d4205751
LP
135 free(i);
136 return 0;
137 }
138
139 return 0;
140}
141
844ec79b 142int catalog_import_file(Hashmap *h, struct strbuf *sb, const char *path) {
d4205751
LP
143 _cleanup_fclose_ FILE *f = NULL;
144 _cleanup_free_ char *payload = NULL;
145 unsigned n = 0;
146 sd_id128_t id;
147 char language[32];
148 bool got_id = false, empty_line = true;
149 int r;
150
151 assert(h);
152 assert(sb);
153 assert(path);
154
155 f = fopen(path, "re");
156 if (!f) {
157 log_error("Failed to open file %s: %m", path);
158 return -errno;
159 }
160
161 for (;;) {
162 char line[LINE_MAX];
163 size_t a, b, c;
164 char *t;
165
166 if (!fgets(line, sizeof(line), f)) {
167 if (feof(f))
168 break;
169
170 log_error("Failed to read file %s: %m", path);
171 return -errno;
172 }
173
174 n++;
175
176 truncate_nl(line);
177
178 if (line[0] == 0) {
179 empty_line = true;
180 continue;
181 }
182
d3b6d0c2 183 if (strchr(COMMENTS "\n", line[0]))
d4205751
LP
184 continue;
185
186 if (empty_line &&
187 strlen(line) >= 2+1+32 &&
188 line[0] == '-' &&
189 line[1] == '-' &&
190 line[2] == ' ' &&
18cd5fe9 191 (line[2+1+32] == ' ' || line[2+1+32] == '\0')) {
d4205751
LP
192
193 bool with_language;
194 sd_id128_t jd;
195
196 /* New entry */
197
18cd5fe9
ZJS
198 with_language = line[2+1+32] != '\0';
199 line[2+1+32] = '\0';
d4205751
LP
200
201 if (sd_id128_from_string(line + 2 + 1, &jd) >= 0) {
202
203 if (got_id) {
204 r = finish_item(h, sb, id, language, payload);
205 if (r < 0)
206 return r;
207 }
208
209 if (with_language) {
210 t = strstrip(line + 2 + 1 + 32 + 1);
211
212 c = strlen(t);
213 if (c <= 0) {
214 log_error("[%s:%u] Language too short.", path, n);
215 return -EINVAL;
216 }
18cd5fe9 217 if (c > sizeof(language) - 1) {
d4205751
LP
218 log_error("[%s:%u] language too long.", path, n);
219 return -EINVAL;
220 }
221
18cd5fe9 222 strscpy(language, sizeof(language), t);
d4205751 223 } else
18cd5fe9 224 language[0] = '\0';
d4205751
LP
225
226 got_id = true;
227 empty_line = false;
228 id = jd;
229
230 if (payload)
18cd5fe9 231 payload[0] = '\0';
d4205751
LP
232
233 continue;
234 }
235 }
236
237 /* Payload */
238 if (!got_id) {
239 log_error("[%s:%u] Got payload before ID.", path, n);
240 return -EINVAL;
241 }
242
243 a = payload ? strlen(payload) : 0;
244 b = strlen(line);
245
246 c = a + (empty_line ? 1 : 0) + b + 1 + 1;
247 t = realloc(payload, c);
248 if (!t)
249 return log_oom();
250
251 if (empty_line) {
252 t[a] = '\n';
253 memcpy(t + a + 1, line, b);
254 t[a+b+1] = '\n';
255 t[a+b+2] = 0;
256 } else {
257 memcpy(t + a, line, b);
258 t[a+b] = '\n';
259 t[a+b+1] = 0;
260 }
261
262 payload = t;
263 empty_line = false;
264 }
265
266 if (got_id) {
267 r = finish_item(h, sb, id, language, payload);
268 if (r < 0)
269 return r;
270 }
271
272 return 0;
273}
274
844ec79b
ZJS
275static long write_catalog(const char *database, Hashmap *h, struct strbuf *sb,
276 CatalogItem *items, size_t n) {
277 CatalogHeader header;
278 _cleanup_fclose_ FILE *w = NULL;
279 int r;
7fd1b19b 280 _cleanup_free_ char *d, *p = NULL;
844ec79b
ZJS
281 size_t k;
282
283 d = dirname_malloc(database);
284 if (!d)
285 return log_oom();
286
287 r = mkdir_p(d, 0775);
288 if (r < 0) {
289 log_error("Recursive mkdir %s: %s", d, strerror(-r));
290 return r;
291 }
292
293 r = fopen_temporary(database, &w, &p);
294 if (r < 0) {
295 log_error("Failed to open database for writing: %s: %s",
296 database, strerror(-r));
297 return r;
298 }
299
300 zero(header);
301 memcpy(header.signature, CATALOG_SIGNATURE, sizeof(header.signature));
302 header.header_size = htole64(ALIGN_TO(sizeof(CatalogHeader), 8));
303 header.catalog_item_size = htole64(sizeof(CatalogItem));
304 header.n_items = htole64(hashmap_size(h));
80343dc1 305
844ec79b
ZJS
306 r = -EIO;
307
308 k = fwrite(&header, 1, sizeof(header), w);
309 if (k != sizeof(header)) {
310 log_error("%s: failed to write header.", p);
311 goto error;
312 }
313
314 k = fwrite(items, 1, n * sizeof(CatalogItem), w);
315 if (k != n * sizeof(CatalogItem)) {
316 log_error("%s: failed to write database.", p);
317 goto error;
318 }
319
320 k = fwrite(sb->buf, 1, sb->len, w);
321 if (k != sb->len) {
322 log_error("%s: failed to write strings.", p);
323 goto error;
324 }
325
326 fflush(w);
327
328 if (ferror(w)) {
329 log_error("%s: failed to write database.", p);
330 goto error;
331 }
332
333 fchmod(fileno(w), 0644);
334
335 if (rename(p, database) < 0) {
336 log_error("rename (%s -> %s) failed: %m", p, database);
337 r = -errno;
338 goto error;
339 }
340
341 return ftell(w);
342
343error:
344 unlink(p);
345 return r;
346}
347
348int catalog_update(const char* database, const char* root, const char* const* dirs) {
d4205751 349 _cleanup_strv_free_ char **files = NULL;
d4205751
LP
350 char **f;
351 Hashmap *h;
352 struct strbuf *sb = NULL;
353 _cleanup_free_ CatalogItem *items = NULL;
354 CatalogItem *i;
d4205751
LP
355 Iterator j;
356 unsigned n;
844ec79b 357 long r;
d4205751
LP
358
359 h = hashmap_new(catalog_hash_func, catalog_compare_func);
d4205751 360 sb = strbuf_new();
844ec79b
ZJS
361
362 if (!h || !sb) {
d4205751
LP
363 r = log_oom();
364 goto finish;
365 }
366
844ec79b 367 r = conf_files_list_strv(&files, ".catalog", root, dirs);
d4205751
LP
368 if (r < 0) {
369 log_error("Failed to get catalog files: %s", strerror(-r));
370 goto finish;
371 }
372
373 STRV_FOREACH(f, files) {
374 log_debug("reading file '%s'", *f);
844ec79b 375 catalog_import_file(h, sb, *f);
d4205751
LP
376 }
377
378 if (hashmap_size(h) <= 0) {
379 log_info("No items in catalog.");
380 r = 0;
381 goto finish;
80343dc1
ZJS
382 } else
383 log_debug("Found %u items in catalog.", hashmap_size(h));
d4205751
LP
384
385 strbuf_complete(sb);
386
387 items = new(CatalogItem, hashmap_size(h));
388 if (!items) {
389 r = log_oom();
390 goto finish;
391 }
392
393 n = 0;
394 HASHMAP_FOREACH(i, h, j) {
18cd5fe9
ZJS
395 log_debug("Found " SD_ID128_FORMAT_STR ", language %s",
396 SD_ID128_FORMAT_VAL(i->id),
397 isempty(i->language) ? "C" : i->language);
d4205751
LP
398 items[n++] = *i;
399 }
400
401 assert(n == hashmap_size(h));
7ff7394d 402 qsort_safe(items, n, sizeof(CatalogItem), catalog_compare_func);
d4205751 403
844ec79b
ZJS
404 r = write_catalog(database, h, sb, items, n);
405 if (r < 0)
406 log_error("Failed to write %s: %s", database, strerror(-r));
407 else
408 log_debug("%s: wrote %u items, with %zu bytes of strings, %ld total size.",
409 database, n, sb->len, r);
d4205751
LP
410
411 r = 0;
412
413finish:
844ec79b
ZJS
414 if (h)
415 hashmap_free_free(h);
d4205751
LP
416 if (sb)
417 strbuf_cleanup(sb);
418
844ec79b 419 return r < 0 ? r : 0;
d4205751
LP
420}
421
844ec79b 422static int open_mmap(const char *database, int *_fd, struct stat *_st, void **_p) {
d4205751
LP
423 const CatalogHeader *h;
424 int fd;
425 void *p;
426 struct stat st;
427
428 assert(_fd);
429 assert(_st);
430 assert(_p);
431
844ec79b 432 fd = open(database, O_RDONLY|O_CLOEXEC);
d4205751
LP
433 if (fd < 0)
434 return -errno;
435
436 if (fstat(fd, &st) < 0) {
437 close_nointr_nofail(fd);
438 return -errno;
439 }
440
441 if (st.st_size < (off_t) sizeof(CatalogHeader)) {
442 close_nointr_nofail(fd);
443 return -EINVAL;
444 }
445
446 p = mmap(NULL, PAGE_ALIGN(st.st_size), PROT_READ, MAP_SHARED, fd, 0);
447 if (p == MAP_FAILED) {
448 close_nointr_nofail(fd);
449 return -errno;
450 }
451
452 h = p;
453 if (memcmp(h->signature, CATALOG_SIGNATURE, sizeof(h->signature)) != 0 ||
83f6936a
LP
454 le64toh(h->header_size) < sizeof(CatalogHeader) ||
455 le64toh(h->catalog_item_size) < sizeof(CatalogItem) ||
d4205751 456 h->incompatible_flags != 0 ||
83f6936a
LP
457 le64toh(h->n_items) <= 0 ||
458 st.st_size < (off_t) (le64toh(h->header_size) + le64toh(h->catalog_item_size) * le64toh(h->n_items))) {
d4205751
LP
459 close_nointr_nofail(fd);
460 munmap(p, st.st_size);
461 return -EBADMSG;
462 }
463
464 *_fd = fd;
465 *_st = st;
466 *_p = p;
467
468 return 0;
469}
470
471static const char *find_id(void *p, sd_id128_t id) {
472 CatalogItem key, *f = NULL;
473 const CatalogHeader *h = p;
474 const char *loc;
475
476 zero(key);
477 key.id = id;
478
479 loc = setlocale(LC_MESSAGES, NULL);
480 if (loc && loc[0] && !streq(loc, "C") && !streq(loc, "POSIX")) {
481 strncpy(key.language, loc, sizeof(key.language));
482 key.language[strcspn(key.language, ".@")] = 0;
483
83f6936a 484 f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
d4205751
LP
485 if (!f) {
486 char *e;
487
488 e = strchr(key.language, '_');
489 if (e) {
490 *e = 0;
83f6936a 491 f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
d4205751
LP
492 }
493 }
494 }
495
496 if (!f) {
497 zero(key.language);
83f6936a 498 f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func);
d4205751
LP
499 }
500
501 if (!f)
502 return NULL;
503
504 return (const char*) p +
83f6936a
LP
505 le64toh(h->header_size) +
506 le64toh(h->n_items) * le64toh(h->catalog_item_size) +
507 le64toh(f->offset);
d4205751
LP
508}
509
844ec79b 510int catalog_get(const char* database, sd_id128_t id, char **_text) {
d4205751
LP
511 _cleanup_close_ int fd = -1;
512 void *p = NULL;
513 struct stat st;
514 char *text = NULL;
515 int r;
516 const char *s;
517
518 assert(_text);
519
844ec79b 520 r = open_mmap(database, &fd, &st, &p);
d4205751
LP
521 if (r < 0)
522 return r;
523
524 s = find_id(p, id);
525 if (!s) {
526 r = -ENOENT;
527 goto finish;
528 }
529
530 text = strdup(s);
531 if (!text) {
532 r = -ENOMEM;
533 goto finish;
534 }
535
536 *_text = text;
537 r = 0;
538
539finish:
540 if (p)
541 munmap(p, st.st_size);
542
543 return r;
544}
545
546static char *find_header(const char *s, const char *header) {
547
548 for (;;) {
549 const char *v, *e;
550
551 v = startswith(s, header);
552 if (v) {
553 v += strspn(v, WHITESPACE);
554 return strndup(v, strcspn(v, NEWLINE));
555 }
556
557 /* End of text */
558 e = strchr(s, '\n');
559 if (!e)
560 return NULL;
561
562 /* End of header */
563 if (e == s)
564 return NULL;
565
566 s = e + 1;
567 }
568}
569
54b7254c
ZJS
570static void dump_catalog_entry(FILE *f, sd_id128_t id, const char *s, bool oneline) {
571 if (oneline) {
572 _cleanup_free_ char *subject = NULL, *defined_by = NULL;
573
574 subject = find_header(s, "Subject:");
575 defined_by = find_header(s, "Defined-By:");
576
577 fprintf(f, SD_ID128_FORMAT_STR " %s: %s\n",
578 SD_ID128_FORMAT_VAL(id),
579 strna(defined_by), strna(subject));
580 } else
581 fprintf(f, "-- " SD_ID128_FORMAT_STR "\n%s\n",
582 SD_ID128_FORMAT_VAL(id), s);
583}
584
585
844ec79b 586int catalog_list(FILE *f, const char *database, bool oneline) {
d4205751
LP
587 _cleanup_close_ int fd = -1;
588 void *p = NULL;
589 struct stat st;
590 const CatalogHeader *h;
591 const CatalogItem *items;
592 int r;
593 unsigned n;
594 sd_id128_t last_id;
595 bool last_id_set = false;
596
844ec79b 597 r = open_mmap(database, &fd, &st, &p);
d4205751
LP
598 if (r < 0)
599 return r;
600
601 h = p;
83f6936a 602 items = (const CatalogItem*) ((const uint8_t*) p + le64toh(h->header_size));
d4205751 603
83f6936a 604 for (n = 0; n < le64toh(h->n_items); n++) {
d4205751 605 const char *s;
d4205751
LP
606
607 if (last_id_set && sd_id128_equal(last_id, items[n].id))
608 continue;
609
610 assert_se(s = find_id(p, items[n].id));
611
54b7254c 612 dump_catalog_entry(f, items[n].id, s, oneline);
d4205751
LP
613
614 last_id_set = true;
615 last_id = items[n].id;
616 }
617
618 munmap(p, st.st_size);
619
620 return 0;
621}
54b7254c 622
844ec79b 623int catalog_list_items(FILE *f, const char *database, bool oneline, char **items) {
54b7254c
ZJS
624 char **item;
625 int r = 0;
626
627 STRV_FOREACH(item, items) {
628 sd_id128_t id;
629 int k;
7fd1b19b 630 _cleanup_free_ char *msg = NULL;
54b7254c
ZJS
631
632 k = sd_id128_from_string(*item, &id);
633 if (k < 0) {
634 log_error("Failed to parse id128 '%s': %s",
2e8fb702 635 *item, strerror(-k));
464264ac 636 if (r == 0)
54b7254c
ZJS
637 r = k;
638 continue;
639 }
640
844ec79b 641 k = catalog_get(database, id, &msg);
54b7254c
ZJS
642 if (k < 0) {
643 log_full(k == -ENOENT ? LOG_NOTICE : LOG_ERR,
644 "Failed to retrieve catalog entry for '%s': %s",
2e8fb702 645 *item, strerror(-k));
464264ac 646 if (r == 0)
54b7254c
ZJS
647 r = k;
648 continue;
649 }
650
651 dump_catalog_entry(f, id, msg, oneline);
652 }
653
654 return r;
655}