]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/sd-journal.c
journalctl: add new --fields switch to dump all currently used field names
[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
eb86030e
LP
1341 if (j->fields_file == f) {
1342 j->fields_file = ordered_hashmap_next(j->files, j->fields_file->path);
1343 j->fields_offset = 0;
1344 if (!j->fields_file)
1345 j->fields_file_lost = true;
1346 }
1347
50f20cfd
LP
1348 journal_file_close(f);
1349
a963990f 1350 j->current_invalidate_counter ++;
3fbf9cbb
LP
1351}
1352
d617408e
LP
1353static int dirname_is_machine_id(const char *fn) {
1354 sd_id128_t id, machine;
1355 int r;
1356
1357 r = sd_id128_get_machine(&machine);
1358 if (r < 0)
1359 return r;
1360
1361 r = sd_id128_from_string(fn, &id);
1362 if (r < 0)
1363 return r;
1364
1365 return sd_id128_equal(id, machine);
1366}
1367
a963990f 1368static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
7fd1b19b 1369 _cleanup_free_ char *path = NULL;
7fd1b19b 1370 _cleanup_closedir_ DIR *d = NULL;
d617408e 1371 struct dirent *de = NULL;
a963990f 1372 Directory *m;
d617408e 1373 int r, k;
3fbf9cbb
LP
1374
1375 assert(j);
1376 assert(prefix);
a963990f 1377 assert(dirname);
3fbf9cbb 1378
d95b1fb3
ZJS
1379 log_debug("Considering %s/%s.", prefix, dirname);
1380
cf244689 1381 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
d617408e 1382 !(dirname_is_machine_id(dirname) > 0 || path_startswith(prefix, "/run")))
cf244689
LP
1383 return 0;
1384
b7def684 1385 path = strjoin(prefix, "/", dirname, NULL);
d617408e
LP
1386 if (!path) {
1387 r = -ENOMEM;
1388 goto fail;
1389 }
3fbf9cbb 1390
a963990f 1391 d = opendir(path);
3fbf9cbb 1392 if (!d) {
d617408e
LP
1393 r = log_debug_errno(errno, "Failed to open directory %s: %m", path);
1394 goto fail;
3fbf9cbb
LP
1395 }
1396
a963990f
LP
1397 m = hashmap_get(j->directories_by_path, path);
1398 if (!m) {
1399 m = new0(Directory, 1);
d617408e
LP
1400 if (!m) {
1401 r = -ENOMEM;
1402 goto fail;
1403 }
a963990f
LP
1404
1405 m->is_root = false;
1406 m->path = path;
1407
1408 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
a963990f 1409 free(m);
d617408e
LP
1410 r = -ENOMEM;
1411 goto fail;
a963990f
LP
1412 }
1413
a50d7d43 1414 path = NULL; /* avoid freeing in cleanup */
a963990f
LP
1415 j->current_invalidate_counter ++;
1416
5ec76417 1417 log_debug("Directory %s added.", m->path);
a963990f 1418
a50d7d43 1419 } else if (m->is_root)
a963990f 1420 return 0;
a963990f
LP
1421
1422 if (m->wd <= 0 && j->inotify_fd >= 0) {
1423
1424 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1425 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
5e6870ea 1426 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
4a842cad 1427 IN_ONLYDIR);
a963990f
LP
1428
1429 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1430 inotify_rm_watch(j->inotify_fd, m->wd);
1431 }
1432
d617408e 1433 FOREACH_DIRENT_ALL(de, d, return log_debug_errno(errno, "Failed to read directory %s: %m", m->path)) {
a963990f 1434
de2c3907 1435 if (dirent_is_file_with_suffix(de, ".journal") ||
d617408e
LP
1436 dirent_is_file_with_suffix(de, ".journal~"))
1437 (void) add_file(j, m->path, de->d_name);
a963990f
LP
1438 }
1439
85210bff
LP
1440 check_network(j, dirfd(d));
1441
a963990f 1442 return 0;
d617408e
LP
1443
1444fail:
5768d259 1445 k = journal_put_error(j, r, path ?: dirname);
d617408e
LP
1446 if (k < 0)
1447 return k;
1448
1449 return r;
a963990f
LP
1450}
1451
d617408e 1452static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) {
7fd1b19b 1453 _cleanup_closedir_ DIR *d = NULL;
d617408e 1454 struct dirent *de;
a963990f 1455 Directory *m;
d617408e 1456 int r, k;
a963990f
LP
1457
1458 assert(j);
1459 assert(p);
1460
1461 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1462 !path_startswith(p, "/run"))
1463 return -EINVAL;
1464
89739579 1465 if (j->prefix)
63c372cb 1466 p = strjoina(j->prefix, p);
b6741478 1467
a963990f 1468 d = opendir(p);
d617408e
LP
1469 if (!d) {
1470 if (errno == ENOENT && missing_ok)
1471 return 0;
1472
1473 r = log_debug_errno(errno, "Failed to open root directory %s: %m", p);
1474 goto fail;
1475 }
a963990f
LP
1476
1477 m = hashmap_get(j->directories_by_path, p);
1478 if (!m) {
1479 m = new0(Directory, 1);
d617408e
LP
1480 if (!m) {
1481 r = -ENOMEM;
1482 goto fail;
1483 }
a963990f
LP
1484
1485 m->is_root = true;
1486 m->path = strdup(p);
1487 if (!m->path) {
a963990f 1488 free(m);
d617408e
LP
1489 r = -ENOMEM;
1490 goto fail;
a963990f
LP
1491 }
1492
1493 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
a963990f
LP
1494 free(m->path);
1495 free(m);
d617408e
LP
1496 r = -ENOMEM;
1497 goto fail;
a963990f
LP
1498 }
1499
1500 j->current_invalidate_counter ++;
1501
5ec76417 1502 log_debug("Root directory %s added.", m->path);
a963990f 1503
a50d7d43 1504 } else if (!m->is_root)
a963990f 1505 return 0;
50f20cfd 1506
a963990f
LP
1507 if (m->wd <= 0 && j->inotify_fd >= 0) {
1508
1509 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1510 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
4a842cad 1511 IN_ONLYDIR);
a963990f
LP
1512
1513 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1514 inotify_rm_watch(j->inotify_fd, m->wd);
1515 }
50f20cfd 1516
5302ebe1
ZJS
1517 if (j->no_new_files)
1518 return 0;
1519
d617408e 1520 FOREACH_DIRENT_ALL(de, d, return log_debug_errno(errno, "Failed to read directory %s: %m", m->path)) {
a963990f 1521 sd_id128_t id;
3fbf9cbb 1522
de2c3907 1523 if (dirent_is_file_with_suffix(de, ".journal") ||
d617408e
LP
1524 dirent_is_file_with_suffix(de, ".journal~"))
1525 (void) add_file(j, m->path, de->d_name);
1526 else if (IN_SET(de->d_type, DT_DIR, DT_LNK, DT_UNKNOWN) &&
1527 sd_id128_from_string(de->d_name, &id) >= 0)
1528 (void) add_directory(j, m->path, de->d_name);
3fbf9cbb
LP
1529 }
1530
85210bff
LP
1531 check_network(j, dirfd(d));
1532
a963990f 1533 return 0;
d617408e
LP
1534
1535fail:
5768d259 1536 k = journal_put_error(j, r, p);
d617408e
LP
1537 if (k < 0)
1538 return k;
1539
1540 return r;
a963990f
LP
1541}
1542
b2b46f91 1543static void remove_directory(sd_journal *j, Directory *d) {
a963990f
LP
1544 assert(j);
1545
1546 if (d->wd > 0) {
1547 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1548
1549 if (j->inotify_fd >= 0)
1550 inotify_rm_watch(j->inotify_fd, d->wd);
1551 }
1552
1553 hashmap_remove(j->directories_by_path, d->path);
1554
1555 if (d->is_root)
5ec76417 1556 log_debug("Root directory %s removed.", d->path);
a963990f 1557 else
5ec76417 1558 log_debug("Directory %s removed.", d->path);
a963990f
LP
1559
1560 free(d->path);
1561 free(d);
3fbf9cbb
LP
1562}
1563
89739579 1564static int add_search_paths(sd_journal *j) {
d617408e
LP
1565
1566 static const char search_paths[] =
a963990f
LP
1567 "/run/log/journal\0"
1568 "/var/log/journal\0";
1569 const char *p;
50f20cfd
LP
1570
1571 assert(j);
50f20cfd 1572
a963990f
LP
1573 /* We ignore most errors here, since the idea is to only open
1574 * what's actually accessible, and ignore the rest. */
50f20cfd 1575
d617408e
LP
1576 NULSTR_FOREACH(p, search_paths)
1577 (void) add_root_directory(j, p, true);
50f20cfd 1578
a963990f 1579 return 0;
50f20cfd
LP
1580}
1581
5302ebe1
ZJS
1582static int add_current_paths(sd_journal *j) {
1583 Iterator i;
1584 JournalFile *f;
1585
1586 assert(j);
1587 assert(j->no_new_files);
1588
1589 /* Simply adds all directories for files we have open as
1590 * "root" directories. We don't expect errors here, so we
1591 * treat them as fatal. */
1592
c1f906bd 1593 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
5302ebe1 1594 _cleanup_free_ char *dir;
e9174f29 1595 int r;
5302ebe1
ZJS
1596
1597 dir = dirname_malloc(f->path);
1598 if (!dir)
1599 return -ENOMEM;
1600
d617408e
LP
1601 r = add_root_directory(j, dir, true);
1602 if (r < 0)
5302ebe1 1603 return r;
5302ebe1
ZJS
1604 }
1605
1606 return 0;
1607}
1608
a963990f 1609static int allocate_inotify(sd_journal *j) {
50f20cfd 1610 assert(j);
50f20cfd 1611
a963990f
LP
1612 if (j->inotify_fd < 0) {
1613 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1614 if (j->inotify_fd < 0)
1615 return -errno;
1616 }
50f20cfd 1617
a963990f 1618 if (!j->directories_by_wd) {
d5099efc 1619 j->directories_by_wd = hashmap_new(NULL);
a963990f
LP
1620 if (!j->directories_by_wd)
1621 return -ENOMEM;
50f20cfd 1622 }
a963990f
LP
1623
1624 return 0;
50f20cfd
LP
1625}
1626
7827b1a1 1627static sd_journal *journal_new(int flags, const char *path) {
a963990f 1628 sd_journal *j;
50f20cfd 1629
a963990f
LP
1630 j = new0(sd_journal, 1);
1631 if (!j)
1632 return NULL;
50f20cfd 1633
a65f06bb 1634 j->original_pid = getpid();
a963990f
LP
1635 j->inotify_fd = -1;
1636 j->flags = flags;
93b73b06 1637 j->data_threshold = DEFAULT_DATA_THRESHOLD;
50f20cfd 1638
7827b1a1
LP
1639 if (path) {
1640 j->path = strdup(path);
6180fc61
ZJS
1641 if (!j->path)
1642 goto fail;
7827b1a1
LP
1643 }
1644
c1f906bd 1645 j->files = ordered_hashmap_new(&string_hash_ops);
d5099efc 1646 j->directories_by_path = hashmap_new(&string_hash_ops);
84168d80 1647 j->mmap = mmap_cache_new();
3ac251b8 1648 if (!j->files || !j->directories_by_path || !j->mmap)
6180fc61 1649 goto fail;
16e9f408 1650
a963990f 1651 return j;
6180fc61
ZJS
1652
1653fail:
1654 sd_journal_close(j);
1655 return NULL;
50f20cfd
LP
1656}
1657
a5344d2c 1658_public_ int sd_journal_open(sd_journal **ret, int flags) {
87d2c1ff 1659 sd_journal *j;
3fbf9cbb 1660 int r;
87d2c1ff 1661
1ae464e0 1662 assert_return(ret, -EINVAL);
b6741478 1663 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL);
87d2c1ff 1664
7827b1a1 1665 j = journal_new(flags, NULL);
87d2c1ff
LP
1666 if (!j)
1667 return -ENOMEM;
1668
89739579 1669 r = add_search_paths(j);
a963990f 1670 if (r < 0)
50f20cfd 1671 goto fail;
50f20cfd 1672
a963990f
LP
1673 *ret = j;
1674 return 0;
cf244689 1675
a963990f
LP
1676fail:
1677 sd_journal_close(j);
87d2c1ff 1678
a963990f
LP
1679 return r;
1680}
50f20cfd 1681
b6741478
LP
1682_public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
1683 _cleanup_free_ char *root = NULL, *class = NULL;
1684 sd_journal *j;
1685 char *p;
1686 int r;
1687
1688 assert_return(machine, -EINVAL);
1689 assert_return(ret, -EINVAL);
1690 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL);
affcf189 1691 assert_return(machine_name_is_valid(machine), -EINVAL);
b6741478 1692
63c372cb 1693 p = strjoina("/run/systemd/machines/", machine);
b6741478
LP
1694 r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL);
1695 if (r == -ENOENT)
1696 return -EHOSTDOWN;
1697 if (r < 0)
1698 return r;
1699 if (!root)
1700 return -ENODATA;
1701
1702 if (!streq_ptr(class, "container"))
1703 return -EIO;
1704
1705 j = journal_new(flags, NULL);
1706 if (!j)
1707 return -ENOMEM;
1708
89739579
LP
1709 j->prefix = root;
1710 root = NULL;
1711
1712 r = add_search_paths(j);
b6741478
LP
1713 if (r < 0)
1714 goto fail;
1715
1716 *ret = j;
1717 return 0;
1718
1719fail:
1720 sd_journal_close(j);
1721 return r;
1722}
1723
a963990f
LP
1724_public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1725 sd_journal *j;
1726 int r;
87d2c1ff 1727
1ae464e0
TA
1728 assert_return(ret, -EINVAL);
1729 assert_return(path, -EINVAL);
1730 assert_return(flags == 0, -EINVAL);
87d2c1ff 1731
7827b1a1 1732 j = journal_new(flags, path);
a963990f
LP
1733 if (!j)
1734 return -ENOMEM;
3fbf9cbb 1735
d617408e
LP
1736 r = add_root_directory(j, path, false);
1737 if (r < 0)
a963990f 1738 goto fail;
87d2c1ff
LP
1739
1740 *ret = j;
1741 return 0;
1742
1743fail:
1744 sd_journal_close(j);
1745
1746 return r;
a963990f 1747}
87d2c1ff 1748
5302ebe1
ZJS
1749_public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
1750 sd_journal *j;
1751 const char **path;
1752 int r;
1753
1ae464e0
TA
1754 assert_return(ret, -EINVAL);
1755 assert_return(flags == 0, -EINVAL);
5302ebe1
ZJS
1756
1757 j = journal_new(flags, NULL);
1758 if (!j)
1759 return -ENOMEM;
1760
1761 STRV_FOREACH(path, paths) {
1762 r = add_any_file(j, *path);
d617408e 1763 if (r < 0)
5302ebe1 1764 goto fail;
5302ebe1
ZJS
1765 }
1766
1767 j->no_new_files = true;
1768
1769 *ret = j;
1770 return 0;
1771
1772fail:
1773 sd_journal_close(j);
1774
1775 return r;
1776}
1777
a5344d2c 1778_public_ void sd_journal_close(sd_journal *j) {
a963990f
LP
1779 Directory *d;
1780 JournalFile *f;
5768d259 1781 char *p;
a963990f 1782
a5344d2c
LP
1783 if (!j)
1784 return;
87d2c1ff 1785
54b1da83
LP
1786 sd_journal_flush_matches(j);
1787
c1f906bd 1788 while ((f = ordered_hashmap_steal_first(j->files)))
a963990f 1789 journal_file_close(f);
50f20cfd 1790
c1f906bd 1791 ordered_hashmap_free(j->files);
260a2be4 1792
a963990f
LP
1793 while ((d = hashmap_first(j->directories_by_path)))
1794 remove_directory(j, d);
260a2be4 1795
a963990f
LP
1796 while ((d = hashmap_first(j->directories_by_wd)))
1797 remove_directory(j, d);
87d2c1ff 1798
a963990f
LP
1799 hashmap_free(j->directories_by_path);
1800 hashmap_free(j->directories_by_wd);
1cc101f1 1801
03e334a1 1802 safe_close(j->inotify_fd);
50f20cfd 1803
bf807d4d
LP
1804 if (j->mmap) {
1805 log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap));
16e9f408 1806 mmap_cache_unref(j->mmap);
bf807d4d 1807 }
16e9f408 1808
5768d259
LP
1809 while ((p = hashmap_steal_first(j->errors)))
1810 free(p);
1811 hashmap_free(j->errors);
1812
7827b1a1 1813 free(j->path);
89739579 1814 free(j->prefix);
3c1668da 1815 free(j->unique_field);
eb86030e 1816 free(j->fields_buffer);
87d2c1ff
LP
1817 free(j);
1818}
3fbf9cbb 1819
a5344d2c 1820_public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
3fbf9cbb
LP
1821 Object *o;
1822 JournalFile *f;
1823 int r;
1824
1ae464e0
TA
1825 assert_return(j, -EINVAL);
1826 assert_return(!journal_pid_changed(j), -ECHILD);
1827 assert_return(ret, -EINVAL);
3fbf9cbb
LP
1828
1829 f = j->current_file;
1830 if (!f)
de190aef 1831 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1832
1833 if (f->current_offset <= 0)
de190aef 1834 return -EADDRNOTAVAIL;
3fbf9cbb 1835
de190aef 1836 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1837 if (r < 0)
1838 return r;
1839
1840 *ret = le64toh(o->entry.realtime);
de190aef 1841 return 0;
3fbf9cbb
LP
1842}
1843
a5344d2c 1844_public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
3fbf9cbb
LP
1845 Object *o;
1846 JournalFile *f;
1847 int r;
1848 sd_id128_t id;
1849
1ae464e0
TA
1850 assert_return(j, -EINVAL);
1851 assert_return(!journal_pid_changed(j), -ECHILD);
3fbf9cbb
LP
1852
1853 f = j->current_file;
1854 if (!f)
de190aef 1855 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1856
1857 if (f->current_offset <= 0)
de190aef 1858 return -EADDRNOTAVAIL;
3fbf9cbb 1859
de190aef 1860 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1861 if (r < 0)
1862 return r;
1863
de190aef
LP
1864 if (ret_boot_id)
1865 *ret_boot_id = o->entry.boot_id;
1866 else {
1867 r = sd_id128_get_boot(&id);
1868 if (r < 0)
1869 return r;
3fbf9cbb 1870
de190aef 1871 if (!sd_id128_equal(id, o->entry.boot_id))
df50185b 1872 return -ESTALE;
de190aef 1873 }
3fbf9cbb 1874
14a65d65
LP
1875 if (ret)
1876 *ret = le64toh(o->entry.monotonic);
1877
de190aef 1878 return 0;
3fbf9cbb
LP
1879}
1880
362a3f81
LP
1881static bool field_is_valid(const char *field) {
1882 const char *p;
1883
1884 assert(field);
1885
1886 if (isempty(field))
1887 return false;
1888
1889 if (startswith(field, "__"))
1890 return false;
1891
1892 for (p = field; *p; p++) {
1893
1894 if (*p == '_')
1895 continue;
1896
1897 if (*p >= 'A' && *p <= 'Z')
1898 continue;
1899
1900 if (*p >= '0' && *p <= '9')
1901 continue;
1902
1903 return false;
1904 }
1905
1906 return true;
1907}
1908
a5344d2c 1909_public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
3fbf9cbb
LP
1910 JournalFile *f;
1911 uint64_t i, n;
1912 size_t field_length;
1913 int r;
1914 Object *o;
1915
1ae464e0
TA
1916 assert_return(j, -EINVAL);
1917 assert_return(!journal_pid_changed(j), -ECHILD);
1918 assert_return(field, -EINVAL);
1919 assert_return(data, -EINVAL);
1920 assert_return(size, -EINVAL);
1921 assert_return(field_is_valid(field), -EINVAL);
3fbf9cbb
LP
1922
1923 f = j->current_file;
1924 if (!f)
de190aef 1925 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1926
1927 if (f->current_offset <= 0)
de190aef 1928 return -EADDRNOTAVAIL;
3fbf9cbb 1929
de190aef 1930 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1931 if (r < 0)
1932 return r;
1933
1934 field_length = strlen(field);
1935
1936 n = journal_file_entry_n_items(o);
1937 for (i = 0; i < n; i++) {
4fd052ae
FC
1938 uint64_t p, l;
1939 le64_t le_hash;
3fbf9cbb 1940 size_t t;
1ec7120e 1941 int compression;
3fbf9cbb
LP
1942
1943 p = le64toh(o->entry.items[i].object_offset);
807e17f0 1944 le_hash = o->entry.items[i].hash;
de190aef 1945 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
3fbf9cbb
LP
1946 if (r < 0)
1947 return r;
1948
de190aef 1949 if (le_hash != o->data.hash)
de7b95cd
LP
1950 return -EBADMSG;
1951
3fbf9cbb
LP
1952 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1953
1ec7120e 1954 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
3b1a55e1
ZJS
1955 if (compression) {
1956#if defined(HAVE_XZ) || defined(HAVE_LZ4)
2aaec9b4 1957 r = decompress_startswith(compression,
3b1a55e1
ZJS
1958 o->data.payload, l,
1959 &f->compress_buffer, &f->compress_buffer_size,
2aaec9b4
ZJS
1960 field, field_length, '=');
1961 if (r < 0)
1962 log_debug_errno(r, "Cannot decompress %s object of length %zu at offset "OFSfmt": %m",
1963 object_compressed_to_string(compression), l, p);
1964 else if (r > 0) {
3b1a55e1 1965
fa1c4b51 1966 size_t rsize;
3b1a55e1
ZJS
1967
1968 r = decompress_blob(compression,
1969 o->data.payload, l,
1970 &f->compress_buffer, &f->compress_buffer_size, &rsize,
1971 j->data_threshold);
1972 if (r < 0)
1973 return r;
807e17f0 1974
3b1a55e1
ZJS
1975 *data = f->compress_buffer;
1976 *size = (size_t) rsize;
807e17f0 1977
3b1a55e1
ZJS
1978 return 0;
1979 }
1980#else
1981 return -EPROTONOSUPPORT;
1982#endif
807e17f0
LP
1983 } else if (l >= field_length+1 &&
1984 memcmp(o->data.payload, field, field_length) == 0 &&
1985 o->data.payload[field_length] == '=') {
3fbf9cbb 1986
161e54f8 1987 t = (size_t) l;
3fbf9cbb 1988
161e54f8
LP
1989 if ((uint64_t) t != l)
1990 return -E2BIG;
3fbf9cbb 1991
161e54f8
LP
1992 *data = o->data.payload;
1993 *size = t;
3fbf9cbb 1994
99613ec5 1995 return 0;
161e54f8 1996 }
3fbf9cbb 1997
de190aef 1998 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
161e54f8
LP
1999 if (r < 0)
2000 return r;
3fbf9cbb
LP
2001 }
2002
de190aef 2003 return -ENOENT;
3fbf9cbb
LP
2004}
2005
93b73b06 2006static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
3c1668da
LP
2007 size_t t;
2008 uint64_t l;
3b1a55e1 2009 int compression;
3c1668da
LP
2010
2011 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2012 t = (size_t) l;
2013
2014 /* We can't read objects larger than 4G on a 32bit machine */
2015 if ((uint64_t) t != l)
2016 return -E2BIG;
2017
1ec7120e
ZJS
2018 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
2019 if (compression) {
3b1a55e1 2020#if defined(HAVE_XZ) || defined(HAVE_LZ4)
fa1c4b51 2021 size_t rsize;
3b1a55e1 2022 int r;
3c1668da 2023
1ec7120e
ZJS
2024 r = decompress_blob(compression,
2025 o->data.payload, l, &f->compress_buffer,
2026 &f->compress_buffer_size, &rsize, j->data_threshold);
2027 if (r < 0)
2028 return r;
3c1668da
LP
2029
2030 *data = f->compress_buffer;
2031 *size = (size_t) rsize;
3b1a55e1
ZJS
2032#else
2033 return -EPROTONOSUPPORT;
2034#endif
3c1668da
LP
2035 } else {
2036 *data = o->data.payload;
2037 *size = t;
2038 }
2039
2040 return 0;
2041}
2042
a5344d2c 2043_public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
3fbf9cbb 2044 JournalFile *f;
3c1668da 2045 uint64_t p, n;
4fd052ae 2046 le64_t le_hash;
3fbf9cbb
LP
2047 int r;
2048 Object *o;
2049
1ae464e0
TA
2050 assert_return(j, -EINVAL);
2051 assert_return(!journal_pid_changed(j), -ECHILD);
2052 assert_return(data, -EINVAL);
2053 assert_return(size, -EINVAL);
3fbf9cbb
LP
2054
2055 f = j->current_file;
2056 if (!f)
de190aef 2057 return -EADDRNOTAVAIL;
3fbf9cbb
LP
2058
2059 if (f->current_offset <= 0)
de190aef 2060 return -EADDRNOTAVAIL;
3fbf9cbb 2061
de190aef 2062 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
2063 if (r < 0)
2064 return r;
2065
2066 n = journal_file_entry_n_items(o);
7210bfb3 2067 if (j->current_field >= n)
3fbf9cbb
LP
2068 return 0;
2069
7210bfb3 2070 p = le64toh(o->entry.items[j->current_field].object_offset);
de190aef
LP
2071 le_hash = o->entry.items[j->current_field].hash;
2072 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
3fbf9cbb
LP
2073 if (r < 0)
2074 return r;
2075
de190aef 2076 if (le_hash != o->data.hash)
de7b95cd
LP
2077 return -EBADMSG;
2078
93b73b06 2079 r = return_data(j, f, o, data, size);
3c1668da
LP
2080 if (r < 0)
2081 return r;
3fbf9cbb 2082
7210bfb3 2083 j->current_field ++;
3fbf9cbb
LP
2084
2085 return 1;
2086}
c2373f84 2087
a5344d2c
LP
2088_public_ void sd_journal_restart_data(sd_journal *j) {
2089 if (!j)
2090 return;
8725d60a
LP
2091
2092 j->current_field = 0;
c2373f84 2093}
50f20cfd 2094
a5344d2c 2095_public_ int sd_journal_get_fd(sd_journal *j) {
a963990f
LP
2096 int r;
2097
1ae464e0
TA
2098 assert_return(j, -EINVAL);
2099 assert_return(!journal_pid_changed(j), -ECHILD);
50f20cfd 2100
a963990f
LP
2101 if (j->inotify_fd >= 0)
2102 return j->inotify_fd;
2103
2104 r = allocate_inotify(j);
2105 if (r < 0)
2106 return r;
2107
2108 /* Iterate through all dirs again, to add them to the
2109 * inotify */
5302ebe1
ZJS
2110 if (j->no_new_files)
2111 r = add_current_paths(j);
2112 else if (j->path)
d617408e 2113 r = add_root_directory(j, j->path, true);
7827b1a1 2114 else
89739579 2115 r = add_search_paths(j);
a963990f
LP
2116 if (r < 0)
2117 return r;
2118
50f20cfd
LP
2119 return j->inotify_fd;
2120}
2121
ee531d94
LP
2122_public_ int sd_journal_get_events(sd_journal *j) {
2123 int fd;
2124
1ae464e0
TA
2125 assert_return(j, -EINVAL);
2126 assert_return(!journal_pid_changed(j), -ECHILD);
ee531d94
LP
2127
2128 fd = sd_journal_get_fd(j);
2129 if (fd < 0)
2130 return fd;
2131
2132 return POLLIN;
2133}
2134
39c155ea
LP
2135_public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2136 int fd;
2137
1ae464e0
TA
2138 assert_return(j, -EINVAL);
2139 assert_return(!journal_pid_changed(j), -ECHILD);
2140 assert_return(timeout_usec, -EINVAL);
39c155ea
LP
2141
2142 fd = sd_journal_get_fd(j);
2143 if (fd < 0)
2144 return fd;
2145
2146 if (!j->on_network) {
2147 *timeout_usec = (uint64_t) -1;
2148 return 0;
2149 }
2150
2151 /* If we are on the network we need to regularly check for
2152 * changes manually */
2153
2154 *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2155 return 1;
2156}
2157
50f20cfd 2158static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
a963990f 2159 Directory *d;
50f20cfd
LP
2160
2161 assert(j);
2162 assert(e);
2163
2164 /* Is this a subdirectory we watch? */
a963990f
LP
2165 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2166 if (d) {
2167 sd_id128_t id;
50f20cfd 2168
de2c3907
LP
2169 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2170 (endswith(e->name, ".journal") ||
2171 endswith(e->name, ".journal~"))) {
50f20cfd
LP
2172
2173 /* Event for a journal file */
2174
d617408e
LP
2175 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB))
2176 (void) add_file(j, d->path, e->name);
2177 else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT))
2178 remove_file(j, d->path, e->name);
50f20cfd 2179
a963990f 2180 } else if (!d->is_root && e->len == 0) {
50f20cfd 2181
a963990f 2182 /* Event for a subdirectory */
50f20cfd 2183
b2b46f91
TA
2184 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT))
2185 remove_directory(j, d);
50f20cfd 2186
a963990f 2187 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
50f20cfd 2188
a963990f 2189 /* Event for root directory */
50f20cfd 2190
d617408e
LP
2191 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB))
2192 (void) add_directory(j, d->path, e->name);
50f20cfd
LP
2193 }
2194
2195 return;
2196 }
2197
2198 if (e->mask & IN_IGNORED)
2199 return;
2200
d617408e 2201 log_debug("Unknown inotify event.");
50f20cfd
LP
2202}
2203
a963990f
LP
2204static int determine_change(sd_journal *j) {
2205 bool b;
2206
2207 assert(j);
2208
2209 b = j->current_invalidate_counter != j->last_invalidate_counter;
2210 j->last_invalidate_counter = j->current_invalidate_counter;
2211
2212 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2213}
2214
a5344d2c 2215_public_ int sd_journal_process(sd_journal *j) {
a963990f 2216 bool got_something = false;
50f20cfd 2217
1ae464e0
TA
2218 assert_return(j, -EINVAL);
2219 assert_return(!journal_pid_changed(j), -ECHILD);
50f20cfd 2220
39c155ea
LP
2221 j->last_process_usec = now(CLOCK_MONOTONIC);
2222
50f20cfd 2223 for (;;) {
0254e944 2224 union inotify_event_buffer buffer;
50f20cfd
LP
2225 struct inotify_event *e;
2226 ssize_t l;
2227
0254e944 2228 l = read(j->inotify_fd, &buffer, sizeof(buffer));
50f20cfd 2229 if (l < 0) {
a963990f
LP
2230 if (errno == EAGAIN || errno == EINTR)
2231 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
50f20cfd
LP
2232
2233 return -errno;
2234 }
2235
a963990f
LP
2236 got_something = true;
2237
f7c1ad4f 2238 FOREACH_INOTIFY_EVENT(e, buffer, l)
50f20cfd 2239 process_inotify_event(j, e);
50f20cfd
LP
2240 }
2241}
6ad1d1c3 2242
e02d1cf7 2243_public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
a963990f 2244 int r;
39c155ea 2245 uint64_t t;
e02d1cf7 2246
1ae464e0
TA
2247 assert_return(j, -EINVAL);
2248 assert_return(!journal_pid_changed(j), -ECHILD);
e02d1cf7 2249
a963990f
LP
2250 if (j->inotify_fd < 0) {
2251
2252 /* This is the first invocation, hence create the
2253 * inotify watch */
2254 r = sd_journal_get_fd(j);
2255 if (r < 0)
2256 return r;
2257
2258 /* The journal might have changed since the context
2259 * object was created and we weren't watching before,
2260 * hence don't wait for anything, and return
2261 * immediately. */
2262 return determine_change(j);
2263 }
2264
39c155ea
LP
2265 r = sd_journal_get_timeout(j, &t);
2266 if (r < 0)
2267 return r;
2268
2269 if (t != (uint64_t) -1) {
2270 usec_t n;
2271
2272 n = now(CLOCK_MONOTONIC);
2273 t = t > n ? t - n : 0;
85210bff 2274
39c155ea
LP
2275 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2276 timeout_usec = t;
85210bff
LP
2277 }
2278
a963990f
LP
2279 do {
2280 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2281 } while (r == -EINTR);
e02d1cf7
LP
2282
2283 if (r < 0)
2284 return r;
2285
a963990f 2286 return sd_journal_process(j);
e02d1cf7
LP
2287}
2288
08984293
LP
2289_public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2290 Iterator i;
2291 JournalFile *f;
2292 bool first = true;
581483bf 2293 uint64_t fmin = 0, tmax = 0;
08984293
LP
2294 int r;
2295
1ae464e0
TA
2296 assert_return(j, -EINVAL);
2297 assert_return(!journal_pid_changed(j), -ECHILD);
2298 assert_return(from || to, -EINVAL);
2299 assert_return(from != to, -EINVAL);
08984293 2300
c1f906bd 2301 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
08984293
LP
2302 usec_t fr, t;
2303
2304 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
9f8d2983
LP
2305 if (r == -ENOENT)
2306 continue;
08984293
LP
2307 if (r < 0)
2308 return r;
2309 if (r == 0)
2310 continue;
2311
2312 if (first) {
581483bf
LP
2313 fmin = fr;
2314 tmax = t;
08984293
LP
2315 first = false;
2316 } else {
581483bf
LP
2317 fmin = MIN(fr, fmin);
2318 tmax = MAX(t, tmax);
08984293
LP
2319 }
2320 }
2321
581483bf
LP
2322 if (from)
2323 *from = fmin;
2324 if (to)
2325 *to = tmax;
2326
08984293
LP
2327 return first ? 0 : 1;
2328}
2329
2330_public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2331 Iterator i;
2332 JournalFile *f;
1651e2c6 2333 bool found = false;
08984293
LP
2334 int r;
2335
1ae464e0
TA
2336 assert_return(j, -EINVAL);
2337 assert_return(!journal_pid_changed(j), -ECHILD);
2338 assert_return(from || to, -EINVAL);
2339 assert_return(from != to, -EINVAL);
08984293 2340
c1f906bd 2341 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
08984293
LP
2342 usec_t fr, t;
2343
2344 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
9f8d2983
LP
2345 if (r == -ENOENT)
2346 continue;
08984293
LP
2347 if (r < 0)
2348 return r;
2349 if (r == 0)
2350 continue;
2351
1651e2c6 2352 if (found) {
08984293 2353 if (from)
1651e2c6 2354 *from = MIN(fr, *from);
08984293 2355 if (to)
1651e2c6 2356 *to = MAX(t, *to);
08984293
LP
2357 } else {
2358 if (from)
1651e2c6 2359 *from = fr;
08984293 2360 if (to)
1651e2c6
ZJS
2361 *to = t;
2362 found = true;
08984293
LP
2363 }
2364 }
2365
1651e2c6 2366 return found;
08984293
LP
2367}
2368
dca6219e
LP
2369void journal_print_header(sd_journal *j) {
2370 Iterator i;
2371 JournalFile *f;
2372 bool newline = false;
2373
2374 assert(j);
2375
c1f906bd 2376 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
dca6219e
LP
2377 if (newline)
2378 putchar('\n');
2379 else
2380 newline = true;
2381
2382 journal_file_print_header(f);
2383 }
2384}
08984293 2385
a1a03e30
LP
2386_public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2387 Iterator i;
2388 JournalFile *f;
2389 uint64_t sum = 0;
2390
1ae464e0
TA
2391 assert_return(j, -EINVAL);
2392 assert_return(!journal_pid_changed(j), -ECHILD);
2393 assert_return(bytes, -EINVAL);
a1a03e30 2394
c1f906bd 2395 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
a1a03e30
LP
2396 struct stat st;
2397
2398 if (fstat(f->fd, &st) < 0)
2399 return -errno;
2400
2401 sum += (uint64_t) st.st_blocks * 512ULL;
2402 }
2403
2404 *bytes = sum;
2405 return 0;
2406}
2407
3c1668da
LP
2408_public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2409 char *f;
2410
1ae464e0
TA
2411 assert_return(j, -EINVAL);
2412 assert_return(!journal_pid_changed(j), -ECHILD);
2413 assert_return(!isempty(field), -EINVAL);
2414 assert_return(field_is_valid(field), -EINVAL);
3c1668da
LP
2415
2416 f = strdup(field);
2417 if (!f)
2418 return -ENOMEM;
2419
2420 free(j->unique_field);
2421 j->unique_field = f;
2422 j->unique_file = NULL;
2423 j->unique_offset = 0;
360af4cf 2424 j->unique_file_lost = false;
3c1668da
LP
2425
2426 return 0;
2427}
2428
2429_public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
3c1668da 2430 size_t k;
19a2bd80 2431
1ae464e0
TA
2432 assert_return(j, -EINVAL);
2433 assert_return(!journal_pid_changed(j), -ECHILD);
2434 assert_return(data, -EINVAL);
2435 assert_return(l, -EINVAL);
2436 assert_return(j->unique_field, -EINVAL);
19a2bd80 2437
3c1668da 2438 k = strlen(j->unique_field);
19a2bd80 2439
3c1668da 2440 if (!j->unique_file) {
360af4cf
ZJS
2441 if (j->unique_file_lost)
2442 return 0;
2443
c1f906bd 2444 j->unique_file = ordered_hashmap_first(j->files);
3c1668da
LP
2445 if (!j->unique_file)
2446 return 0;
360af4cf 2447
3c1668da
LP
2448 j->unique_offset = 0;
2449 }
19a2bd80 2450
3c1668da
LP
2451 for (;;) {
2452 JournalFile *of;
2453 Iterator i;
ae97089d 2454 Object *o;
3c1668da
LP
2455 const void *odata;
2456 size_t ol;
2457 bool found;
ae97089d 2458 int r;
3c1668da 2459
bdc02927 2460 /* Proceed to next data object in the field's linked list */
3c1668da
LP
2461 if (j->unique_offset == 0) {
2462 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2463 if (r < 0)
2464 return r;
2465
2466 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2467 } else {
2468 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2469 if (r < 0)
2470 return r;
2471
2472 j->unique_offset = le64toh(o->data.next_field_offset);
2473 }
2474
2475 /* We reached the end of the list? Then start again, with the next file */
2476 if (j->unique_offset == 0) {
c1f906bd 2477 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
360af4cf 2478 if (!j->unique_file)
3c1668da
LP
2479 return 0;
2480
3c1668da
LP
2481 continue;
2482 }
2483
d05089d8
MS
2484 /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED
2485 * instead, so that we can look at this data object at the same
3c1668da 2486 * time as one on another file */
d05089d8 2487 r = journal_file_move_to_object(j->unique_file, OBJECT_UNUSED, j->unique_offset, &o);
3c1668da
LP
2488 if (r < 0)
2489 return r;
2490
2491 /* Let's do the type check by hand, since we used 0 context above. */
ae97089d 2492 if (o->object.type != OBJECT_DATA) {
36202fd2 2493 log_debug("%s:offset " OFSfmt ": object has type %d, expected %d",
ae97089d
ZJS
2494 j->unique_file->path, j->unique_offset,
2495 o->object.type, OBJECT_DATA);
3c1668da 2496 return -EBADMSG;
ae97089d
ZJS
2497 }
2498
93b73b06 2499 r = return_data(j, j->unique_file, o, &odata, &ol);
3c1668da
LP
2500 if (r < 0)
2501 return r;
2502
0f99f74a
ZJS
2503 /* Check if we have at least the field name and "=". */
2504 if (ol <= k) {
2505 log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
2506 j->unique_file->path, j->unique_offset,
2507 ol, k + 1);
2508 return -EBADMSG;
2509 }
2510
2511 if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') {
2512 log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"",
2513 j->unique_file->path, j->unique_offset,
2514 j->unique_field);
2515 return -EBADMSG;
2516 }
2517
3c1668da
LP
2518 /* OK, now let's see if we already returned this data
2519 * object by checking if it exists in the earlier
2520 * traversed files. */
2521 found = false;
c1f906bd 2522 ORDERED_HASHMAP_FOREACH(of, j->files, i) {
3c1668da
LP
2523 Object *oo;
2524 uint64_t op;
2525
2526 if (of == j->unique_file)
2527 break;
2528
2529 /* Skip this file it didn't have any fields
2530 * indexed */
2531 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2532 le64toh(of->header->n_fields) <= 0)
2533 continue;
2534
2535 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2536 if (r < 0)
2537 return r;
2538
2539 if (r > 0)
2540 found = true;
2541 }
2542
06cc69d4
JJ
2543 if (found)
2544 continue;
2545
93b73b06 2546 r = return_data(j, j->unique_file, o, data, l);
3c1668da
LP
2547 if (r < 0)
2548 return r;
2549
2550 return 1;
2551 }
2552}
2553
115646c7 2554_public_ void sd_journal_restart_unique(sd_journal *j) {
3c1668da
LP
2555 if (!j)
2556 return;
2557
2558 j->unique_file = NULL;
2559 j->unique_offset = 0;
360af4cf 2560 j->unique_file_lost = false;
3c1668da 2561}
85210bff 2562
eb86030e
LP
2563_public_ int sd_journal_enumerate_fields(sd_journal *j, const char **field) {
2564 int r;
2565
2566 assert_return(j, -EINVAL);
2567 assert_return(!journal_pid_changed(j), -ECHILD);
2568 assert_return(field, -EINVAL);
2569
2570 if (!j->fields_file) {
2571 if (j->fields_file_lost)
2572 return 0;
2573
2574 j->fields_file = ordered_hashmap_first(j->files);
2575 if (!j->fields_file)
2576 return 0;
2577
2578 j->fields_hash_table_index = 0;
2579 j->fields_offset = 0;
2580 }
2581
2582 for (;;) {
2583 JournalFile *f, *of;
2584 Iterator i;
2585 uint64_t m;
2586 Object *o;
2587 size_t sz;
2588 bool found;
2589
2590 f = j->fields_file;
2591
2592 if (j->fields_offset == 0) {
2593 bool eof = false;
2594
2595 /* We are not yet positioned at any field. Let's pick the first one */
2596 r = journal_file_map_field_hash_table(f);
2597 if (r < 0)
2598 return r;
2599
2600 m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem);
2601 for (;;) {
2602 if (j->fields_hash_table_index >= m) {
2603 /* Reached the end of the hash table, go to the next file. */
2604 eof = true;
2605 break;
2606 }
2607
2608 j->fields_offset = le64toh(f->field_hash_table[j->fields_hash_table_index].head_hash_offset);
2609
2610 if (j->fields_offset != 0)
2611 break;
2612
2613 /* Empty hash table bucket, go to next one */
2614 j->fields_hash_table_index++;
2615 }
2616
2617 if (eof) {
2618 /* Proceed with next file */
2619 j->fields_file = ordered_hashmap_next(j->files, f->path);
2620 if (!j->fields_file) {
2621 *field = NULL;
2622 return 0;
2623 }
2624
2625 j->fields_offset = 0;
2626 j->fields_hash_table_index = 0;
2627 continue;
2628 }
2629
2630 } else {
2631 /* We are already positioned at a field. If so, let's figure out the next field from it */
2632
2633 r = journal_file_move_to_object(f, OBJECT_FIELD, j->fields_offset, &o);
2634 if (r < 0)
2635 return r;
2636
2637 j->fields_offset = le64toh(o->field.next_hash_offset);
2638 if (j->fields_offset == 0) {
2639 /* Reached the end of the hash table chain */
2640 j->fields_hash_table_index++;
2641 continue;
2642 }
2643 }
2644
2645 /* We use OBJECT_UNUSED here, so that the iteator below doesn't remove our mmap window */
2646 r = journal_file_move_to_object(f, OBJECT_UNUSED, j->fields_offset, &o);
2647 if (r < 0)
2648 return r;
2649
2650 /* Because we used OBJECT_UNUSED above, we need to do our type check manually */
2651 if (o->object.type != OBJECT_FIELD) {
2652 log_debug("%s:offset " OFSfmt ": object has type %i, expected %i", f->path, j->fields_offset, o->object.type, OBJECT_FIELD);
2653 return -EBADMSG;
2654 }
2655
2656 sz = le64toh(o->object.size) - offsetof(Object, field.payload);
2657
2658 /* Let's see if we already returned this field name before. */
2659 found = false;
2660 ORDERED_HASHMAP_FOREACH(of, j->files, i) {
2661 if (of == f)
2662 break;
2663
2664 /* Skip this file it didn't have any fields indexed */
2665 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) && le64toh(of->header->n_fields) <= 0)
2666 continue;
2667
2668 r = journal_file_find_field_object_with_hash(of, o->field.payload, sz, le64toh(o->field.hash), NULL, NULL);
2669 if (r < 0)
2670 return r;
2671 if (r > 0) {
2672 found = true;
2673 break;
2674 }
2675 }
2676
2677 if (found)
2678 continue;
2679
2680 /* Check if this is really a valid string containing no NUL byte */
2681 if (memchr(o->field.payload, 0, sz))
2682 return -EBADMSG;
2683
2684 if (sz > j->data_threshold)
2685 sz = j->data_threshold;
2686
2687 if (!GREEDY_REALLOC(j->fields_buffer, j->fields_buffer_allocated, sz + 1))
2688 return -ENOMEM;
2689
2690 memcpy(j->fields_buffer, o->field.payload, sz);
2691 j->fields_buffer[sz] = 0;
2692
2693 if (!field_is_valid(j->fields_buffer))
2694 return -EBADMSG;
2695
2696 *field = j->fields_buffer;
2697 return 1;
2698 }
2699}
2700
2701_public_ void sd_journal_restart_fields(sd_journal *j) {
2702 if (!j)
2703 return;
2704
2705 j->fields_file = NULL;
2706 j->fields_hash_table_index = 0;
2707 j->fields_offset = 0;
2708 j->fields_file_lost = false;
2709}
2710
85210bff 2711_public_ int sd_journal_reliable_fd(sd_journal *j) {
1ae464e0
TA
2712 assert_return(j, -EINVAL);
2713 assert_return(!journal_pid_changed(j), -ECHILD);
85210bff
LP
2714
2715 return !j->on_network;
2716}
d4205751
LP
2717
2718static char *lookup_field(const char *field, void *userdata) {
2719 sd_journal *j = userdata;
2720 const void *data;
2721 size_t size, d;
2722 int r;
2723
2724 assert(field);
2725 assert(j);
2726
2727 r = sd_journal_get_data(j, field, &data, &size);
2728 if (r < 0 ||
2729 size > REPLACE_VAR_MAX)
2730 return strdup(field);
2731
2732 d = strlen(field) + 1;
2733
2734 return strndup((const char*) data + d, size - d);
2735}
2736
2737_public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
2738 const void *data;
2739 size_t size;
2740 sd_id128_t id;
2741 _cleanup_free_ char *text = NULL, *cid = NULL;
2742 char *t;
2743 int r;
2744
1ae464e0
TA
2745 assert_return(j, -EINVAL);
2746 assert_return(!journal_pid_changed(j), -ECHILD);
2747 assert_return(ret, -EINVAL);
d4205751
LP
2748
2749 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
2750 if (r < 0)
2751 return r;
2752
2753 cid = strndup((const char*) data + 11, size - 11);
2754 if (!cid)
2755 return -ENOMEM;
2756
2757 r = sd_id128_from_string(cid, &id);
2758 if (r < 0)
2759 return r;
2760
844ec79b 2761 r = catalog_get(CATALOG_DATABASE, id, &text);
d4205751
LP
2762 if (r < 0)
2763 return r;
2764
2765 t = replace_var(text, lookup_field, j);
2766 if (!t)
2767 return -ENOMEM;
2768
2769 *ret = t;
2770 return 0;
2771}
8f1e860f
LP
2772
2773_public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
1ae464e0 2774 assert_return(ret, -EINVAL);
8f1e860f 2775
844ec79b 2776 return catalog_get(CATALOG_DATABASE, id, ret);
8f1e860f 2777}
93b73b06
LP
2778
2779_public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
1ae464e0
TA
2780 assert_return(j, -EINVAL);
2781 assert_return(!journal_pid_changed(j), -ECHILD);
93b73b06
LP
2782
2783 j->data_threshold = sz;
2784 return 0;
2785}
2786
2787_public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
1ae464e0
TA
2788 assert_return(j, -EINVAL);
2789 assert_return(!journal_pid_changed(j), -ECHILD);
2790 assert_return(sz, -EINVAL);
93b73b06
LP
2791
2792 *sz = j->data_threshold;
2793 return 0;
2794}
39fd5b08
JS
2795
2796_public_ int sd_journal_has_runtime_files(sd_journal *j) {
2797 assert_return(j, -EINVAL);
2798
2799 return j->has_runtime_files;
2800}
2801
2802_public_ int sd_journal_has_persistent_files(sd_journal *j) {
2803 assert_return(j, -EINVAL);
2804
2805 return j->has_persistent_files;
2806}