chiark / gitweb /
localectl: add support for listing X11 keymap information
[elogind.git] / src / journal / sd-journal.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2011 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 <errno.h>
23 #include <fcntl.h>
24 #include <stddef.h>
25 #include <unistd.h>
26 #include <sys/inotify.h>
27 #include <sys/poll.h>
28 #include <sys/vfs.h>
29 #include <linux/magic.h>
30
31 #include "sd-journal.h"
32 #include "journal-def.h"
33 #include "journal-file.h"
34 #include "hashmap.h"
35 #include "list.h"
36 #include "path-util.h"
37 #include "lookup3.h"
38 #include "compress.h"
39 #include "journal-internal.h"
40 #include "missing.h"
41 #include "catalog.h"
42 #include "replace-var.h"
43
44 #define JOURNAL_FILES_MAX 1024
45
46 #define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC)
47
48 #define REPLACE_VAR_MAX 256
49
50 #define DEFAULT_DATA_THRESHOLD (64*1024)
51
52 /* We return an error here only if we didn't manage to
53    memorize the real error. */
54 static int set_put_error(sd_journal *j, int r) {
55         int k;
56
57         if (r >= 0)
58                 return r;
59
60         k = set_ensure_allocated(&j->errors, trivial_hash_func, trivial_compare_func);
61         if (k < 0)
62                 return k;
63
64         return set_put(j->errors, INT_TO_PTR(r));
65 }
66
67 static void detach_location(sd_journal *j) {
68         Iterator i;
69         JournalFile *f;
70
71         assert(j);
72
73         j->current_file = NULL;
74         j->current_field = 0;
75
76         HASHMAP_FOREACH(f, j->files, i)
77                 f->current_offset = 0;
78 }
79
80 static void reset_location(sd_journal *j) {
81         assert(j);
82
83         detach_location(j);
84         zero(j->current_location);
85 }
86
87 static void init_location(Location *l, LocationType type, JournalFile *f, Object *o) {
88         assert(l);
89         assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK);
90         assert(f);
91         assert(o->object.type == OBJECT_ENTRY);
92
93         l->type = type;
94         l->seqnum = le64toh(o->entry.seqnum);
95         l->seqnum_id = f->header->seqnum_id;
96         l->realtime = le64toh(o->entry.realtime);
97         l->monotonic = le64toh(o->entry.monotonic);
98         l->boot_id = o->entry.boot_id;
99         l->xor_hash = le64toh(o->entry.xor_hash);
100
101         l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
102 }
103
104 static void set_location(sd_journal *j, LocationType type, JournalFile *f, Object *o, uint64_t offset) {
105         assert(j);
106         assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK);
107         assert(f);
108         assert(o);
109
110         init_location(&j->current_location, type, f, o);
111
112         j->current_file = f;
113         j->current_field = 0;
114
115         f->current_offset = offset;
116 }
117
118 static int match_is_valid(const void *data, size_t size) {
119         const char *b, *p;
120
121         assert(data);
122
123         if (size < 2)
124                 return false;
125
126         if (startswith(data, "__"))
127                 return false;
128
129         b = data;
130         for (p = b; p < b + size; p++) {
131
132                 if (*p == '=')
133                         return p > b;
134
135                 if (*p == '_')
136                         continue;
137
138                 if (*p >= 'A' && *p <= 'Z')
139                         continue;
140
141                 if (*p >= '0' && *p <= '9')
142                         continue;
143
144                 return false;
145         }
146
147         return false;
148 }
149
150 static bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
151         const uint8_t *a = _a, *b = _b;
152         size_t j;
153
154         for (j = 0; j < s && j < t; j++) {
155
156                 if (a[j] != b[j])
157                         return false;
158
159                 if (a[j] == '=')
160                         return true;
161         }
162
163         return true;
164 }
165
166 static Match *match_new(Match *p, MatchType t) {
167         Match *m;
168
169         m = new0(Match, 1);
170         if (!m)
171                 return NULL;
172
173         m->type = t;
174
175         if (p) {
176                 m->parent = p;
177                 LIST_PREPEND(Match, matches, p->matches, m);
178         }
179
180         return m;
181 }
182
183 static void match_free(Match *m) {
184         assert(m);
185
186         while (m->matches)
187                 match_free(m->matches);
188
189         if (m->parent)
190                 LIST_REMOVE(Match, matches, m->parent->matches, m);
191
192         free(m->data);
193         free(m);
194 }
195
196 static void match_free_if_empty(Match *m) {
197         assert(m);
198
199         if (m->matches)
200                 return;
201
202         match_free(m);
203 }
204
205 _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
206         Match *l2, *l3, *add_here = NULL, *m;
207         le64_t le_hash;
208
209         if (!j)
210                 return -EINVAL;
211
212         if (!data)
213                 return -EINVAL;
214
215         if (size == 0)
216                 size = strlen(data);
217
218         if (!match_is_valid(data, size))
219                 return -EINVAL;
220
221         /* level 0: OR term
222          * level 1: AND terms
223          * level 2: OR terms
224          * level 3: concrete matches */
225
226         if (!j->level0) {
227                 j->level0 = match_new(NULL, MATCH_OR_TERM);
228                 if (!j->level0)
229                         return -ENOMEM;
230         }
231
232         if (!j->level1) {
233                 j->level1 = match_new(j->level0, MATCH_AND_TERM);
234                 if (!j->level1)
235                         return -ENOMEM;
236         }
237
238         assert(j->level0->type == MATCH_OR_TERM);
239         assert(j->level1->type == MATCH_AND_TERM);
240
241         le_hash = htole64(hash64(data, size));
242
243         LIST_FOREACH(matches, l2, j->level1->matches) {
244                 assert(l2->type == MATCH_OR_TERM);
245
246                 LIST_FOREACH(matches, l3, l2->matches) {
247                         assert(l3->type == MATCH_DISCRETE);
248
249                         /* Exactly the same match already? Then ignore
250                          * this addition */
251                         if (l3->le_hash == le_hash &&
252                             l3->size == size &&
253                             memcmp(l3->data, data, size) == 0)
254                                 return 0;
255
256                         /* Same field? Then let's add this to this OR term */
257                         if (same_field(data, size, l3->data, l3->size)) {
258                                 add_here = l2;
259                                 break;
260                         }
261                 }
262
263                 if (add_here)
264                         break;
265         }
266
267         if (!add_here) {
268                 add_here = match_new(j->level1, MATCH_OR_TERM);
269                 if (!add_here)
270                         goto fail;
271         }
272
273         m = match_new(add_here, MATCH_DISCRETE);
274         if (!m)
275                 goto fail;
276
277         m->le_hash = le_hash;
278         m->size = size;
279         m->data = memdup(data, size);
280         if (!m->data)
281                 goto fail;
282
283         detach_location(j);
284
285         return 0;
286
287 fail:
288         if (add_here)
289                 match_free_if_empty(add_here);
290
291         if (j->level1)
292                 match_free_if_empty(j->level1);
293
294         if (j->level0)
295                 match_free_if_empty(j->level0);
296
297         return -ENOMEM;
298 }
299
300 _public_ int sd_journal_add_disjunction(sd_journal *j) {
301         Match *m;
302
303         assert(j);
304
305         if (!j->level0)
306                 return 0;
307
308         if (!j->level1)
309                 return 0;
310
311         if (!j->level1->matches)
312                 return 0;
313
314         m = match_new(j->level0, MATCH_AND_TERM);
315         if (!m)
316                 return -ENOMEM;
317
318         j->level1 = m;
319         return 0;
320 }
321
322 static char *match_make_string(Match *m) {
323         char *p, *r;
324         Match *i;
325         bool enclose = false;
326
327         if (!m)
328                 return strdup("");
329
330         if (m->type == MATCH_DISCRETE)
331                 return strndup(m->data, m->size);
332
333         p = NULL;
334         LIST_FOREACH(matches, i, m->matches) {
335                 char *t, *k;
336
337                 t = match_make_string(i);
338                 if (!t) {
339                         free(p);
340                         return NULL;
341                 }
342
343                 if (p) {
344                         k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t, NULL);
345                         free(p);
346                         free(t);
347
348                         if (!k)
349                                 return NULL;
350
351                         p = k;
352
353                         enclose = true;
354                 } else {
355                         free(p);
356                         p = t;
357                 }
358         }
359
360         if (enclose) {
361                 r = strjoin("(", p, ")", NULL);
362                 free(p);
363                 return r;
364         }
365
366         return p;
367 }
368
369 char *journal_make_match_string(sd_journal *j) {
370         assert(j);
371
372         return match_make_string(j->level0);
373 }
374
375 _public_ void sd_journal_flush_matches(sd_journal *j) {
376
377         if (!j)
378                 return;
379
380         if (j->level0)
381                 match_free(j->level0);
382
383         j->level0 = j->level1 = NULL;
384
385         detach_location(j);
386 }
387
388 static int compare_entry_order(JournalFile *af, Object *_ao,
389                          JournalFile *bf, uint64_t bp) {
390
391         uint64_t a, b;
392         Object *ao, *bo;
393         int r;
394
395         assert(af);
396         assert(bf);
397         assert(_ao);
398
399         /* The mmap cache might invalidate the object from the first
400          * file if we look at the one from the second file. Hence
401          * temporarily copy the header of the first one, and look at
402          * that only. */
403         ao = alloca(offsetof(EntryObject, items));
404         memcpy(ao, _ao, offsetof(EntryObject, items));
405
406         r = journal_file_move_to_object(bf, OBJECT_ENTRY, bp, &bo);
407         if (r < 0)
408                 return strcmp(af->path, bf->path);
409
410         /* We operate on two different files here, hence we can access
411          * two objects at the same time, which we normally can't.
412          *
413          * If contents and timestamps match, these entries are
414          * identical, even if the seqnum does not match */
415
416         if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
417             ao->entry.monotonic == bo->entry.monotonic &&
418             ao->entry.realtime == bo->entry.realtime &&
419             ao->entry.xor_hash == bo->entry.xor_hash)
420                 return 0;
421
422         if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
423
424                 /* If this is from the same seqnum source, compare
425                  * seqnums */
426                 a = le64toh(ao->entry.seqnum);
427                 b = le64toh(bo->entry.seqnum);
428
429                 if (a < b)
430                         return -1;
431                 if (a > b)
432                         return 1;
433
434                 /* Wow! This is weird, different data but the same
435                  * seqnums? Something is borked, but let's make the
436                  * best of it and compare by time. */
437         }
438
439         if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
440
441                 /* If the boot id matches compare monotonic time */
442                 a = le64toh(ao->entry.monotonic);
443                 b = le64toh(bo->entry.monotonic);
444
445                 if (a < b)
446                         return -1;
447                 if (a > b)
448                         return 1;
449         }
450
451         /* Otherwise compare UTC time */
452         a = le64toh(ao->entry.realtime);
453         b = le64toh(bo->entry.realtime);
454
455         if (a < b)
456                 return -1;
457         if (a > b)
458                 return 1;
459
460         /* Finally, compare by contents */
461         a = le64toh(ao->entry.xor_hash);
462         b = le64toh(bo->entry.xor_hash);
463
464         if (a < b)
465                 return -1;
466         if (a > b)
467                 return 1;
468
469         return 0;
470 }
471
472 static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
473         uint64_t a;
474
475         assert(af);
476         assert(ao);
477         assert(l);
478         assert(l->type == LOCATION_DISCRETE || l->type == LOCATION_SEEK);
479
480         if (l->monotonic_set &&
481             sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
482             l->realtime_set &&
483             le64toh(ao->entry.realtime) == l->realtime &&
484             l->xor_hash_set &&
485             le64toh(ao->entry.xor_hash) == l->xor_hash)
486                 return 0;
487
488         if (l->seqnum_set &&
489             sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
490
491                 a = le64toh(ao->entry.seqnum);
492
493                 if (a < l->seqnum)
494                         return -1;
495                 if (a > l->seqnum)
496                         return 1;
497         }
498
499         if (l->monotonic_set &&
500             sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
501
502                 a = le64toh(ao->entry.monotonic);
503
504                 if (a < l->monotonic)
505                         return -1;
506                 if (a > l->monotonic)
507                         return 1;
508         }
509
510         if (l->realtime_set) {
511
512                 a = le64toh(ao->entry.realtime);
513
514                 if (a < l->realtime)
515                         return -1;
516                 if (a > l->realtime)
517                         return 1;
518         }
519
520         if (l->xor_hash_set) {
521                 a = le64toh(ao->entry.xor_hash);
522
523                 if (a < l->xor_hash)
524                         return -1;
525                 if (a > l->xor_hash)
526                         return 1;
527         }
528
529         return 0;
530 }
531
532 static int next_for_match(
533                 sd_journal *j,
534                 Match *m,
535                 JournalFile *f,
536                 uint64_t after_offset,
537                 direction_t direction,
538                 Object **ret,
539                 uint64_t *offset) {
540
541         int r;
542         uint64_t np = 0;
543         Object *n;
544
545         assert(j);
546         assert(m);
547         assert(f);
548
549         if (m->type == MATCH_DISCRETE) {
550                 uint64_t dp;
551
552                 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
553                 if (r <= 0)
554                         return r;
555
556                 return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
557
558         } else if (m->type == MATCH_OR_TERM) {
559                 Match *i;
560
561                 /* Find the earliest match beyond after_offset */
562
563                 LIST_FOREACH(matches, i, m->matches) {
564                         uint64_t cp;
565
566                         r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
567                         if (r < 0)
568                                 return r;
569                         else if (r > 0) {
570                                 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
571                                         np = cp;
572                         }
573                 }
574
575         } else if (m->type == MATCH_AND_TERM) {
576                 Match *i;
577                 bool continue_looking;
578
579                 /* Always jump to the next matching entry and repeat
580                  * this until we fine and offset that matches for all
581                  * matches. */
582
583                 if (!m->matches)
584                         return 0;
585
586                 np = 0;
587                 do {
588                         continue_looking = false;
589
590                         LIST_FOREACH(matches, i, m->matches) {
591                                 uint64_t cp, limit;
592
593                                 if (np == 0)
594                                         limit = after_offset;
595                                 else if (direction == DIRECTION_DOWN)
596                                         limit = MAX(np, after_offset);
597                                 else
598                                         limit = MIN(np, after_offset);
599
600                                 r = next_for_match(j, i, f, limit, direction, NULL, &cp);
601                                 if (r <= 0)
602                                         return r;
603
604                                 if ((direction == DIRECTION_DOWN ? cp >= after_offset : cp <= after_offset) &&
605                                     (np == 0 || (direction == DIRECTION_DOWN ? cp > np : np < cp))) {
606                                         np = cp;
607                                         continue_looking = true;
608                                 }
609                         }
610
611                 } while (continue_looking);
612         }
613
614         if (np == 0)
615                 return 0;
616
617         r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
618         if (r < 0)
619                 return r;
620
621         if (ret)
622                 *ret = n;
623         if (offset)
624                 *offset = np;
625
626         return 1;
627 }
628
629 static int find_location_for_match(
630                 sd_journal *j,
631                 Match *m,
632                 JournalFile *f,
633                 direction_t direction,
634                 Object **ret,
635                 uint64_t *offset) {
636
637         int r;
638
639         assert(j);
640         assert(m);
641         assert(f);
642
643         if (m->type == MATCH_DISCRETE) {
644                 uint64_t dp;
645
646                 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
647                 if (r <= 0)
648                         return r;
649
650                 /* FIXME: missing: find by monotonic */
651
652                 if (j->current_location.type == LOCATION_HEAD)
653                         return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset);
654                 if (j->current_location.type == LOCATION_TAIL)
655                         return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, ret, offset);
656                 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
657                         return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset);
658                 if (j->current_location.monotonic_set) {
659                         r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
660                         if (r != -ENOENT)
661                                 return r;
662                 }
663                 if (j->current_location.realtime_set)
664                         return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
665
666                 return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset);
667
668         } else if (m->type == MATCH_OR_TERM) {
669                 uint64_t np = 0;
670                 Object *n;
671                 Match *i;
672
673                 /* Find the earliest match */
674
675                 LIST_FOREACH(matches, i, m->matches) {
676                         uint64_t cp;
677
678                         r = find_location_for_match(j, i, f, direction, NULL, &cp);
679                         if (r < 0)
680                                 return r;
681                         else if (r > 0) {
682                                 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
683                                         np = cp;
684                         }
685                 }
686
687                 if (np == 0)
688                         return 0;
689
690                 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
691                 if (r < 0)
692                         return r;
693
694                 if (ret)
695                         *ret = n;
696                 if (offset)
697                         *offset = np;
698
699                 return 1;
700
701         } else {
702                 Match *i;
703                 uint64_t np = 0;
704
705                 assert(m->type == MATCH_AND_TERM);
706
707                 /* First jump to the last match, and then find the
708                  * next one where all matches match */
709
710                 if (!m->matches)
711                         return 0;
712
713                 LIST_FOREACH(matches, i, m->matches) {
714                         uint64_t cp;
715
716                         r = find_location_for_match(j, i, f, direction, NULL, &cp);
717                         if (r <= 0)
718                                 return r;
719
720                         if (np == 0 || (direction == DIRECTION_DOWN ? np < cp : np > cp))
721                                 np = cp;
722                 }
723
724                 return next_for_match(j, m, f, np, direction, ret, offset);
725         }
726 }
727
728 static int find_location_with_matches(
729                 sd_journal *j,
730                 JournalFile *f,
731                 direction_t direction,
732                 Object **ret,
733                 uint64_t *offset) {
734
735         int r;
736
737         assert(j);
738         assert(f);
739         assert(ret);
740         assert(offset);
741
742         if (!j->level0) {
743                 /* No matches is simple */
744
745                 if (j->current_location.type == LOCATION_HEAD)
746                         return journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, ret, offset);
747                 if (j->current_location.type == LOCATION_TAIL)
748                         return journal_file_next_entry(f, NULL, 0, DIRECTION_UP, ret, offset);
749                 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
750                         return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
751                 if (j->current_location.monotonic_set) {
752                         r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
753                         if (r != -ENOENT)
754                                 return r;
755                 }
756                 if (j->current_location.realtime_set)
757                         return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
758
759                 return journal_file_next_entry(f, NULL, 0, direction, ret, offset);
760         } else
761                 return find_location_for_match(j, j->level0, f, direction, ret, offset);
762 }
763
764 static int next_with_matches(
765                 sd_journal *j,
766                 JournalFile *f,
767                 direction_t direction,
768                 Object **ret,
769                 uint64_t *offset) {
770
771         Object *c;
772         uint64_t cp;
773
774         assert(j);
775         assert(f);
776         assert(ret);
777         assert(offset);
778
779         c = *ret;
780         cp = *offset;
781
782         /* No matches is easy. We simple advance the file
783          * pointer by one. */
784         if (!j->level0)
785                 return journal_file_next_entry(f, c, cp, direction, ret, offset);
786
787         /* If we have a match then we look for the next matching entry
788          * with an offset at least one step larger */
789         return next_for_match(j, j->level0, f, direction == DIRECTION_DOWN ? cp+1 : cp-1, direction, ret, offset);
790 }
791
792 static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
793         Object *c;
794         uint64_t cp;
795         int r;
796
797         assert(j);
798         assert(f);
799
800         if (f->current_offset > 0) {
801                 cp = f->current_offset;
802
803                 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
804                 if (r < 0)
805                         return r;
806
807                 r = next_with_matches(j, f, direction, &c, &cp);
808                 if (r <= 0)
809                         return r;
810         } else {
811                 r = find_location_with_matches(j, f, direction, &c, &cp);
812                 if (r <= 0)
813                         return r;
814         }
815
816         /* OK, we found the spot, now let's advance until to an entry
817          * that is actually different from what we were previously
818          * looking at. This is necessary to handle entries which exist
819          * in two (or more) journal files, and which shall all be
820          * suppressed but one. */
821
822         for (;;) {
823                 bool found;
824
825                 if (j->current_location.type == LOCATION_DISCRETE) {
826                         int k;
827
828                         k = compare_with_location(f, c, &j->current_location);
829                         if (direction == DIRECTION_DOWN)
830                                 found = k > 0;
831                         else
832                                 found = k < 0;
833                 } else
834                         found = true;
835
836                 if (found) {
837                         if (ret)
838                                 *ret = c;
839                         if (offset)
840                                 *offset = cp;
841                         return 1;
842                 }
843
844                 r = next_with_matches(j, f, direction, &c, &cp);
845                 if (r <= 0)
846                         return r;
847         }
848 }
849
850 static int real_journal_next(sd_journal *j, direction_t direction) {
851         JournalFile *f, *new_file = NULL;
852         uint64_t new_offset = 0;
853         Object *o;
854         uint64_t p;
855         Iterator i;
856         int r;
857
858         if (!j)
859                 return -EINVAL;
860
861         HASHMAP_FOREACH(f, j->files, i) {
862                 bool found;
863
864                 r = next_beyond_location(j, f, direction, &o, &p);
865                 if (r < 0) {
866                         log_debug("Can't iterate through %s, ignoring: %s", f->path, strerror(-r));
867                         continue;
868                 } else if (r == 0)
869                         continue;
870
871                 if (!new_file)
872                         found = true;
873                 else {
874                         int k;
875
876                         k = compare_entry_order(f, o, new_file, new_offset);
877
878                         if (direction == DIRECTION_DOWN)
879                                 found = k < 0;
880                         else
881                                 found = k > 0;
882                 }
883
884                 if (found) {
885                         new_file = f;
886                         new_offset = p;
887                 }
888         }
889
890         if (!new_file)
891                 return 0;
892
893         r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_offset, &o);
894         if (r < 0)
895                 return r;
896
897         set_location(j, LOCATION_DISCRETE, new_file, o, new_offset);
898
899         return 1;
900 }
901
902 _public_ int sd_journal_next(sd_journal *j) {
903         return real_journal_next(j, DIRECTION_DOWN);
904 }
905
906 _public_ int sd_journal_previous(sd_journal *j) {
907         return real_journal_next(j, DIRECTION_UP);
908 }
909
910 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
911         int c = 0, r;
912
913         if (!j)
914                 return -EINVAL;
915
916         if (skip == 0) {
917                 /* If this is not a discrete skip, then at least
918                  * resolve the current location */
919                 if (j->current_location.type != LOCATION_DISCRETE)
920                         return real_journal_next(j, direction);
921
922                 return 0;
923         }
924
925         do {
926                 r = real_journal_next(j, direction);
927                 if (r < 0)
928                         return r;
929
930                 if (r == 0)
931                         return c;
932
933                 skip--;
934                 c++;
935         } while (skip > 0);
936
937         return c;
938 }
939
940 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
941         return real_journal_next_skip(j, DIRECTION_DOWN, skip);
942 }
943
944 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
945         return real_journal_next_skip(j, DIRECTION_UP, skip);
946 }
947
948 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
949         Object *o;
950         int r;
951         char bid[33], sid[33];
952
953         if (!j)
954                 return -EINVAL;
955         if (!cursor)
956                 return -EINVAL;
957
958         if (!j->current_file || j->current_file->current_offset <= 0)
959                 return -EADDRNOTAVAIL;
960
961         r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
962         if (r < 0)
963                 return r;
964
965         sd_id128_to_string(j->current_file->header->seqnum_id, sid);
966         sd_id128_to_string(o->entry.boot_id, bid);
967
968         if (asprintf(cursor,
969                      "s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx",
970                      sid, (unsigned long long) le64toh(o->entry.seqnum),
971                      bid, (unsigned long long) le64toh(o->entry.monotonic),
972                      (unsigned long long) le64toh(o->entry.realtime),
973                      (unsigned long long) le64toh(o->entry.xor_hash)) < 0)
974                 return -ENOMEM;
975
976         return 0;
977 }
978
979 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
980         char *w, *state;
981         size_t l;
982         unsigned long long seqnum, monotonic, realtime, xor_hash;
983         bool
984                 seqnum_id_set = false,
985                 seqnum_set = false,
986                 boot_id_set = false,
987                 monotonic_set = false,
988                 realtime_set = false,
989                 xor_hash_set = false;
990         sd_id128_t seqnum_id, boot_id;
991
992         if (!j)
993                 return -EINVAL;
994         if (isempty(cursor))
995                 return -EINVAL;
996
997         FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
998                 char *item;
999                 int k = 0;
1000
1001                 if (l < 2 || w[1] != '=')
1002                         return -EINVAL;
1003
1004                 item = strndup(w, l);
1005                 if (!item)
1006                         return -ENOMEM;
1007
1008                 switch (w[0]) {
1009
1010                 case 's':
1011                         seqnum_id_set = true;
1012                         k = sd_id128_from_string(item+2, &seqnum_id);
1013                         break;
1014
1015                 case 'i':
1016                         seqnum_set = true;
1017                         if (sscanf(item+2, "%llx", &seqnum) != 1)
1018                                 k = -EINVAL;
1019                         break;
1020
1021                 case 'b':
1022                         boot_id_set = true;
1023                         k = sd_id128_from_string(item+2, &boot_id);
1024                         break;
1025
1026                 case 'm':
1027                         monotonic_set = true;
1028                         if (sscanf(item+2, "%llx", &monotonic) != 1)
1029                                 k = -EINVAL;
1030                         break;
1031
1032                 case 't':
1033                         realtime_set = true;
1034                         if (sscanf(item+2, "%llx", &realtime) != 1)
1035                                 k = -EINVAL;
1036                         break;
1037
1038                 case 'x':
1039                         xor_hash_set = true;
1040                         if (sscanf(item+2, "%llx", &xor_hash) != 1)
1041                                 k = -EINVAL;
1042                         break;
1043                 }
1044
1045                 free(item);
1046
1047                 if (k < 0)
1048                         return k;
1049         }
1050
1051         if ((!seqnum_set || !seqnum_id_set) &&
1052             (!monotonic_set || !boot_id_set) &&
1053             !realtime_set)
1054                 return -EINVAL;
1055
1056         reset_location(j);
1057
1058         j->current_location.type = LOCATION_SEEK;
1059
1060         if (realtime_set) {
1061                 j->current_location.realtime = (uint64_t) realtime;
1062                 j->current_location.realtime_set = true;
1063         }
1064
1065         if (seqnum_set && seqnum_id_set) {
1066                 j->current_location.seqnum = (uint64_t) seqnum;
1067                 j->current_location.seqnum_id = seqnum_id;
1068                 j->current_location.seqnum_set = true;
1069         }
1070
1071         if (monotonic_set && boot_id_set) {
1072                 j->current_location.monotonic = (uint64_t) monotonic;
1073                 j->current_location.boot_id = boot_id;
1074                 j->current_location.monotonic_set = true;
1075         }
1076
1077         if (xor_hash_set) {
1078                 j->current_location.xor_hash = (uint64_t) xor_hash;
1079                 j->current_location.xor_hash_set = true;
1080         }
1081
1082         return 0;
1083 }
1084
1085 _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
1086         int r;
1087         char *w, *state;
1088         size_t l;
1089         Object *o;
1090
1091         if (!j)
1092                 return -EINVAL;
1093         if (isempty(cursor))
1094                 return -EINVAL;
1095
1096         if (!j->current_file || j->current_file->current_offset <= 0)
1097                 return -EADDRNOTAVAIL;
1098
1099         r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1100         if (r < 0)
1101                 return r;
1102
1103         FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
1104                 _cleanup_free_ char *item = NULL;
1105                 sd_id128_t id;
1106                 unsigned long long ll;
1107                 int k = 0;
1108
1109                 if (l < 2 || w[1] != '=')
1110                         return -EINVAL;
1111
1112                 item = strndup(w, l);
1113                 if (!item)
1114                         return -ENOMEM;
1115
1116                 switch (w[0]) {
1117
1118                 case 's':
1119                         k = sd_id128_from_string(item+2, &id);
1120                         if (k < 0)
1121                                 return k;
1122                         if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
1123                                 return 0;
1124                         break;
1125
1126                 case 'i':
1127                         if (sscanf(item+2, "%llx", &ll) != 1)
1128                                 return -EINVAL;
1129                         if (ll != le64toh(o->entry.seqnum))
1130                                 return 0;
1131                         break;
1132
1133                 case 'b':
1134                         k = sd_id128_from_string(item+2, &id);
1135                         if (k < 0)
1136                                 return k;
1137                         if (!sd_id128_equal(id, o->entry.boot_id))
1138                                 return 0;
1139                         break;
1140
1141                 case 'm':
1142                         if (sscanf(item+2, "%llx", &ll) != 1)
1143                                 return -EINVAL;
1144                         if (ll != le64toh(o->entry.monotonic))
1145                                 return 0;
1146                         break;
1147
1148                 case 't':
1149                         if (sscanf(item+2, "%llx", &ll) != 1)
1150                                 return -EINVAL;
1151                         if (ll != le64toh(o->entry.realtime))
1152                                 return 0;
1153                         break;
1154
1155                 case 'x':
1156                         if (sscanf(item+2, "%llx", &ll) != 1)
1157                                 return -EINVAL;
1158                         if (ll != le64toh(o->entry.xor_hash))
1159                                 return 0;
1160                         break;
1161                 }
1162         }
1163
1164         return 1;
1165 }
1166
1167
1168 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1169         if (!j)
1170                 return -EINVAL;
1171
1172         reset_location(j);
1173         j->current_location.type = LOCATION_SEEK;
1174         j->current_location.boot_id = boot_id;
1175         j->current_location.monotonic = usec;
1176         j->current_location.monotonic_set = true;
1177
1178         return 0;
1179 }
1180
1181 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1182         if (!j)
1183                 return -EINVAL;
1184
1185         reset_location(j);
1186         j->current_location.type = LOCATION_SEEK;
1187         j->current_location.realtime = usec;
1188         j->current_location.realtime_set = true;
1189
1190         return 0;
1191 }
1192
1193 _public_ int sd_journal_seek_head(sd_journal *j) {
1194         if (!j)
1195                 return -EINVAL;
1196
1197         reset_location(j);
1198         j->current_location.type = LOCATION_HEAD;
1199
1200         return 0;
1201 }
1202
1203 _public_ int sd_journal_seek_tail(sd_journal *j) {
1204         if (!j)
1205                 return -EINVAL;
1206
1207         reset_location(j);
1208         j->current_location.type = LOCATION_TAIL;
1209
1210         return 0;
1211 }
1212
1213 static void check_network(sd_journal *j, int fd) {
1214         struct statfs sfs;
1215
1216         assert(j);
1217
1218         if (j->on_network)
1219                 return;
1220
1221         if (fstatfs(fd, &sfs) < 0)
1222                 return;
1223
1224         j->on_network =
1225                 (long)sfs.f_type == (long)CIFS_MAGIC_NUMBER ||
1226                 sfs.f_type == CODA_SUPER_MAGIC ||
1227                 sfs.f_type == NCP_SUPER_MAGIC ||
1228                 sfs.f_type == NFS_SUPER_MAGIC ||
1229                 sfs.f_type == SMB_SUPER_MAGIC;
1230 }
1231
1232 static int add_file(sd_journal *j, const char *prefix, const char *filename) {
1233         char _cleanup_free_ *path = NULL;
1234         int r;
1235         JournalFile *f;
1236
1237         assert(j);
1238         assert(prefix);
1239         assert(filename);
1240
1241         if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) &&
1242             !(streq(filename, "system.journal") ||
1243               streq(filename, "system.journal~") ||
1244               (startswith(filename, "system@") &&
1245                (endswith(filename, ".journal") || endswith(filename, ".journal~")))))
1246                 return 0;
1247
1248         path = strjoin(prefix, "/", filename, NULL);
1249         if (!path)
1250                 return -ENOMEM;
1251
1252         if (hashmap_get(j->files, path))
1253                 return 0;
1254
1255         if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
1256                 log_debug("Too many open journal files, not adding %s, ignoring.", path);
1257                 return set_put_error(j, -ETOOMANYREFS);
1258         }
1259
1260         r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
1261         if (r < 0) {
1262                 if (errno == ENOENT)
1263                         return 0;
1264
1265                 return r;
1266         }
1267
1268         /* journal_file_dump(f); */
1269
1270         r = hashmap_put(j->files, f->path, f);
1271         if (r < 0) {
1272                 journal_file_close(f);
1273                 return r;
1274         }
1275
1276         log_debug("File %s got added.", f->path);
1277
1278         check_network(j, f->fd);
1279
1280         j->current_invalidate_counter ++;
1281
1282         return 0;
1283 }
1284
1285 static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
1286         char *path;
1287         JournalFile *f;
1288
1289         assert(j);
1290         assert(prefix);
1291         assert(filename);
1292
1293         path = strjoin(prefix, "/", filename, NULL);
1294         if (!path)
1295                 return -ENOMEM;
1296
1297         f = hashmap_get(j->files, path);
1298         free(path);
1299         if (!f)
1300                 return 0;
1301
1302         hashmap_remove(j->files, f->path);
1303
1304         log_debug("File %s got removed.", f->path);
1305
1306         if (j->current_file == f) {
1307                 j->current_file = NULL;
1308                 j->current_field = 0;
1309         }
1310
1311         if (j->unique_file == f) {
1312                 j->unique_file = NULL;
1313                 j->unique_offset = 0;
1314         }
1315
1316         journal_file_close(f);
1317
1318         j->current_invalidate_counter ++;
1319
1320         return 0;
1321 }
1322
1323 static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1324         char _cleanup_free_ *path = NULL;
1325         int r;
1326         DIR _cleanup_closedir_ *d = NULL;
1327         sd_id128_t id, mid;
1328         Directory *m;
1329
1330         assert(j);
1331         assert(prefix);
1332         assert(dirname);
1333
1334         log_debug("Considering %s/%s.", prefix, dirname);
1335
1336         if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1337             (sd_id128_from_string(dirname, &id) < 0 ||
1338              sd_id128_get_machine(&mid) < 0 ||
1339              !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
1340             return 0;
1341
1342         path = strjoin(prefix, "/", dirname, NULL);
1343         if (!path)
1344                 return -ENOMEM;
1345
1346         d = opendir(path);
1347         if (!d) {
1348                 log_debug("Failed to open %s: %m", path);
1349                 if (errno == ENOENT)
1350                         return 0;
1351                 return -errno;
1352         }
1353
1354         m = hashmap_get(j->directories_by_path, path);
1355         if (!m) {
1356                 m = new0(Directory, 1);
1357                 if (!m)
1358                         return -ENOMEM;
1359
1360                 m->is_root = false;
1361                 m->path = path;
1362
1363                 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1364                         free(m);
1365                         return -ENOMEM;
1366                 }
1367
1368                 path = NULL; /* avoid freeing in cleanup */
1369                 j->current_invalidate_counter ++;
1370
1371                 log_debug("Directory %s got added.", m->path);
1372
1373         } else if (m->is_root)
1374                 return 0;
1375
1376         if (m->wd <= 0 && j->inotify_fd >= 0) {
1377
1378                 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1379                                           IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1380                                           IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
1381                                           IN_ONLYDIR);
1382
1383                 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1384                         inotify_rm_watch(j->inotify_fd, m->wd);
1385         }
1386
1387         for (;;) {
1388                 struct dirent *de;
1389                 union dirent_storage buf;
1390
1391                 r = readdir_r(d, &buf.de, &de);
1392                 if (r != 0 || !de)
1393                         break;
1394
1395                 if (dirent_is_file_with_suffix(de, ".journal") ||
1396                     dirent_is_file_with_suffix(de, ".journal~")) {
1397                         r = add_file(j, m->path, de->d_name);
1398                         if (r < 0) {
1399                                 log_debug("Failed to add file %s/%s: %s",
1400                                           m->path, de->d_name, strerror(-r));
1401                                 r = set_put_error(j, r);
1402                                 if (r < 0)
1403                                         return r;
1404                         }
1405                 }
1406         }
1407
1408         check_network(j, dirfd(d));
1409
1410         return 0;
1411 }
1412
1413 static int add_root_directory(sd_journal *j, const char *p) {
1414         DIR _cleanup_closedir_ *d = NULL;
1415         Directory *m;
1416         int r;
1417
1418         assert(j);
1419         assert(p);
1420
1421         if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1422             !path_startswith(p, "/run"))
1423                 return -EINVAL;
1424
1425         d = opendir(p);
1426         if (!d)
1427                 return -errno;
1428
1429         m = hashmap_get(j->directories_by_path, p);
1430         if (!m) {
1431                 m = new0(Directory, 1);
1432                 if (!m)
1433                         return -ENOMEM;
1434
1435                 m->is_root = true;
1436                 m->path = strdup(p);
1437                 if (!m->path) {
1438                         free(m);
1439                         return -ENOMEM;
1440                 }
1441
1442                 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1443                         free(m->path);
1444                         free(m);
1445                         return -ENOMEM;
1446                 }
1447
1448                 j->current_invalidate_counter ++;
1449
1450                 log_debug("Root directory %s got added.", m->path);
1451
1452         } else if (!m->is_root)
1453                 return 0;
1454
1455         if (m->wd <= 0 && j->inotify_fd >= 0) {
1456
1457                 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1458                                           IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1459                                           IN_ONLYDIR);
1460
1461                 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1462                         inotify_rm_watch(j->inotify_fd, m->wd);
1463         }
1464
1465         for (;;) {
1466                 struct dirent *de;
1467                 union dirent_storage buf;
1468                 sd_id128_t id;
1469
1470                 r = readdir_r(d, &buf.de, &de);
1471                 if (r != 0 || !de)
1472                         break;
1473
1474                 if (dirent_is_file_with_suffix(de, ".journal") ||
1475                     dirent_is_file_with_suffix(de, ".journal~")) {
1476                         r = add_file(j, m->path, de->d_name);
1477                         if (r < 0) {
1478                                 log_debug("Failed to add file %s/%s: %s",
1479                                           m->path, de->d_name, strerror(-r));
1480                                 r = set_put_error(j, r);
1481                                 if (r < 0)
1482                                         return r;
1483                         }
1484                 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
1485                            sd_id128_from_string(de->d_name, &id) >= 0) {
1486
1487                         r = add_directory(j, m->path, de->d_name);
1488                         if (r < 0)
1489                                 log_debug("Failed to add directory %s/%s: %s", m->path, de->d_name, strerror(-r));
1490                 }
1491         }
1492
1493         check_network(j, dirfd(d));
1494
1495         return 0;
1496 }
1497
1498 static int remove_directory(sd_journal *j, Directory *d) {
1499         assert(j);
1500
1501         if (d->wd > 0) {
1502                 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1503
1504                 if (j->inotify_fd >= 0)
1505                         inotify_rm_watch(j->inotify_fd, d->wd);
1506         }
1507
1508         hashmap_remove(j->directories_by_path, d->path);
1509
1510         if (d->is_root)
1511                 log_debug("Root directory %s got removed.", d->path);
1512         else
1513                 log_debug("Directory %s got removed.", d->path);
1514
1515         free(d->path);
1516         free(d);
1517
1518         return 0;
1519 }
1520
1521 static int add_search_paths(sd_journal *j) {
1522         int r;
1523         const char search_paths[] =
1524                 "/run/log/journal\0"
1525                 "/var/log/journal\0";
1526         const char *p;
1527
1528         assert(j);
1529
1530         /* We ignore most errors here, since the idea is to only open
1531          * what's actually accessible, and ignore the rest. */
1532
1533         NULSTR_FOREACH(p, search_paths) {
1534                 r = add_root_directory(j, p);
1535                 if (r < 0 && r != -ENOENT) {
1536                         r = set_put_error(j, r);
1537                         if (r < 0)
1538                                 return r;
1539                 }
1540         }
1541
1542         return 0;
1543 }
1544
1545 static int allocate_inotify(sd_journal *j) {
1546         assert(j);
1547
1548         if (j->inotify_fd < 0) {
1549                 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1550                 if (j->inotify_fd < 0)
1551                         return -errno;
1552         }
1553
1554         if (!j->directories_by_wd) {
1555                 j->directories_by_wd = hashmap_new(trivial_hash_func, trivial_compare_func);
1556                 if (!j->directories_by_wd)
1557                         return -ENOMEM;
1558         }
1559
1560         return 0;
1561 }
1562
1563 static sd_journal *journal_new(int flags, const char *path) {
1564         sd_journal *j;
1565
1566         j = new0(sd_journal, 1);
1567         if (!j)
1568                 return NULL;
1569
1570         j->inotify_fd = -1;
1571         j->flags = flags;
1572         j->data_threshold = DEFAULT_DATA_THRESHOLD;
1573
1574         if (path) {
1575                 j->path = strdup(path);
1576                 if (!j->path)
1577                         goto fail;
1578         }
1579
1580         j->files = hashmap_new(string_hash_func, string_compare_func);
1581         j->directories_by_path = hashmap_new(string_hash_func, string_compare_func);
1582         j->mmap = mmap_cache_new();
1583         if (!j->files || !j->directories_by_path || !j->mmap)
1584                 goto fail;
1585
1586         return j;
1587
1588 fail:
1589         sd_journal_close(j);
1590         return NULL;
1591 }
1592
1593 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1594         sd_journal *j;
1595         int r;
1596
1597         if (!ret)
1598                 return -EINVAL;
1599
1600         if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
1601                       SD_JOURNAL_RUNTIME_ONLY|
1602                       SD_JOURNAL_SYSTEM_ONLY))
1603                 return -EINVAL;
1604
1605         j = journal_new(flags, NULL);
1606         if (!j)
1607                 return -ENOMEM;
1608
1609         r = add_search_paths(j);
1610         if (r < 0)
1611                 goto fail;
1612
1613         *ret = j;
1614         return 0;
1615
1616 fail:
1617         sd_journal_close(j);
1618
1619         return r;
1620 }
1621
1622 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1623         sd_journal *j;
1624         int r;
1625
1626         if (!ret)
1627                 return -EINVAL;
1628
1629         if (!path)
1630                 return -EINVAL;
1631
1632         if (flags != 0)
1633                 return -EINVAL;
1634
1635         j = journal_new(flags, path);
1636         if (!j)
1637                 return -ENOMEM;
1638
1639         r = add_root_directory(j, path);
1640         if (r < 0) {
1641                 set_put_error(j, r);
1642                 goto fail;
1643         }
1644
1645         *ret = j;
1646         return 0;
1647
1648 fail:
1649         sd_journal_close(j);
1650
1651         return r;
1652 }
1653
1654 _public_ void sd_journal_close(sd_journal *j) {
1655         Directory *d;
1656         JournalFile *f;
1657
1658         if (!j)
1659                 return;
1660
1661         sd_journal_flush_matches(j);
1662
1663         while ((f = hashmap_steal_first(j->files)))
1664                 journal_file_close(f);
1665
1666         hashmap_free(j->files);
1667
1668         while ((d = hashmap_first(j->directories_by_path)))
1669                 remove_directory(j, d);
1670
1671         while ((d = hashmap_first(j->directories_by_wd)))
1672                 remove_directory(j, d);
1673
1674         hashmap_free(j->directories_by_path);
1675         hashmap_free(j->directories_by_wd);
1676
1677         if (j->inotify_fd >= 0)
1678                 close_nointr_nofail(j->inotify_fd);
1679
1680         if (j->mmap)
1681                 mmap_cache_unref(j->mmap);
1682
1683         free(j->path);
1684         free(j->unique_field);
1685         set_free(j->errors);
1686         free(j);
1687 }
1688
1689 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1690         Object *o;
1691         JournalFile *f;
1692         int r;
1693
1694         if (!j)
1695                 return -EINVAL;
1696         if (!ret)
1697                 return -EINVAL;
1698
1699         f = j->current_file;
1700         if (!f)
1701                 return -EADDRNOTAVAIL;
1702
1703         if (f->current_offset <= 0)
1704                 return -EADDRNOTAVAIL;
1705
1706         r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1707         if (r < 0)
1708                 return r;
1709
1710         *ret = le64toh(o->entry.realtime);
1711         return 0;
1712 }
1713
1714 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1715         Object *o;
1716         JournalFile *f;
1717         int r;
1718         sd_id128_t id;
1719
1720         if (!j)
1721                 return -EINVAL;
1722
1723         f = j->current_file;
1724         if (!f)
1725                 return -EADDRNOTAVAIL;
1726
1727         if (f->current_offset <= 0)
1728                 return -EADDRNOTAVAIL;
1729
1730         r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1731         if (r < 0)
1732                 return r;
1733
1734         if (ret_boot_id)
1735                 *ret_boot_id = o->entry.boot_id;
1736         else {
1737                 r = sd_id128_get_boot(&id);
1738                 if (r < 0)
1739                         return r;
1740
1741                 if (!sd_id128_equal(id, o->entry.boot_id))
1742                         return -ESTALE;
1743         }
1744
1745         if (ret)
1746                 *ret = le64toh(o->entry.monotonic);
1747
1748         return 0;
1749 }
1750
1751 static bool field_is_valid(const char *field) {
1752         const char *p;
1753
1754         assert(field);
1755
1756         if (isempty(field))
1757                 return false;
1758
1759         if (startswith(field, "__"))
1760                 return false;
1761
1762         for (p = field; *p; p++) {
1763
1764                 if (*p == '_')
1765                         continue;
1766
1767                 if (*p >= 'A' && *p <= 'Z')
1768                         continue;
1769
1770                 if (*p >= '0' && *p <= '9')
1771                         continue;
1772
1773                 return false;
1774         }
1775
1776         return true;
1777 }
1778
1779 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1780         JournalFile *f;
1781         uint64_t i, n;
1782         size_t field_length;
1783         int r;
1784         Object *o;
1785
1786         if (!j)
1787                 return -EINVAL;
1788         if (!field)
1789                 return -EINVAL;
1790         if (!data)
1791                 return -EINVAL;
1792         if (!size)
1793                 return -EINVAL;
1794
1795         if (!field_is_valid(field))
1796                 return -EINVAL;
1797
1798         f = j->current_file;
1799         if (!f)
1800                 return -EADDRNOTAVAIL;
1801
1802         if (f->current_offset <= 0)
1803                 return -EADDRNOTAVAIL;
1804
1805         r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1806         if (r < 0)
1807                 return r;
1808
1809         field_length = strlen(field);
1810
1811         n = journal_file_entry_n_items(o);
1812         for (i = 0; i < n; i++) {
1813                 uint64_t p, l;
1814                 le64_t le_hash;
1815                 size_t t;
1816
1817                 p = le64toh(o->entry.items[i].object_offset);
1818                 le_hash = o->entry.items[i].hash;
1819                 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1820                 if (r < 0)
1821                         return r;
1822
1823                 if (le_hash != o->data.hash)
1824                         return -EBADMSG;
1825
1826                 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1827
1828                 if (o->object.flags & OBJECT_COMPRESSED) {
1829
1830 #ifdef HAVE_XZ
1831                         if (uncompress_startswith(o->data.payload, l,
1832                                                   &f->compress_buffer, &f->compress_buffer_size,
1833                                                   field, field_length, '=')) {
1834
1835                                 uint64_t rsize;
1836
1837                                 if (!uncompress_blob(o->data.payload, l,
1838                                                      &f->compress_buffer, &f->compress_buffer_size, &rsize,
1839                                                      j->data_threshold))
1840                                         return -EBADMSG;
1841
1842                                 *data = f->compress_buffer;
1843                                 *size = (size_t) rsize;
1844
1845                                 return 0;
1846                         }
1847 #else
1848                         return -EPROTONOSUPPORT;
1849 #endif
1850
1851                 } else if (l >= field_length+1 &&
1852                            memcmp(o->data.payload, field, field_length) == 0 &&
1853                            o->data.payload[field_length] == '=') {
1854
1855                         t = (size_t) l;
1856
1857                         if ((uint64_t) t != l)
1858                                 return -E2BIG;
1859
1860                         *data = o->data.payload;
1861                         *size = t;
1862
1863                         return 0;
1864                 }
1865
1866                 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1867                 if (r < 0)
1868                         return r;
1869         }
1870
1871         return -ENOENT;
1872 }
1873
1874 static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
1875         size_t t;
1876         uint64_t l;
1877
1878         l = le64toh(o->object.size) - offsetof(Object, data.payload);
1879         t = (size_t) l;
1880
1881         /* We can't read objects larger than 4G on a 32bit machine */
1882         if ((uint64_t) t != l)
1883                 return -E2BIG;
1884
1885         if (o->object.flags & OBJECT_COMPRESSED) {
1886 #ifdef HAVE_XZ
1887                 uint64_t rsize;
1888
1889                 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize, j->data_threshold))
1890                         return -EBADMSG;
1891
1892                 *data = f->compress_buffer;
1893                 *size = (size_t) rsize;
1894 #else
1895                 return -EPROTONOSUPPORT;
1896 #endif
1897         } else {
1898                 *data = o->data.payload;
1899                 *size = t;
1900         }
1901
1902         return 0;
1903 }
1904
1905 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
1906         JournalFile *f;
1907         uint64_t p, n;
1908         le64_t le_hash;
1909         int r;
1910         Object *o;
1911
1912         if (!j)
1913                 return -EINVAL;
1914         if (!data)
1915                 return -EINVAL;
1916         if (!size)
1917                 return -EINVAL;
1918
1919         f = j->current_file;
1920         if (!f)
1921                 return -EADDRNOTAVAIL;
1922
1923         if (f->current_offset <= 0)
1924                 return -EADDRNOTAVAIL;
1925
1926         r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1927         if (r < 0)
1928                 return r;
1929
1930         n = journal_file_entry_n_items(o);
1931         if (j->current_field >= n)
1932                 return 0;
1933
1934         p = le64toh(o->entry.items[j->current_field].object_offset);
1935         le_hash = o->entry.items[j->current_field].hash;
1936         r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1937         if (r < 0)
1938                 return r;
1939
1940         if (le_hash != o->data.hash)
1941                 return -EBADMSG;
1942
1943         r = return_data(j, f, o, data, size);
1944         if (r < 0)
1945                 return r;
1946
1947         j->current_field ++;
1948
1949         return 1;
1950 }
1951
1952 _public_ void sd_journal_restart_data(sd_journal *j) {
1953         if (!j)
1954                 return;
1955
1956         j->current_field = 0;
1957 }
1958
1959 _public_ int sd_journal_get_fd(sd_journal *j) {
1960         int r;
1961
1962         if (!j)
1963                 return -EINVAL;
1964
1965         if (j->inotify_fd >= 0)
1966                 return j->inotify_fd;
1967
1968         r = allocate_inotify(j);
1969         if (r < 0)
1970                 return r;
1971
1972         /* Iterate through all dirs again, to add them to the
1973          * inotify */
1974         if (j->path)
1975                 r = add_root_directory(j, j->path);
1976         else
1977                 r = add_search_paths(j);
1978         if (r < 0)
1979                 return r;
1980
1981         return j->inotify_fd;
1982 }
1983
1984 _public_ int sd_journal_get_events(sd_journal *j) {
1985         int fd;
1986
1987         if (!j)
1988                 return -EINVAL;
1989
1990         fd = sd_journal_get_fd(j);
1991         if (fd < 0)
1992                 return fd;
1993
1994         return POLLIN;
1995 }
1996
1997 _public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
1998         int fd;
1999
2000         if (!j)
2001                 return -EINVAL;
2002         if (!timeout_usec)
2003                 return -EINVAL;
2004
2005         fd = sd_journal_get_fd(j);
2006         if (fd < 0)
2007                 return fd;
2008
2009         if (!j->on_network) {
2010                 *timeout_usec = (uint64_t) -1;
2011                 return 0;
2012         }
2013
2014         /* If we are on the network we need to regularly check for
2015          * changes manually */
2016
2017         *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2018         return 1;
2019 }
2020
2021 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
2022         Directory *d;
2023         int r;
2024
2025         assert(j);
2026         assert(e);
2027
2028         /* Is this a subdirectory we watch? */
2029         d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2030         if (d) {
2031                 sd_id128_t id;
2032
2033                 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2034                     (endswith(e->name, ".journal") ||
2035                      endswith(e->name, ".journal~"))) {
2036
2037                         /* Event for a journal file */
2038
2039                         if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2040                                 r = add_file(j, d->path, e->name);
2041                                 if (r < 0) {
2042                                         log_debug("Failed to add file %s/%s: %s",
2043                                                   d->path, e->name, strerror(-r));
2044                                         set_put_error(j, r);
2045                                 }
2046
2047                         } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
2048
2049                                 r = remove_file(j, d->path, e->name);
2050                                 if (r < 0)
2051                                         log_debug("Failed to remove file %s/%s: %s", d->path, e->name, strerror(-r));
2052                         }
2053
2054                 } else if (!d->is_root && e->len == 0) {
2055
2056                         /* Event for a subdirectory */
2057
2058                         if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
2059                                 r = remove_directory(j, d);
2060                                 if (r < 0)
2061                                         log_debug("Failed to remove directory %s: %s", d->path, strerror(-r));
2062                         }
2063
2064
2065                 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
2066
2067                         /* Event for root directory */
2068
2069                         if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2070                                 r = add_directory(j, d->path, e->name);
2071                                 if (r < 0)
2072                                         log_debug("Failed to add directory %s/%s: %s", d->path, e->name, strerror(-r));
2073                         }
2074                 }
2075
2076                 return;
2077         }
2078
2079         if (e->mask & IN_IGNORED)
2080                 return;
2081
2082         log_warning("Unknown inotify event.");
2083 }
2084
2085 static int determine_change(sd_journal *j) {
2086         bool b;
2087
2088         assert(j);
2089
2090         b = j->current_invalidate_counter != j->last_invalidate_counter;
2091         j->last_invalidate_counter = j->current_invalidate_counter;
2092
2093         return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2094 }
2095
2096 _public_ int sd_journal_process(sd_journal *j) {
2097         uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX] _alignas_(struct inotify_event);
2098         bool got_something = false;
2099
2100         if (!j)
2101                 return -EINVAL;
2102
2103         j->last_process_usec = now(CLOCK_MONOTONIC);
2104
2105         for (;;) {
2106                 struct inotify_event *e;
2107                 ssize_t l;
2108
2109                 l = read(j->inotify_fd, buffer, sizeof(buffer));
2110                 if (l < 0) {
2111                         if (errno == EAGAIN || errno == EINTR)
2112                                 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
2113
2114                         return -errno;
2115                 }
2116
2117                 got_something = true;
2118
2119                 e = (struct inotify_event*) buffer;
2120                 while (l > 0) {
2121                         size_t step;
2122
2123                         process_inotify_event(j, e);
2124
2125                         step = sizeof(struct inotify_event) + e->len;
2126                         assert(step <= (size_t) l);
2127
2128                         e = (struct inotify_event*) ((uint8_t*) e + step);
2129                         l -= step;
2130                 }
2131         }
2132
2133         return determine_change(j);
2134 }
2135
2136 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
2137         int r;
2138         uint64_t t;
2139
2140         assert(j);
2141
2142         if (j->inotify_fd < 0) {
2143
2144                 /* This is the first invocation, hence create the
2145                  * inotify watch */
2146                 r = sd_journal_get_fd(j);
2147                 if (r < 0)
2148                         return r;
2149
2150                 /* The journal might have changed since the context
2151                  * object was created and we weren't watching before,
2152                  * hence don't wait for anything, and return
2153                  * immediately. */
2154                 return determine_change(j);
2155         }
2156
2157         r = sd_journal_get_timeout(j, &t);
2158         if (r < 0)
2159                 return r;
2160
2161         if (t != (uint64_t) -1) {
2162                 usec_t n;
2163
2164                 n = now(CLOCK_MONOTONIC);
2165                 t = t > n ? t - n : 0;
2166
2167                 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2168                         timeout_usec = t;
2169         }
2170
2171         do {
2172                 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2173         } while (r == -EINTR);
2174
2175         if (r < 0)
2176                 return r;
2177
2178         return sd_journal_process(j);
2179 }
2180
2181 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2182         Iterator i;
2183         JournalFile *f;
2184         bool first = true;
2185         int r;
2186
2187         if (!j)
2188                 return -EINVAL;
2189         if (!from && !to)
2190                 return -EINVAL;
2191
2192         HASHMAP_FOREACH(f, j->files, i) {
2193                 usec_t fr, t;
2194
2195                 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
2196                 if (r == -ENOENT)
2197                         continue;
2198                 if (r < 0)
2199                         return r;
2200                 if (r == 0)
2201                         continue;
2202
2203                 if (first) {
2204                         if (from)
2205                                 *from = fr;
2206                         if (to)
2207                                 *to = t;
2208                         first = false;
2209                 } else {
2210                         if (from)
2211                                 *from = MIN(fr, *from);
2212                         if (to)
2213                                 *to = MAX(t, *to);
2214                 }
2215         }
2216
2217         return first ? 0 : 1;
2218 }
2219
2220 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2221         Iterator i;
2222         JournalFile *f;
2223         bool first = true;
2224         int r;
2225
2226         if (!j)
2227                 return -EINVAL;
2228         if (!from && !to)
2229                 return -EINVAL;
2230
2231         HASHMAP_FOREACH(f, j->files, i) {
2232                 usec_t fr, t;
2233
2234                 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
2235                 if (r == -ENOENT)
2236                         continue;
2237                 if (r < 0)
2238                         return r;
2239                 if (r == 0)
2240                         continue;
2241
2242                 if (first) {
2243                         if (from)
2244                                 *from = fr;
2245                         if (to)
2246                                 *to = t;
2247                         first = false;
2248                 } else {
2249                         if (from)
2250                                 *from = MIN(fr, *from);
2251                         if (to)
2252                                 *to = MAX(t, *to);
2253                 }
2254         }
2255
2256         return first ? 0 : 1;
2257 }
2258
2259 void journal_print_header(sd_journal *j) {
2260         Iterator i;
2261         JournalFile *f;
2262         bool newline = false;
2263
2264         assert(j);
2265
2266         HASHMAP_FOREACH(f, j->files, i) {
2267                 if (newline)
2268                         putchar('\n');
2269                 else
2270                         newline = true;
2271
2272                 journal_file_print_header(f);
2273         }
2274 }
2275
2276 _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2277         Iterator i;
2278         JournalFile *f;
2279         uint64_t sum = 0;
2280
2281         if (!j)
2282                 return -EINVAL;
2283         if (!bytes)
2284                 return -EINVAL;
2285
2286         HASHMAP_FOREACH(f, j->files, i) {
2287                 struct stat st;
2288
2289                 if (fstat(f->fd, &st) < 0)
2290                         return -errno;
2291
2292                 sum += (uint64_t) st.st_blocks * 512ULL;
2293         }
2294
2295         *bytes = sum;
2296         return 0;
2297 }
2298
2299 _public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2300         char *f;
2301
2302         if (!j)
2303                 return -EINVAL;
2304         if (isempty(field))
2305                 return -EINVAL;
2306         if (!field_is_valid(field))
2307                 return -EINVAL;
2308
2309         f = strdup(field);
2310         if (!f)
2311                 return -ENOMEM;
2312
2313         free(j->unique_field);
2314         j->unique_field = f;
2315         j->unique_file = NULL;
2316         j->unique_offset = 0;
2317
2318         return 0;
2319 }
2320
2321 _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
2322         Object *o;
2323         size_t k;
2324         int r;
2325
2326         if (!j)
2327                 return -EINVAL;
2328         if (!data)
2329                 return -EINVAL;
2330         if (!l)
2331                 return -EINVAL;
2332         if (!j->unique_field)
2333                 return -EINVAL;
2334
2335         k = strlen(j->unique_field);
2336
2337         if (!j->unique_file) {
2338                 j->unique_file = hashmap_first(j->files);
2339                 if (!j->unique_file)
2340                         return 0;
2341                 j->unique_offset = 0;
2342         }
2343
2344         for (;;) {
2345                 JournalFile *of;
2346                 Iterator i;
2347                 const void *odata;
2348                 size_t ol;
2349                 bool found;
2350
2351                 /* Proceed to next data object in the field's linked list */
2352                 if (j->unique_offset == 0) {
2353                         r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2354                         if (r < 0)
2355                                 return r;
2356
2357                         j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2358                 } else {
2359                         r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2360                         if (r < 0)
2361                                 return r;
2362
2363                         j->unique_offset = le64toh(o->data.next_field_offset);
2364                 }
2365
2366                 /* We reached the end of the list? Then start again, with the next file */
2367                 if (j->unique_offset == 0) {
2368                         JournalFile *n;
2369
2370                         n = hashmap_next(j->files, j->unique_file->path);
2371                         if (!n)
2372                                 return 0;
2373
2374                         j->unique_file = n;
2375                         continue;
2376                 }
2377
2378                 /* We do not use the type context here, but 0 instead,
2379                  * so that we can look at this data object at the same
2380                  * time as one on another file */
2381                 r = journal_file_move_to_object(j->unique_file, 0, j->unique_offset, &o);
2382                 if (r < 0)
2383                         return r;
2384
2385                 /* Let's do the type check by hand, since we used 0 context above. */
2386                 if (o->object.type != OBJECT_DATA)
2387                         return -EBADMSG;
2388
2389                 r = return_data(j, j->unique_file, o, &odata, &ol);
2390                 if (r < 0)
2391                         return r;
2392
2393                 /* OK, now let's see if we already returned this data
2394                  * object by checking if it exists in the earlier
2395                  * traversed files. */
2396                 found = false;
2397                 HASHMAP_FOREACH(of, j->files, i) {
2398                         Object *oo;
2399                         uint64_t op;
2400
2401                         if (of == j->unique_file)
2402                                 break;
2403
2404                         /* Skip this file it didn't have any fields
2405                          * indexed */
2406                         if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2407                             le64toh(of->header->n_fields) <= 0)
2408                                 continue;
2409
2410                         r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2411                         if (r < 0)
2412                                 return r;
2413
2414                         if (r > 0)
2415                                 found = true;
2416                 }
2417
2418                 if (found)
2419                         continue;
2420
2421                 r = return_data(j, j->unique_file, o, data, l);
2422                 if (r < 0)
2423                         return r;
2424
2425                 return 1;
2426         }
2427 }
2428
2429 _public_ void sd_journal_restart_unique(sd_journal *j) {
2430         if (!j)
2431                 return;
2432
2433         j->unique_file = NULL;
2434         j->unique_offset = 0;
2435 }
2436
2437 _public_ int sd_journal_reliable_fd(sd_journal *j) {
2438         if (!j)
2439                 return -EINVAL;
2440
2441         return !j->on_network;
2442 }
2443
2444 static char *lookup_field(const char *field, void *userdata) {
2445         sd_journal *j = userdata;
2446         const void *data;
2447         size_t size, d;
2448         int r;
2449
2450         assert(field);
2451         assert(j);
2452
2453         r = sd_journal_get_data(j, field, &data, &size);
2454         if (r < 0 ||
2455             size > REPLACE_VAR_MAX)
2456                 return strdup(field);
2457
2458         d = strlen(field) + 1;
2459
2460         return strndup((const char*) data + d, size - d);
2461 }
2462
2463 _public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
2464         const void *data;
2465         size_t size;
2466         sd_id128_t id;
2467         _cleanup_free_ char *text = NULL, *cid = NULL;
2468         char *t;
2469         int r;
2470
2471         if (!j)
2472                 return -EINVAL;
2473         if (!ret)
2474                 return -EINVAL;
2475
2476         r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
2477         if (r < 0)
2478                 return r;
2479
2480         cid = strndup((const char*) data + 11, size - 11);
2481         if (!cid)
2482                 return -ENOMEM;
2483
2484         r = sd_id128_from_string(cid, &id);
2485         if (r < 0)
2486                 return r;
2487
2488         r = catalog_get(CATALOG_DATABASE, id, &text);
2489         if (r < 0)
2490                 return r;
2491
2492         t = replace_var(text, lookup_field, j);
2493         if (!t)
2494                 return -ENOMEM;
2495
2496         *ret = t;
2497         return 0;
2498 }
2499
2500 _public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
2501         if (!ret)
2502                 return -EINVAL;
2503
2504         return catalog_get(CATALOG_DATABASE, id, ret);
2505 }
2506
2507 _public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
2508         if (!j)
2509                 return -EINVAL;
2510
2511         j->data_threshold = sz;
2512         return 0;
2513 }
2514
2515 _public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
2516         if (!j)
2517                 return -EINVAL;
2518         if (!sz)
2519                 return -EINVAL;
2520
2521         *sz = j->data_threshold;
2522         return 0;
2523 }