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