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