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