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