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