]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/sd-journal.c
mount-setup: change system mount propagation to shared by default
[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>
87d2c1ff
LP
28
29#include "sd-journal.h"
30#include "journal-def.h"
cec736d2 31#include "journal-file.h"
260a2be4 32#include "hashmap.h"
cec736d2 33#include "list.h"
9eb977db 34#include "path-util.h"
de7b95cd 35#include "lookup3.h"
807e17f0 36#include "compress.h"
cf244689 37#include "journal-internal.h"
87d2c1ff 38
cab8ac60
LP
39#define JOURNAL_FILES_MAX 1024
40
de190aef 41static void detach_location(sd_journal *j) {
8f9b6cd9
LP
42 Iterator i;
43 JournalFile *f;
44
45 assert(j);
46
47 j->current_file = NULL;
48 j->current_field = 0;
49
50 HASHMAP_FOREACH(f, j->files, i)
51 f->current_offset = 0;
52}
53
de190aef
LP
54static void reset_location(sd_journal *j) {
55 assert(j);
56
57 detach_location(j);
58 zero(j->current_location);
59}
60
61static void init_location(Location *l, JournalFile *f, Object *o) {
62 assert(l);
63 assert(f);
64 assert(o->object.type == OBJECT_ENTRY);
65
66 l->type = LOCATION_DISCRETE;
67 l->seqnum = le64toh(o->entry.seqnum);
68 l->seqnum_id = f->header->seqnum_id;
69 l->realtime = le64toh(o->entry.realtime);
70 l->monotonic = le64toh(o->entry.monotonic);
ce3fd7e7 71 l->boot_id = o->entry.boot_id;
de190aef
LP
72 l->xor_hash = le64toh(o->entry.xor_hash);
73
74 l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
75}
76
77static void set_location(sd_journal *j, JournalFile *f, Object *o, uint64_t offset) {
78 assert(j);
79 assert(f);
80 assert(o);
81
82 init_location(&j->current_location, f, o);
83
84 j->current_file = f;
85 j->current_field = 0;
86
87 f->current_offset = offset;
88}
89
cbdca852
LP
90static int match_is_valid(const void *data, size_t size) {
91 const char *b, *p;
92
93 assert(data);
94
95 if (size < 2)
96 return false;
97
98 if (startswith(data, "__"))
99 return false;
100
101 b = data;
102 for (p = b; p < b + size; p++) {
103
104 if (*p == '=')
105 return p > b;
106
107 if (*p == '_')
108 continue;
109
110 if (*p >= 'A' && *p <= 'Z')
111 continue;
112
113 if (*p >= '0' && *p <= '9')
114 continue;
115
116 return false;
117 }
118
119 return false;
120}
121
122static bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
de190aef
LP
123 const uint8_t *a = _a, *b = _b;
124 size_t j;
de190aef
LP
125
126 for (j = 0; j < s && j < t; j++) {
127
de190aef 128 if (a[j] != b[j])
cbdca852 129 return false;
de190aef 130
cbdca852
LP
131 if (a[j] == '=')
132 return true;
de190aef
LP
133 }
134
cbdca852
LP
135 return true;
136}
137
138static Match *match_new(Match *p, MatchType t) {
139 Match *m;
140
141 m = new0(Match, 1);
142 if (!m)
143 return NULL;
144
145 m->type = t;
146
147 if (p) {
148 m->parent = p;
149 LIST_PREPEND(Match, matches, p->matches, m);
150 }
151
152 return m;
153}
154
155static void match_free(Match *m) {
156 assert(m);
157
158 while (m->matches)
159 match_free(m->matches);
160
161 if (m->parent)
162 LIST_REMOVE(Match, matches, m->parent->matches, m);
163
164 free(m->data);
165 free(m);
166}
167
168static void match_free_if_empty(Match *m) {
169 assert(m);
170
171 if (m->matches)
172 return;
173
174 match_free(m);
de190aef
LP
175}
176
a5344d2c 177_public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
cbdca852 178 Match *l2, *l3, *add_here = NULL, *m;
4fd052ae 179 le64_t le_hash;
87d2c1ff 180
a5344d2c
LP
181 if (!j)
182 return -EINVAL;
cbdca852 183
a5344d2c
LP
184 if (!data)
185 return -EINVAL;
cbdca852
LP
186
187 if (size == 0)
188 size = strlen(data);
189
190 if (!match_is_valid(data, size))
1cc101f1
LP
191 return -EINVAL;
192
cbdca852
LP
193 /* level 0: OR term
194 * level 1: AND terms
195 * level 2: OR terms
196 * level 3: concrete matches */
197
198 if (!j->level0) {
199 j->level0 = match_new(NULL, MATCH_OR_TERM);
200 if (!j->level0)
201 return -ENOMEM;
202 }
203
204 if (!j->level1) {
205 j->level1 = match_new(j->level0, MATCH_AND_TERM);
206 if (!j->level1)
207 return -ENOMEM;
208 }
209
210 assert(j->level0->type == MATCH_OR_TERM);
211 assert(j->level1->type == MATCH_AND_TERM);
ab4979d2 212
de190aef
LP
213 le_hash = htole64(hash64(data, size));
214
cbdca852
LP
215 LIST_FOREACH(matches, l2, j->level1->matches) {
216 assert(l2->type == MATCH_OR_TERM);
de190aef 217
cbdca852
LP
218 LIST_FOREACH(matches, l3, l2->matches) {
219 assert(l3->type == MATCH_DISCRETE);
de190aef 220
cbdca852
LP
221 /* Exactly the same match already? Then ignore
222 * this addition */
223 if (l3->le_hash == le_hash &&
224 l3->size == size &&
225 memcmp(l3->data, data, size) == 0)
226 return 0;
227
228 /* Same field? Then let's add this to this OR term */
229 if (same_field(data, size, l3->data, l3->size)) {
230 add_here = l2;
231 break;
232 }
233 }
234
235 if (add_here)
236 break;
de190aef
LP
237 }
238
cbdca852
LP
239 if (!add_here) {
240 add_here = match_new(j->level1, MATCH_OR_TERM);
241 if (!add_here)
242 goto fail;
243 }
244
245 m = match_new(add_here, MATCH_DISCRETE);
cec736d2 246 if (!m)
cbdca852 247 goto fail;
87d2c1ff 248
cbdca852 249 m->le_hash = le_hash;
1cc101f1 250 m->size = size;
cbdca852
LP
251 m->data = memdup(data, size);
252 if (!m->data)
253 goto fail;
254
255 detach_location(j);
256
257 return 0;
258
259fail:
260 if (add_here)
261 match_free_if_empty(add_here);
262
263 if (j->level1)
264 match_free_if_empty(j->level1);
265
266 if (j->level0)
267 match_free_if_empty(j->level0);
268
269 return -ENOMEM;
270}
271
272_public_ int sd_journal_add_disjunction(sd_journal *j) {
273 Match *m;
274
275 assert(j);
1cc101f1 276
cbdca852
LP
277 if (!j->level0)
278 return 0;
279
280 if (!j->level1)
281 return 0;
282
283 if (!j->level1->matches)
284 return 0;
285
286 m = match_new(j->level0, MATCH_AND_TERM);
287 if (!m)
cec736d2 288 return -ENOMEM;
cbdca852
LP
289
290 j->level1 = m;
291 return 0;
292}
293
294static char *match_make_string(Match *m) {
295 char *p, *r;
296 Match *i;
297 bool enclose = false;
298
299 if (!m)
300 return strdup("");
301
302 if (m->type == MATCH_DISCRETE)
303 return strndup(m->data, m->size);
304
305 p = NULL;
306 LIST_FOREACH(matches, i, m->matches) {
307 char *t, *k;
308
309 t = match_make_string(i);
310 if (!t) {
311 free(p);
312 return NULL;
313 }
314
315 if (p) {
b7def684 316 k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t, NULL);
cbdca852
LP
317 free(p);
318 free(t);
319
320 if (!k)
321 return NULL;
322
323 p = k;
324
325 enclose = true;
326 } else {
327 free(p);
328 p = t;
329 }
87d2c1ff
LP
330 }
331
cbdca852 332 if (enclose) {
b7def684 333 r = strjoin("(", p, ")", NULL);
cbdca852
LP
334 free(p);
335 return r;
336 }
87d2c1ff 337
cbdca852
LP
338 return p;
339}
de7b95cd 340
cbdca852
LP
341char *journal_make_match_string(sd_journal *j) {
342 assert(j);
8f9b6cd9 343
cbdca852 344 return match_make_string(j->level0);
87d2c1ff
LP
345}
346
a5344d2c 347_public_ void sd_journal_flush_matches(sd_journal *j) {
cbdca852 348
a5344d2c
LP
349 if (!j)
350 return;
87d2c1ff 351
cbdca852
LP
352 if (j->level0)
353 match_free(j->level0);
de7b95cd 354
cbdca852 355 j->level0 = j->level1 = NULL;
8f9b6cd9 356
de190aef 357 detach_location(j);
87d2c1ff
LP
358}
359
de190aef
LP
360static int compare_order(JournalFile *af, Object *ao,
361 JournalFile *bf, Object *bo) {
87d2c1ff 362
cec736d2 363 uint64_t a, b;
87d2c1ff 364
de190aef
LP
365 assert(af);
366 assert(ao);
367 assert(bf);
368 assert(bo);
369
ae2cc8ef 370 /* We operate on two different files here, hence we can access
1cc101f1
LP
371 * two objects at the same time, which we normally can't.
372 *
373 * If contents and timestamps match, these entries are
374 * identical, even if the seqnum does not match */
375
376 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
377 ao->entry.monotonic == bo->entry.monotonic &&
378 ao->entry.realtime == bo->entry.realtime &&
379 ao->entry.xor_hash == bo->entry.xor_hash)
380 return 0;
ae2cc8ef 381
cec736d2 382 if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
87d2c1ff 383
cec736d2
LP
384 /* If this is from the same seqnum source, compare
385 * seqnums */
386 a = le64toh(ao->entry.seqnum);
387 b = le64toh(bo->entry.seqnum);
87d2c1ff 388
ae2cc8ef
LP
389 if (a < b)
390 return -1;
391 if (a > b)
392 return 1;
1cc101f1
LP
393
394 /* Wow! This is weird, different data but the same
395 * seqnums? Something is borked, but let's make the
396 * best of it and compare by time. */
ae2cc8ef 397 }
87d2c1ff 398
ae2cc8ef 399 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
87d2c1ff 400
cec736d2
LP
401 /* If the boot id matches compare monotonic time */
402 a = le64toh(ao->entry.monotonic);
403 b = le64toh(bo->entry.monotonic);
87d2c1ff 404
ae2cc8ef
LP
405 if (a < b)
406 return -1;
407 if (a > b)
408 return 1;
87d2c1ff
LP
409 }
410
ae2cc8ef
LP
411 /* Otherwise compare UTC time */
412 a = le64toh(ao->entry.realtime);
c4aff78b 413 b = le64toh(bo->entry.realtime);
ae2cc8ef
LP
414
415 if (a < b)
416 return -1;
417 if (a > b)
418 return 1;
419
420 /* Finally, compare by contents */
421 a = le64toh(ao->entry.xor_hash);
c4aff78b 422 b = le64toh(bo->entry.xor_hash);
ae2cc8ef
LP
423
424 if (a < b)
425 return -1;
426 if (a > b)
427 return 1;
428
429 return 0;
87d2c1ff
LP
430}
431
de190aef
LP
432static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
433 uint64_t a;
434
435 assert(af);
436 assert(ao);
437 assert(l);
438 assert(l->type == LOCATION_DISCRETE);
439
440 if (l->monotonic_set &&
441 sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
442 l->realtime_set &&
443 le64toh(ao->entry.realtime) == l->realtime &&
444 l->xor_hash_set &&
445 le64toh(ao->entry.xor_hash) == l->xor_hash)
446 return 0;
447
448 if (l->seqnum_set &&
449 sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
450
451 a = le64toh(ao->entry.seqnum);
452
453 if (a < l->seqnum)
454 return -1;
455 if (a > l->seqnum)
456 return 1;
457 }
458
459 if (l->monotonic_set &&
460 sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
461
462 a = le64toh(ao->entry.monotonic);
463
464 if (a < l->monotonic)
465 return -1;
466 if (a > l->monotonic)
467 return 1;
468 }
469
470 if (l->realtime_set) {
471
472 a = le64toh(ao->entry.realtime);
473
474 if (a < l->realtime)
475 return -1;
476 if (a > l->realtime)
477 return 1;
478 }
479
480 if (l->xor_hash_set) {
481 a = le64toh(ao->entry.xor_hash);
482
483 if (a < l->xor_hash)
484 return -1;
485 if (a > l->xor_hash)
486 return 1;
487 }
488
489 return 0;
490}
491
cbdca852
LP
492static int next_for_match(
493 sd_journal *j,
494 Match *m,
495 JournalFile *f,
496 uint64_t after_offset,
497 direction_t direction,
498 Object **ret,
499 uint64_t *offset) {
500
de7b95cd 501 int r;
cbdca852
LP
502 uint64_t np = 0;
503 Object *n;
de7b95cd
LP
504
505 assert(j);
cbdca852
LP
506 assert(m);
507 assert(f);
de7b95cd 508
cbdca852
LP
509 if (m->type == MATCH_DISCRETE) {
510 uint64_t dp;
de190aef 511
cbdca852 512 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
de190aef
LP
513 if (r <= 0)
514 return r;
515
cbdca852 516 return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
de190aef 517
cbdca852
LP
518 } else if (m->type == MATCH_OR_TERM) {
519 Match *i;
de7b95cd 520
cbdca852 521 /* Find the earliest match beyond after_offset */
de190aef 522
cbdca852
LP
523 LIST_FOREACH(matches, i, m->matches) {
524 uint64_t cp;
de190aef 525
cbdca852 526 r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
b4e5f920
LP
527 if (r < 0)
528 return r;
cbdca852
LP
529 else if (r > 0) {
530 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
531 np = cp;
532 }
533 }
b4e5f920 534
cbdca852
LP
535 } else if (m->type == MATCH_AND_TERM) {
536 Match *i;
537 bool continue_looking;
de190aef 538
cbdca852
LP
539 /* Always jump to the next matching entry and repeat
540 * this until we fine and offset that matches for all
541 * matches. */
de190aef 542
cbdca852
LP
543 if (!m->matches)
544 return 0;
de7b95cd 545
cbdca852
LP
546 np = 0;
547 do {
548 continue_looking = false;
de190aef 549
cbdca852
LP
550 LIST_FOREACH(matches, i, m->matches) {
551 uint64_t cp, limit;
de190aef 552
cbdca852
LP
553 if (np == 0)
554 limit = after_offset;
555 else if (direction == DIRECTION_DOWN)
556 limit = MAX(np, after_offset);
557 else
558 limit = MIN(np, after_offset);
de190aef 559
cbdca852
LP
560 r = next_for_match(j, i, f, limit, direction, NULL, &cp);
561 if (r <= 0)
562 return r;
de190aef 563
cbdca852
LP
564 if ((direction == DIRECTION_DOWN ? cp >= after_offset : cp <= after_offset) &&
565 (np == 0 || (direction == DIRECTION_DOWN ? cp > np : np < cp))) {
566 np = cp;
567 continue_looking = true;
de190aef
LP
568 }
569 }
de190aef 570
cbdca852
LP
571 } while (continue_looking);
572 }
de190aef 573
cbdca852
LP
574 if (np == 0)
575 return 0;
de190aef 576
cbdca852
LP
577 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
578 if (r < 0)
579 return r;
de7b95cd 580
de190aef 581 if (ret)
cbdca852 582 *ret = n;
de190aef 583 if (offset)
cbdca852 584 *offset = np;
de190aef
LP
585
586 return 1;
587}
588
cbdca852
LP
589static int find_location_for_match(
590 sd_journal *j,
591 Match *m,
592 JournalFile *f,
593 direction_t direction,
594 Object **ret,
595 uint64_t *offset) {
596
de190aef 597 int r;
de190aef
LP
598
599 assert(j);
cbdca852 600 assert(m);
de190aef 601 assert(f);
de190aef 602
cbdca852
LP
603 if (m->type == MATCH_DISCRETE) {
604 uint64_t dp;
de190aef 605
cbdca852 606 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
de7b95cd
LP
607 if (r <= 0)
608 return r;
609
cbdca852 610 /* FIXME: missing: find by monotonic */
de7b95cd 611
cbdca852
LP
612 if (j->current_location.type == LOCATION_HEAD)
613 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset);
614 if (j->current_location.type == LOCATION_TAIL)
615 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, ret, offset);
616 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
617 return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset);
618 if (j->current_location.monotonic_set) {
619 r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
620 if (r != -ENOENT)
621 return r;
622 }
623 if (j->current_location.realtime_set)
624 return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
de190aef 625
cbdca852 626 return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset);
de7b95cd 627
cbdca852
LP
628 } else if (m->type == MATCH_OR_TERM) {
629 uint64_t np = 0;
630 Object *n;
631 Match *i;
de7b95cd 632
cbdca852 633 /* Find the earliest match */
de7b95cd 634
cbdca852
LP
635 LIST_FOREACH(matches, i, m->matches) {
636 uint64_t cp;
637
638 r = find_location_for_match(j, i, f, direction, NULL, &cp);
639 if (r < 0)
640 return r;
641 else if (r > 0) {
642 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
643 np = cp;
de190aef 644 }
cbdca852 645 }
de190aef 646
cbdca852
LP
647 if (np == 0)
648 return 0;
de7b95cd 649
cbdca852
LP
650 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
651 if (r < 0)
652 return r;
de7b95cd 653
cbdca852
LP
654 if (ret)
655 *ret = n;
656 if (offset)
657 *offset = np;
de190aef 658
cbdca852 659 return 1;
e892bd17 660
cbdca852
LP
661 } else {
662 Match *i;
663 uint64_t np = 0;
664
665 assert(m->type == MATCH_AND_TERM);
666
667 /* First jump to the last match, and then find the
668 * next one where all matches match */
669
670 if (!m->matches)
671 return 0;
672
673 LIST_FOREACH(matches, i, m->matches) {
674 uint64_t cp;
675
676 r = find_location_for_match(j, i, f, direction, NULL, &cp);
677 if (r <= 0)
4b067dc9
LP
678 return r;
679
cbdca852
LP
680 if (np == 0 || (direction == DIRECTION_DOWN ? np < cp : np > cp))
681 np = cp;
de7b95cd
LP
682 }
683
cbdca852
LP
684 return next_for_match(j, m, f, np, direction, ret, offset);
685 }
686}
de190aef 687
cbdca852
LP
688static int find_location_with_matches(
689 sd_journal *j,
690 JournalFile *f,
691 direction_t direction,
692 Object **ret,
693 uint64_t *offset) {
694
695 int r;
696
697 assert(j);
698 assert(f);
699 assert(ret);
700 assert(offset);
701
702 if (!j->level0) {
703 /* No matches is simple */
704
705 if (j->current_location.type == LOCATION_HEAD)
706 return journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, ret, offset);
707 if (j->current_location.type == LOCATION_TAIL)
708 return journal_file_next_entry(f, NULL, 0, DIRECTION_UP, ret, offset);
709 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
710 return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
711 if (j->current_location.monotonic_set) {
712 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
713 if (r != -ENOENT)
714 return r;
de7b95cd 715 }
cbdca852
LP
716 if (j->current_location.realtime_set)
717 return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
de7b95cd 718
cbdca852
LP
719 return journal_file_next_entry(f, NULL, 0, direction, ret, offset);
720 } else
721 return find_location_for_match(j, j->level0, f, direction, ret, offset);
722}
de7b95cd 723
cbdca852
LP
724static int next_with_matches(
725 sd_journal *j,
726 JournalFile *f,
727 direction_t direction,
728 Object **ret,
729 uint64_t *offset) {
730
731 Object *c;
732 uint64_t cp;
733
734 assert(j);
735 assert(f);
736 assert(ret);
737 assert(offset);
738
739 c = *ret;
740 cp = *offset;
741
742 /* No matches is easy. We simple advance the file
743 * pointer by one. */
744 if (!j->level0)
745 return journal_file_next_entry(f, c, cp, direction, ret, offset);
746
747 /* If we have a match then we look for the next matching entry
49f43d5f 748 * with an offset at least one step larger */
cbdca852 749 return next_for_match(j, j->level0, f, direction == DIRECTION_DOWN ? cp+1 : cp-1, direction, ret, offset);
de7b95cd
LP
750}
751
de190aef
LP
752static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
753 Object *c;
754 uint64_t cp;
cbdca852 755 int r;
de190aef
LP
756
757 assert(j);
758 assert(f);
759
760 if (f->current_offset > 0) {
466ccd92
LP
761 cp = f->current_offset;
762
763 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
de190aef
LP
764 if (r < 0)
765 return r;
766
de190aef
LP
767 r = next_with_matches(j, f, direction, &c, &cp);
768 if (r <= 0)
769 return r;
de190aef 770 } else {
cbdca852 771 r = find_location_with_matches(j, f, direction, &c, &cp);
de190aef
LP
772 if (r <= 0)
773 return r;
de190aef
LP
774 }
775
cbdca852
LP
776 /* OK, we found the spot, now let's advance until to an entry
777 * that is actually different from what we were previously
778 * looking at. This is necessary to handle entries which exist
779 * in two (or more) journal files, and which shall all be
780 * suppressed but one. */
781
de190aef
LP
782 for (;;) {
783 bool found;
784
785 if (j->current_location.type == LOCATION_DISCRETE) {
786 int k;
787
788 k = compare_with_location(f, c, &j->current_location);
789 if (direction == DIRECTION_DOWN)
cbdca852 790 found = k > 0;
de190aef 791 else
cbdca852 792 found = k < 0;
de190aef
LP
793 } else
794 found = true;
795
796 if (found) {
797 if (ret)
798 *ret = c;
799 if (offset)
800 *offset = cp;
801 return 1;
802 }
803
804 r = next_with_matches(j, f, direction, &c, &cp);
805 if (r <= 0)
806 return r;
807 }
808}
809
e892bd17 810static int real_journal_next(sd_journal *j, direction_t direction) {
cec736d2
LP
811 JournalFile *f, *new_current = NULL;
812 Iterator i;
87d2c1ff 813 int r;
cec736d2
LP
814 uint64_t new_offset = 0;
815 Object *new_entry = NULL;
87d2c1ff 816
a5344d2c
LP
817 if (!j)
818 return -EINVAL;
87d2c1ff 819
cec736d2
LP
820 HASHMAP_FOREACH(f, j->files, i) {
821 Object *o;
822 uint64_t p;
de190aef 823 bool found;
87d2c1ff 824
de190aef 825 r = next_beyond_location(j, f, direction, &o, &p);
e590af26
LP
826 if (r < 0) {
827 log_debug("Can't iterate through %s, ignoring: %s", f->path, strerror(-r));
828 continue;
829 } else if (r == 0)
cec736d2 830 continue;
87d2c1ff 831
de190aef
LP
832 if (!new_current)
833 found = true;
834 else {
835 int k;
836
837 k = compare_order(f, o, new_current, new_entry);
838
839 if (direction == DIRECTION_DOWN)
840 found = k < 0;
841 else
842 found = k > 0;
843 }
844
845 if (found) {
cec736d2
LP
846 new_current = f;
847 new_entry = o;
848 new_offset = p;
87d2c1ff 849 }
87d2c1ff
LP
850 }
851
de190aef
LP
852 if (!new_current)
853 return 0;
ae2cc8ef 854
de190aef 855 set_location(j, new_current, new_entry, new_offset);
ae2cc8ef 856
de190aef
LP
857 return 1;
858}
ae2cc8ef 859
a5344d2c 860_public_ int sd_journal_next(sd_journal *j) {
de190aef
LP
861 return real_journal_next(j, DIRECTION_DOWN);
862}
ae2cc8ef 863
a5344d2c 864_public_ int sd_journal_previous(sd_journal *j) {
de190aef
LP
865 return real_journal_next(j, DIRECTION_UP);
866}
ae2cc8ef 867
6f003b43 868static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
de190aef 869 int c = 0, r;
ae2cc8ef 870
a5344d2c
LP
871 if (!j)
872 return -EINVAL;
de190aef 873
6f003b43
LP
874 if (skip == 0) {
875 /* If this is not a discrete skip, then at least
876 * resolve the current location */
877 if (j->current_location.type != LOCATION_DISCRETE)
878 return real_journal_next(j, direction);
879
880 return 0;
881 }
882
883 do {
884 r = real_journal_next(j, direction);
de190aef
LP
885 if (r < 0)
886 return r;
887
888 if (r == 0)
889 return c;
890
891 skip--;
892 c++;
6f003b43 893 } while (skip > 0);
87d2c1ff 894
de190aef 895 return c;
87d2c1ff
LP
896}
897
6f003b43
LP
898_public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
899 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
900}
de190aef 901
6f003b43
LP
902_public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
903 return real_journal_next_skip(j, DIRECTION_UP, skip);
87d2c1ff
LP
904}
905
a5344d2c 906_public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
cec736d2 907 Object *o;
87d2c1ff 908 int r;
3fbf9cbb 909 char bid[33], sid[33];
87d2c1ff 910
a5344d2c
LP
911 if (!j)
912 return -EINVAL;
913 if (!cursor)
914 return -EINVAL;
87d2c1ff 915
3fbf9cbb
LP
916 if (!j->current_file || j->current_file->current_offset <= 0)
917 return -EADDRNOTAVAIL;
87d2c1ff 918
de190aef 919 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
87d2c1ff
LP
920 if (r < 0)
921 return r;
922
3fbf9cbb
LP
923 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
924 sd_id128_to_string(o->entry.boot_id, bid);
87d2c1ff 925
3fbf9cbb
LP
926 if (asprintf(cursor,
927 "s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx;p=%s",
928 sid, (unsigned long long) le64toh(o->entry.seqnum),
929 bid, (unsigned long long) le64toh(o->entry.monotonic),
930 (unsigned long long) le64toh(o->entry.realtime),
931 (unsigned long long) le64toh(o->entry.xor_hash),
9eb977db 932 path_get_file_name(j->current_file->path)) < 0)
3fbf9cbb 933 return -ENOMEM;
87d2c1ff
LP
934
935 return 1;
936}
937
a5344d2c 938_public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
de190aef
LP
939 char *w;
940 size_t l;
941 char *state;
942 unsigned long long seqnum, monotonic, realtime, xor_hash;
943 bool
944 seqnum_id_set = false,
945 seqnum_set = false,
946 boot_id_set = false,
947 monotonic_set = false,
948 realtime_set = false,
949 xor_hash_set = false;
950 sd_id128_t seqnum_id, boot_id;
951
a5344d2c
LP
952 if (!j)
953 return -EINVAL;
954 if (!cursor)
955 return -EINVAL;
de190aef
LP
956
957 FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
958 char *item;
959 int k = 0;
960
961 if (l < 2 || w[1] != '=')
962 return -EINVAL;
963
964 item = strndup(w, l);
965 if (!item)
966 return -ENOMEM;
967
968 switch (w[0]) {
969
970 case 's':
971 seqnum_id_set = true;
972 k = sd_id128_from_string(w+2, &seqnum_id);
973 break;
974
975 case 'i':
976 seqnum_set = true;
977 if (sscanf(w+2, "%llx", &seqnum) != 1)
978 k = -EINVAL;
979 break;
980
981 case 'b':
982 boot_id_set = true;
983 k = sd_id128_from_string(w+2, &boot_id);
984 break;
985
986 case 'm':
987 monotonic_set = true;
988 if (sscanf(w+2, "%llx", &monotonic) != 1)
989 k = -EINVAL;
990 break;
991
992 case 't':
993 realtime_set = true;
994 if (sscanf(w+2, "%llx", &realtime) != 1)
995 k = -EINVAL;
996 break;
997
998 case 'x':
999 xor_hash_set = true;
1000 if (sscanf(w+2, "%llx", &xor_hash) != 1)
1001 k = -EINVAL;
1002 break;
1003 }
1004
1005 free(item);
1006
1007 if (k < 0)
1008 return k;
1009 }
1010
1011 if ((!seqnum_set || !seqnum_id_set) &&
1012 (!monotonic_set || !boot_id_set) &&
1013 !realtime_set)
1014 return -EINVAL;
1015
1016 reset_location(j);
1017
1018 j->current_location.type = LOCATION_DISCRETE;
1019
1020 if (realtime_set) {
1021 j->current_location.realtime = (uint64_t) realtime;
1022 j->current_location.realtime_set = true;
1023 }
1024
1025 if (seqnum_set && seqnum_id_set) {
1026 j->current_location.seqnum = (uint64_t) seqnum;
1027 j->current_location.seqnum_id = seqnum_id;
1028 j->current_location.seqnum_set = true;
1029 }
1030
1031 if (monotonic_set && boot_id_set) {
1032 j->current_location.monotonic = (uint64_t) monotonic;
1033 j->current_location.boot_id = boot_id;
1034 j->current_location.monotonic_set = true;
1035 }
1036
1037 if (xor_hash_set) {
1038 j->current_location.xor_hash = (uint64_t) xor_hash;
1039 j->current_location.xor_hash_set = true;
1040 }
1041
1042 return 0;
1043}
1044
a5344d2c
LP
1045_public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1046 if (!j)
1047 return -EINVAL;
de190aef
LP
1048
1049 reset_location(j);
1050 j->current_location.type = LOCATION_DISCRETE;
1051 j->current_location.boot_id = boot_id;
1052 j->current_location.monotonic = usec;
1053 j->current_location.monotonic_set = true;
1054
1055 return 0;
1056}
1057
a5344d2c
LP
1058_public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1059 if (!j)
1060 return -EINVAL;
de190aef
LP
1061
1062 reset_location(j);
1063 j->current_location.type = LOCATION_DISCRETE;
1064 j->current_location.realtime = usec;
1065 j->current_location.realtime_set = true;
1066
1067 return 0;
1068}
1069
a5344d2c
LP
1070_public_ int sd_journal_seek_head(sd_journal *j) {
1071 if (!j)
1072 return -EINVAL;
de190aef
LP
1073
1074 reset_location(j);
1075 j->current_location.type = LOCATION_HEAD;
1076
1077 return 0;
1078}
1079
a5344d2c
LP
1080_public_ int sd_journal_seek_tail(sd_journal *j) {
1081 if (!j)
1082 return -EINVAL;
de190aef
LP
1083
1084 reset_location(j);
1085 j->current_location.type = LOCATION_TAIL;
1086
1087 return 0;
87d2c1ff
LP
1088}
1089
a963990f
LP
1090static int add_file(sd_journal *j, const char *prefix, const char *filename) {
1091 char *path;
3fbf9cbb
LP
1092 int r;
1093 JournalFile *f;
1094
1095 assert(j);
1096 assert(prefix);
1097 assert(filename);
1098
cf244689 1099 if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) &&
38a6db16
LP
1100 !(streq(filename, "system.journal") ||
1101 (startswith(filename, "system@") && endswith(filename, ".journal"))))
cf244689
LP
1102 return 0;
1103
b7def684 1104 path = strjoin(prefix, "/", filename, NULL);
a963990f 1105 if (!path)
3fbf9cbb
LP
1106 return -ENOMEM;
1107
a963990f
LP
1108 if (hashmap_get(j->files, path)) {
1109 free(path);
50f20cfd
LP
1110 return 0;
1111 }
1112
1113 if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
a963990f
LP
1114 log_debug("Too many open journal files, not adding %s, ignoring.", path);
1115 free(path);
50f20cfd
LP
1116 return 0;
1117 }
1118
4a92baf3 1119 r = journal_file_open(path, O_RDONLY, 0, NULL, NULL, &f);
a963990f 1120 free(path);
3fbf9cbb
LP
1121
1122 if (r < 0) {
1123 if (errno == ENOENT)
1124 return 0;
1125
1126 return r;
1127 }
1128
72f59706 1129 /* journal_file_dump(f); */
de190aef 1130
3fbf9cbb
LP
1131 r = hashmap_put(j->files, f->path, f);
1132 if (r < 0) {
1133 journal_file_close(f);
1134 return r;
1135 }
1136
a963990f
LP
1137 j->current_invalidate_counter ++;
1138
50f20cfd
LP
1139 log_debug("File %s got added.", f->path);
1140
1141 return 0;
1142}
1143
a963990f
LP
1144static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
1145 char *path;
50f20cfd
LP
1146 JournalFile *f;
1147
1148 assert(j);
1149 assert(prefix);
1150 assert(filename);
1151
b7def684 1152 path = strjoin(prefix, "/", filename, NULL);
a963990f 1153 if (!path)
50f20cfd
LP
1154 return -ENOMEM;
1155
a963990f
LP
1156 f = hashmap_get(j->files, path);
1157 free(path);
50f20cfd
LP
1158 if (!f)
1159 return 0;
1160
1161 hashmap_remove(j->files, f->path);
1162 journal_file_close(f);
1163
a963990f
LP
1164 j->current_invalidate_counter ++;
1165
50f20cfd 1166 log_debug("File %s got removed.", f->path);
3fbf9cbb
LP
1167 return 0;
1168}
1169
a963990f
LP
1170static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1171 char *path;
3fbf9cbb
LP
1172 int r;
1173 DIR *d;
cf244689 1174 sd_id128_t id, mid;
a963990f 1175 Directory *m;
3fbf9cbb
LP
1176
1177 assert(j);
1178 assert(prefix);
a963990f 1179 assert(dirname);
3fbf9cbb 1180
cf244689 1181 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
a963990f 1182 (sd_id128_from_string(dirname, &id) < 0 ||
cf244689
LP
1183 sd_id128_get_machine(&mid) < 0 ||
1184 !sd_id128_equal(id, mid)))
1185 return 0;
1186
b7def684 1187 path = strjoin(prefix, "/", dirname, NULL);
a963990f 1188 if (!path)
3fbf9cbb
LP
1189 return -ENOMEM;
1190
a963990f 1191 d = opendir(path);
3fbf9cbb 1192 if (!d) {
a963990f
LP
1193 log_debug("Failed to open %s: %m", path);
1194 free(path);
1195
3fbf9cbb
LP
1196 if (errno == ENOENT)
1197 return 0;
3fbf9cbb
LP
1198 return -errno;
1199 }
1200
a963990f
LP
1201 m = hashmap_get(j->directories_by_path, path);
1202 if (!m) {
1203 m = new0(Directory, 1);
1204 if (!m) {
1205 closedir(d);
1206 free(path);
1207 return -ENOMEM;
1208 }
1209
1210 m->is_root = false;
1211 m->path = path;
1212
1213 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1214 closedir(d);
1215 free(m->path);
1216 free(m);
1217 return -ENOMEM;
1218 }
1219
1220 j->current_invalidate_counter ++;
1221
1222 log_debug("Directory %s got added.", m->path);
1223
1224 } else if (m->is_root) {
1225 free (path);
1226 closedir(d);
1227 return 0;
1228 } else
1229 free(path);
1230
1231 if (m->wd <= 0 && j->inotify_fd >= 0) {
1232
1233 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1234 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1235 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|
4a842cad 1236 IN_ONLYDIR);
a963990f
LP
1237
1238 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1239 inotify_rm_watch(j->inotify_fd, m->wd);
1240 }
1241
1242 for (;;) {
1243 struct dirent buf, *de;
1244
1245 r = readdir_r(d, &buf, &de);
1246 if (r != 0 || !de)
1247 break;
1248
1249 if (dirent_is_file_with_suffix(de, ".journal")) {
1250 r = add_file(j, m->path, de->d_name);
1251 if (r < 0)
1252 log_debug("Failed to add file %s/%s: %s", m->path, de->d_name, strerror(-r));
1253 }
1254 }
1255
1256 closedir(d);
1257
1258 return 0;
1259}
1260
1261static int add_root_directory(sd_journal *j, const char *p) {
1262 DIR *d;
1263 Directory *m;
1264 int r;
1265
1266 assert(j);
1267 assert(p);
1268
1269 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1270 !path_startswith(p, "/run"))
1271 return -EINVAL;
1272
1273 d = opendir(p);
1274 if (!d)
1275 return -errno;
1276
1277 m = hashmap_get(j->directories_by_path, p);
1278 if (!m) {
1279 m = new0(Directory, 1);
1280 if (!m) {
1281 closedir(d);
1282 return -ENOMEM;
1283 }
1284
1285 m->is_root = true;
1286 m->path = strdup(p);
1287 if (!m->path) {
1288 closedir(d);
1289 free(m);
1290 return -ENOMEM;
1291 }
1292
1293 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1294 closedir(d);
1295 free(m->path);
1296 free(m);
1297 return -ENOMEM;
1298 }
1299
1300 j->current_invalidate_counter ++;
1301
1302 log_debug("Root directory %s got added.", m->path);
1303
1304 } else if (!m->is_root) {
1305 closedir(d);
1306 return 0;
50f20cfd
LP
1307 }
1308
a963990f
LP
1309 if (m->wd <= 0 && j->inotify_fd >= 0) {
1310
1311 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1312 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
4a842cad 1313 IN_ONLYDIR);
a963990f
LP
1314
1315 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1316 inotify_rm_watch(j->inotify_fd, m->wd);
1317 }
50f20cfd 1318
3fbf9cbb
LP
1319 for (;;) {
1320 struct dirent buf, *de;
a963990f 1321 sd_id128_t id;
3fbf9cbb
LP
1322
1323 r = readdir_r(d, &buf, &de);
1324 if (r != 0 || !de)
1325 break;
1326
a963990f
LP
1327 if (dirent_is_file_with_suffix(de, ".journal")) {
1328 r = add_file(j, m->path, de->d_name);
1329 if (r < 0)
1330 log_debug("Failed to add file %s/%s: %s", m->path, de->d_name, strerror(-r));
3fbf9cbb 1331
6f5878a2 1332 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
a963990f
LP
1333 sd_id128_from_string(de->d_name, &id) >= 0) {
1334
1335 r = add_directory(j, m->path, de->d_name);
1336 if (r < 0)
1337 log_debug("Failed to add directory %s/%s: %s", m->path, de->d_name, strerror(-r));
1338 }
3fbf9cbb
LP
1339 }
1340
1341 closedir(d);
1342
a963990f
LP
1343 return 0;
1344}
1345
1346static int remove_directory(sd_journal *j, Directory *d) {
1347 assert(j);
1348
1349 if (d->wd > 0) {
1350 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1351
1352 if (j->inotify_fd >= 0)
1353 inotify_rm_watch(j->inotify_fd, d->wd);
1354 }
1355
1356 hashmap_remove(j->directories_by_path, d->path);
1357
1358 if (d->is_root)
1359 log_debug("Root directory %s got removed.", d->path);
1360 else
1361 log_debug("Directory %s got removed.", d->path);
1362
1363 free(d->path);
1364 free(d);
50f20cfd 1365
3fbf9cbb
LP
1366 return 0;
1367}
1368
a963990f
LP
1369static int add_search_paths(sd_journal *j) {
1370
1371 const char search_paths[] =
1372 "/run/log/journal\0"
1373 "/var/log/journal\0";
1374 const char *p;
50f20cfd
LP
1375
1376 assert(j);
50f20cfd 1377
a963990f
LP
1378 /* We ignore most errors here, since the idea is to only open
1379 * what's actually accessible, and ignore the rest. */
50f20cfd 1380
a963990f
LP
1381 NULSTR_FOREACH(p, search_paths)
1382 add_root_directory(j, p);
50f20cfd 1383
a963990f 1384 return 0;
50f20cfd
LP
1385}
1386
a963990f 1387static int allocate_inotify(sd_journal *j) {
50f20cfd 1388 assert(j);
50f20cfd 1389
a963990f
LP
1390 if (j->inotify_fd < 0) {
1391 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1392 if (j->inotify_fd < 0)
1393 return -errno;
1394 }
50f20cfd 1395
a963990f
LP
1396 if (!j->directories_by_wd) {
1397 j->directories_by_wd = hashmap_new(trivial_hash_func, trivial_compare_func);
1398 if (!j->directories_by_wd)
1399 return -ENOMEM;
50f20cfd 1400 }
a963990f
LP
1401
1402 return 0;
50f20cfd
LP
1403}
1404
7827b1a1 1405static sd_journal *journal_new(int flags, const char *path) {
a963990f 1406 sd_journal *j;
50f20cfd 1407
a963990f
LP
1408 j = new0(sd_journal, 1);
1409 if (!j)
1410 return NULL;
50f20cfd 1411
a963990f
LP
1412 j->inotify_fd = -1;
1413 j->flags = flags;
50f20cfd 1414
7827b1a1
LP
1415 if (path) {
1416 j->path = strdup(path);
1417 if (!j->path) {
1418 free(j);
1419 return NULL;
1420 }
1421 }
1422
a963990f
LP
1423 j->files = hashmap_new(string_hash_func, string_compare_func);
1424 if (!j->files) {
7827b1a1 1425 free(j->path);
a963990f
LP
1426 free(j);
1427 return NULL;
1428 }
50f20cfd 1429
a963990f
LP
1430 j->directories_by_path = hashmap_new(string_hash_func, string_compare_func);
1431 if (!j->directories_by_path) {
1432 hashmap_free(j->files);
7827b1a1 1433 free(j->path);
a963990f
LP
1434 free(j);
1435 return NULL;
50f20cfd 1436 }
a963990f
LP
1437
1438 return j;
50f20cfd
LP
1439}
1440
a5344d2c 1441_public_ int sd_journal_open(sd_journal **ret, int flags) {
87d2c1ff 1442 sd_journal *j;
3fbf9cbb 1443 int r;
87d2c1ff 1444
a5344d2c
LP
1445 if (!ret)
1446 return -EINVAL;
1447
1448 if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
1449 SD_JOURNAL_RUNTIME_ONLY|
1450 SD_JOURNAL_SYSTEM_ONLY))
1451 return -EINVAL;
87d2c1ff 1452
7827b1a1 1453 j = journal_new(flags, NULL);
87d2c1ff
LP
1454 if (!j)
1455 return -ENOMEM;
1456
a963990f
LP
1457 r = add_search_paths(j);
1458 if (r < 0)
50f20cfd 1459 goto fail;
50f20cfd 1460
a963990f
LP
1461 *ret = j;
1462 return 0;
cf244689 1463
a963990f
LP
1464fail:
1465 sd_journal_close(j);
87d2c1ff 1466
a963990f
LP
1467 return r;
1468}
50f20cfd 1469
a963990f
LP
1470_public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1471 sd_journal *j;
1472 int r;
87d2c1ff 1473
a963990f
LP
1474 if (!ret)
1475 return -EINVAL;
87d2c1ff 1476
a963990f
LP
1477 if (!path || !path_is_absolute(path))
1478 return -EINVAL;
87d2c1ff 1479
a963990f
LP
1480 if (flags != 0)
1481 return -EINVAL;
87d2c1ff 1482
7827b1a1 1483 j = journal_new(flags, path);
a963990f
LP
1484 if (!j)
1485 return -ENOMEM;
3fbf9cbb 1486
a963990f
LP
1487 r = add_root_directory(j, path);
1488 if (r < 0)
1489 goto fail;
87d2c1ff
LP
1490
1491 *ret = j;
1492 return 0;
1493
1494fail:
1495 sd_journal_close(j);
1496
1497 return r;
a963990f 1498}
87d2c1ff 1499
a5344d2c 1500_public_ void sd_journal_close(sd_journal *j) {
a963990f
LP
1501 Directory *d;
1502 JournalFile *f;
1503
a5344d2c
LP
1504 if (!j)
1505 return;
87d2c1ff 1506
a963990f
LP
1507 while ((f = hashmap_steal_first(j->files)))
1508 journal_file_close(f);
50f20cfd 1509
a963990f 1510 hashmap_free(j->files);
260a2be4 1511
a963990f
LP
1512 while ((d = hashmap_first(j->directories_by_path)))
1513 remove_directory(j, d);
260a2be4 1514
a963990f
LP
1515 while ((d = hashmap_first(j->directories_by_wd)))
1516 remove_directory(j, d);
87d2c1ff 1517
a963990f
LP
1518 hashmap_free(j->directories_by_path);
1519 hashmap_free(j->directories_by_wd);
1cc101f1 1520
50f20cfd
LP
1521 if (j->inotify_fd >= 0)
1522 close_nointr_nofail(j->inotify_fd);
1523
a963990f
LP
1524 sd_journal_flush_matches(j);
1525
7827b1a1 1526 free(j->path);
87d2c1ff
LP
1527 free(j);
1528}
3fbf9cbb 1529
a5344d2c 1530_public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
3fbf9cbb
LP
1531 Object *o;
1532 JournalFile *f;
1533 int r;
1534
a5344d2c
LP
1535 if (!j)
1536 return -EINVAL;
1537 if (!ret)
1538 return -EINVAL;
3fbf9cbb
LP
1539
1540 f = j->current_file;
1541 if (!f)
de190aef 1542 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1543
1544 if (f->current_offset <= 0)
de190aef 1545 return -EADDRNOTAVAIL;
3fbf9cbb 1546
de190aef 1547 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1548 if (r < 0)
1549 return r;
1550
1551 *ret = le64toh(o->entry.realtime);
de190aef 1552 return 0;
3fbf9cbb
LP
1553}
1554
a5344d2c 1555_public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
3fbf9cbb
LP
1556 Object *o;
1557 JournalFile *f;
1558 int r;
1559 sd_id128_t id;
1560
a5344d2c
LP
1561 if (!j)
1562 return -EINVAL;
3fbf9cbb
LP
1563
1564 f = j->current_file;
1565 if (!f)
de190aef 1566 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1567
1568 if (f->current_offset <= 0)
de190aef 1569 return -EADDRNOTAVAIL;
3fbf9cbb 1570
de190aef 1571 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1572 if (r < 0)
1573 return r;
1574
de190aef
LP
1575 if (ret_boot_id)
1576 *ret_boot_id = o->entry.boot_id;
1577 else {
1578 r = sd_id128_get_boot(&id);
1579 if (r < 0)
1580 return r;
3fbf9cbb 1581
de190aef 1582 if (!sd_id128_equal(id, o->entry.boot_id))
df50185b 1583 return -ESTALE;
de190aef 1584 }
3fbf9cbb 1585
14a65d65
LP
1586 if (ret)
1587 *ret = le64toh(o->entry.monotonic);
1588
de190aef 1589 return 0;
3fbf9cbb
LP
1590}
1591
362a3f81
LP
1592static bool field_is_valid(const char *field) {
1593 const char *p;
1594
1595 assert(field);
1596
1597 if (isempty(field))
1598 return false;
1599
1600 if (startswith(field, "__"))
1601 return false;
1602
1603 for (p = field; *p; p++) {
1604
1605 if (*p == '_')
1606 continue;
1607
1608 if (*p >= 'A' && *p <= 'Z')
1609 continue;
1610
1611 if (*p >= '0' && *p <= '9')
1612 continue;
1613
1614 return false;
1615 }
1616
1617 return true;
1618}
1619
a5344d2c 1620_public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
3fbf9cbb
LP
1621 JournalFile *f;
1622 uint64_t i, n;
1623 size_t field_length;
1624 int r;
1625 Object *o;
1626
a5344d2c
LP
1627 if (!j)
1628 return -EINVAL;
1629 if (!field)
1630 return -EINVAL;
1631 if (!data)
1632 return -EINVAL;
1633 if (!size)
1634 return -EINVAL;
3fbf9cbb 1635
362a3f81 1636 if (!field_is_valid(field))
3fbf9cbb
LP
1637 return -EINVAL;
1638
1639 f = j->current_file;
1640 if (!f)
de190aef 1641 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1642
1643 if (f->current_offset <= 0)
de190aef 1644 return -EADDRNOTAVAIL;
3fbf9cbb 1645
de190aef 1646 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1647 if (r < 0)
1648 return r;
1649
1650 field_length = strlen(field);
1651
1652 n = journal_file_entry_n_items(o);
1653 for (i = 0; i < n; i++) {
4fd052ae
FC
1654 uint64_t p, l;
1655 le64_t le_hash;
3fbf9cbb
LP
1656 size_t t;
1657
1658 p = le64toh(o->entry.items[i].object_offset);
807e17f0 1659 le_hash = o->entry.items[i].hash;
de190aef 1660 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
3fbf9cbb
LP
1661 if (r < 0)
1662 return r;
1663
de190aef 1664 if (le_hash != o->data.hash)
de7b95cd
LP
1665 return -EBADMSG;
1666
3fbf9cbb
LP
1667 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1668
807e17f0
LP
1669 if (o->object.flags & OBJECT_COMPRESSED) {
1670
1671#ifdef HAVE_XZ
1672 if (uncompress_startswith(o->data.payload, l,
1673 &f->compress_buffer, &f->compress_buffer_size,
1674 field, field_length, '=')) {
1675
1676 uint64_t rsize;
1677
1678 if (!uncompress_blob(o->data.payload, l,
1679 &f->compress_buffer, &f->compress_buffer_size, &rsize))
1680 return -EBADMSG;
1681
1682 *data = f->compress_buffer;
1683 *size = (size_t) rsize;
1684
1685 return 0;
1686 }
1687#else
1688 return -EPROTONOSUPPORT;
1689#endif
1690
1691 } else if (l >= field_length+1 &&
1692 memcmp(o->data.payload, field, field_length) == 0 &&
1693 o->data.payload[field_length] == '=') {
3fbf9cbb 1694
161e54f8 1695 t = (size_t) l;
3fbf9cbb 1696
161e54f8
LP
1697 if ((uint64_t) t != l)
1698 return -E2BIG;
3fbf9cbb 1699
161e54f8
LP
1700 *data = o->data.payload;
1701 *size = t;
3fbf9cbb 1702
de190aef 1703 return 0;
161e54f8 1704 }
3fbf9cbb 1705
de190aef 1706 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
161e54f8
LP
1707 if (r < 0)
1708 return r;
3fbf9cbb
LP
1709 }
1710
de190aef 1711 return -ENOENT;
3fbf9cbb
LP
1712}
1713
a5344d2c 1714_public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
3fbf9cbb 1715 JournalFile *f;
4fd052ae
FC
1716 uint64_t p, l, n;
1717 le64_t le_hash;
3fbf9cbb
LP
1718 int r;
1719 Object *o;
de190aef 1720 size_t t;
3fbf9cbb 1721
a5344d2c
LP
1722 if (!j)
1723 return -EINVAL;
1724 if (!data)
1725 return -EINVAL;
1726 if (!size)
1727 return -EINVAL;
3fbf9cbb
LP
1728
1729 f = j->current_file;
1730 if (!f)
de190aef 1731 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1732
1733 if (f->current_offset <= 0)
de190aef 1734 return -EADDRNOTAVAIL;
3fbf9cbb 1735
de190aef 1736 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1737 if (r < 0)
1738 return r;
1739
1740 n = journal_file_entry_n_items(o);
7210bfb3 1741 if (j->current_field >= n)
3fbf9cbb
LP
1742 return 0;
1743
7210bfb3 1744 p = le64toh(o->entry.items[j->current_field].object_offset);
de190aef
LP
1745 le_hash = o->entry.items[j->current_field].hash;
1746 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
3fbf9cbb
LP
1747 if (r < 0)
1748 return r;
1749
de190aef 1750 if (le_hash != o->data.hash)
de7b95cd
LP
1751 return -EBADMSG;
1752
3fbf9cbb
LP
1753 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1754 t = (size_t) l;
1755
1756 /* We can't read objects larger than 4G on a 32bit machine */
1757 if ((uint64_t) t != l)
1758 return -E2BIG;
1759
807e17f0
LP
1760 if (o->object.flags & OBJECT_COMPRESSED) {
1761#ifdef HAVE_XZ
1762 uint64_t rsize;
1763
1764 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize))
1765 return -EBADMSG;
1766
1767 *data = f->compress_buffer;
1768 *size = (size_t) rsize;
1769#else
1770 return -EPROTONOSUPPORT;
1771#endif
1772 } else {
1773 *data = o->data.payload;
1774 *size = t;
1775 }
3fbf9cbb 1776
7210bfb3 1777 j->current_field ++;
3fbf9cbb
LP
1778
1779 return 1;
1780}
c2373f84 1781
a5344d2c
LP
1782_public_ void sd_journal_restart_data(sd_journal *j) {
1783 if (!j)
1784 return;
8725d60a
LP
1785
1786 j->current_field = 0;
c2373f84 1787}
50f20cfd 1788
a5344d2c 1789_public_ int sd_journal_get_fd(sd_journal *j) {
a963990f
LP
1790 int r;
1791
a5344d2c
LP
1792 if (!j)
1793 return -EINVAL;
50f20cfd 1794
a963990f
LP
1795 if (j->inotify_fd >= 0)
1796 return j->inotify_fd;
1797
1798 r = allocate_inotify(j);
1799 if (r < 0)
1800 return r;
1801
1802 /* Iterate through all dirs again, to add them to the
1803 * inotify */
7827b1a1
LP
1804 if (j->path)
1805 r = add_root_directory(j, j->path);
1806 else
1807 r = add_search_paths(j);
a963990f
LP
1808 if (r < 0)
1809 return r;
1810
50f20cfd
LP
1811 return j->inotify_fd;
1812}
1813
1814static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
a963990f 1815 Directory *d;
50f20cfd
LP
1816 int r;
1817
1818 assert(j);
1819 assert(e);
1820
1821 /* Is this a subdirectory we watch? */
a963990f
LP
1822 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
1823 if (d) {
1824 sd_id128_t id;
50f20cfd
LP
1825
1826 if (!(e->mask & IN_ISDIR) && e->len > 0 && endswith(e->name, ".journal")) {
1827
1828 /* Event for a journal file */
1829
1830 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
a963990f 1831 r = add_file(j, d->path, e->name);
50f20cfd 1832 if (r < 0)
a963990f
LP
1833 log_debug("Failed to add file %s/%s: %s", d->path, e->name, strerror(-r));
1834
50f20cfd
LP
1835 } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
1836
a963990f 1837 r = remove_file(j, d->path, e->name);
50f20cfd 1838 if (r < 0)
a963990f 1839 log_debug("Failed to remove file %s/%s: %s", d->path, e->name, strerror(-r));
50f20cfd
LP
1840 }
1841
a963990f 1842 } else if (!d->is_root && e->len == 0) {
50f20cfd 1843
a963990f 1844 /* Event for a subdirectory */
50f20cfd 1845
a963990f
LP
1846 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
1847 r = remove_directory(j, d);
50f20cfd 1848 if (r < 0)
a963990f 1849 log_debug("Failed to remove directory %s: %s", d->path, strerror(-r));
50f20cfd
LP
1850 }
1851
50f20cfd 1852
a963990f 1853 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
50f20cfd 1854
a963990f 1855 /* Event for root directory */
50f20cfd 1856
a963990f
LP
1857 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1858 r = add_directory(j, d->path, e->name);
50f20cfd 1859 if (r < 0)
a963990f 1860 log_debug("Failed to add directory %s/%s: %s", d->path, e->name, strerror(-r));
50f20cfd
LP
1861 }
1862 }
1863
1864 return;
1865 }
1866
1867 if (e->mask & IN_IGNORED)
1868 return;
1869
1870 log_warning("Unknown inotify event.");
1871}
1872
a963990f
LP
1873static int determine_change(sd_journal *j) {
1874 bool b;
1875
1876 assert(j);
1877
1878 b = j->current_invalidate_counter != j->last_invalidate_counter;
1879 j->last_invalidate_counter = j->current_invalidate_counter;
1880
1881 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
1882}
1883
a5344d2c 1884_public_ int sd_journal_process(sd_journal *j) {
19d1e4ee 1885 uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX] _alignas_(struct inotify_event);
a963990f 1886 bool got_something = false;
50f20cfd 1887
a5344d2c
LP
1888 if (!j)
1889 return -EINVAL;
50f20cfd
LP
1890
1891 for (;;) {
1892 struct inotify_event *e;
1893 ssize_t l;
1894
1895 l = read(j->inotify_fd, buffer, sizeof(buffer));
1896 if (l < 0) {
a963990f
LP
1897 if (errno == EAGAIN || errno == EINTR)
1898 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
50f20cfd
LP
1899
1900 return -errno;
1901 }
1902
a963990f
LP
1903 got_something = true;
1904
50f20cfd
LP
1905 e = (struct inotify_event*) buffer;
1906 while (l > 0) {
1907 size_t step;
1908
1909 process_inotify_event(j, e);
1910
1911 step = sizeof(struct inotify_event) + e->len;
1912 assert(step <= (size_t) l);
1913
1914 e = (struct inotify_event*) ((uint8_t*) e + step);
1915 l -= step;
1916 }
1917 }
a963990f
LP
1918
1919 return determine_change(j);
50f20cfd 1920}
6ad1d1c3 1921
e02d1cf7 1922_public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
a963990f 1923 int r;
e02d1cf7
LP
1924
1925 assert(j);
1926
a963990f
LP
1927 if (j->inotify_fd < 0) {
1928
1929 /* This is the first invocation, hence create the
1930 * inotify watch */
1931 r = sd_journal_get_fd(j);
1932 if (r < 0)
1933 return r;
1934
1935 /* The journal might have changed since the context
1936 * object was created and we weren't watching before,
1937 * hence don't wait for anything, and return
1938 * immediately. */
1939 return determine_change(j);
1940 }
1941
1942 do {
1943 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
1944 } while (r == -EINTR);
e02d1cf7
LP
1945
1946 if (r < 0)
1947 return r;
1948
a963990f 1949 return sd_journal_process(j);
e02d1cf7
LP
1950}
1951
08984293
LP
1952_public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
1953 Iterator i;
1954 JournalFile *f;
1955 bool first = true;
1956 int r;
1957
1958 if (!j)
1959 return -EINVAL;
1960 if (!from && !to)
1961 return -EINVAL;
1962
1963 HASHMAP_FOREACH(f, j->files, i) {
1964 usec_t fr, t;
1965
1966 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
9f8d2983
LP
1967 if (r == -ENOENT)
1968 continue;
08984293
LP
1969 if (r < 0)
1970 return r;
1971 if (r == 0)
1972 continue;
1973
1974 if (first) {
1975 if (from)
1976 *from = fr;
1977 if (to)
1978 *to = t;
1979 first = false;
1980 } else {
1981 if (from)
1982 *from = MIN(fr, *from);
1983 if (to)
1984 *to = MIN(t, *to);
1985 }
1986 }
1987
1988 return first ? 0 : 1;
1989}
1990
1991_public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
1992 Iterator i;
1993 JournalFile *f;
1994 bool first = true;
1995 int r;
1996
1997 if (!j)
1998 return -EINVAL;
1999 if (!from && !to)
2000 return -EINVAL;
2001
2002 HASHMAP_FOREACH(f, j->files, i) {
2003 usec_t fr, t;
2004
2005 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
9f8d2983
LP
2006 if (r == -ENOENT)
2007 continue;
08984293
LP
2008 if (r < 0)
2009 return r;
2010 if (r == 0)
2011 continue;
2012
2013 if (first) {
2014 if (from)
2015 *from = fr;
2016 if (to)
2017 *to = t;
2018 first = false;
2019 } else {
2020 if (from)
2021 *from = MIN(fr, *from);
2022 if (to)
2023 *to = MIN(t, *to);
2024 }
2025 }
2026
2027 return first ? 0 : 1;
2028}
2029
dca6219e
LP
2030void journal_print_header(sd_journal *j) {
2031 Iterator i;
2032 JournalFile *f;
2033 bool newline = false;
2034
2035 assert(j);
2036
2037 HASHMAP_FOREACH(f, j->files, i) {
2038 if (newline)
2039 putchar('\n');
2040 else
2041 newline = true;
2042
2043 journal_file_print_header(f);
2044 }
2045}
08984293 2046
19a2bd80
LP
2047/* _public_ int sd_journal_query_unique(sd_journal *j, const char *field) { */
2048/* if (!j) */
2049/* return -EINVAL; */
2050/* if (!field) */
2051/* return -EINVAL; */
2052
2053/* return -ENOTSUP; */
2054/* } */
2055
2056/* _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { */
2057/* if (!j) */
2058/* return -EINVAL; */
2059/* if (!data) */
2060/* return -EINVAL; */
2061/* if (!l) */
2062/* return -EINVAL; */
2063
2064/* return -ENOTSUP; */
2065/* } */
2066
2067/* _public_ void sd_journal_restart_unique(sd_journal *j) { */
2068/* if (!j) */
2069/* return; */
2070/* } */