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