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