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