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