]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/sd-journal.c
41526b35bf8e485411095c7ba03ff9c962162f02
[thirdparty/systemd.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 /* One context for each type, plus the zeroth catchall
1443 * context. One fd for each file plus one for each type, which
1444 * is need when verifying things */
1445 j->mmap = mmap_cache_new(_OBJECT_TYPE_MAX, JOURNAL_FILES_MAX + _OBJECT_TYPE_MAX);
1446 if (!j->mmap) {
1447 hashmap_free(j->files);
1448 hashmap_free(j->directories_by_path);
1449 free(j->path);
1450 free(j);
1451 }
1452
1453 return j;
1454 }
1455
1456 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1457 sd_journal *j;
1458 int r;
1459
1460 if (!ret)
1461 return -EINVAL;
1462
1463 if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
1464 SD_JOURNAL_RUNTIME_ONLY|
1465 SD_JOURNAL_SYSTEM_ONLY))
1466 return -EINVAL;
1467
1468 j = journal_new(flags, NULL);
1469 if (!j)
1470 return -ENOMEM;
1471
1472 r = add_search_paths(j);
1473 if (r < 0)
1474 goto fail;
1475
1476 *ret = j;
1477 return 0;
1478
1479 fail:
1480 sd_journal_close(j);
1481
1482 return r;
1483 }
1484
1485 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1486 sd_journal *j;
1487 int r;
1488
1489 if (!ret)
1490 return -EINVAL;
1491
1492 if (!path || !path_is_absolute(path))
1493 return -EINVAL;
1494
1495 if (flags != 0)
1496 return -EINVAL;
1497
1498 j = journal_new(flags, path);
1499 if (!j)
1500 return -ENOMEM;
1501
1502 r = add_root_directory(j, path);
1503 if (r < 0)
1504 goto fail;
1505
1506 *ret = j;
1507 return 0;
1508
1509 fail:
1510 sd_journal_close(j);
1511
1512 return r;
1513 }
1514
1515 _public_ void sd_journal_close(sd_journal *j) {
1516 Directory *d;
1517 JournalFile *f;
1518
1519 if (!j)
1520 return;
1521
1522 while ((f = hashmap_steal_first(j->files)))
1523 journal_file_close(f);
1524
1525 hashmap_free(j->files);
1526
1527 while ((d = hashmap_first(j->directories_by_path)))
1528 remove_directory(j, d);
1529
1530 while ((d = hashmap_first(j->directories_by_wd)))
1531 remove_directory(j, d);
1532
1533 hashmap_free(j->directories_by_path);
1534 hashmap_free(j->directories_by_wd);
1535
1536 if (j->inotify_fd >= 0)
1537 close_nointr_nofail(j->inotify_fd);
1538
1539 sd_journal_flush_matches(j);
1540
1541 if (j->mmap)
1542 mmap_cache_unref(j->mmap);
1543
1544 free(j->path);
1545 free(j);
1546 }
1547
1548 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1549 Object *o;
1550 JournalFile *f;
1551 int r;
1552
1553 if (!j)
1554 return -EINVAL;
1555 if (!ret)
1556 return -EINVAL;
1557
1558 f = j->current_file;
1559 if (!f)
1560 return -EADDRNOTAVAIL;
1561
1562 if (f->current_offset <= 0)
1563 return -EADDRNOTAVAIL;
1564
1565 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1566 if (r < 0)
1567 return r;
1568
1569 *ret = le64toh(o->entry.realtime);
1570 return 0;
1571 }
1572
1573 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1574 Object *o;
1575 JournalFile *f;
1576 int r;
1577 sd_id128_t id;
1578
1579 if (!j)
1580 return -EINVAL;
1581
1582 f = j->current_file;
1583 if (!f)
1584 return -EADDRNOTAVAIL;
1585
1586 if (f->current_offset <= 0)
1587 return -EADDRNOTAVAIL;
1588
1589 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1590 if (r < 0)
1591 return r;
1592
1593 if (ret_boot_id)
1594 *ret_boot_id = o->entry.boot_id;
1595 else {
1596 r = sd_id128_get_boot(&id);
1597 if (r < 0)
1598 return r;
1599
1600 if (!sd_id128_equal(id, o->entry.boot_id))
1601 return -ESTALE;
1602 }
1603
1604 if (ret)
1605 *ret = le64toh(o->entry.monotonic);
1606
1607 return 0;
1608 }
1609
1610 static bool field_is_valid(const char *field) {
1611 const char *p;
1612
1613 assert(field);
1614
1615 if (isempty(field))
1616 return false;
1617
1618 if (startswith(field, "__"))
1619 return false;
1620
1621 for (p = field; *p; p++) {
1622
1623 if (*p == '_')
1624 continue;
1625
1626 if (*p >= 'A' && *p <= 'Z')
1627 continue;
1628
1629 if (*p >= '0' && *p <= '9')
1630 continue;
1631
1632 return false;
1633 }
1634
1635 return true;
1636 }
1637
1638 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1639 JournalFile *f;
1640 uint64_t i, n;
1641 size_t field_length;
1642 int r;
1643 Object *o;
1644
1645 if (!j)
1646 return -EINVAL;
1647 if (!field)
1648 return -EINVAL;
1649 if (!data)
1650 return -EINVAL;
1651 if (!size)
1652 return -EINVAL;
1653
1654 if (!field_is_valid(field))
1655 return -EINVAL;
1656
1657 f = j->current_file;
1658 if (!f)
1659 return -EADDRNOTAVAIL;
1660
1661 if (f->current_offset <= 0)
1662 return -EADDRNOTAVAIL;
1663
1664 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1665 if (r < 0)
1666 return r;
1667
1668 field_length = strlen(field);
1669
1670 n = journal_file_entry_n_items(o);
1671 for (i = 0; i < n; i++) {
1672 uint64_t p, l;
1673 le64_t le_hash;
1674 size_t t;
1675
1676 p = le64toh(o->entry.items[i].object_offset);
1677 le_hash = o->entry.items[i].hash;
1678 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1679 if (r < 0)
1680 return r;
1681
1682 if (le_hash != o->data.hash)
1683 return -EBADMSG;
1684
1685 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1686
1687 if (o->object.flags & OBJECT_COMPRESSED) {
1688
1689 #ifdef HAVE_XZ
1690 if (uncompress_startswith(o->data.payload, l,
1691 &f->compress_buffer, &f->compress_buffer_size,
1692 field, field_length, '=')) {
1693
1694 uint64_t rsize;
1695
1696 if (!uncompress_blob(o->data.payload, l,
1697 &f->compress_buffer, &f->compress_buffer_size, &rsize))
1698 return -EBADMSG;
1699
1700 *data = f->compress_buffer;
1701 *size = (size_t) rsize;
1702
1703 return 0;
1704 }
1705 #else
1706 return -EPROTONOSUPPORT;
1707 #endif
1708
1709 } else if (l >= field_length+1 &&
1710 memcmp(o->data.payload, field, field_length) == 0 &&
1711 o->data.payload[field_length] == '=') {
1712
1713 t = (size_t) l;
1714
1715 if ((uint64_t) t != l)
1716 return -E2BIG;
1717
1718 *data = o->data.payload;
1719 *size = t;
1720
1721 return 0;
1722 }
1723
1724 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1725 if (r < 0)
1726 return r;
1727 }
1728
1729 return -ENOENT;
1730 }
1731
1732 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
1733 JournalFile *f;
1734 uint64_t p, l, n;
1735 le64_t le_hash;
1736 int r;
1737 Object *o;
1738 size_t t;
1739
1740 if (!j)
1741 return -EINVAL;
1742 if (!data)
1743 return -EINVAL;
1744 if (!size)
1745 return -EINVAL;
1746
1747 f = j->current_file;
1748 if (!f)
1749 return -EADDRNOTAVAIL;
1750
1751 if (f->current_offset <= 0)
1752 return -EADDRNOTAVAIL;
1753
1754 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1755 if (r < 0)
1756 return r;
1757
1758 n = journal_file_entry_n_items(o);
1759 if (j->current_field >= n)
1760 return 0;
1761
1762 p = le64toh(o->entry.items[j->current_field].object_offset);
1763 le_hash = o->entry.items[j->current_field].hash;
1764 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1765 if (r < 0)
1766 return r;
1767
1768 if (le_hash != o->data.hash)
1769 return -EBADMSG;
1770
1771 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1772 t = (size_t) l;
1773
1774 /* We can't read objects larger than 4G on a 32bit machine */
1775 if ((uint64_t) t != l)
1776 return -E2BIG;
1777
1778 if (o->object.flags & OBJECT_COMPRESSED) {
1779 #ifdef HAVE_XZ
1780 uint64_t rsize;
1781
1782 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize))
1783 return -EBADMSG;
1784
1785 *data = f->compress_buffer;
1786 *size = (size_t) rsize;
1787 #else
1788 return -EPROTONOSUPPORT;
1789 #endif
1790 } else {
1791 *data = o->data.payload;
1792 *size = t;
1793 }
1794
1795 j->current_field ++;
1796
1797 return 1;
1798 }
1799
1800 _public_ void sd_journal_restart_data(sd_journal *j) {
1801 if (!j)
1802 return;
1803
1804 j->current_field = 0;
1805 }
1806
1807 _public_ int sd_journal_get_fd(sd_journal *j) {
1808 int r;
1809
1810 if (!j)
1811 return -EINVAL;
1812
1813 if (j->inotify_fd >= 0)
1814 return j->inotify_fd;
1815
1816 r = allocate_inotify(j);
1817 if (r < 0)
1818 return r;
1819
1820 /* Iterate through all dirs again, to add them to the
1821 * inotify */
1822 if (j->path)
1823 r = add_root_directory(j, j->path);
1824 else
1825 r = add_search_paths(j);
1826 if (r < 0)
1827 return r;
1828
1829 return j->inotify_fd;
1830 }
1831
1832 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
1833 Directory *d;
1834 int r;
1835
1836 assert(j);
1837 assert(e);
1838
1839 /* Is this a subdirectory we watch? */
1840 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
1841 if (d) {
1842 sd_id128_t id;
1843
1844 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
1845 (endswith(e->name, ".journal") ||
1846 endswith(e->name, ".journal~"))) {
1847
1848 /* Event for a journal file */
1849
1850 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1851 r = add_file(j, d->path, e->name);
1852 if (r < 0)
1853 log_debug("Failed to add file %s/%s: %s", d->path, e->name, strerror(-r));
1854
1855 } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
1856
1857 r = remove_file(j, d->path, e->name);
1858 if (r < 0)
1859 log_debug("Failed to remove file %s/%s: %s", d->path, e->name, strerror(-r));
1860 }
1861
1862 } else if (!d->is_root && e->len == 0) {
1863
1864 /* Event for a subdirectory */
1865
1866 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
1867 r = remove_directory(j, d);
1868 if (r < 0)
1869 log_debug("Failed to remove directory %s: %s", d->path, strerror(-r));
1870 }
1871
1872
1873 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
1874
1875 /* Event for root directory */
1876
1877 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1878 r = add_directory(j, d->path, e->name);
1879 if (r < 0)
1880 log_debug("Failed to add directory %s/%s: %s", d->path, e->name, strerror(-r));
1881 }
1882 }
1883
1884 return;
1885 }
1886
1887 if (e->mask & IN_IGNORED)
1888 return;
1889
1890 log_warning("Unknown inotify event.");
1891 }
1892
1893 static int determine_change(sd_journal *j) {
1894 bool b;
1895
1896 assert(j);
1897
1898 b = j->current_invalidate_counter != j->last_invalidate_counter;
1899 j->last_invalidate_counter = j->current_invalidate_counter;
1900
1901 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
1902 }
1903
1904 _public_ int sd_journal_process(sd_journal *j) {
1905 uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX] _alignas_(struct inotify_event);
1906 bool got_something = false;
1907
1908 if (!j)
1909 return -EINVAL;
1910
1911 for (;;) {
1912 struct inotify_event *e;
1913 ssize_t l;
1914
1915 l = read(j->inotify_fd, buffer, sizeof(buffer));
1916 if (l < 0) {
1917 if (errno == EAGAIN || errno == EINTR)
1918 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
1919
1920 return -errno;
1921 }
1922
1923 got_something = true;
1924
1925 e = (struct inotify_event*) buffer;
1926 while (l > 0) {
1927 size_t step;
1928
1929 process_inotify_event(j, e);
1930
1931 step = sizeof(struct inotify_event) + e->len;
1932 assert(step <= (size_t) l);
1933
1934 e = (struct inotify_event*) ((uint8_t*) e + step);
1935 l -= step;
1936 }
1937 }
1938
1939 return determine_change(j);
1940 }
1941
1942 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
1943 int r;
1944
1945 assert(j);
1946
1947 if (j->inotify_fd < 0) {
1948
1949 /* This is the first invocation, hence create the
1950 * inotify watch */
1951 r = sd_journal_get_fd(j);
1952 if (r < 0)
1953 return r;
1954
1955 /* The journal might have changed since the context
1956 * object was created and we weren't watching before,
1957 * hence don't wait for anything, and return
1958 * immediately. */
1959 return determine_change(j);
1960 }
1961
1962 do {
1963 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
1964 } while (r == -EINTR);
1965
1966 if (r < 0)
1967 return r;
1968
1969 return sd_journal_process(j);
1970 }
1971
1972 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
1973 Iterator i;
1974 JournalFile *f;
1975 bool first = true;
1976 int r;
1977
1978 if (!j)
1979 return -EINVAL;
1980 if (!from && !to)
1981 return -EINVAL;
1982
1983 HASHMAP_FOREACH(f, j->files, i) {
1984 usec_t fr, t;
1985
1986 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
1987 if (r == -ENOENT)
1988 continue;
1989 if (r < 0)
1990 return r;
1991 if (r == 0)
1992 continue;
1993
1994 if (first) {
1995 if (from)
1996 *from = fr;
1997 if (to)
1998 *to = t;
1999 first = false;
2000 } else {
2001 if (from)
2002 *from = MIN(fr, *from);
2003 if (to)
2004 *to = MIN(t, *to);
2005 }
2006 }
2007
2008 return first ? 0 : 1;
2009 }
2010
2011 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2012 Iterator i;
2013 JournalFile *f;
2014 bool first = true;
2015 int r;
2016
2017 if (!j)
2018 return -EINVAL;
2019 if (!from && !to)
2020 return -EINVAL;
2021
2022 HASHMAP_FOREACH(f, j->files, i) {
2023 usec_t fr, t;
2024
2025 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
2026 if (r == -ENOENT)
2027 continue;
2028 if (r < 0)
2029 return r;
2030 if (r == 0)
2031 continue;
2032
2033 if (first) {
2034 if (from)
2035 *from = fr;
2036 if (to)
2037 *to = t;
2038 first = false;
2039 } else {
2040 if (from)
2041 *from = MIN(fr, *from);
2042 if (to)
2043 *to = MIN(t, *to);
2044 }
2045 }
2046
2047 return first ? 0 : 1;
2048 }
2049
2050 void journal_print_header(sd_journal *j) {
2051 Iterator i;
2052 JournalFile *f;
2053 bool newline = false;
2054
2055 assert(j);
2056
2057 HASHMAP_FOREACH(f, j->files, i) {
2058 if (newline)
2059 putchar('\n');
2060 else
2061 newline = true;
2062
2063 journal_file_print_header(f);
2064 }
2065 }
2066
2067 /* _public_ int sd_journal_query_unique(sd_journal *j, const char *field) { */
2068 /* if (!j) */
2069 /* return -EINVAL; */
2070 /* if (!field) */
2071 /* return -EINVAL; */
2072
2073 /* return -ENOTSUP; */
2074 /* } */
2075
2076 /* _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { */
2077 /* if (!j) */
2078 /* return -EINVAL; */
2079 /* if (!data) */
2080 /* return -EINVAL; */
2081 /* if (!l) */
2082 /* return -EINVAL; */
2083
2084 /* return -ENOTSUP; */
2085 /* } */
2086
2087 /* _public_ void sd_journal_restart_unique(sd_journal *j) { */
2088 /* if (!j) */
2089 /* return; */
2090 /* } */