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