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