chiark / gitweb /
journal: make sure sd_journal_seek_cursor() seeks to the specified entry if it exists...
[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;p=%s",
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),
948                      path_get_file_name(j->current_file->path)) < 0)
949                 return -ENOMEM;
950
951         return 1;
952 }
953
954 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
955         char *w;
956         size_t l;
957         char *state;
958         unsigned long long seqnum, monotonic, realtime, xor_hash;
959         bool
960                 seqnum_id_set = false,
961                 seqnum_set = false,
962                 boot_id_set = false,
963                 monotonic_set = false,
964                 realtime_set = false,
965                 xor_hash_set = false;
966         sd_id128_t seqnum_id, boot_id;
967
968         if (!j)
969                 return -EINVAL;
970         if (!cursor)
971                 return -EINVAL;
972
973         FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
974                 char *item;
975                 int k = 0;
976
977                 if (l < 2 || w[1] != '=')
978                         return -EINVAL;
979
980                 item = strndup(w, l);
981                 if (!item)
982                         return -ENOMEM;
983
984                 switch (w[0]) {
985
986                 case 's':
987                         seqnum_id_set = true;
988                         k = sd_id128_from_string(item+2, &seqnum_id);
989                         break;
990
991                 case 'i':
992                         seqnum_set = true;
993                         if (sscanf(item+2, "%llx", &seqnum) != 1)
994                                 k = -EINVAL;
995                         break;
996
997                 case 'b':
998                         boot_id_set = true;
999                         k = sd_id128_from_string(item+2, &boot_id);
1000                         break;
1001
1002                 case 'm':
1003                         monotonic_set = true;
1004                         if (sscanf(item+2, "%llx", &monotonic) != 1)
1005                                 k = -EINVAL;
1006                         break;
1007
1008                 case 't':
1009                         realtime_set = true;
1010                         if (sscanf(item+2, "%llx", &realtime) != 1)
1011                                 k = -EINVAL;
1012                         break;
1013
1014                 case 'x':
1015                         xor_hash_set = true;
1016                         if (sscanf(item+2, "%llx", &xor_hash) != 1)
1017                                 k = -EINVAL;
1018                         break;
1019                 }
1020
1021                 free(item);
1022
1023                 if (k < 0)
1024                         return k;
1025         }
1026
1027         if ((!seqnum_set || !seqnum_id_set) &&
1028             (!monotonic_set || !boot_id_set) &&
1029             !realtime_set)
1030                 return -EINVAL;
1031
1032         reset_location(j);
1033
1034         j->current_location.type = LOCATION_SEEK;
1035
1036         if (realtime_set) {
1037                 j->current_location.realtime = (uint64_t) realtime;
1038                 j->current_location.realtime_set = true;
1039         }
1040
1041         if (seqnum_set && seqnum_id_set) {
1042                 j->current_location.seqnum = (uint64_t) seqnum;
1043                 j->current_location.seqnum_id = seqnum_id;
1044                 j->current_location.seqnum_set = true;
1045         }
1046
1047         if (monotonic_set && boot_id_set) {
1048                 j->current_location.monotonic = (uint64_t) monotonic;
1049                 j->current_location.boot_id = boot_id;
1050                 j->current_location.monotonic_set = true;
1051         }
1052
1053         if (xor_hash_set) {
1054                 j->current_location.xor_hash = (uint64_t) xor_hash;
1055                 j->current_location.xor_hash_set = true;
1056         }
1057
1058         return 0;
1059 }
1060
1061 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1062         if (!j)
1063                 return -EINVAL;
1064
1065         reset_location(j);
1066         j->current_location.type = LOCATION_SEEK;
1067         j->current_location.boot_id = boot_id;
1068         j->current_location.monotonic = usec;
1069         j->current_location.monotonic_set = true;
1070
1071         return 0;
1072 }
1073
1074 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1075         if (!j)
1076                 return -EINVAL;
1077
1078         reset_location(j);
1079         j->current_location.type = LOCATION_SEEK;
1080         j->current_location.realtime = usec;
1081         j->current_location.realtime_set = true;
1082
1083         return 0;
1084 }
1085
1086 _public_ int sd_journal_seek_head(sd_journal *j) {
1087         if (!j)
1088                 return -EINVAL;
1089
1090         reset_location(j);
1091         j->current_location.type = LOCATION_HEAD;
1092
1093         return 0;
1094 }
1095
1096 _public_ int sd_journal_seek_tail(sd_journal *j) {
1097         if (!j)
1098                 return -EINVAL;
1099
1100         reset_location(j);
1101         j->current_location.type = LOCATION_TAIL;
1102
1103         return 0;
1104 }
1105
1106 static int add_file(sd_journal *j, const char *prefix, const char *filename) {
1107         char *path;
1108         int r;
1109         JournalFile *f;
1110
1111         assert(j);
1112         assert(prefix);
1113         assert(filename);
1114
1115         if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) &&
1116             !(streq(filename, "system.journal") ||
1117               streq(filename, "system.journal~") ||
1118               (startswith(filename, "system@") &&
1119                (endswith(filename, ".journal") || endswith(filename, ".journal~")))))
1120                 return 0;
1121
1122         path = strjoin(prefix, "/", filename, NULL);
1123         if (!path)
1124                 return -ENOMEM;
1125
1126         if (hashmap_get(j->files, path)) {
1127                 free(path);
1128                 return 0;
1129         }
1130
1131         if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
1132                 log_debug("Too many open journal files, not adding %s, ignoring.", path);
1133                 free(path);
1134                 return 0;
1135         }
1136
1137         r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
1138         free(path);
1139
1140         if (r < 0) {
1141                 if (errno == ENOENT)
1142                         return 0;
1143
1144                 return r;
1145         }
1146
1147         /* journal_file_dump(f); */
1148
1149         r = hashmap_put(j->files, f->path, f);
1150         if (r < 0) {
1151                 journal_file_close(f);
1152                 return r;
1153         }
1154
1155         j->current_invalidate_counter ++;
1156
1157         log_debug("File %s got added.", f->path);
1158
1159         return 0;
1160 }
1161
1162 static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
1163         char *path;
1164         JournalFile *f;
1165
1166         assert(j);
1167         assert(prefix);
1168         assert(filename);
1169
1170         path = strjoin(prefix, "/", filename, NULL);
1171         if (!path)
1172                 return -ENOMEM;
1173
1174         f = hashmap_get(j->files, path);
1175         free(path);
1176         if (!f)
1177                 return 0;
1178
1179         hashmap_remove(j->files, f->path);
1180         journal_file_close(f);
1181
1182         j->current_invalidate_counter ++;
1183
1184         log_debug("File %s got removed.", f->path);
1185         return 0;
1186 }
1187
1188 static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1189         char *path;
1190         int r;
1191         DIR *d;
1192         sd_id128_t id, mid;
1193         Directory *m;
1194
1195         assert(j);
1196         assert(prefix);
1197         assert(dirname);
1198
1199         if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1200             (sd_id128_from_string(dirname, &id) < 0 ||
1201              sd_id128_get_machine(&mid) < 0 ||
1202              !sd_id128_equal(id, mid)))
1203             return 0;
1204
1205         path = strjoin(prefix, "/", dirname, NULL);
1206         if (!path)
1207                 return -ENOMEM;
1208
1209         d = opendir(path);
1210         if (!d) {
1211                 log_debug("Failed to open %s: %m", path);
1212                 free(path);
1213
1214                 if (errno == ENOENT)
1215                         return 0;
1216                 return -errno;
1217         }
1218
1219         m = hashmap_get(j->directories_by_path, path);
1220         if (!m) {
1221                 m = new0(Directory, 1);
1222                 if (!m) {
1223                         closedir(d);
1224                         free(path);
1225                         return -ENOMEM;
1226                 }
1227
1228                 m->is_root = false;
1229                 m->path = path;
1230
1231                 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1232                         closedir(d);
1233                         free(m->path);
1234                         free(m);
1235                         return -ENOMEM;
1236                 }
1237
1238                 j->current_invalidate_counter ++;
1239
1240                 log_debug("Directory %s got added.", m->path);
1241
1242         } else if (m->is_root) {
1243                 free (path);
1244                 closedir(d);
1245                 return 0;
1246         }  else
1247                 free(path);
1248
1249         if (m->wd <= 0 && j->inotify_fd >= 0) {
1250
1251                 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1252                                           IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1253                                           IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|
1254                                           IN_ONLYDIR);
1255
1256                 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1257                         inotify_rm_watch(j->inotify_fd, m->wd);
1258         }
1259
1260         for (;;) {
1261                 struct dirent *de;
1262                 union dirent_storage buf;
1263
1264                 r = readdir_r(d, &buf.de, &de);
1265                 if (r != 0 || !de)
1266                         break;
1267
1268                 if (dirent_is_file_with_suffix(de, ".journal") ||
1269                     dirent_is_file_with_suffix(de, ".journal~")) {
1270                         r = add_file(j, m->path, de->d_name);
1271                         if (r < 0)
1272                                 log_debug("Failed to add file %s/%s: %s", m->path, de->d_name, strerror(-r));
1273                 }
1274         }
1275
1276         closedir(d);
1277
1278         return 0;
1279 }
1280
1281 static int add_root_directory(sd_journal *j, const char *p) {
1282         DIR *d;
1283         Directory *m;
1284         int r;
1285
1286         assert(j);
1287         assert(p);
1288
1289         if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1290             !path_startswith(p, "/run"))
1291                 return -EINVAL;
1292
1293         d = opendir(p);
1294         if (!d)
1295                 return -errno;
1296
1297         m = hashmap_get(j->directories_by_path, p);
1298         if (!m) {
1299                 m = new0(Directory, 1);
1300                 if (!m) {
1301                         closedir(d);
1302                         return -ENOMEM;
1303                 }
1304
1305                 m->is_root = true;
1306                 m->path = strdup(p);
1307                 if (!m->path) {
1308                         closedir(d);
1309                         free(m);
1310                         return -ENOMEM;
1311                 }
1312
1313                 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1314                         closedir(d);
1315                         free(m->path);
1316                         free(m);
1317                         return -ENOMEM;
1318                 }
1319
1320                 j->current_invalidate_counter ++;
1321
1322                 log_debug("Root directory %s got added.", m->path);
1323
1324         } else if (!m->is_root) {
1325                 closedir(d);
1326                 return 0;
1327         }
1328
1329         if (m->wd <= 0 && j->inotify_fd >= 0) {
1330
1331                 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1332                                           IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1333                                           IN_ONLYDIR);
1334
1335                 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1336                         inotify_rm_watch(j->inotify_fd, m->wd);
1337         }
1338
1339         for (;;) {
1340                 struct dirent *de;
1341                 union dirent_storage buf;
1342                 sd_id128_t id;
1343
1344                 r = readdir_r(d, &buf.de, &de);
1345                 if (r != 0 || !de)
1346                         break;
1347
1348                 if (dirent_is_file_with_suffix(de, ".journal") ||
1349                     dirent_is_file_with_suffix(de, ".journal~")) {
1350                         r = add_file(j, m->path, de->d_name);
1351                         if (r < 0)
1352                                 log_debug("Failed to add file %s/%s: %s", m->path, de->d_name, strerror(-r));
1353
1354                 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
1355                            sd_id128_from_string(de->d_name, &id) >= 0) {
1356
1357                         r = add_directory(j, m->path, de->d_name);
1358                         if (r < 0)
1359                                 log_debug("Failed to add directory %s/%s: %s", m->path, de->d_name, strerror(-r));
1360                 }
1361         }
1362
1363         closedir(d);
1364
1365         return 0;
1366 }
1367
1368 static int remove_directory(sd_journal *j, Directory *d) {
1369         assert(j);
1370
1371         if (d->wd > 0) {
1372                 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1373
1374                 if (j->inotify_fd >= 0)
1375                         inotify_rm_watch(j->inotify_fd, d->wd);
1376         }
1377
1378         hashmap_remove(j->directories_by_path, d->path);
1379
1380         if (d->is_root)
1381                 log_debug("Root directory %s got removed.", d->path);
1382         else
1383                 log_debug("Directory %s got removed.", d->path);
1384
1385         free(d->path);
1386         free(d);
1387
1388         return 0;
1389 }
1390
1391 static int add_search_paths(sd_journal *j) {
1392
1393         const char search_paths[] =
1394                 "/run/log/journal\0"
1395                 "/var/log/journal\0";
1396         const char *p;
1397
1398         assert(j);
1399
1400         /* We ignore most errors here, since the idea is to only open
1401          * what's actually accessible, and ignore the rest. */
1402
1403         NULSTR_FOREACH(p, search_paths)
1404                 add_root_directory(j, p);
1405
1406         return 0;
1407 }
1408
1409 static int allocate_inotify(sd_journal *j) {
1410         assert(j);
1411
1412         if (j->inotify_fd < 0) {
1413                 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1414                 if (j->inotify_fd < 0)
1415                         return -errno;
1416         }
1417
1418         if (!j->directories_by_wd) {
1419                 j->directories_by_wd = hashmap_new(trivial_hash_func, trivial_compare_func);
1420                 if (!j->directories_by_wd)
1421                         return -ENOMEM;
1422         }
1423
1424         return 0;
1425 }
1426
1427 static sd_journal *journal_new(int flags, const char *path) {
1428         sd_journal *j;
1429
1430         j = new0(sd_journal, 1);
1431         if (!j)
1432                 return NULL;
1433
1434         j->inotify_fd = -1;
1435         j->flags = flags;
1436
1437         if (path) {
1438                 j->path = strdup(path);
1439                 if (!j->path) {
1440                         free(j);
1441                         return NULL;
1442                 }
1443         }
1444
1445         j->files = hashmap_new(string_hash_func, string_compare_func);
1446         if (!j->files) {
1447                 free(j->path);
1448                 free(j);
1449                 return NULL;
1450         }
1451
1452         j->directories_by_path = hashmap_new(string_hash_func, string_compare_func);
1453         if (!j->directories_by_path) {
1454                 hashmap_free(j->files);
1455                 free(j->path);
1456                 free(j);
1457                 return NULL;
1458         }
1459
1460         j->mmap = mmap_cache_new();
1461         if (!j->mmap) {
1462                 hashmap_free(j->files);
1463                 hashmap_free(j->directories_by_path);
1464                 free(j->path);
1465                 free(j);
1466                 return NULL;
1467         }
1468
1469         return j;
1470 }
1471
1472 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1473         sd_journal *j;
1474         int r;
1475
1476         if (!ret)
1477                 return -EINVAL;
1478
1479         if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
1480                       SD_JOURNAL_RUNTIME_ONLY|
1481                       SD_JOURNAL_SYSTEM_ONLY))
1482                 return -EINVAL;
1483
1484         j = journal_new(flags, NULL);
1485         if (!j)
1486                 return -ENOMEM;
1487
1488         r = add_search_paths(j);
1489         if (r < 0)
1490                 goto fail;
1491
1492         *ret = j;
1493         return 0;
1494
1495 fail:
1496         sd_journal_close(j);
1497
1498         return r;
1499 }
1500
1501 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1502         sd_journal *j;
1503         int r;
1504
1505         if (!ret)
1506                 return -EINVAL;
1507
1508         if (!path || !path_is_absolute(path))
1509                 return -EINVAL;
1510
1511         if (flags != 0)
1512                 return -EINVAL;
1513
1514         j = journal_new(flags, path);
1515         if (!j)
1516                 return -ENOMEM;
1517
1518         r = add_root_directory(j, path);
1519         if (r < 0)
1520                 goto fail;
1521
1522         *ret = j;
1523         return 0;
1524
1525 fail:
1526         sd_journal_close(j);
1527
1528         return r;
1529 }
1530
1531 _public_ void sd_journal_close(sd_journal *j) {
1532         Directory *d;
1533         JournalFile *f;
1534
1535         if (!j)
1536                 return;
1537
1538         while ((f = hashmap_steal_first(j->files)))
1539                 journal_file_close(f);
1540
1541         hashmap_free(j->files);
1542
1543         while ((d = hashmap_first(j->directories_by_path)))
1544                 remove_directory(j, d);
1545
1546         while ((d = hashmap_first(j->directories_by_wd)))
1547                 remove_directory(j, d);
1548
1549         hashmap_free(j->directories_by_path);
1550         hashmap_free(j->directories_by_wd);
1551
1552         if (j->inotify_fd >= 0)
1553                 close_nointr_nofail(j->inotify_fd);
1554
1555         sd_journal_flush_matches(j);
1556
1557         if (j->mmap)
1558                 mmap_cache_unref(j->mmap);
1559
1560         free(j->path);
1561         free(j);
1562 }
1563
1564 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1565         Object *o;
1566         JournalFile *f;
1567         int r;
1568
1569         if (!j)
1570                 return -EINVAL;
1571         if (!ret)
1572                 return -EINVAL;
1573
1574         f = j->current_file;
1575         if (!f)
1576                 return -EADDRNOTAVAIL;
1577
1578         if (f->current_offset <= 0)
1579                 return -EADDRNOTAVAIL;
1580
1581         r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1582         if (r < 0)
1583                 return r;
1584
1585         *ret = le64toh(o->entry.realtime);
1586         return 0;
1587 }
1588
1589 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1590         Object *o;
1591         JournalFile *f;
1592         int r;
1593         sd_id128_t id;
1594
1595         if (!j)
1596                 return -EINVAL;
1597
1598         f = j->current_file;
1599         if (!f)
1600                 return -EADDRNOTAVAIL;
1601
1602         if (f->current_offset <= 0)
1603                 return -EADDRNOTAVAIL;
1604
1605         r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1606         if (r < 0)
1607                 return r;
1608
1609         if (ret_boot_id)
1610                 *ret_boot_id = o->entry.boot_id;
1611         else {
1612                 r = sd_id128_get_boot(&id);
1613                 if (r < 0)
1614                         return r;
1615
1616                 if (!sd_id128_equal(id, o->entry.boot_id))
1617                         return -ESTALE;
1618         }
1619
1620         if (ret)
1621                 *ret = le64toh(o->entry.monotonic);
1622
1623         return 0;
1624 }
1625
1626 static bool field_is_valid(const char *field) {
1627         const char *p;
1628
1629         assert(field);
1630
1631         if (isempty(field))
1632                 return false;
1633
1634         if (startswith(field, "__"))
1635                 return false;
1636
1637         for (p = field; *p; p++) {
1638
1639                 if (*p == '_')
1640                         continue;
1641
1642                 if (*p >= 'A' && *p <= 'Z')
1643                         continue;
1644
1645                 if (*p >= '0' && *p <= '9')
1646                         continue;
1647
1648                 return false;
1649         }
1650
1651         return true;
1652 }
1653
1654 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1655         JournalFile *f;
1656         uint64_t i, n;
1657         size_t field_length;
1658         int r;
1659         Object *o;
1660
1661         if (!j)
1662                 return -EINVAL;
1663         if (!field)
1664                 return -EINVAL;
1665         if (!data)
1666                 return -EINVAL;
1667         if (!size)
1668                 return -EINVAL;
1669
1670         if (!field_is_valid(field))
1671                 return -EINVAL;
1672
1673         f = j->current_file;
1674         if (!f)
1675                 return -EADDRNOTAVAIL;
1676
1677         if (f->current_offset <= 0)
1678                 return -EADDRNOTAVAIL;
1679
1680         r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1681         if (r < 0)
1682                 return r;
1683
1684         field_length = strlen(field);
1685
1686         n = journal_file_entry_n_items(o);
1687         for (i = 0; i < n; i++) {
1688                 uint64_t p, l;
1689                 le64_t le_hash;
1690                 size_t t;
1691
1692                 p = le64toh(o->entry.items[i].object_offset);
1693                 le_hash = o->entry.items[i].hash;
1694                 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1695                 if (r < 0)
1696                         return r;
1697
1698                 if (le_hash != o->data.hash)
1699                         return -EBADMSG;
1700
1701                 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1702
1703                 if (o->object.flags & OBJECT_COMPRESSED) {
1704
1705 #ifdef HAVE_XZ
1706                         if (uncompress_startswith(o->data.payload, l,
1707                                                   &f->compress_buffer, &f->compress_buffer_size,
1708                                                   field, field_length, '=')) {
1709
1710                                 uint64_t rsize;
1711
1712                                 if (!uncompress_blob(o->data.payload, l,
1713                                                      &f->compress_buffer, &f->compress_buffer_size, &rsize))
1714                                         return -EBADMSG;
1715
1716                                 *data = f->compress_buffer;
1717                                 *size = (size_t) rsize;
1718
1719                                 return 0;
1720                         }
1721 #else
1722                         return -EPROTONOSUPPORT;
1723 #endif
1724
1725                 } else if (l >= field_length+1 &&
1726                            memcmp(o->data.payload, field, field_length) == 0 &&
1727                            o->data.payload[field_length] == '=') {
1728
1729                         t = (size_t) l;
1730
1731                         if ((uint64_t) t != l)
1732                                 return -E2BIG;
1733
1734                         *data = o->data.payload;
1735                         *size = t;
1736
1737                         return 0;
1738                 }
1739
1740                 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1741                 if (r < 0)
1742                         return r;
1743         }
1744
1745         return -ENOENT;
1746 }
1747
1748 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
1749         JournalFile *f;
1750         uint64_t p, l, n;
1751         le64_t le_hash;
1752         int r;
1753         Object *o;
1754         size_t t;
1755
1756         if (!j)
1757                 return -EINVAL;
1758         if (!data)
1759                 return -EINVAL;
1760         if (!size)
1761                 return -EINVAL;
1762
1763         f = j->current_file;
1764         if (!f)
1765                 return -EADDRNOTAVAIL;
1766
1767         if (f->current_offset <= 0)
1768                 return -EADDRNOTAVAIL;
1769
1770         r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1771         if (r < 0)
1772                 return r;
1773
1774         n = journal_file_entry_n_items(o);
1775         if (j->current_field >= n)
1776                 return 0;
1777
1778         p = le64toh(o->entry.items[j->current_field].object_offset);
1779         le_hash = o->entry.items[j->current_field].hash;
1780         r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1781         if (r < 0)
1782                 return r;
1783
1784         if (le_hash != o->data.hash)
1785                 return -EBADMSG;
1786
1787         l = le64toh(o->object.size) - offsetof(Object, data.payload);
1788         t = (size_t) l;
1789
1790         /* We can't read objects larger than 4G on a 32bit machine */
1791         if ((uint64_t) t != l)
1792                 return -E2BIG;
1793
1794         if (o->object.flags & OBJECT_COMPRESSED) {
1795 #ifdef HAVE_XZ
1796                 uint64_t rsize;
1797
1798                 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize))
1799                         return -EBADMSG;
1800
1801                 *data = f->compress_buffer;
1802                 *size = (size_t) rsize;
1803 #else
1804                 return -EPROTONOSUPPORT;
1805 #endif
1806         } else {
1807                 *data = o->data.payload;
1808                 *size = t;
1809         }
1810
1811         j->current_field ++;
1812
1813         return 1;
1814 }
1815
1816 _public_ void sd_journal_restart_data(sd_journal *j) {
1817         if (!j)
1818                 return;
1819
1820         j->current_field = 0;
1821 }
1822
1823 _public_ int sd_journal_get_fd(sd_journal *j) {
1824         int r;
1825
1826         if (!j)
1827                 return -EINVAL;
1828
1829         if (j->inotify_fd >= 0)
1830                 return j->inotify_fd;
1831
1832         r = allocate_inotify(j);
1833         if (r < 0)
1834                 return r;
1835
1836         /* Iterate through all dirs again, to add them to the
1837          * inotify */
1838         if (j->path)
1839                 r = add_root_directory(j, j->path);
1840         else
1841                 r = add_search_paths(j);
1842         if (r < 0)
1843                 return r;
1844
1845         return j->inotify_fd;
1846 }
1847
1848 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
1849         Directory *d;
1850         int r;
1851
1852         assert(j);
1853         assert(e);
1854
1855         /* Is this a subdirectory we watch? */
1856         d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
1857         if (d) {
1858                 sd_id128_t id;
1859
1860                 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
1861                     (endswith(e->name, ".journal") ||
1862                      endswith(e->name, ".journal~"))) {
1863
1864                         /* Event for a journal file */
1865
1866                         if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1867                                 r = add_file(j, d->path, e->name);
1868                                 if (r < 0)
1869                                         log_debug("Failed to add file %s/%s: %s", d->path, e->name, strerror(-r));
1870
1871                         } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
1872
1873                                 r = remove_file(j, d->path, e->name);
1874                                 if (r < 0)
1875                                         log_debug("Failed to remove file %s/%s: %s", d->path, e->name, strerror(-r));
1876                         }
1877
1878                 } else if (!d->is_root && e->len == 0) {
1879
1880                         /* Event for a subdirectory */
1881
1882                         if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
1883                                 r = remove_directory(j, d);
1884                                 if (r < 0)
1885                                         log_debug("Failed to remove directory %s: %s", d->path, strerror(-r));
1886                         }
1887
1888
1889                 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
1890
1891                         /* Event for root directory */
1892
1893                         if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1894                                 r = add_directory(j, d->path, e->name);
1895                                 if (r < 0)
1896                                         log_debug("Failed to add directory %s/%s: %s", d->path, e->name, strerror(-r));
1897                         }
1898                 }
1899
1900                 return;
1901         }
1902
1903         if (e->mask & IN_IGNORED)
1904                 return;
1905
1906         log_warning("Unknown inotify event.");
1907 }
1908
1909 static int determine_change(sd_journal *j) {
1910         bool b;
1911
1912         assert(j);
1913
1914         b = j->current_invalidate_counter != j->last_invalidate_counter;
1915         j->last_invalidate_counter = j->current_invalidate_counter;
1916
1917         return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
1918 }
1919
1920 _public_ int sd_journal_process(sd_journal *j) {
1921         uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX] _alignas_(struct inotify_event);
1922         bool got_something = false;
1923
1924         if (!j)
1925                 return -EINVAL;
1926
1927         for (;;) {
1928                 struct inotify_event *e;
1929                 ssize_t l;
1930
1931                 l = read(j->inotify_fd, buffer, sizeof(buffer));
1932                 if (l < 0) {
1933                         if (errno == EAGAIN || errno == EINTR)
1934                                 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
1935
1936                         return -errno;
1937                 }
1938
1939                 got_something = true;
1940
1941                 e = (struct inotify_event*) buffer;
1942                 while (l > 0) {
1943                         size_t step;
1944
1945                         process_inotify_event(j, e);
1946
1947                         step = sizeof(struct inotify_event) + e->len;
1948                         assert(step <= (size_t) l);
1949
1950                         e = (struct inotify_event*) ((uint8_t*) e + step);
1951                         l -= step;
1952                 }
1953         }
1954
1955         return determine_change(j);
1956 }
1957
1958 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
1959         int r;
1960
1961         assert(j);
1962
1963         if (j->inotify_fd < 0) {
1964
1965                 /* This is the first invocation, hence create the
1966                  * inotify watch */
1967                 r = sd_journal_get_fd(j);
1968                 if (r < 0)
1969                         return r;
1970
1971                 /* The journal might have changed since the context
1972                  * object was created and we weren't watching before,
1973                  * hence don't wait for anything, and return
1974                  * immediately. */
1975                 return determine_change(j);
1976         }
1977
1978         do {
1979                 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
1980         } while (r == -EINTR);
1981
1982         if (r < 0)
1983                 return r;
1984
1985         return sd_journal_process(j);
1986 }
1987
1988 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
1989         Iterator i;
1990         JournalFile *f;
1991         bool first = true;
1992         int r;
1993
1994         if (!j)
1995                 return -EINVAL;
1996         if (!from && !to)
1997                 return -EINVAL;
1998
1999         HASHMAP_FOREACH(f, j->files, i) {
2000                 usec_t fr, t;
2001
2002                 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
2003                 if (r == -ENOENT)
2004                         continue;
2005                 if (r < 0)
2006                         return r;
2007                 if (r == 0)
2008                         continue;
2009
2010                 if (first) {
2011                         if (from)
2012                                 *from = fr;
2013                         if (to)
2014                                 *to = t;
2015                         first = false;
2016                 } else {
2017                         if (from)
2018                                 *from = MIN(fr, *from);
2019                         if (to)
2020                                 *to = MIN(t, *to);
2021                 }
2022         }
2023
2024         return first ? 0 : 1;
2025 }
2026
2027 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2028         Iterator i;
2029         JournalFile *f;
2030         bool first = true;
2031         int r;
2032
2033         if (!j)
2034                 return -EINVAL;
2035         if (!from && !to)
2036                 return -EINVAL;
2037
2038         HASHMAP_FOREACH(f, j->files, i) {
2039                 usec_t fr, t;
2040
2041                 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
2042                 if (r == -ENOENT)
2043                         continue;
2044                 if (r < 0)
2045                         return r;
2046                 if (r == 0)
2047                         continue;
2048
2049                 if (first) {
2050                         if (from)
2051                                 *from = fr;
2052                         if (to)
2053                                 *to = t;
2054                         first = false;
2055                 } else {
2056                         if (from)
2057                                 *from = MIN(fr, *from);
2058                         if (to)
2059                                 *to = MIN(t, *to);
2060                 }
2061         }
2062
2063         return first ? 0 : 1;
2064 }
2065
2066 void journal_print_header(sd_journal *j) {
2067         Iterator i;
2068         JournalFile *f;
2069         bool newline = false;
2070
2071         assert(j);
2072
2073         HASHMAP_FOREACH(f, j->files, i) {
2074                 if (newline)
2075                         putchar('\n');
2076                 else
2077                         newline = true;
2078
2079                 journal_file_print_header(f);
2080         }
2081 }
2082
2083 _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2084         Iterator i;
2085         JournalFile *f;
2086         uint64_t sum = 0;
2087
2088         if (!j)
2089                 return -EINVAL;
2090         if (!bytes)
2091                 return -EINVAL;
2092
2093         HASHMAP_FOREACH(f, j->files, i) {
2094                 struct stat st;
2095
2096                 if (fstat(f->fd, &st) < 0)
2097                         return -errno;
2098
2099                 sum += (uint64_t) st.st_blocks * 512ULL;
2100         }
2101
2102         *bytes = sum;
2103         return 0;
2104 }
2105
2106 /* _public_ int sd_journal_query_unique(sd_journal *j, const char *field) { */
2107 /*         if (!j) */
2108 /*                 return -EINVAL; */
2109 /*         if (!field) */
2110 /*                 return -EINVAL; */
2111
2112 /*         return -ENOTSUP; */
2113 /* } */
2114
2115 /* _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { */
2116 /*         if (!j) */
2117 /*                 return -EINVAL; */
2118 /*         if (!data) */
2119 /*                 return -EINVAL; */
2120 /*         if (!l) */
2121 /*                 return -EINVAL; */
2122
2123 /*         return -ENOTSUP; */
2124 /* } */
2125
2126 /* _public_ void sd_journal_restart_unique(sd_journal *j) { */
2127 /*         if (!j) */
2128 /*                 return; */
2129 /* } */