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