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