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