]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/catalog.c
resolved: add enablers for DNS-SD
[thirdparty/systemd.git] / src / journal / catalog.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
d4205751
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2012 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
07630cea 21#include <errno.h>
d4205751 22#include <fcntl.h>
07630cea 23#include <locale.h>
d4205751 24#include <stdio.h>
d4205751
LP
25#include <string.h>
26#include <sys/mman.h>
07630cea 27#include <unistd.h>
d4205751 28
d4205751 29#include "sd-id128.h"
07630cea 30
b5efdb8a 31#include "alloc-util.h"
07630cea 32#include "catalog.h"
d4205751 33#include "conf-files.h"
3ffd4af2 34#include "fd-util.h"
0d39fa9c 35#include "fileio.h"
07630cea
LP
36#include "hashmap.h"
37#include "log.h"
d4205751 38#include "mkdir.h"
5f311f8c 39#include "path-util.h"
9bf3b535 40#include "siphash24.h"
07630cea
LP
41#include "sparse-endian.h"
42#include "strbuf.h"
43#include "string-util.h"
44#include "strv.h"
45#include "util.h"
d4205751 46
844ec79b 47const char * const catalog_file_dirs[] = {
d4205751
LP
48 "/usr/local/lib/systemd/catalog/",
49 "/usr/lib/systemd/catalog/",
50 NULL
51};
52
53#define CATALOG_SIGNATURE (uint8_t[]) { 'R', 'H', 'H', 'H', 'K', 'S', 'L', 'P' }
54
55typedef struct CatalogHeader {
56 uint8_t signature[8]; /* "RHHHKSLP" */
57 le32_t compatible_flags;
58 le32_t incompatible_flags;
83f6936a
LP
59 le64_t header_size;
60 le64_t n_items;
61 le64_t catalog_item_size;
d4205751
LP
62} CatalogHeader;
63
64typedef struct CatalogItem {
65 sd_id128_t id;
66 char language[32];
83f6936a 67 le64_t offset;
d4205751
LP
68} CatalogItem;
69
b826ab58 70static void catalog_hash_func(const void *p, struct siphash *state) {
d4205751
LP
71 const CatalogItem *i = p;
72
b826ab58
TG
73 siphash24_compress(&i->id, sizeof(i->id), state);
74 siphash24_compress(i->language, strlen(i->language), state);
d4205751
LP
75}
76
d5099efc 77static int catalog_compare_func(const void *a, const void *b) {
d4205751
LP
78 const CatalogItem *i = a, *j = b;
79 unsigned k;
80
81 for (k = 0; k < ELEMENTSOF(j->id.bytes); k++) {
82 if (i->id.bytes[k] < j->id.bytes[k])
83 return -1;
84 if (i->id.bytes[k] > j->id.bytes[k])
85 return 1;
86 }
87
18cd5fe9 88 return strcmp(i->language, j->language);
d4205751
LP
89}
90
d5099efc
MS
91const struct hash_ops catalog_hash_ops = {
92 .hash = catalog_hash_func,
93 .compare = catalog_compare_func
94};
95
dbae138d
SW
96static bool next_header(const char **s) {
97 const char *e;
98
99 e = strchr(*s, '\n');
100
101 /* Unexpected end */
102 if (!e)
103 return false;
104
105 /* End of headers */
106 if (e == *s)
107 return false;
108
109 *s = e + 1;
110 return true;
111}
112
113static const char *skip_header(const char *s) {
114 while (next_header(&s))
115 ;
116 return s;
117}
118
119static char *combine_entries(const char *one, const char *two) {
120 const char *b1, *b2;
121 size_t l1, l2, n;
122 char *dest, *p;
123
124 /* Find split point of headers to body */
125 b1 = skip_header(one);
126 b2 = skip_header(two);
127
128 l1 = strlen(one);
129 l2 = strlen(two);
130 dest = new(char, l1 + l2 + 1);
131 if (!dest) {
132 log_oom();
133 return NULL;
134 }
135
136 p = dest;
137
138 /* Headers from @one */
139 n = b1 - one;
140 p = mempcpy(p, one, n);
141
142 /* Headers from @two, these will only be found if not present above */
143 n = b2 - two;
144 p = mempcpy(p, two, n);
145
146 /* Body from @one */
147 n = l1 - (b1 - one);
148 if (n > 0) {
149 memcpy(p, b1, n);
150 p += n;
151
152 /* Body from @two */
153 } else {
154 n = l2 - (b2 - two);
155 memcpy(p, b2, n);
156 p += n;
157 }
158
159 assert(p - dest <= (ptrdiff_t)(l1 + l2));
160 p[0] = '\0';
161 return dest;
162}
163
d4205751
LP
164static int finish_item(
165 Hashmap *h,
d4205751
LP
166 sd_id128_t id,
167 const char *language,
06466a7f 168 char *payload, size_t payload_size) {
d4205751 169
e3b9d9c8 170 _cleanup_free_ CatalogItem *i = NULL;
06466a7f 171 _cleanup_free_ char *prev = NULL, *combined = NULL;
d4205751
LP
172
173 assert(h);
d4205751 174 assert(payload);
06466a7f 175 assert(payload_size > 0);
d4205751 176
d4205751
LP
177 i = new0(CatalogItem, 1);
178 if (!i)
179 return log_oom();
180
181 i->id = id;
c7332b08
ZJS
182 if (language) {
183 assert(strlen(language) > 1 && strlen(language) < 32);
184 strcpy(i->language, language);
185 }
d4205751 186
dbae138d 187 prev = hashmap_get(h, i);
dbae138d 188 if (prev) {
06466a7f 189 /* Already have such an item, combine them */
dbae138d
SW
190 combined = combine_entries(payload, prev);
191 if (!combined)
192 return log_oom();
dbae138d 193
06466a7f
ZJS
194 if (hashmap_update(h, i, combined) < 0)
195 return log_oom();
196 combined = NULL;
dbae138d 197 } else {
06466a7f
ZJS
198 /* A new item */
199 combined = memdup(payload, payload_size + 1);
200 if (!combined)
201 return log_oom();
202
203 if (hashmap_put(h, i, combined) < 0)
204 return log_oom();
dbae138d 205 i = NULL;
06466a7f 206 combined = NULL;
dbae138d 207 }
d4205751
LP
208
209 return 0;
210}
211
c7332b08
ZJS
212int catalog_file_lang(const char* filename, char **lang) {
213 char *beg, *end, *_lang;
214
215 end = endswith(filename, ".catalog");
216 if (!end)
217 return 0;
218
219 beg = end - 1;
4c701096 220 while (beg > filename && !IN_SET(*beg, '.', '/') && end - beg < 32)
313cefa1 221 beg--;
c7332b08 222
4b8268f8 223 if (*beg != '.' || end <= beg + 1)
c7332b08
ZJS
224 return 0;
225
226 _lang = strndup(beg + 1, end - beg - 1);
227 if (!_lang)
228 return -ENOMEM;
229
230 *lang = _lang;
231 return 1;
232}
233
baf167ee
ZJS
234static int catalog_entry_lang(const char* filename, int line,
235 const char* t, const char* deflang, char **lang) {
236 size_t c;
237
238 c = strlen(t);
239 if (c == 0) {
240 log_error("[%s:%u] Language too short.", filename, line);
241 return -EINVAL;
242 }
243 if (c > 31) {
244 log_error("[%s:%u] language too long.", filename, line);
245 return -EINVAL;
246 }
247
248 if (deflang) {
249 if (streq(t, deflang)) {
250 log_warning("[%s:%u] language specified unnecessarily",
251 filename, line);
252 return 0;
253 } else
254 log_warning("[%s:%u] language differs from default for file",
255 filename, line);
256 }
257
258 *lang = strdup(t);
259 if (!*lang)
260 return -ENOMEM;
261
262 return 0;
263}
264
dbae138d 265int catalog_import_file(Hashmap *h, const char *path) {
d4205751
LP
266 _cleanup_fclose_ FILE *f = NULL;
267 _cleanup_free_ char *payload = NULL;
06466a7f 268 size_t payload_size = 0, payload_allocated = 0;
d4205751
LP
269 unsigned n = 0;
270 sd_id128_t id;
c7332b08 271 _cleanup_free_ char *deflang = NULL, *lang = NULL;
d4205751
LP
272 bool got_id = false, empty_line = true;
273 int r;
274
275 assert(h);
d4205751
LP
276 assert(path);
277
278 f = fopen(path, "re");
4a62c710
MS
279 if (!f)
280 return log_error_errno(errno, "Failed to open file %s: %m", path);
d4205751 281
c7332b08
ZJS
282 r = catalog_file_lang(path, &deflang);
283 if (r < 0)
709f6e46 284 log_error_errno(r, "Failed to determine language for file %s: %m", path);
c7332b08
ZJS
285 if (r == 1)
286 log_debug("File %s has language %s.", path, deflang);
287
d4205751
LP
288 for (;;) {
289 char line[LINE_MAX];
06466a7f 290 size_t line_len;
d4205751
LP
291
292 if (!fgets(line, sizeof(line), f)) {
293 if (feof(f))
294 break;
295
e1427b13 296 return log_error_errno(errno, "Failed to read file %s: %m", path);
d4205751
LP
297 }
298
299 n++;
300
301 truncate_nl(line);
302
303 if (line[0] == 0) {
304 empty_line = true;
305 continue;
306 }
307
d3b6d0c2 308 if (strchr(COMMENTS "\n", line[0]))
d4205751
LP
309 continue;
310
311 if (empty_line &&
312 strlen(line) >= 2+1+32 &&
313 line[0] == '-' &&
314 line[1] == '-' &&
315 line[2] == ' ' &&
4c701096 316 IN_SET(line[2+1+32], ' ', '\0')) {
d4205751
LP
317
318 bool with_language;
319 sd_id128_t jd;
320
321 /* New entry */
322
18cd5fe9
ZJS
323 with_language = line[2+1+32] != '\0';
324 line[2+1+32] = '\0';
d4205751
LP
325
326 if (sd_id128_from_string(line + 2 + 1, &jd) >= 0) {
327
328 if (got_id) {
06466a7f
ZJS
329 if (payload_size == 0) {
330 log_error("[%s:%u] No payload text.", path, n);
331 return -EINVAL;
332 }
333
334 r = finish_item(h, id, lang ?: deflang, payload, payload_size);
d4205751
LP
335 if (r < 0)
336 return r;
c7332b08 337
97b11eed 338 lang = mfree(lang);
06466a7f 339 payload_size = 0;
d4205751
LP
340 }
341
342 if (with_language) {
06466a7f 343 char *t;
d4205751 344
06466a7f 345 t = strstrip(line + 2 + 1 + 32 + 1);
baf167ee
ZJS
346 r = catalog_entry_lang(path, n, t, deflang, &lang);
347 if (r < 0)
348 return r;
c7332b08 349 }
d4205751
LP
350
351 got_id = true;
352 empty_line = false;
353 id = jd;
354
d4205751
LP
355 continue;
356 }
357 }
358
359 /* Payload */
360 if (!got_id) {
361 log_error("[%s:%u] Got payload before ID.", path, n);
362 return -EINVAL;
363 }
364
06466a7f
ZJS
365 line_len = strlen(line);
366 if (!GREEDY_REALLOC(payload, payload_allocated,
367 payload_size + (empty_line ? 1 : 0) + line_len + 1 + 1))
d4205751
LP
368 return log_oom();
369
06466a7f
ZJS
370 if (empty_line)
371 payload[payload_size++] = '\n';
372 memcpy(payload + payload_size, line, line_len);
373 payload_size += line_len;
374 payload[payload_size++] = '\n';
375 payload[payload_size] = '\0';
d4205751 376
d4205751
LP
377 empty_line = false;
378 }
379
380 if (got_id) {
06466a7f
ZJS
381 if (payload_size == 0) {
382 log_error("[%s:%u] No payload text.", path, n);
383 return -EINVAL;
384 }
385
386 r = finish_item(h, id, lang ?: deflang, payload, payload_size);
d4205751
LP
387 if (r < 0)
388 return r;
389 }
390
391 return 0;
392}
393
77ba8233
MS
394static int64_t write_catalog(const char *database, struct strbuf *sb,
395 CatalogItem *items, size_t n) {
844ec79b
ZJS
396 CatalogHeader header;
397 _cleanup_fclose_ FILE *w = NULL;
398 int r;
7fd1b19b 399 _cleanup_free_ char *d, *p = NULL;
844ec79b
ZJS
400 size_t k;
401
402 d = dirname_malloc(database);
403 if (!d)
404 return log_oom();
405
406 r = mkdir_p(d, 0775);
eb56eb9b
MS
407 if (r < 0)
408 return log_error_errno(r, "Recursive mkdir %s: %m", d);
844ec79b
ZJS
409
410 r = fopen_temporary(database, &w, &p);
eb56eb9b
MS
411 if (r < 0)
412 return log_error_errno(r, "Failed to open database for writing: %s: %m",
413 database);
844ec79b
ZJS
414
415 zero(header);
416 memcpy(header.signature, CATALOG_SIGNATURE, sizeof(header.signature));
417 header.header_size = htole64(ALIGN_TO(sizeof(CatalogHeader), 8));
418 header.catalog_item_size = htole64(sizeof(CatalogItem));
8d2ecdb2 419 header.n_items = htole64(n);
80343dc1 420
844ec79b
ZJS
421 r = -EIO;
422
423 k = fwrite(&header, 1, sizeof(header), w);
424 if (k != sizeof(header)) {
425 log_error("%s: failed to write header.", p);
426 goto error;
427 }
428
429 k = fwrite(items, 1, n * sizeof(CatalogItem), w);
430 if (k != n * sizeof(CatalogItem)) {
431 log_error("%s: failed to write database.", p);
432 goto error;
433 }
434
435 k = fwrite(sb->buf, 1, sb->len, w);
436 if (k != sb->len) {
437 log_error("%s: failed to write strings.", p);
438 goto error;
439 }
440
dacd6cee
LP
441 r = fflush_and_check(w);
442 if (r < 0) {
443 log_error_errno(r, "%s: failed to write database: %m", p);
844ec79b
ZJS
444 goto error;
445 }
446
447 fchmod(fileno(w), 0644);
448
449 if (rename(p, database) < 0) {
dacd6cee 450 r = log_error_errno(errno, "rename (%s -> %s) failed: %m", p, database);
844ec79b
ZJS
451 goto error;
452 }
453
77ba8233 454 return ftello(w);
844ec79b
ZJS
455
456error:
dacd6cee 457 (void) unlink(p);
844ec79b
ZJS
458 return r;
459}
460
461int catalog_update(const char* database, const char* root, const char* const* dirs) {
d4205751 462 _cleanup_strv_free_ char **files = NULL;
d4205751 463 char **f;
d4205751 464 struct strbuf *sb = NULL;
dbae138d 465 _cleanup_hashmap_free_free_free_ Hashmap *h = NULL;
d4205751 466 _cleanup_free_ CatalogItem *items = NULL;
dbae138d
SW
467 ssize_t offset;
468 char *payload;
d4205751 469 CatalogItem *i;
d4205751
LP
470 Iterator j;
471 unsigned n;
77ba8233
MS
472 int r;
473 int64_t sz;
d4205751 474
d5099efc 475 h = hashmap_new(&catalog_hash_ops);
d4205751 476 sb = strbuf_new();
844ec79b
ZJS
477
478 if (!h || !sb) {
d4205751
LP
479 r = log_oom();
480 goto finish;
481 }
482
b5084605 483 r = conf_files_list_strv(&files, ".catalog", root, 0, dirs);
d4205751 484 if (r < 0) {
da927ba9 485 log_error_errno(r, "Failed to get catalog files: %m");
d4205751
LP
486 goto finish;
487 }
488
489 STRV_FOREACH(f, files) {
e3b9d9c8 490 log_debug("Reading file '%s'", *f);
dbae138d 491 r = catalog_import_file(h, *f);
e3b9d9c8 492 if (r < 0) {
e53fc357 493 log_error_errno(r, "Failed to import file '%s': %m", *f);
e3b9d9c8
ZJS
494 goto finish;
495 }
d4205751
LP
496 }
497
498 if (hashmap_size(h) <= 0) {
499 log_info("No items in catalog.");
d4205751 500 goto finish;
80343dc1
ZJS
501 } else
502 log_debug("Found %u items in catalog.", hashmap_size(h));
d4205751 503
d4205751
LP
504 items = new(CatalogItem, hashmap_size(h));
505 if (!items) {
506 r = log_oom();
507 goto finish;
508 }
509
510 n = 0;
dbae138d 511 HASHMAP_FOREACH_KEY(payload, i, h, j) {
18cd5fe9
ZJS
512 log_debug("Found " SD_ID128_FORMAT_STR ", language %s",
513 SD_ID128_FORMAT_VAL(i->id),
514 isempty(i->language) ? "C" : i->language);
dbae138d
SW
515
516 offset = strbuf_add_string(sb, payload, strlen(payload));
517 if (offset < 0) {
518 r = log_oom();
519 goto finish;
520 }
521 i->offset = htole64((uint64_t) offset);
d4205751
LP
522 items[n++] = *i;
523 }
524
525 assert(n == hashmap_size(h));
7ff7394d 526 qsort_safe(items, n, sizeof(CatalogItem), catalog_compare_func);
d4205751 527
dbae138d
SW
528 strbuf_complete(sb);
529
77ba8233
MS
530 sz = write_catalog(database, sb, items, n);
531 if (sz < 0)
532 r = log_error_errno(sz, "Failed to write %s: %m", database);
533 else {
534 r = 0;
535 log_debug("%s: wrote %u items, with %zu bytes of strings, %"PRIi64" total size.",
536 database, n, sb->len, sz);
537 }
d4205751 538
d4205751 539finish:
77ba8233 540 strbuf_cleanup(sb);
d4205751 541
77ba8233 542 return r;
d4205751
LP
543}
544
844ec79b 545static int open_mmap(const char *database, int *_fd, struct stat *_st, void **_p) {
d4205751
LP
546 const CatalogHeader *h;
547 int fd;
548 void *p;
549 struct stat st;
550
551 assert(_fd);
552 assert(_st);
553 assert(_p);
554
844ec79b 555 fd = open(database, O_RDONLY|O_CLOEXEC);
d4205751
LP
556 if (fd < 0)
557 return -errno;
558
559 if (fstat(fd, &st) < 0) {
03e334a1 560 safe_close(fd);
d4205751
LP
561 return -errno;
562 }
563
564 if (st.st_size < (off_t) sizeof(CatalogHeader)) {
03e334a1 565 safe_close(fd);
d4205751
LP
566 return -EINVAL;
567 }
568
569 p = mmap(NULL, PAGE_ALIGN(st.st_size), PROT_READ, MAP_SHARED, fd, 0);
570 if (p == MAP_FAILED) {
03e334a1 571 safe_close(fd);
d4205751
LP
572 return -errno;
573 }
574
575 h = p;
576 if (memcmp(h->signature, CATALOG_SIGNATURE, sizeof(h->signature)) != 0 ||
83f6936a
LP
577 le64toh(h->header_size) < sizeof(CatalogHeader) ||
578 le64toh(h->catalog_item_size) < sizeof(CatalogItem) ||
d4205751 579 h->incompatible_flags != 0 ||
83f6936a
LP
580 le64toh(h->n_items) <= 0 ||
581 st.st_size < (off_t) (le64toh(h->header_size) + le64toh(h->catalog_item_size) * le64toh(h->n_items))) {
03e334a1 582 safe_close(fd);
d4205751
LP
583 munmap(p, st.st_size);
584 return -EBADMSG;
585 }
586
587 *_fd = fd;
588 *_st = st;
589 *_p = p;
590
591 return 0;
592}
593
594static const char *find_id(void *p, sd_id128_t id) {
595 CatalogItem key, *f = NULL;
596 const CatalogHeader *h = p;
597 const char *loc;
598
599 zero(key);
600 key.id = id;
601
602 loc = setlocale(LC_MESSAGES, NULL);
603 if (loc && loc[0] && !streq(loc, "C") && !streq(loc, "POSIX")) {
604 strncpy(key.language, loc, sizeof(key.language));
605 key.language[strcspn(key.language, ".@")] = 0;
606
83f6936a 607 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
608 if (!f) {
609 char *e;
610
611 e = strchr(key.language, '_');
612 if (e) {
613 *e = 0;
83f6936a 614 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
615 }
616 }
617 }
618
619 if (!f) {
620 zero(key.language);
83f6936a 621 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
622 }
623
624 if (!f)
625 return NULL;
626
627 return (const char*) p +
83f6936a
LP
628 le64toh(h->header_size) +
629 le64toh(h->n_items) * le64toh(h->catalog_item_size) +
630 le64toh(f->offset);
d4205751
LP
631}
632
844ec79b 633int catalog_get(const char* database, sd_id128_t id, char **_text) {
d4205751
LP
634 _cleanup_close_ int fd = -1;
635 void *p = NULL;
a7f7d1bd 636 struct stat st = {};
d4205751
LP
637 char *text = NULL;
638 int r;
639 const char *s;
640
641 assert(_text);
642
844ec79b 643 r = open_mmap(database, &fd, &st, &p);
d4205751
LP
644 if (r < 0)
645 return r;
646
647 s = find_id(p, id);
648 if (!s) {
649 r = -ENOENT;
650 goto finish;
651 }
652
653 text = strdup(s);
654 if (!text) {
655 r = -ENOMEM;
656 goto finish;
657 }
658
659 *_text = text;
660 r = 0;
661
662finish:
663 if (p)
664 munmap(p, st.st_size);
665
666 return r;
667}
668
669static char *find_header(const char *s, const char *header) {
670
671 for (;;) {
dbae138d 672 const char *v;
d4205751
LP
673
674 v = startswith(s, header);
675 if (v) {
676 v += strspn(v, WHITESPACE);
677 return strndup(v, strcspn(v, NEWLINE));
678 }
679
dbae138d 680 if (!next_header(&s))
d4205751 681 return NULL;
d4205751
LP
682 }
683}
684
54b7254c
ZJS
685static void dump_catalog_entry(FILE *f, sd_id128_t id, const char *s, bool oneline) {
686 if (oneline) {
687 _cleanup_free_ char *subject = NULL, *defined_by = NULL;
688
689 subject = find_header(s, "Subject:");
690 defined_by = find_header(s, "Defined-By:");
691
692 fprintf(f, SD_ID128_FORMAT_STR " %s: %s\n",
693 SD_ID128_FORMAT_VAL(id),
694 strna(defined_by), strna(subject));
695 } else
696 fprintf(f, "-- " SD_ID128_FORMAT_STR "\n%s\n",
697 SD_ID128_FORMAT_VAL(id), s);
698}
699
700
844ec79b 701int catalog_list(FILE *f, const char *database, bool oneline) {
d4205751
LP
702 _cleanup_close_ int fd = -1;
703 void *p = NULL;
704 struct stat st;
705 const CatalogHeader *h;
706 const CatalogItem *items;
707 int r;
708 unsigned n;
709 sd_id128_t last_id;
710 bool last_id_set = false;
711
844ec79b 712 r = open_mmap(database, &fd, &st, &p);
d4205751
LP
713 if (r < 0)
714 return r;
715
716 h = p;
83f6936a 717 items = (const CatalogItem*) ((const uint8_t*) p + le64toh(h->header_size));
d4205751 718
83f6936a 719 for (n = 0; n < le64toh(h->n_items); n++) {
d4205751 720 const char *s;
d4205751
LP
721
722 if (last_id_set && sd_id128_equal(last_id, items[n].id))
723 continue;
724
725 assert_se(s = find_id(p, items[n].id));
726
54b7254c 727 dump_catalog_entry(f, items[n].id, s, oneline);
d4205751
LP
728
729 last_id_set = true;
730 last_id = items[n].id;
731 }
732
733 munmap(p, st.st_size);
734
735 return 0;
736}
54b7254c 737
844ec79b 738int catalog_list_items(FILE *f, const char *database, bool oneline, char **items) {
54b7254c
ZJS
739 char **item;
740 int r = 0;
741
742 STRV_FOREACH(item, items) {
743 sd_id128_t id;
744 int k;
7fd1b19b 745 _cleanup_free_ char *msg = NULL;
54b7254c
ZJS
746
747 k = sd_id128_from_string(*item, &id);
748 if (k < 0) {
e53fc357 749 log_error_errno(k, "Failed to parse id128 '%s': %m", *item);
464264ac 750 if (r == 0)
54b7254c
ZJS
751 r = k;
752 continue;
753 }
754
844ec79b 755 k = catalog_get(database, id, &msg);
54b7254c 756 if (k < 0) {
e53fc357
LP
757 log_full_errno(k == -ENOENT ? LOG_NOTICE : LOG_ERR, k,
758 "Failed to retrieve catalog entry for '%s': %m", *item);
464264ac 759 if (r == 0)
54b7254c
ZJS
760 r = k;
761 continue;
762 }
763
764 dump_catalog_entry(f, id, msg, oneline);
765 }
766
767 return r;
768}