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