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