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