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