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