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