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