]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/sd-journal.c
journal: abstract the resetting of JournalFile's location
[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)
1fc605b0 90 journal_file_reset_location(f);
8f9b6cd9
LP
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
44a6b1b6 500_pure_ static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
de190aef
LP
501 uint64_t a;
502
503 assert(af);
504 assert(ao);
505 assert(l);
a87247dd 506 assert(l->type == LOCATION_DISCRETE || l->type == LOCATION_SEEK);
de190aef
LP
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
cbdca852
LP
560static 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
de7b95cd 569 int r;
cbdca852
LP
570 uint64_t np = 0;
571 Object *n;
de7b95cd
LP
572
573 assert(j);
cbdca852
LP
574 assert(m);
575 assert(f);
de7b95cd 576
cbdca852
LP
577 if (m->type == MATCH_DISCRETE) {
578 uint64_t dp;
de190aef 579
cbdca852 580 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
de190aef
LP
581 if (r <= 0)
582 return r;
583
cbdca852 584 return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
de190aef 585
cbdca852
LP
586 } else if (m->type == MATCH_OR_TERM) {
587 Match *i;
de7b95cd 588
cbdca852 589 /* Find the earliest match beyond after_offset */
de190aef 590
cbdca852
LP
591 LIST_FOREACH(matches, i, m->matches) {
592 uint64_t cp;
de190aef 593
cbdca852 594 r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
b4e5f920
LP
595 if (r < 0)
596 return r;
cbdca852 597 else if (r > 0) {
bc302926 598 if (np == 0 || (direction == DIRECTION_DOWN ? cp < np : cp > np))
cbdca852
LP
599 np = cp;
600 }
601 }
b4e5f920 602
bc302926
ZJS
603 if (np == 0)
604 return 0;
605
cbdca852 606 } else if (m->type == MATCH_AND_TERM) {
2bc8ca0c 607 Match *i, *last_moved;
de190aef 608
cbdca852 609 /* Always jump to the next matching entry and repeat
2bc8ca0c 610 * this until we find an offset that matches for all
cbdca852 611 * matches. */
de190aef 612
cbdca852
LP
613 if (!m->matches)
614 return 0;
de7b95cd 615
2bc8ca0c
ZJS
616 r = next_for_match(j, m->matches, f, after_offset, direction, NULL, &np);
617 if (r <= 0)
618 return r;
de190aef 619
2bc8ca0c
ZJS
620 assert(direction == DIRECTION_DOWN ? np >= after_offset : np <= after_offset);
621 last_moved = m->matches;
de190aef 622
2bc8ca0c
ZJS
623 LIST_LOOP_BUT_ONE(matches, i, m->matches, last_moved) {
624 uint64_t cp;
de190aef 625
2bc8ca0c
ZJS
626 r = next_for_match(j, i, f, np, direction, NULL, &cp);
627 if (r <= 0)
628 return r;
de190aef 629
2bc8ca0c
ZJS
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;
de190aef 634 }
2bc8ca0c 635 }
cbdca852 636 }
de190aef 637
bc302926 638 assert(np > 0);
de190aef 639
cbdca852
LP
640 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
641 if (r < 0)
642 return r;
de7b95cd 643
de190aef 644 if (ret)
cbdca852 645 *ret = n;
de190aef 646 if (offset)
cbdca852 647 *offset = np;
de190aef
LP
648
649 return 1;
650}
651
cbdca852
LP
652static 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
de190aef 660 int r;
de190aef
LP
661
662 assert(j);
cbdca852 663 assert(m);
de190aef 664 assert(f);
de190aef 665
cbdca852
LP
666 if (m->type == MATCH_DISCRETE) {
667 uint64_t dp;
de190aef 668
cbdca852 669 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
de7b95cd
LP
670 if (r <= 0)
671 return r;
672
cbdca852 673 /* FIXME: missing: find by monotonic */
de7b95cd 674
cbdca852
LP
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);
de190aef 688
cbdca852 689 return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset);
de7b95cd 690
cbdca852
LP
691 } else if (m->type == MATCH_OR_TERM) {
692 uint64_t np = 0;
693 Object *n;
694 Match *i;
de7b95cd 695
cbdca852 696 /* Find the earliest match */
de7b95cd 697
cbdca852
LP
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;
de190aef 707 }
cbdca852 708 }
de190aef 709
cbdca852
LP
710 if (np == 0)
711 return 0;
de7b95cd 712
cbdca852
LP
713 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
714 if (r < 0)
715 return r;
de7b95cd 716
cbdca852
LP
717 if (ret)
718 *ret = n;
719 if (offset)
720 *offset = np;
de190aef 721
cbdca852 722 return 1;
e892bd17 723
cbdca852
LP
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)
4b067dc9
LP
741 return r;
742
bc302926 743 if (np == 0 || (direction == DIRECTION_DOWN ? cp > np : cp < np))
cbdca852 744 np = cp;
de7b95cd
LP
745 }
746
cbdca852
LP
747 return next_for_match(j, m, f, np, direction, ret, offset);
748 }
749}
de190aef 750
cbdca852
LP
751static 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;
de7b95cd 778 }
cbdca852
LP
779 if (j->current_location.realtime_set)
780 return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
de7b95cd 781
cbdca852
LP
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}
de7b95cd 786
cbdca852
LP
787static 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
49f43d5f 811 * with an offset at least one step larger */
cbdca852 812 return next_for_match(j, j->level0, f, direction == DIRECTION_DOWN ? cp+1 : cp-1, direction, ret, offset);
de7b95cd
LP
813}
814
de190aef
LP
815static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
816 Object *c;
817 uint64_t cp;
cbdca852 818 int r;
de190aef
LP
819
820 assert(j);
821 assert(f);
822
87011c25 823 if (f->last_direction == direction && f->current_offset > 0) {
466ccd92
LP
824 cp = f->current_offset;
825
826 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
de190aef
LP
827 if (r < 0)
828 return r;
829
de190aef
LP
830 r = next_with_matches(j, f, direction, &c, &cp);
831 if (r <= 0)
832 return r;
de190aef 833 } else {
cbdca852 834 r = find_location_with_matches(j, f, direction, &c, &cp);
de190aef
LP
835 if (r <= 0)
836 return r;
de190aef
LP
837 }
838
bc302926 839 /* OK, we found the spot, now let's advance until an entry
cbdca852
LP
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
de190aef
LP
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);
1cdf7175
CH
852
853 found = direction == DIRECTION_DOWN ? k > 0 : k < 0;
de190aef
LP
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
e892bd17 871static int real_journal_next(sd_journal *j, direction_t direction) {
468b21de
LP
872 JournalFile *f, *new_file = NULL;
873 uint64_t new_offset = 0;
a002d44b 874 uint64_t p = 0;
cec736d2 875 Iterator i;
a002d44b 876 Object *o;
87d2c1ff
LP
877 int r;
878
1ae464e0
TA
879 assert_return(j, -EINVAL);
880 assert_return(!journal_pid_changed(j), -ECHILD);
87d2c1ff 881
c1f906bd 882 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
de190aef 883 bool found;
87d2c1ff 884
de190aef 885 r = next_beyond_location(j, f, direction, &o, &p);
e590af26 886 if (r < 0) {
da927ba9 887 log_debug_errno(r, "Can't iterate through %s, ignoring: %m", f->path);
a9a245c1 888 remove_file_real(j, f);
e590af26
LP
889 continue;
890 } else if (r == 0)
cec736d2 891 continue;
87d2c1ff 892
468b21de 893 if (!new_file)
de190aef
LP
894 found = true;
895 else {
896 int k;
897
468b21de 898 k = compare_entry_order(f, o, new_file, new_offset);
de190aef 899
bc302926 900 found = direction == DIRECTION_DOWN ? k < 0 : k > 0;
de190aef
LP
901 }
902
903 if (found) {
468b21de 904 new_file = f;
cec736d2 905 new_offset = p;
87d2c1ff 906 }
87d2c1ff
LP
907 }
908
468b21de 909 if (!new_file)
de190aef 910 return 0;
ae2cc8ef 911
468b21de
LP
912 r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_offset, &o);
913 if (r < 0)
914 return r;
915
87011c25 916 set_location(j, LOCATION_DISCRETE, new_file, o, direction, new_offset);
ae2cc8ef 917
de190aef
LP
918 return 1;
919}
ae2cc8ef 920
a5344d2c 921_public_ int sd_journal_next(sd_journal *j) {
de190aef
LP
922 return real_journal_next(j, DIRECTION_DOWN);
923}
ae2cc8ef 924
a5344d2c 925_public_ int sd_journal_previous(sd_journal *j) {
de190aef
LP
926 return real_journal_next(j, DIRECTION_UP);
927}
ae2cc8ef 928
6f003b43 929static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
de190aef 930 int c = 0, r;
ae2cc8ef 931
1ae464e0
TA
932 assert_return(j, -EINVAL);
933 assert_return(!journal_pid_changed(j), -ECHILD);
de190aef 934
6f003b43
LP
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);
de190aef
LP
946 if (r < 0)
947 return r;
948
949 if (r == 0)
950 return c;
951
952 skip--;
953 c++;
6f003b43 954 } while (skip > 0);
87d2c1ff 955
de190aef 956 return c;
87d2c1ff
LP
957}
958
6f003b43
LP
959_public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
960 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
961}
de190aef 962
6f003b43
LP
963_public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
964 return real_journal_next_skip(j, DIRECTION_UP, skip);
87d2c1ff
LP
965}
966
a5344d2c 967_public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
cec736d2 968 Object *o;
87d2c1ff 969 int r;
3fbf9cbb 970 char bid[33], sid[33];
87d2c1ff 971
1ae464e0
TA
972 assert_return(j, -EINVAL);
973 assert_return(!journal_pid_changed(j), -ECHILD);
974 assert_return(cursor, -EINVAL);
87d2c1ff 975
3fbf9cbb
LP
976 if (!j->current_file || j->current_file->current_offset <= 0)
977 return -EADDRNOTAVAIL;
87d2c1ff 978
de190aef 979 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
87d2c1ff
LP
980 if (r < 0)
981 return r;
982
3fbf9cbb
LP
983 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
984 sd_id128_to_string(o->entry.boot_id, bid);
87d2c1ff 985
3fbf9cbb 986 if (asprintf(cursor,
507f22bd
ZJS
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)
3fbf9cbb 992 return -ENOMEM;
87d2c1ff 993
6f47ad30 994 return 0;
87d2c1ff
LP
995}
996
a5344d2c 997_public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
a2a5291b 998 const char *word, *state;
de190aef 999 size_t l;
de190aef
LP
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
1ae464e0
TA
1010 assert_return(j, -EINVAL);
1011 assert_return(!journal_pid_changed(j), -ECHILD);
1012 assert_return(!isempty(cursor), -EINVAL);
de190aef 1013
a2a5291b 1014 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
de190aef
LP
1015 char *item;
1016 int k = 0;
1017
a2a5291b 1018 if (l < 2 || word[1] != '=')
de190aef
LP
1019 return -EINVAL;
1020
a2a5291b 1021 item = strndup(word, l);
de190aef
LP
1022 if (!item)
1023 return -ENOMEM;
1024
a2a5291b 1025 switch (word[0]) {
de190aef
LP
1026
1027 case 's':
1028 seqnum_id_set = true;
be3ea5ea 1029 k = sd_id128_from_string(item+2, &seqnum_id);
de190aef
LP
1030 break;
1031
1032 case 'i':
1033 seqnum_set = true;
be3ea5ea 1034 if (sscanf(item+2, "%llx", &seqnum) != 1)
de190aef
LP
1035 k = -EINVAL;
1036 break;
1037
1038 case 'b':
1039 boot_id_set = true;
be3ea5ea 1040 k = sd_id128_from_string(item+2, &boot_id);
de190aef
LP
1041 break;
1042
1043 case 'm':
1044 monotonic_set = true;
be3ea5ea 1045 if (sscanf(item+2, "%llx", &monotonic) != 1)
de190aef
LP
1046 k = -EINVAL;
1047 break;
1048
1049 case 't':
1050 realtime_set = true;
be3ea5ea 1051 if (sscanf(item+2, "%llx", &realtime) != 1)
de190aef
LP
1052 k = -EINVAL;
1053 break;
1054
1055 case 'x':
1056 xor_hash_set = true;
be3ea5ea 1057 if (sscanf(item+2, "%llx", &xor_hash) != 1)
de190aef
LP
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
a87247dd 1075 j->current_location.type = LOCATION_SEEK;
de190aef
LP
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
c6511e85
LP
1102_public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
1103 int r;
a2a5291b 1104 const char *word, *state;
c6511e85
LP
1105 size_t l;
1106 Object *o;
1107
1ae464e0
TA
1108 assert_return(j, -EINVAL);
1109 assert_return(!journal_pid_changed(j), -ECHILD);
1110 assert_return(!isempty(cursor), -EINVAL);
c6511e85
LP
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
a2a5291b 1119 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
c6511e85
LP
1120 _cleanup_free_ char *item = NULL;
1121 sd_id128_t id;
1122 unsigned long long ll;
1123 int k = 0;
1124
a2a5291b 1125 if (l < 2 || word[1] != '=')
c6511e85
LP
1126 return -EINVAL;
1127
a2a5291b 1128 item = strndup(word, l);
c6511e85
LP
1129 if (!item)
1130 return -ENOMEM;
1131
a2a5291b 1132 switch (word[0]) {
c6511e85
LP
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
a5344d2c 1184_public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1ae464e0
TA
1185 assert_return(j, -EINVAL);
1186 assert_return(!journal_pid_changed(j), -ECHILD);
de190aef
LP
1187
1188 reset_location(j);
a87247dd 1189 j->current_location.type = LOCATION_SEEK;
de190aef
LP
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
a5344d2c 1197_public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1ae464e0
TA
1198 assert_return(j, -EINVAL);
1199 assert_return(!journal_pid_changed(j), -ECHILD);
de190aef
LP
1200
1201 reset_location(j);
a87247dd 1202 j->current_location.type = LOCATION_SEEK;
de190aef
LP
1203 j->current_location.realtime = usec;
1204 j->current_location.realtime_set = true;
1205
1206 return 0;
1207}
1208
a5344d2c 1209_public_ int sd_journal_seek_head(sd_journal *j) {
1ae464e0
TA
1210 assert_return(j, -EINVAL);
1211 assert_return(!journal_pid_changed(j), -ECHILD);
de190aef
LP
1212
1213 reset_location(j);
1214 j->current_location.type = LOCATION_HEAD;
1215
1216 return 0;
1217}
1218
a5344d2c 1219_public_ int sd_journal_seek_tail(sd_journal *j) {
1ae464e0
TA
1220 assert_return(j, -EINVAL);
1221 assert_return(!journal_pid_changed(j), -ECHILD);
de190aef
LP
1222
1223 reset_location(j);
1224 j->current_location.type = LOCATION_TAIL;
1225
1226 return 0;
87d2c1ff
LP
1227}
1228
85210bff
LP
1229static 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 =
c51cf056
ZJS
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);
85210bff
LP
1246}
1247
a688baa8
ZJS
1248static bool file_has_type_prefix(const char *prefix, const char *filename) {
1249 const char *full, *tilded, *atted;
1250
baabc091 1251 full = strappenda(prefix, ".journal");
a688baa8
ZJS
1252 tilded = strappenda(full, "~");
1253 atted = strappenda(prefix, "@");
1254
1255 return streq(filename, full) ||
1256 streq(filename, tilded) ||
1257 startswith(filename, atted);
1258}
1259
1260static 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
de0671ee 1274 assert_se(snprintf(prefix, sizeof(prefix), "user-"UID_FMT, getuid())
a688baa8
ZJS
1275 < (int) sizeof(prefix));
1276
1277 if (file_has_type_prefix(prefix, filename))
1278 return true;
1279 }
1280
1281 return false;
1282}
1283
5302ebe1 1284static int add_any_file(sd_journal *j, const char *path) {
39883f62 1285 JournalFile *f = NULL;
5302ebe1 1286 int r;
3fbf9cbb
LP
1287
1288 assert(j);
5302ebe1 1289 assert(path);
3fbf9cbb 1290
c1f906bd 1291 if (ordered_hashmap_get(j->files, path))
50f20cfd 1292 return 0;
50f20cfd 1293
c1f906bd 1294 if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
5302ebe1 1295 log_warning("Too many open journal files, not adding %s.", path);
3ac251b8 1296 return set_put_error(j, -ETOOMANYREFS);
50f20cfd
LP
1297 }
1298
16e9f408 1299 r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
5302ebe1 1300 if (r < 0)
3fbf9cbb 1301 return r;
3fbf9cbb 1302
72f59706 1303 /* journal_file_dump(f); */
de190aef 1304
c1f906bd 1305 r = ordered_hashmap_put(j->files, f->path, f);
3fbf9cbb
LP
1306 if (r < 0) {
1307 journal_file_close(f);
1308 return r;
1309 }
1310
5ec76417 1311 log_debug("File %s added.", f->path);
a50d7d43 1312
85210bff
LP
1313 check_network(j, f->fd);
1314
a963990f
LP
1315 j->current_invalidate_counter ++;
1316
50f20cfd
LP
1317 return 0;
1318}
1319
5302ebe1
ZJS
1320static 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
a963990f 1342static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
a9a245c1 1343 _cleanup_free_ char *path;
50f20cfd
LP
1344 JournalFile *f;
1345
1346 assert(j);
1347 assert(prefix);
1348 assert(filename);
1349
b7def684 1350 path = strjoin(prefix, "/", filename, NULL);
a963990f 1351 if (!path)
50f20cfd
LP
1352 return -ENOMEM;
1353
c1f906bd 1354 f = ordered_hashmap_get(j->files, path);
50f20cfd
LP
1355 if (!f)
1356 return 0;
1357
a9a245c1
ZJS
1358 remove_file_real(j, f);
1359 return 0;
1360}
1361
1362static void remove_file_real(sd_journal *j, JournalFile *f) {
1363 assert(j);
1364 assert(f);
1365
c1f906bd 1366 ordered_hashmap_remove(j->files, f->path);
44a5fa34 1367
5ec76417 1368 log_debug("File %s removed.", f->path);
44a5fa34 1369
3c1668da
LP
1370 if (j->current_file == f) {
1371 j->current_file = NULL;
1372 j->current_field = 0;
1373 }
1374
1375 if (j->unique_file == f) {
360af4cf 1376 /* Jump to the next unique_file or NULL if that one was last */
c1f906bd 1377 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
3c1668da 1378 j->unique_offset = 0;
360af4cf
ZJS
1379 if (!j->unique_file)
1380 j->unique_file_lost = true;
3c1668da
LP
1381 }
1382
50f20cfd
LP
1383 journal_file_close(f);
1384
a963990f 1385 j->current_invalidate_counter ++;
3fbf9cbb
LP
1386}
1387
a963990f 1388static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
7fd1b19b 1389 _cleanup_free_ char *path = NULL;
3fbf9cbb 1390 int r;
7fd1b19b 1391 _cleanup_closedir_ DIR *d = NULL;
cf244689 1392 sd_id128_t id, mid;
a963990f 1393 Directory *m;
3fbf9cbb
LP
1394
1395 assert(j);
1396 assert(prefix);
a963990f 1397 assert(dirname);
3fbf9cbb 1398
d95b1fb3
ZJS
1399 log_debug("Considering %s/%s.", prefix, dirname);
1400
cf244689 1401 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
a963990f 1402 (sd_id128_from_string(dirname, &id) < 0 ||
cf244689 1403 sd_id128_get_machine(&mid) < 0 ||
d95b1fb3 1404 !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
cf244689
LP
1405 return 0;
1406
b7def684 1407 path = strjoin(prefix, "/", dirname, NULL);
a963990f 1408 if (!path)
3fbf9cbb
LP
1409 return -ENOMEM;
1410
a963990f 1411 d = opendir(path);
3fbf9cbb 1412 if (!d) {
56f64d95 1413 log_debug_errno(errno, "Failed to open %s: %m", path);
3fbf9cbb
LP
1414 if (errno == ENOENT)
1415 return 0;
3fbf9cbb
LP
1416 return -errno;
1417 }
1418
a963990f
LP
1419 m = hashmap_get(j->directories_by_path, path);
1420 if (!m) {
1421 m = new0(Directory, 1);
a50d7d43 1422 if (!m)
a963990f 1423 return -ENOMEM;
a963990f
LP
1424
1425 m->is_root = false;
1426 m->path = path;
1427
1428 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
a963990f
LP
1429 free(m);
1430 return -ENOMEM;
1431 }
1432
a50d7d43 1433 path = NULL; /* avoid freeing in cleanup */
a963990f
LP
1434 j->current_invalidate_counter ++;
1435
5ec76417 1436 log_debug("Directory %s added.", m->path);
a963990f 1437
a50d7d43 1438 } else if (m->is_root)
a963990f 1439 return 0;
a963990f
LP
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|
5e6870ea 1445 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
4a842cad 1446 IN_ONLYDIR);
a963990f
LP
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 (;;) {
7d5e9c0f 1453 struct dirent *de;
a963990f 1454
bde1fdd7
FW
1455 errno = 0;
1456 de = readdir(d);
1457 if (!de && errno != 0) {
1458 r = -errno;
56f64d95 1459 log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
bde1fdd7
FW
1460 return r;
1461 }
1462 if (!de)
a963990f
LP
1463 break;
1464
de2c3907
LP
1465 if (dirent_is_file_with_suffix(de, ".journal") ||
1466 dirent_is_file_with_suffix(de, ".journal~")) {
a963990f 1467 r = add_file(j, m->path, de->d_name);
6fe391c5 1468 if (r < 0) {
c33b3297
MS
1469 log_debug_errno(r, "Failed to add file %s/%s: %m",
1470 m->path, de->d_name);
3ac251b8 1471 r = set_put_error(j, r);
6fe391c5
ZJS
1472 if (r < 0)
1473 return r;
1474 }
a963990f
LP
1475 }
1476 }
1477
85210bff
LP
1478 check_network(j, dirfd(d));
1479
a963990f
LP
1480 return 0;
1481}
1482
89739579 1483static int add_root_directory(sd_journal *j, const char *p) {
7fd1b19b 1484 _cleanup_closedir_ DIR *d = NULL;
a963990f
LP
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
89739579
LP
1495 if (j->prefix)
1496 p = strappenda(j->prefix, p);
b6741478 1497
a963990f
LP
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);
a50d7d43 1505 if (!m)
a963990f 1506 return -ENOMEM;
a963990f
LP
1507
1508 m->is_root = true;
1509 m->path = strdup(p);
1510 if (!m->path) {
a963990f
LP
1511 free(m);
1512 return -ENOMEM;
1513 }
1514
1515 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
a963990f
LP
1516 free(m->path);
1517 free(m);
1518 return -ENOMEM;
1519 }
1520
1521 j->current_invalidate_counter ++;
1522
5ec76417 1523 log_debug("Root directory %s added.", m->path);
a963990f 1524
a50d7d43 1525 } else if (!m->is_root)
a963990f 1526 return 0;
50f20cfd 1527
a963990f
LP
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|
4a842cad 1532 IN_ONLYDIR);
a963990f
LP
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 }
50f20cfd 1537
5302ebe1
ZJS
1538 if (j->no_new_files)
1539 return 0;
1540
3fbf9cbb 1541 for (;;) {
7d5e9c0f 1542 struct dirent *de;
a963990f 1543 sd_id128_t id;
3fbf9cbb 1544
bde1fdd7
FW
1545 errno = 0;
1546 de = readdir(d);
1547 if (!de && errno != 0) {
1548 r = -errno;
56f64d95 1549 log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
bde1fdd7
FW
1550 return r;
1551 }
1552 if (!de)
3fbf9cbb
LP
1553 break;
1554
de2c3907
LP
1555 if (dirent_is_file_with_suffix(de, ".journal") ||
1556 dirent_is_file_with_suffix(de, ".journal~")) {
a963990f 1557 r = add_file(j, m->path, de->d_name);
6fe391c5 1558 if (r < 0) {
c33b3297
MS
1559 log_debug_errno(r, "Failed to add file %s/%s: %m",
1560 m->path, de->d_name);
3ac251b8 1561 r = set_put_error(j, r);
6fe391c5
ZJS
1562 if (r < 0)
1563 return r;
1564 }
6f5878a2 1565 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
a963990f
LP
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)
da927ba9 1570 log_debug_errno(r, "Failed to add directory %s/%s: %m", m->path, de->d_name);
a963990f 1571 }
3fbf9cbb
LP
1572 }
1573
85210bff
LP
1574 check_network(j, dirfd(d));
1575
a963990f
LP
1576 return 0;
1577}
1578
1579static 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)
5ec76417 1592 log_debug("Root directory %s removed.", d->path);
a963990f 1593 else
5ec76417 1594 log_debug("Directory %s removed.", d->path);
a963990f
LP
1595
1596 free(d->path);
1597 free(d);
50f20cfd 1598
3fbf9cbb
LP
1599 return 0;
1600}
1601
89739579 1602static int add_search_paths(sd_journal *j) {
6fe391c5 1603 int r;
a963990f
LP
1604 const char search_paths[] =
1605 "/run/log/journal\0"
1606 "/var/log/journal\0";
1607 const char *p;
50f20cfd
LP
1608
1609 assert(j);
50f20cfd 1610
a963990f
LP
1611 /* We ignore most errors here, since the idea is to only open
1612 * what's actually accessible, and ignore the rest. */
50f20cfd 1613
6fe391c5 1614 NULSTR_FOREACH(p, search_paths) {
89739579 1615 r = add_root_directory(j, p);
3ac251b8
LP
1616 if (r < 0 && r != -ENOENT) {
1617 r = set_put_error(j, r);
1618 if (r < 0)
1619 return r;
1620 }
6fe391c5 1621 }
50f20cfd 1622
a963990f 1623 return 0;
50f20cfd
LP
1624}
1625
5302ebe1
ZJS
1626static 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
c1f906bd 1637 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
5302ebe1 1638 _cleanup_free_ char *dir;
e9174f29 1639 int r;
5302ebe1
ZJS
1640
1641 dir = dirname_malloc(f->path);
1642 if (!dir)
1643 return -ENOMEM;
1644
89739579 1645 r = add_root_directory(j, dir);
5302ebe1
ZJS
1646 if (r < 0) {
1647 set_put_error(j, r);
1648 return r;
1649 }
1650 }
1651
1652 return 0;
1653}
1654
1655
a963990f 1656static int allocate_inotify(sd_journal *j) {
50f20cfd 1657 assert(j);
50f20cfd 1658
a963990f
LP
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 }
50f20cfd 1664
a963990f 1665 if (!j->directories_by_wd) {
d5099efc 1666 j->directories_by_wd = hashmap_new(NULL);
a963990f
LP
1667 if (!j->directories_by_wd)
1668 return -ENOMEM;
50f20cfd 1669 }
a963990f
LP
1670
1671 return 0;
50f20cfd
LP
1672}
1673
7827b1a1 1674static sd_journal *journal_new(int flags, const char *path) {
a963990f 1675 sd_journal *j;
50f20cfd 1676
a963990f
LP
1677 j = new0(sd_journal, 1);
1678 if (!j)
1679 return NULL;
50f20cfd 1680
a65f06bb 1681 j->original_pid = getpid();
a963990f
LP
1682 j->inotify_fd = -1;
1683 j->flags = flags;
93b73b06 1684 j->data_threshold = DEFAULT_DATA_THRESHOLD;
50f20cfd 1685
7827b1a1
LP
1686 if (path) {
1687 j->path = strdup(path);
6180fc61
ZJS
1688 if (!j->path)
1689 goto fail;
7827b1a1
LP
1690 }
1691
c1f906bd 1692 j->files = ordered_hashmap_new(&string_hash_ops);
d5099efc 1693 j->directories_by_path = hashmap_new(&string_hash_ops);
84168d80 1694 j->mmap = mmap_cache_new();
3ac251b8 1695 if (!j->files || !j->directories_by_path || !j->mmap)
6180fc61 1696 goto fail;
16e9f408 1697
a963990f 1698 return j;
6180fc61
ZJS
1699
1700fail:
1701 sd_journal_close(j);
1702 return NULL;
50f20cfd
LP
1703}
1704
a5344d2c 1705_public_ int sd_journal_open(sd_journal **ret, int flags) {
87d2c1ff 1706 sd_journal *j;
3fbf9cbb 1707 int r;
87d2c1ff 1708
1ae464e0 1709 assert_return(ret, -EINVAL);
b6741478 1710 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL);
87d2c1ff 1711
7827b1a1 1712 j = journal_new(flags, NULL);
87d2c1ff
LP
1713 if (!j)
1714 return -ENOMEM;
1715
89739579 1716 r = add_search_paths(j);
a963990f 1717 if (r < 0)
50f20cfd 1718 goto fail;
50f20cfd 1719
a963990f
LP
1720 *ret = j;
1721 return 0;
cf244689 1722
a963990f
LP
1723fail:
1724 sd_journal_close(j);
87d2c1ff 1725
a963990f
LP
1726 return r;
1727}
50f20cfd 1728
b6741478
LP
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);
affcf189 1738 assert_return(machine_name_is_valid(machine), -EINVAL);
b6741478
LP
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
89739579
LP
1756 j->prefix = root;
1757 root = NULL;
1758
1759 r = add_search_paths(j);
b6741478
LP
1760 if (r < 0)
1761 goto fail;
1762
1763 *ret = j;
1764 return 0;
1765
1766fail:
1767 sd_journal_close(j);
1768 return r;
1769}
1770
a963990f
LP
1771_public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1772 sd_journal *j;
1773 int r;
87d2c1ff 1774
1ae464e0
TA
1775 assert_return(ret, -EINVAL);
1776 assert_return(path, -EINVAL);
1777 assert_return(flags == 0, -EINVAL);
87d2c1ff 1778
7827b1a1 1779 j = journal_new(flags, path);
a963990f
LP
1780 if (!j)
1781 return -ENOMEM;
3fbf9cbb 1782
89739579 1783 r = add_root_directory(j, path);
6fe391c5 1784 if (r < 0) {
3ac251b8 1785 set_put_error(j, r);
a963990f 1786 goto fail;
6fe391c5 1787 }
87d2c1ff
LP
1788
1789 *ret = j;
1790 return 0;
1791
1792fail:
1793 sd_journal_close(j);
1794
1795 return r;
a963990f 1796}
87d2c1ff 1797
5302ebe1
ZJS
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
1ae464e0
TA
1803 assert_return(ret, -EINVAL);
1804 assert_return(flags == 0, -EINVAL);
5302ebe1
ZJS
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) {
da927ba9 1813 log_error_errno(r, "Failed to open %s: %m", *path);
5302ebe1
ZJS
1814 goto fail;
1815 }
1816 }
1817
1818 j->no_new_files = true;
1819
1820 *ret = j;
1821 return 0;
1822
1823fail:
1824 sd_journal_close(j);
1825
1826 return r;
1827}
1828
a5344d2c 1829_public_ void sd_journal_close(sd_journal *j) {
a963990f
LP
1830 Directory *d;
1831 JournalFile *f;
1832
a5344d2c
LP
1833 if (!j)
1834 return;
87d2c1ff 1835
54b1da83
LP
1836 sd_journal_flush_matches(j);
1837
c1f906bd 1838 while ((f = ordered_hashmap_steal_first(j->files)))
a963990f 1839 journal_file_close(f);
50f20cfd 1840
c1f906bd 1841 ordered_hashmap_free(j->files);
260a2be4 1842
a963990f
LP
1843 while ((d = hashmap_first(j->directories_by_path)))
1844 remove_directory(j, d);
260a2be4 1845
a963990f
LP
1846 while ((d = hashmap_first(j->directories_by_wd)))
1847 remove_directory(j, d);
87d2c1ff 1848
a963990f
LP
1849 hashmap_free(j->directories_by_path);
1850 hashmap_free(j->directories_by_wd);
1cc101f1 1851
03e334a1 1852 safe_close(j->inotify_fd);
50f20cfd 1853
bf807d4d
LP
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));
16e9f408 1856 mmap_cache_unref(j->mmap);
bf807d4d 1857 }
16e9f408 1858
7827b1a1 1859 free(j->path);
89739579 1860 free(j->prefix);
3c1668da 1861 free(j->unique_field);
6fe391c5 1862 set_free(j->errors);
87d2c1ff
LP
1863 free(j);
1864}
3fbf9cbb 1865
a5344d2c 1866_public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
3fbf9cbb
LP
1867 Object *o;
1868 JournalFile *f;
1869 int r;
1870
1ae464e0
TA
1871 assert_return(j, -EINVAL);
1872 assert_return(!journal_pid_changed(j), -ECHILD);
1873 assert_return(ret, -EINVAL);
3fbf9cbb
LP
1874
1875 f = j->current_file;
1876 if (!f)
de190aef 1877 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1878
1879 if (f->current_offset <= 0)
de190aef 1880 return -EADDRNOTAVAIL;
3fbf9cbb 1881
de190aef 1882 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1883 if (r < 0)
1884 return r;
1885
1886 *ret = le64toh(o->entry.realtime);
de190aef 1887 return 0;
3fbf9cbb
LP
1888}
1889
a5344d2c 1890_public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
3fbf9cbb
LP
1891 Object *o;
1892 JournalFile *f;
1893 int r;
1894 sd_id128_t id;
1895
1ae464e0
TA
1896 assert_return(j, -EINVAL);
1897 assert_return(!journal_pid_changed(j), -ECHILD);
3fbf9cbb
LP
1898
1899 f = j->current_file;
1900 if (!f)
de190aef 1901 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1902
1903 if (f->current_offset <= 0)
de190aef 1904 return -EADDRNOTAVAIL;
3fbf9cbb 1905
de190aef 1906 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1907 if (r < 0)
1908 return r;
1909
de190aef
LP
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;
3fbf9cbb 1916
de190aef 1917 if (!sd_id128_equal(id, o->entry.boot_id))
df50185b 1918 return -ESTALE;
de190aef 1919 }
3fbf9cbb 1920
14a65d65
LP
1921 if (ret)
1922 *ret = le64toh(o->entry.monotonic);
1923
de190aef 1924 return 0;
3fbf9cbb
LP
1925}
1926
362a3f81
LP
1927static 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
a5344d2c 1955_public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
3fbf9cbb
LP
1956 JournalFile *f;
1957 uint64_t i, n;
1958 size_t field_length;
1959 int r;
1960 Object *o;
1961
1ae464e0
TA
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);
3fbf9cbb
LP
1968
1969 f = j->current_file;
1970 if (!f)
de190aef 1971 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1972
1973 if (f->current_offset <= 0)
de190aef 1974 return -EADDRNOTAVAIL;
3fbf9cbb 1975
de190aef 1976 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
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++) {
4fd052ae
FC
1984 uint64_t p, l;
1985 le64_t le_hash;
3fbf9cbb 1986 size_t t;
1ec7120e 1987 int compression;
3fbf9cbb
LP
1988
1989 p = le64toh(o->entry.items[i].object_offset);
807e17f0 1990 le_hash = o->entry.items[i].hash;
de190aef 1991 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
3fbf9cbb
LP
1992 if (r < 0)
1993 return r;
1994
de190aef 1995 if (le_hash != o->data.hash)
de7b95cd
LP
1996 return -EBADMSG;
1997
3fbf9cbb
LP
1998 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1999
1ec7120e 2000 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
3b1a55e1
ZJS
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
fa1c4b51 2008 size_t rsize;
3b1a55e1
ZJS
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;
807e17f0 2016
3b1a55e1
ZJS
2017 *data = f->compress_buffer;
2018 *size = (size_t) rsize;
807e17f0 2019
3b1a55e1
ZJS
2020 return 0;
2021 }
2022#else
2023 return -EPROTONOSUPPORT;
2024#endif
807e17f0
LP
2025 } else if (l >= field_length+1 &&
2026 memcmp(o->data.payload, field, field_length) == 0 &&
2027 o->data.payload[field_length] == '=') {
3fbf9cbb 2028
161e54f8 2029 t = (size_t) l;
3fbf9cbb 2030
161e54f8
LP
2031 if ((uint64_t) t != l)
2032 return -E2BIG;
3fbf9cbb 2033
161e54f8
LP
2034 *data = o->data.payload;
2035 *size = t;
3fbf9cbb 2036
99613ec5 2037 return 0;
161e54f8 2038 }
3fbf9cbb 2039
de190aef 2040 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
161e54f8
LP
2041 if (r < 0)
2042 return r;
3fbf9cbb
LP
2043 }
2044
de190aef 2045 return -ENOENT;
3fbf9cbb
LP
2046}
2047
93b73b06 2048static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
3c1668da
LP
2049 size_t t;
2050 uint64_t l;
3b1a55e1 2051 int compression;
3c1668da
LP
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
1ec7120e
ZJS
2060 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
2061 if (compression) {
3b1a55e1 2062#if defined(HAVE_XZ) || defined(HAVE_LZ4)
fa1c4b51 2063 size_t rsize;
3b1a55e1 2064 int r;
3c1668da 2065
1ec7120e
ZJS
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;
3c1668da
LP
2071
2072 *data = f->compress_buffer;
2073 *size = (size_t) rsize;
3b1a55e1
ZJS
2074#else
2075 return -EPROTONOSUPPORT;
2076#endif
3c1668da
LP
2077 } else {
2078 *data = o->data.payload;
2079 *size = t;
2080 }
2081
2082 return 0;
2083}
2084
a5344d2c 2085_public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
3fbf9cbb 2086 JournalFile *f;
3c1668da 2087 uint64_t p, n;
4fd052ae 2088 le64_t le_hash;
3fbf9cbb
LP
2089 int r;
2090 Object *o;
2091
1ae464e0
TA
2092 assert_return(j, -EINVAL);
2093 assert_return(!journal_pid_changed(j), -ECHILD);
2094 assert_return(data, -EINVAL);
2095 assert_return(size, -EINVAL);
3fbf9cbb
LP
2096
2097 f = j->current_file;
2098 if (!f)
de190aef 2099 return -EADDRNOTAVAIL;
3fbf9cbb
LP
2100
2101 if (f->current_offset <= 0)
de190aef 2102 return -EADDRNOTAVAIL;
3fbf9cbb 2103
de190aef 2104 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
2105 if (r < 0)
2106 return r;
2107
2108 n = journal_file_entry_n_items(o);
7210bfb3 2109 if (j->current_field >= n)
3fbf9cbb
LP
2110 return 0;
2111
7210bfb3 2112 p = le64toh(o->entry.items[j->current_field].object_offset);
de190aef
LP
2113 le_hash = o->entry.items[j->current_field].hash;
2114 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
3fbf9cbb
LP
2115 if (r < 0)
2116 return r;
2117
de190aef 2118 if (le_hash != o->data.hash)
de7b95cd
LP
2119 return -EBADMSG;
2120
93b73b06 2121 r = return_data(j, f, o, data, size);
3c1668da
LP
2122 if (r < 0)
2123 return r;
3fbf9cbb 2124
7210bfb3 2125 j->current_field ++;
3fbf9cbb
LP
2126
2127 return 1;
2128}
c2373f84 2129
a5344d2c
LP
2130_public_ void sd_journal_restart_data(sd_journal *j) {
2131 if (!j)
2132 return;
8725d60a
LP
2133
2134 j->current_field = 0;
c2373f84 2135}
50f20cfd 2136
a5344d2c 2137_public_ int sd_journal_get_fd(sd_journal *j) {
a963990f
LP
2138 int r;
2139
1ae464e0
TA
2140 assert_return(j, -EINVAL);
2141 assert_return(!journal_pid_changed(j), -ECHILD);
50f20cfd 2142
a963990f
LP
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 */
5302ebe1
ZJS
2152 if (j->no_new_files)
2153 r = add_current_paths(j);
2154 else if (j->path)
89739579 2155 r = add_root_directory(j, j->path);
7827b1a1 2156 else
89739579 2157 r = add_search_paths(j);
a963990f
LP
2158 if (r < 0)
2159 return r;
2160
50f20cfd
LP
2161 return j->inotify_fd;
2162}
2163
ee531d94
LP
2164_public_ int sd_journal_get_events(sd_journal *j) {
2165 int fd;
2166
1ae464e0
TA
2167 assert_return(j, -EINVAL);
2168 assert_return(!journal_pid_changed(j), -ECHILD);
ee531d94
LP
2169
2170 fd = sd_journal_get_fd(j);
2171 if (fd < 0)
2172 return fd;
2173
2174 return POLLIN;
2175}
2176
39c155ea
LP
2177_public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2178 int fd;
2179
1ae464e0
TA
2180 assert_return(j, -EINVAL);
2181 assert_return(!journal_pid_changed(j), -ECHILD);
2182 assert_return(timeout_usec, -EINVAL);
39c155ea
LP
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
50f20cfd 2200static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
a963990f 2201 Directory *d;
50f20cfd
LP
2202 int r;
2203
2204 assert(j);
2205 assert(e);
2206
2207 /* Is this a subdirectory we watch? */
a963990f
LP
2208 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2209 if (d) {
2210 sd_id128_t id;
50f20cfd 2211
de2c3907
LP
2212 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2213 (endswith(e->name, ".journal") ||
2214 endswith(e->name, ".journal~"))) {
50f20cfd
LP
2215
2216 /* Event for a journal file */
2217
2218 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
a963990f 2219 r = add_file(j, d->path, e->name);
6fe391c5 2220 if (r < 0) {
c33b3297
MS
2221 log_debug_errno(r, "Failed to add file %s/%s: %m",
2222 d->path, e->name);
3ac251b8 2223 set_put_error(j, r);
6fe391c5 2224 }
a963990f 2225
5e6870ea 2226 } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
50f20cfd 2227
a963990f 2228 r = remove_file(j, d->path, e->name);
50f20cfd 2229 if (r < 0)
da927ba9 2230 log_debug_errno(r, "Failed to remove file %s/%s: %m", d->path, e->name);
50f20cfd
LP
2231 }
2232
a963990f 2233 } else if (!d->is_root && e->len == 0) {
50f20cfd 2234
a963990f 2235 /* Event for a subdirectory */
50f20cfd 2236
a963990f
LP
2237 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
2238 r = remove_directory(j, d);
50f20cfd 2239 if (r < 0)
da927ba9 2240 log_debug_errno(r, "Failed to remove directory %s: %m", d->path);
50f20cfd
LP
2241 }
2242
50f20cfd 2243
a963990f 2244 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
50f20cfd 2245
a963990f 2246 /* Event for root directory */
50f20cfd 2247
a963990f
LP
2248 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2249 r = add_directory(j, d->path, e->name);
50f20cfd 2250 if (r < 0)
da927ba9 2251 log_debug_errno(r, "Failed to add directory %s/%s: %m", d->path, e->name);
50f20cfd
LP
2252 }
2253 }
2254
2255 return;
2256 }
2257
2258 if (e->mask & IN_IGNORED)
2259 return;
2260
2261 log_warning("Unknown inotify event.");
2262}
2263
a963990f
LP
2264static 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
a5344d2c 2275_public_ int sd_journal_process(sd_journal *j) {
a963990f 2276 bool got_something = false;
50f20cfd 2277
1ae464e0
TA
2278 assert_return(j, -EINVAL);
2279 assert_return(!journal_pid_changed(j), -ECHILD);
50f20cfd 2280
39c155ea
LP
2281 j->last_process_usec = now(CLOCK_MONOTONIC);
2282
50f20cfd 2283 for (;;) {
f7c1ad4f 2284 uint8_t buffer[INOTIFY_EVENT_MAX] _alignas_(struct inotify_event);
50f20cfd
LP
2285 struct inotify_event *e;
2286 ssize_t l;
2287
2288 l = read(j->inotify_fd, buffer, sizeof(buffer));
2289 if (l < 0) {
a963990f
LP
2290 if (errno == EAGAIN || errno == EINTR)
2291 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
50f20cfd
LP
2292
2293 return -errno;
2294 }
2295
a963990f
LP
2296 got_something = true;
2297
f7c1ad4f 2298 FOREACH_INOTIFY_EVENT(e, buffer, l)
50f20cfd 2299 process_inotify_event(j, e);
50f20cfd
LP
2300 }
2301}
6ad1d1c3 2302
e02d1cf7 2303_public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
a963990f 2304 int r;
39c155ea 2305 uint64_t t;
e02d1cf7 2306
1ae464e0
TA
2307 assert_return(j, -EINVAL);
2308 assert_return(!journal_pid_changed(j), -ECHILD);
e02d1cf7 2309
a963990f
LP
2310 if (j->inotify_fd < 0) {
2311
2312 /* This is the first invocation, hence create the
2313 * inotify watch */
2314 r = sd_journal_get_fd(j);
2315 if (r < 0)
2316 return r;
2317
2318 /* The journal might have changed since the context
2319 * object was created and we weren't watching before,
2320 * hence don't wait for anything, and return
2321 * immediately. */
2322 return determine_change(j);
2323 }
2324
39c155ea
LP
2325 r = sd_journal_get_timeout(j, &t);
2326 if (r < 0)
2327 return r;
2328
2329 if (t != (uint64_t) -1) {
2330 usec_t n;
2331
2332 n = now(CLOCK_MONOTONIC);
2333 t = t > n ? t - n : 0;
85210bff 2334
39c155ea
LP
2335 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2336 timeout_usec = t;
85210bff
LP
2337 }
2338
a963990f
LP
2339 do {
2340 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2341 } while (r == -EINTR);
e02d1cf7
LP
2342
2343 if (r < 0)
2344 return r;
2345
a963990f 2346 return sd_journal_process(j);
e02d1cf7
LP
2347}
2348
08984293
LP
2349_public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2350 Iterator i;
2351 JournalFile *f;
2352 bool first = true;
581483bf 2353 uint64_t fmin = 0, tmax = 0;
08984293
LP
2354 int r;
2355
1ae464e0
TA
2356 assert_return(j, -EINVAL);
2357 assert_return(!journal_pid_changed(j), -ECHILD);
2358 assert_return(from || to, -EINVAL);
2359 assert_return(from != to, -EINVAL);
08984293 2360
c1f906bd 2361 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
08984293
LP
2362 usec_t fr, t;
2363
2364 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
9f8d2983
LP
2365 if (r == -ENOENT)
2366 continue;
08984293
LP
2367 if (r < 0)
2368 return r;
2369 if (r == 0)
2370 continue;
2371
2372 if (first) {
581483bf
LP
2373 fmin = fr;
2374 tmax = t;
08984293
LP
2375 first = false;
2376 } else {
581483bf
LP
2377 fmin = MIN(fr, fmin);
2378 tmax = MAX(t, tmax);
08984293
LP
2379 }
2380 }
2381
581483bf
LP
2382 if (from)
2383 *from = fmin;
2384 if (to)
2385 *to = tmax;
2386
08984293
LP
2387 return first ? 0 : 1;
2388}
2389
2390_public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2391 Iterator i;
2392 JournalFile *f;
1651e2c6 2393 bool found = false;
08984293
LP
2394 int r;
2395
1ae464e0
TA
2396 assert_return(j, -EINVAL);
2397 assert_return(!journal_pid_changed(j), -ECHILD);
2398 assert_return(from || to, -EINVAL);
2399 assert_return(from != to, -EINVAL);
08984293 2400
c1f906bd 2401 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
08984293
LP
2402 usec_t fr, t;
2403
2404 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
9f8d2983
LP
2405 if (r == -ENOENT)
2406 continue;
08984293
LP
2407 if (r < 0)
2408 return r;
2409 if (r == 0)
2410 continue;
2411
1651e2c6 2412 if (found) {
08984293 2413 if (from)
1651e2c6 2414 *from = MIN(fr, *from);
08984293 2415 if (to)
1651e2c6 2416 *to = MAX(t, *to);
08984293
LP
2417 } else {
2418 if (from)
1651e2c6 2419 *from = fr;
08984293 2420 if (to)
1651e2c6
ZJS
2421 *to = t;
2422 found = true;
08984293
LP
2423 }
2424 }
2425
1651e2c6 2426 return found;
08984293
LP
2427}
2428
dca6219e
LP
2429void journal_print_header(sd_journal *j) {
2430 Iterator i;
2431 JournalFile *f;
2432 bool newline = false;
2433
2434 assert(j);
2435
c1f906bd 2436 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
dca6219e
LP
2437 if (newline)
2438 putchar('\n');
2439 else
2440 newline = true;
2441
2442 journal_file_print_header(f);
2443 }
2444}
08984293 2445
a1a03e30
LP
2446_public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2447 Iterator i;
2448 JournalFile *f;
2449 uint64_t sum = 0;
2450
1ae464e0
TA
2451 assert_return(j, -EINVAL);
2452 assert_return(!journal_pid_changed(j), -ECHILD);
2453 assert_return(bytes, -EINVAL);
a1a03e30 2454
c1f906bd 2455 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
a1a03e30
LP
2456 struct stat st;
2457
2458 if (fstat(f->fd, &st) < 0)
2459 return -errno;
2460
2461 sum += (uint64_t) st.st_blocks * 512ULL;
2462 }
2463
2464 *bytes = sum;
2465 return 0;
2466}
2467
3c1668da
LP
2468_public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2469 char *f;
2470
1ae464e0
TA
2471 assert_return(j, -EINVAL);
2472 assert_return(!journal_pid_changed(j), -ECHILD);
2473 assert_return(!isempty(field), -EINVAL);
2474 assert_return(field_is_valid(field), -EINVAL);
3c1668da
LP
2475
2476 f = strdup(field);
2477 if (!f)
2478 return -ENOMEM;
2479
2480 free(j->unique_field);
2481 j->unique_field = f;
2482 j->unique_file = NULL;
2483 j->unique_offset = 0;
360af4cf 2484 j->unique_file_lost = false;
3c1668da
LP
2485
2486 return 0;
2487}
2488
2489_public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
3c1668da 2490 size_t k;
19a2bd80 2491
1ae464e0
TA
2492 assert_return(j, -EINVAL);
2493 assert_return(!journal_pid_changed(j), -ECHILD);
2494 assert_return(data, -EINVAL);
2495 assert_return(l, -EINVAL);
2496 assert_return(j->unique_field, -EINVAL);
19a2bd80 2497
3c1668da 2498 k = strlen(j->unique_field);
19a2bd80 2499
3c1668da 2500 if (!j->unique_file) {
360af4cf
ZJS
2501 if (j->unique_file_lost)
2502 return 0;
2503
c1f906bd 2504 j->unique_file = ordered_hashmap_first(j->files);
3c1668da
LP
2505 if (!j->unique_file)
2506 return 0;
360af4cf 2507
3c1668da
LP
2508 j->unique_offset = 0;
2509 }
19a2bd80 2510
3c1668da
LP
2511 for (;;) {
2512 JournalFile *of;
2513 Iterator i;
ae97089d 2514 Object *o;
3c1668da
LP
2515 const void *odata;
2516 size_t ol;
2517 bool found;
ae97089d 2518 int r;
3c1668da 2519
bdc02927 2520 /* Proceed to next data object in the field's linked list */
3c1668da
LP
2521 if (j->unique_offset == 0) {
2522 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2523 if (r < 0)
2524 return r;
2525
2526 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2527 } else {
2528 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2529 if (r < 0)
2530 return r;
2531
2532 j->unique_offset = le64toh(o->data.next_field_offset);
2533 }
2534
2535 /* We reached the end of the list? Then start again, with the next file */
2536 if (j->unique_offset == 0) {
c1f906bd 2537 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
360af4cf 2538 if (!j->unique_file)
3c1668da
LP
2539 return 0;
2540
3c1668da
LP
2541 continue;
2542 }
2543
d05089d8
MS
2544 /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED
2545 * instead, so that we can look at this data object at the same
3c1668da 2546 * time as one on another file */
d05089d8 2547 r = journal_file_move_to_object(j->unique_file, OBJECT_UNUSED, j->unique_offset, &o);
3c1668da
LP
2548 if (r < 0)
2549 return r;
2550
2551 /* Let's do the type check by hand, since we used 0 context above. */
ae97089d 2552 if (o->object.type != OBJECT_DATA) {
36202fd2 2553 log_debug("%s:offset " OFSfmt ": object has type %d, expected %d",
ae97089d
ZJS
2554 j->unique_file->path, j->unique_offset,
2555 o->object.type, OBJECT_DATA);
3c1668da 2556 return -EBADMSG;
ae97089d
ZJS
2557 }
2558
93b73b06 2559 r = return_data(j, j->unique_file, o, &odata, &ol);
3c1668da
LP
2560 if (r < 0)
2561 return r;
2562
0f99f74a
ZJS
2563 /* Check if we have at least the field name and "=". */
2564 if (ol <= k) {
2565 log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
2566 j->unique_file->path, j->unique_offset,
2567 ol, k + 1);
2568 return -EBADMSG;
2569 }
2570
2571 if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') {
2572 log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"",
2573 j->unique_file->path, j->unique_offset,
2574 j->unique_field);
2575 return -EBADMSG;
2576 }
2577
3c1668da
LP
2578 /* OK, now let's see if we already returned this data
2579 * object by checking if it exists in the earlier
2580 * traversed files. */
2581 found = false;
c1f906bd 2582 ORDERED_HASHMAP_FOREACH(of, j->files, i) {
3c1668da
LP
2583 Object *oo;
2584 uint64_t op;
2585
2586 if (of == j->unique_file)
2587 break;
2588
2589 /* Skip this file it didn't have any fields
2590 * indexed */
2591 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2592 le64toh(of->header->n_fields) <= 0)
2593 continue;
2594
2595 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2596 if (r < 0)
2597 return r;
2598
2599 if (r > 0)
2600 found = true;
2601 }
2602
06cc69d4
JJ
2603 if (found)
2604 continue;
2605
93b73b06 2606 r = return_data(j, j->unique_file, o, data, l);
3c1668da
LP
2607 if (r < 0)
2608 return r;
2609
2610 return 1;
2611 }
2612}
2613
115646c7 2614_public_ void sd_journal_restart_unique(sd_journal *j) {
3c1668da
LP
2615 if (!j)
2616 return;
2617
2618 j->unique_file = NULL;
2619 j->unique_offset = 0;
360af4cf 2620 j->unique_file_lost = false;
3c1668da 2621}
85210bff
LP
2622
2623_public_ int sd_journal_reliable_fd(sd_journal *j) {
1ae464e0
TA
2624 assert_return(j, -EINVAL);
2625 assert_return(!journal_pid_changed(j), -ECHILD);
85210bff
LP
2626
2627 return !j->on_network;
2628}
d4205751
LP
2629
2630static char *lookup_field(const char *field, void *userdata) {
2631 sd_journal *j = userdata;
2632 const void *data;
2633 size_t size, d;
2634 int r;
2635
2636 assert(field);
2637 assert(j);
2638
2639 r = sd_journal_get_data(j, field, &data, &size);
2640 if (r < 0 ||
2641 size > REPLACE_VAR_MAX)
2642 return strdup(field);
2643
2644 d = strlen(field) + 1;
2645
2646 return strndup((const char*) data + d, size - d);
2647}
2648
2649_public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
2650 const void *data;
2651 size_t size;
2652 sd_id128_t id;
2653 _cleanup_free_ char *text = NULL, *cid = NULL;
2654 char *t;
2655 int r;
2656
1ae464e0
TA
2657 assert_return(j, -EINVAL);
2658 assert_return(!journal_pid_changed(j), -ECHILD);
2659 assert_return(ret, -EINVAL);
d4205751
LP
2660
2661 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
2662 if (r < 0)
2663 return r;
2664
2665 cid = strndup((const char*) data + 11, size - 11);
2666 if (!cid)
2667 return -ENOMEM;
2668
2669 r = sd_id128_from_string(cid, &id);
2670 if (r < 0)
2671 return r;
2672
844ec79b 2673 r = catalog_get(CATALOG_DATABASE, id, &text);
d4205751
LP
2674 if (r < 0)
2675 return r;
2676
2677 t = replace_var(text, lookup_field, j);
2678 if (!t)
2679 return -ENOMEM;
2680
2681 *ret = t;
2682 return 0;
2683}
8f1e860f
LP
2684
2685_public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
1ae464e0 2686 assert_return(ret, -EINVAL);
8f1e860f 2687
844ec79b 2688 return catalog_get(CATALOG_DATABASE, id, ret);
8f1e860f 2689}
93b73b06
LP
2690
2691_public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
1ae464e0
TA
2692 assert_return(j, -EINVAL);
2693 assert_return(!journal_pid_changed(j), -ECHILD);
93b73b06
LP
2694
2695 j->data_threshold = sz;
2696 return 0;
2697}
2698
2699_public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
1ae464e0
TA
2700 assert_return(j, -EINVAL);
2701 assert_return(!journal_pid_changed(j), -ECHILD);
2702 assert_return(sz, -EINVAL);
93b73b06
LP
2703
2704 *sz = j->data_threshold;
2705 return 0;
2706}