]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/sd-journal.c
systemd-python: export sd_j_get_fd, sd_j_reliable_fd, sd_j_close
[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
LP
1217static int add_file(sd_journal *j, const char *prefix, const char *filename) {
1218 char *path;
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
a963990f
LP
1237 if (hashmap_get(j->files, path)) {
1238 free(path);
50f20cfd
LP
1239 return 0;
1240 }
1241
1242 if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
a963990f
LP
1243 log_debug("Too many open journal files, not adding %s, ignoring.", path);
1244 free(path);
50f20cfd
LP
1245 return 0;
1246 }
1247
16e9f408 1248 r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
a963990f 1249 free(path);
3fbf9cbb
LP
1250
1251 if (r < 0) {
1252 if (errno == ENOENT)
1253 return 0;
1254
1255 return r;
1256 }
1257
72f59706 1258 /* journal_file_dump(f); */
de190aef 1259
3fbf9cbb
LP
1260 r = hashmap_put(j->files, f->path, f);
1261 if (r < 0) {
1262 journal_file_close(f);
1263 return r;
1264 }
1265
85210bff
LP
1266 check_network(j, f->fd);
1267
a963990f
LP
1268 j->current_invalidate_counter ++;
1269
50f20cfd
LP
1270 log_debug("File %s got added.", f->path);
1271
1272 return 0;
1273}
1274
a963990f
LP
1275static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
1276 char *path;
50f20cfd
LP
1277 JournalFile *f;
1278
1279 assert(j);
1280 assert(prefix);
1281 assert(filename);
1282
b7def684 1283 path = strjoin(prefix, "/", filename, NULL);
a963990f 1284 if (!path)
50f20cfd
LP
1285 return -ENOMEM;
1286
a963990f
LP
1287 f = hashmap_get(j->files, path);
1288 free(path);
50f20cfd
LP
1289 if (!f)
1290 return 0;
1291
1292 hashmap_remove(j->files, f->path);
44a5fa34
LP
1293
1294 log_debug("File %s got removed.", f->path);
1295
3c1668da
LP
1296 if (j->current_file == f) {
1297 j->current_file = NULL;
1298 j->current_field = 0;
1299 }
1300
1301 if (j->unique_file == f) {
1302 j->unique_file = NULL;
1303 j->unique_offset = 0;
1304 }
1305
50f20cfd
LP
1306 journal_file_close(f);
1307
a963990f
LP
1308 j->current_invalidate_counter ++;
1309
3fbf9cbb
LP
1310 return 0;
1311}
1312
a963990f
LP
1313static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1314 char *path;
3fbf9cbb
LP
1315 int r;
1316 DIR *d;
cf244689 1317 sd_id128_t id, mid;
a963990f 1318 Directory *m;
3fbf9cbb
LP
1319
1320 assert(j);
1321 assert(prefix);
a963990f 1322 assert(dirname);
3fbf9cbb 1323
d95b1fb3
ZJS
1324 log_debug("Considering %s/%s.", prefix, dirname);
1325
cf244689 1326 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
a963990f 1327 (sd_id128_from_string(dirname, &id) < 0 ||
cf244689 1328 sd_id128_get_machine(&mid) < 0 ||
d95b1fb3 1329 !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
cf244689
LP
1330 return 0;
1331
b7def684 1332 path = strjoin(prefix, "/", dirname, NULL);
a963990f 1333 if (!path)
3fbf9cbb
LP
1334 return -ENOMEM;
1335
a963990f 1336 d = opendir(path);
3fbf9cbb 1337 if (!d) {
a963990f
LP
1338 log_debug("Failed to open %s: %m", path);
1339 free(path);
1340
3fbf9cbb
LP
1341 if (errno == ENOENT)
1342 return 0;
3fbf9cbb
LP
1343 return -errno;
1344 }
1345
a963990f
LP
1346 m = hashmap_get(j->directories_by_path, path);
1347 if (!m) {
1348 m = new0(Directory, 1);
1349 if (!m) {
1350 closedir(d);
1351 free(path);
1352 return -ENOMEM;
1353 }
1354
1355 m->is_root = false;
1356 m->path = path;
1357
1358 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1359 closedir(d);
1360 free(m->path);
1361 free(m);
1362 return -ENOMEM;
1363 }
1364
1365 j->current_invalidate_counter ++;
1366
1367 log_debug("Directory %s got added.", m->path);
1368
1369 } else if (m->is_root) {
1370 free (path);
1371 closedir(d);
1372 return 0;
1373 } else
1374 free(path);
1375
1376 if (m->wd <= 0 && j->inotify_fd >= 0) {
1377
1378 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1379 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
5e6870ea 1380 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
4a842cad 1381 IN_ONLYDIR);
a963990f
LP
1382
1383 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1384 inotify_rm_watch(j->inotify_fd, m->wd);
1385 }
1386
1387 for (;;) {
7d5e9c0f
LP
1388 struct dirent *de;
1389 union dirent_storage buf;
a963990f 1390
7d5e9c0f 1391 r = readdir_r(d, &buf.de, &de);
a963990f
LP
1392 if (r != 0 || !de)
1393 break;
1394
de2c3907
LP
1395 if (dirent_is_file_with_suffix(de, ".journal") ||
1396 dirent_is_file_with_suffix(de, ".journal~")) {
a963990f
LP
1397 r = add_file(j, m->path, de->d_name);
1398 if (r < 0)
1399 log_debug("Failed to add file %s/%s: %s", m->path, de->d_name, strerror(-r));
1400 }
1401 }
1402
85210bff
LP
1403 check_network(j, dirfd(d));
1404
a963990f
LP
1405 closedir(d);
1406
1407 return 0;
1408}
1409
1410static int add_root_directory(sd_journal *j, const char *p) {
1411 DIR *d;
1412 Directory *m;
1413 int r;
1414
1415 assert(j);
1416 assert(p);
1417
1418 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1419 !path_startswith(p, "/run"))
1420 return -EINVAL;
1421
1422 d = opendir(p);
1423 if (!d)
1424 return -errno;
1425
1426 m = hashmap_get(j->directories_by_path, p);
1427 if (!m) {
1428 m = new0(Directory, 1);
1429 if (!m) {
1430 closedir(d);
1431 return -ENOMEM;
1432 }
1433
1434 m->is_root = true;
1435 m->path = strdup(p);
1436 if (!m->path) {
1437 closedir(d);
1438 free(m);
1439 return -ENOMEM;
1440 }
1441
1442 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1443 closedir(d);
1444 free(m->path);
1445 free(m);
1446 return -ENOMEM;
1447 }
1448
1449 j->current_invalidate_counter ++;
1450
1451 log_debug("Root directory %s got added.", m->path);
1452
1453 } else if (!m->is_root) {
1454 closedir(d);
1455 return 0;
50f20cfd
LP
1456 }
1457
a963990f
LP
1458 if (m->wd <= 0 && j->inotify_fd >= 0) {
1459
1460 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1461 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
4a842cad 1462 IN_ONLYDIR);
a963990f
LP
1463
1464 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1465 inotify_rm_watch(j->inotify_fd, m->wd);
1466 }
50f20cfd 1467
3fbf9cbb 1468 for (;;) {
7d5e9c0f
LP
1469 struct dirent *de;
1470 union dirent_storage buf;
a963990f 1471 sd_id128_t id;
3fbf9cbb 1472
7d5e9c0f 1473 r = readdir_r(d, &buf.de, &de);
3fbf9cbb
LP
1474 if (r != 0 || !de)
1475 break;
1476
de2c3907
LP
1477 if (dirent_is_file_with_suffix(de, ".journal") ||
1478 dirent_is_file_with_suffix(de, ".journal~")) {
a963990f
LP
1479 r = add_file(j, m->path, de->d_name);
1480 if (r < 0)
1481 log_debug("Failed to add file %s/%s: %s", m->path, de->d_name, strerror(-r));
3fbf9cbb 1482
6f5878a2 1483 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
a963990f
LP
1484 sd_id128_from_string(de->d_name, &id) >= 0) {
1485
1486 r = add_directory(j, m->path, de->d_name);
1487 if (r < 0)
1488 log_debug("Failed to add directory %s/%s: %s", m->path, de->d_name, strerror(-r));
1489 }
3fbf9cbb
LP
1490 }
1491
85210bff
LP
1492 check_network(j, dirfd(d));
1493
3fbf9cbb
LP
1494 closedir(d);
1495
a963990f
LP
1496 return 0;
1497}
1498
1499static int remove_directory(sd_journal *j, Directory *d) {
1500 assert(j);
1501
1502 if (d->wd > 0) {
1503 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1504
1505 if (j->inotify_fd >= 0)
1506 inotify_rm_watch(j->inotify_fd, d->wd);
1507 }
1508
1509 hashmap_remove(j->directories_by_path, d->path);
1510
1511 if (d->is_root)
1512 log_debug("Root directory %s got removed.", d->path);
1513 else
1514 log_debug("Directory %s got removed.", d->path);
1515
1516 free(d->path);
1517 free(d);
50f20cfd 1518
3fbf9cbb
LP
1519 return 0;
1520}
1521
a963990f
LP
1522static int add_search_paths(sd_journal *j) {
1523
1524 const char search_paths[] =
1525 "/run/log/journal\0"
1526 "/var/log/journal\0";
1527 const char *p;
50f20cfd
LP
1528
1529 assert(j);
50f20cfd 1530
a963990f
LP
1531 /* We ignore most errors here, since the idea is to only open
1532 * what's actually accessible, and ignore the rest. */
50f20cfd 1533
a963990f
LP
1534 NULSTR_FOREACH(p, search_paths)
1535 add_root_directory(j, p);
50f20cfd 1536
a963990f 1537 return 0;
50f20cfd
LP
1538}
1539
a963990f 1540static int allocate_inotify(sd_journal *j) {
50f20cfd 1541 assert(j);
50f20cfd 1542
a963990f
LP
1543 if (j->inotify_fd < 0) {
1544 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1545 if (j->inotify_fd < 0)
1546 return -errno;
1547 }
50f20cfd 1548
a963990f
LP
1549 if (!j->directories_by_wd) {
1550 j->directories_by_wd = hashmap_new(trivial_hash_func, trivial_compare_func);
1551 if (!j->directories_by_wd)
1552 return -ENOMEM;
50f20cfd 1553 }
a963990f
LP
1554
1555 return 0;
50f20cfd
LP
1556}
1557
7827b1a1 1558static sd_journal *journal_new(int flags, const char *path) {
a963990f 1559 sd_journal *j;
50f20cfd 1560
a963990f
LP
1561 j = new0(sd_journal, 1);
1562 if (!j)
1563 return NULL;
50f20cfd 1564
a963990f
LP
1565 j->inotify_fd = -1;
1566 j->flags = flags;
93b73b06 1567 j->data_threshold = DEFAULT_DATA_THRESHOLD;
50f20cfd 1568
7827b1a1
LP
1569 if (path) {
1570 j->path = strdup(path);
1571 if (!j->path) {
1572 free(j);
1573 return NULL;
1574 }
1575 }
1576
a963990f
LP
1577 j->files = hashmap_new(string_hash_func, string_compare_func);
1578 if (!j->files) {
7827b1a1 1579 free(j->path);
a963990f
LP
1580 free(j);
1581 return NULL;
1582 }
50f20cfd 1583
a963990f
LP
1584 j->directories_by_path = hashmap_new(string_hash_func, string_compare_func);
1585 if (!j->directories_by_path) {
1586 hashmap_free(j->files);
7827b1a1 1587 free(j->path);
a963990f
LP
1588 free(j);
1589 return NULL;
50f20cfd 1590 }
a963990f 1591
84168d80 1592 j->mmap = mmap_cache_new();
16e9f408
LP
1593 if (!j->mmap) {
1594 hashmap_free(j->files);
1595 hashmap_free(j->directories_by_path);
1596 free(j->path);
1597 free(j);
f2848607 1598 return NULL;
16e9f408
LP
1599 }
1600
a963990f 1601 return j;
50f20cfd
LP
1602}
1603
a5344d2c 1604_public_ int sd_journal_open(sd_journal **ret, int flags) {
87d2c1ff 1605 sd_journal *j;
3fbf9cbb 1606 int r;
87d2c1ff 1607
a5344d2c
LP
1608 if (!ret)
1609 return -EINVAL;
1610
1611 if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
1612 SD_JOURNAL_RUNTIME_ONLY|
1613 SD_JOURNAL_SYSTEM_ONLY))
1614 return -EINVAL;
87d2c1ff 1615
7827b1a1 1616 j = journal_new(flags, NULL);
87d2c1ff
LP
1617 if (!j)
1618 return -ENOMEM;
1619
a963990f
LP
1620 r = add_search_paths(j);
1621 if (r < 0)
50f20cfd 1622 goto fail;
50f20cfd 1623
a963990f
LP
1624 *ret = j;
1625 return 0;
cf244689 1626
a963990f
LP
1627fail:
1628 sd_journal_close(j);
87d2c1ff 1629
a963990f
LP
1630 return r;
1631}
50f20cfd 1632
a963990f
LP
1633_public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1634 sd_journal *j;
1635 int r;
87d2c1ff 1636
a963990f
LP
1637 if (!ret)
1638 return -EINVAL;
87d2c1ff 1639
a963990f
LP
1640 if (!path || !path_is_absolute(path))
1641 return -EINVAL;
87d2c1ff 1642
a963990f
LP
1643 if (flags != 0)
1644 return -EINVAL;
87d2c1ff 1645
7827b1a1 1646 j = journal_new(flags, path);
a963990f
LP
1647 if (!j)
1648 return -ENOMEM;
3fbf9cbb 1649
a963990f
LP
1650 r = add_root_directory(j, path);
1651 if (r < 0)
1652 goto fail;
87d2c1ff
LP
1653
1654 *ret = j;
1655 return 0;
1656
1657fail:
1658 sd_journal_close(j);
1659
1660 return r;
a963990f 1661}
87d2c1ff 1662
a5344d2c 1663_public_ void sd_journal_close(sd_journal *j) {
a963990f
LP
1664 Directory *d;
1665 JournalFile *f;
1666
a5344d2c
LP
1667 if (!j)
1668 return;
87d2c1ff 1669
a963990f
LP
1670 while ((f = hashmap_steal_first(j->files)))
1671 journal_file_close(f);
50f20cfd 1672
a963990f 1673 hashmap_free(j->files);
260a2be4 1674
a963990f
LP
1675 while ((d = hashmap_first(j->directories_by_path)))
1676 remove_directory(j, d);
260a2be4 1677
a963990f
LP
1678 while ((d = hashmap_first(j->directories_by_wd)))
1679 remove_directory(j, d);
87d2c1ff 1680
a963990f
LP
1681 hashmap_free(j->directories_by_path);
1682 hashmap_free(j->directories_by_wd);
1cc101f1 1683
50f20cfd
LP
1684 if (j->inotify_fd >= 0)
1685 close_nointr_nofail(j->inotify_fd);
1686
a963990f
LP
1687 sd_journal_flush_matches(j);
1688
16e9f408
LP
1689 if (j->mmap)
1690 mmap_cache_unref(j->mmap);
1691
7827b1a1 1692 free(j->path);
3c1668da 1693 free(j->unique_field);
87d2c1ff
LP
1694 free(j);
1695}
3fbf9cbb 1696
a5344d2c 1697_public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
3fbf9cbb
LP
1698 Object *o;
1699 JournalFile *f;
1700 int r;
1701
a5344d2c
LP
1702 if (!j)
1703 return -EINVAL;
1704 if (!ret)
1705 return -EINVAL;
3fbf9cbb
LP
1706
1707 f = j->current_file;
1708 if (!f)
de190aef 1709 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1710
1711 if (f->current_offset <= 0)
de190aef 1712 return -EADDRNOTAVAIL;
3fbf9cbb 1713
de190aef 1714 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1715 if (r < 0)
1716 return r;
1717
1718 *ret = le64toh(o->entry.realtime);
de190aef 1719 return 0;
3fbf9cbb
LP
1720}
1721
a5344d2c 1722_public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
3fbf9cbb
LP
1723 Object *o;
1724 JournalFile *f;
1725 int r;
1726 sd_id128_t id;
1727
a5344d2c
LP
1728 if (!j)
1729 return -EINVAL;
3fbf9cbb
LP
1730
1731 f = j->current_file;
1732 if (!f)
de190aef 1733 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1734
1735 if (f->current_offset <= 0)
de190aef 1736 return -EADDRNOTAVAIL;
3fbf9cbb 1737
de190aef 1738 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1739 if (r < 0)
1740 return r;
1741
de190aef
LP
1742 if (ret_boot_id)
1743 *ret_boot_id = o->entry.boot_id;
1744 else {
1745 r = sd_id128_get_boot(&id);
1746 if (r < 0)
1747 return r;
3fbf9cbb 1748
de190aef 1749 if (!sd_id128_equal(id, o->entry.boot_id))
df50185b 1750 return -ESTALE;
de190aef 1751 }
3fbf9cbb 1752
14a65d65
LP
1753 if (ret)
1754 *ret = le64toh(o->entry.monotonic);
1755
de190aef 1756 return 0;
3fbf9cbb
LP
1757}
1758
362a3f81
LP
1759static bool field_is_valid(const char *field) {
1760 const char *p;
1761
1762 assert(field);
1763
1764 if (isempty(field))
1765 return false;
1766
1767 if (startswith(field, "__"))
1768 return false;
1769
1770 for (p = field; *p; p++) {
1771
1772 if (*p == '_')
1773 continue;
1774
1775 if (*p >= 'A' && *p <= 'Z')
1776 continue;
1777
1778 if (*p >= '0' && *p <= '9')
1779 continue;
1780
1781 return false;
1782 }
1783
1784 return true;
1785}
1786
a5344d2c 1787_public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
3fbf9cbb
LP
1788 JournalFile *f;
1789 uint64_t i, n;
1790 size_t field_length;
1791 int r;
1792 Object *o;
1793
a5344d2c
LP
1794 if (!j)
1795 return -EINVAL;
1796 if (!field)
1797 return -EINVAL;
1798 if (!data)
1799 return -EINVAL;
1800 if (!size)
1801 return -EINVAL;
3fbf9cbb 1802
362a3f81 1803 if (!field_is_valid(field))
3fbf9cbb
LP
1804 return -EINVAL;
1805
1806 f = j->current_file;
1807 if (!f)
de190aef 1808 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1809
1810 if (f->current_offset <= 0)
de190aef 1811 return -EADDRNOTAVAIL;
3fbf9cbb 1812
de190aef 1813 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1814 if (r < 0)
1815 return r;
1816
1817 field_length = strlen(field);
1818
1819 n = journal_file_entry_n_items(o);
1820 for (i = 0; i < n; i++) {
4fd052ae
FC
1821 uint64_t p, l;
1822 le64_t le_hash;
3fbf9cbb
LP
1823 size_t t;
1824
1825 p = le64toh(o->entry.items[i].object_offset);
807e17f0 1826 le_hash = o->entry.items[i].hash;
de190aef 1827 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
3fbf9cbb
LP
1828 if (r < 0)
1829 return r;
1830
de190aef 1831 if (le_hash != o->data.hash)
de7b95cd
LP
1832 return -EBADMSG;
1833
3fbf9cbb
LP
1834 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1835
807e17f0
LP
1836 if (o->object.flags & OBJECT_COMPRESSED) {
1837
1838#ifdef HAVE_XZ
1839 if (uncompress_startswith(o->data.payload, l,
1840 &f->compress_buffer, &f->compress_buffer_size,
1841 field, field_length, '=')) {
1842
1843 uint64_t rsize;
1844
1845 if (!uncompress_blob(o->data.payload, l,
93b73b06
LP
1846 &f->compress_buffer, &f->compress_buffer_size, &rsize,
1847 j->data_threshold))
807e17f0
LP
1848 return -EBADMSG;
1849
1850 *data = f->compress_buffer;
1851 *size = (size_t) rsize;
1852
1853 return 0;
1854 }
1855#else
1856 return -EPROTONOSUPPORT;
1857#endif
1858
1859 } else if (l >= field_length+1 &&
1860 memcmp(o->data.payload, field, field_length) == 0 &&
1861 o->data.payload[field_length] == '=') {
3fbf9cbb 1862
161e54f8 1863 t = (size_t) l;
3fbf9cbb 1864
161e54f8
LP
1865 if ((uint64_t) t != l)
1866 return -E2BIG;
3fbf9cbb 1867
161e54f8
LP
1868 *data = o->data.payload;
1869 *size = t;
3fbf9cbb 1870
93b73b06 1871 return 1;
161e54f8 1872 }
3fbf9cbb 1873
de190aef 1874 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
161e54f8
LP
1875 if (r < 0)
1876 return r;
3fbf9cbb
LP
1877 }
1878
de190aef 1879 return -ENOENT;
3fbf9cbb
LP
1880}
1881
93b73b06 1882static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
3c1668da
LP
1883 size_t t;
1884 uint64_t l;
1885
1886 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1887 t = (size_t) l;
1888
1889 /* We can't read objects larger than 4G on a 32bit machine */
1890 if ((uint64_t) t != l)
1891 return -E2BIG;
1892
1893 if (o->object.flags & OBJECT_COMPRESSED) {
1894#ifdef HAVE_XZ
1895 uint64_t rsize;
1896
93b73b06 1897 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize, j->data_threshold))
3c1668da
LP
1898 return -EBADMSG;
1899
1900 *data = f->compress_buffer;
1901 *size = (size_t) rsize;
1902#else
1903 return -EPROTONOSUPPORT;
1904#endif
1905 } else {
1906 *data = o->data.payload;
1907 *size = t;
1908 }
1909
1910 return 0;
1911}
1912
a5344d2c 1913_public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
3fbf9cbb 1914 JournalFile *f;
3c1668da 1915 uint64_t p, n;
4fd052ae 1916 le64_t le_hash;
3fbf9cbb
LP
1917 int r;
1918 Object *o;
1919
a5344d2c
LP
1920 if (!j)
1921 return -EINVAL;
1922 if (!data)
1923 return -EINVAL;
1924 if (!size)
1925 return -EINVAL;
3fbf9cbb
LP
1926
1927 f = j->current_file;
1928 if (!f)
de190aef 1929 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1930
1931 if (f->current_offset <= 0)
de190aef 1932 return -EADDRNOTAVAIL;
3fbf9cbb 1933
de190aef 1934 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1935 if (r < 0)
1936 return r;
1937
1938 n = journal_file_entry_n_items(o);
7210bfb3 1939 if (j->current_field >= n)
3fbf9cbb
LP
1940 return 0;
1941
7210bfb3 1942 p = le64toh(o->entry.items[j->current_field].object_offset);
de190aef
LP
1943 le_hash = o->entry.items[j->current_field].hash;
1944 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
3fbf9cbb
LP
1945 if (r < 0)
1946 return r;
1947
de190aef 1948 if (le_hash != o->data.hash)
de7b95cd
LP
1949 return -EBADMSG;
1950
93b73b06 1951 r = return_data(j, f, o, data, size);
3c1668da
LP
1952 if (r < 0)
1953 return r;
3fbf9cbb 1954
7210bfb3 1955 j->current_field ++;
3fbf9cbb
LP
1956
1957 return 1;
1958}
c2373f84 1959
a5344d2c
LP
1960_public_ void sd_journal_restart_data(sd_journal *j) {
1961 if (!j)
1962 return;
8725d60a
LP
1963
1964 j->current_field = 0;
c2373f84 1965}
50f20cfd 1966
a5344d2c 1967_public_ int sd_journal_get_fd(sd_journal *j) {
a963990f
LP
1968 int r;
1969
a5344d2c
LP
1970 if (!j)
1971 return -EINVAL;
50f20cfd 1972
a963990f
LP
1973 if (j->inotify_fd >= 0)
1974 return j->inotify_fd;
1975
1976 r = allocate_inotify(j);
1977 if (r < 0)
1978 return r;
1979
1980 /* Iterate through all dirs again, to add them to the
1981 * inotify */
7827b1a1
LP
1982 if (j->path)
1983 r = add_root_directory(j, j->path);
1984 else
1985 r = add_search_paths(j);
a963990f
LP
1986 if (r < 0)
1987 return r;
1988
50f20cfd
LP
1989 return j->inotify_fd;
1990}
1991
1992static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
a963990f 1993 Directory *d;
50f20cfd
LP
1994 int r;
1995
1996 assert(j);
1997 assert(e);
1998
1999 /* Is this a subdirectory we watch? */
a963990f
LP
2000 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2001 if (d) {
2002 sd_id128_t id;
50f20cfd 2003
de2c3907
LP
2004 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2005 (endswith(e->name, ".journal") ||
2006 endswith(e->name, ".journal~"))) {
50f20cfd
LP
2007
2008 /* Event for a journal file */
2009
2010 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
a963990f 2011 r = add_file(j, d->path, e->name);
50f20cfd 2012 if (r < 0)
a963990f
LP
2013 log_debug("Failed to add file %s/%s: %s", d->path, e->name, strerror(-r));
2014
5e6870ea 2015 } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
50f20cfd 2016
a963990f 2017 r = remove_file(j, d->path, e->name);
50f20cfd 2018 if (r < 0)
a963990f 2019 log_debug("Failed to remove file %s/%s: %s", d->path, e->name, strerror(-r));
50f20cfd
LP
2020 }
2021
a963990f 2022 } else if (!d->is_root && e->len == 0) {
50f20cfd 2023
a963990f 2024 /* Event for a subdirectory */
50f20cfd 2025
a963990f
LP
2026 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
2027 r = remove_directory(j, d);
50f20cfd 2028 if (r < 0)
a963990f 2029 log_debug("Failed to remove directory %s: %s", d->path, strerror(-r));
50f20cfd
LP
2030 }
2031
50f20cfd 2032
a963990f 2033 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
50f20cfd 2034
a963990f 2035 /* Event for root directory */
50f20cfd 2036
a963990f
LP
2037 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2038 r = add_directory(j, d->path, e->name);
50f20cfd 2039 if (r < 0)
a963990f 2040 log_debug("Failed to add directory %s/%s: %s", d->path, e->name, strerror(-r));
50f20cfd
LP
2041 }
2042 }
2043
2044 return;
2045 }
2046
2047 if (e->mask & IN_IGNORED)
2048 return;
2049
2050 log_warning("Unknown inotify event.");
2051}
2052
a963990f
LP
2053static int determine_change(sd_journal *j) {
2054 bool b;
2055
2056 assert(j);
2057
2058 b = j->current_invalidate_counter != j->last_invalidate_counter;
2059 j->last_invalidate_counter = j->current_invalidate_counter;
2060
2061 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2062}
2063
a5344d2c 2064_public_ int sd_journal_process(sd_journal *j) {
19d1e4ee 2065 uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX] _alignas_(struct inotify_event);
a963990f 2066 bool got_something = false;
50f20cfd 2067
a5344d2c
LP
2068 if (!j)
2069 return -EINVAL;
50f20cfd
LP
2070
2071 for (;;) {
2072 struct inotify_event *e;
2073 ssize_t l;
2074
2075 l = read(j->inotify_fd, buffer, sizeof(buffer));
2076 if (l < 0) {
a963990f
LP
2077 if (errno == EAGAIN || errno == EINTR)
2078 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
50f20cfd
LP
2079
2080 return -errno;
2081 }
2082
a963990f
LP
2083 got_something = true;
2084
50f20cfd
LP
2085 e = (struct inotify_event*) buffer;
2086 while (l > 0) {
2087 size_t step;
2088
2089 process_inotify_event(j, e);
2090
2091 step = sizeof(struct inotify_event) + e->len;
2092 assert(step <= (size_t) l);
2093
2094 e = (struct inotify_event*) ((uint8_t*) e + step);
2095 l -= step;
2096 }
2097 }
a963990f
LP
2098
2099 return determine_change(j);
50f20cfd 2100}
6ad1d1c3 2101
e02d1cf7 2102_public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
a963990f 2103 int r;
e02d1cf7
LP
2104
2105 assert(j);
2106
a963990f
LP
2107 if (j->inotify_fd < 0) {
2108
2109 /* This is the first invocation, hence create the
2110 * inotify watch */
2111 r = sd_journal_get_fd(j);
2112 if (r < 0)
2113 return r;
2114
2115 /* The journal might have changed since the context
2116 * object was created and we weren't watching before,
2117 * hence don't wait for anything, and return
2118 * immediately. */
2119 return determine_change(j);
2120 }
2121
85210bff
LP
2122 if (j->on_network) {
2123 /* If we are on the network we need to regularly check
2124 * for changes manually */
2125
2126 if (timeout_usec == (uint64_t) -1 || timeout_usec > JOURNAL_FILES_RECHECK_USEC)
2127 timeout_usec = JOURNAL_FILES_RECHECK_USEC;
2128 }
2129
a963990f
LP
2130 do {
2131 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2132 } while (r == -EINTR);
e02d1cf7
LP
2133
2134 if (r < 0)
2135 return r;
2136
a963990f 2137 return sd_journal_process(j);
e02d1cf7
LP
2138}
2139
08984293
LP
2140_public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2141 Iterator i;
2142 JournalFile *f;
2143 bool first = true;
2144 int r;
2145
2146 if (!j)
2147 return -EINVAL;
2148 if (!from && !to)
2149 return -EINVAL;
2150
2151 HASHMAP_FOREACH(f, j->files, i) {
2152 usec_t fr, t;
2153
2154 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
9f8d2983
LP
2155 if (r == -ENOENT)
2156 continue;
08984293
LP
2157 if (r < 0)
2158 return r;
2159 if (r == 0)
2160 continue;
2161
2162 if (first) {
2163 if (from)
2164 *from = fr;
2165 if (to)
2166 *to = t;
2167 first = false;
2168 } else {
2169 if (from)
2170 *from = MIN(fr, *from);
2171 if (to)
0f91dd87 2172 *to = MAX(t, *to);
08984293
LP
2173 }
2174 }
2175
2176 return first ? 0 : 1;
2177}
2178
2179_public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2180 Iterator i;
2181 JournalFile *f;
2182 bool first = true;
2183 int r;
2184
2185 if (!j)
2186 return -EINVAL;
2187 if (!from && !to)
2188 return -EINVAL;
2189
2190 HASHMAP_FOREACH(f, j->files, i) {
2191 usec_t fr, t;
2192
2193 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
9f8d2983
LP
2194 if (r == -ENOENT)
2195 continue;
08984293
LP
2196 if (r < 0)
2197 return r;
2198 if (r == 0)
2199 continue;
2200
2201 if (first) {
2202 if (from)
2203 *from = fr;
2204 if (to)
2205 *to = t;
2206 first = false;
2207 } else {
2208 if (from)
2209 *from = MIN(fr, *from);
2210 if (to)
0f91dd87 2211 *to = MAX(t, *to);
08984293
LP
2212 }
2213 }
2214
2215 return first ? 0 : 1;
2216}
2217
dca6219e
LP
2218void journal_print_header(sd_journal *j) {
2219 Iterator i;
2220 JournalFile *f;
2221 bool newline = false;
2222
2223 assert(j);
2224
2225 HASHMAP_FOREACH(f, j->files, i) {
2226 if (newline)
2227 putchar('\n');
2228 else
2229 newline = true;
2230
2231 journal_file_print_header(f);
2232 }
2233}
08984293 2234
a1a03e30
LP
2235_public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2236 Iterator i;
2237 JournalFile *f;
2238 uint64_t sum = 0;
2239
2240 if (!j)
2241 return -EINVAL;
2242 if (!bytes)
2243 return -EINVAL;
2244
2245 HASHMAP_FOREACH(f, j->files, i) {
2246 struct stat st;
2247
2248 if (fstat(f->fd, &st) < 0)
2249 return -errno;
2250
2251 sum += (uint64_t) st.st_blocks * 512ULL;
2252 }
2253
2254 *bytes = sum;
2255 return 0;
2256}
2257
3c1668da
LP
2258_public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2259 char *f;
2260
2261 if (!j)
2262 return -EINVAL;
2263 if (isempty(field))
2264 return -EINVAL;
d5c4ed62
LP
2265 if (!field_is_valid(field))
2266 return -EINVAL;
3c1668da
LP
2267
2268 f = strdup(field);
2269 if (!f)
2270 return -ENOMEM;
2271
2272 free(j->unique_field);
2273 j->unique_field = f;
2274 j->unique_file = NULL;
2275 j->unique_offset = 0;
2276
2277 return 0;
2278}
2279
2280_public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
2281 Object *o;
2282 size_t k;
2283 int r;
19a2bd80 2284
3c1668da
LP
2285 if (!j)
2286 return -EINVAL;
2287 if (!data)
2288 return -EINVAL;
2289 if (!l)
2290 return -EINVAL;
2291 if (!j->unique_field)
2292 return -EINVAL;
19a2bd80 2293
3c1668da 2294 k = strlen(j->unique_field);
19a2bd80 2295
3c1668da
LP
2296 if (!j->unique_file) {
2297 j->unique_file = hashmap_first(j->files);
2298 if (!j->unique_file)
2299 return 0;
2300 j->unique_offset = 0;
2301 }
19a2bd80 2302
3c1668da
LP
2303 for (;;) {
2304 JournalFile *of;
2305 Iterator i;
2306 const void *odata;
2307 size_t ol;
2308 bool found;
2309
bdc02927 2310 /* Proceed to next data object in the field's linked list */
3c1668da
LP
2311 if (j->unique_offset == 0) {
2312 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2313 if (r < 0)
2314 return r;
2315
2316 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2317 } else {
2318 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2319 if (r < 0)
2320 return r;
2321
2322 j->unique_offset = le64toh(o->data.next_field_offset);
2323 }
2324
2325 /* We reached the end of the list? Then start again, with the next file */
2326 if (j->unique_offset == 0) {
2327 JournalFile *n;
2328
2329 n = hashmap_next(j->files, j->unique_file->path);
2330 if (!n)
2331 return 0;
2332
2333 j->unique_file = n;
2334 continue;
2335 }
2336
2337 /* We do not use the type context here, but 0 instead,
2338 * so that we can look at this data object at the same
2339 * time as one on another file */
2340 r = journal_file_move_to_object(j->unique_file, 0, j->unique_offset, &o);
2341 if (r < 0)
2342 return r;
2343
2344 /* Let's do the type check by hand, since we used 0 context above. */
2345 if (o->object.type != OBJECT_DATA)
2346 return -EBADMSG;
2347
93b73b06 2348 r = return_data(j, j->unique_file, o, &odata, &ol);
3c1668da
LP
2349 if (r < 0)
2350 return r;
2351
2352 /* OK, now let's see if we already returned this data
2353 * object by checking if it exists in the earlier
2354 * traversed files. */
2355 found = false;
2356 HASHMAP_FOREACH(of, j->files, i) {
2357 Object *oo;
2358 uint64_t op;
2359
2360 if (of == j->unique_file)
2361 break;
2362
2363 /* Skip this file it didn't have any fields
2364 * indexed */
2365 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2366 le64toh(of->header->n_fields) <= 0)
2367 continue;
2368
2369 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2370 if (r < 0)
2371 return r;
2372
2373 if (r > 0)
2374 found = true;
2375 }
2376
2377 if (found)
2378 continue;
2379
93b73b06 2380 r = return_data(j, j->unique_file, o, data, l);
3c1668da
LP
2381 if (r < 0)
2382 return r;
2383
2384 return 1;
2385 }
2386}
2387
115646c7 2388_public_ void sd_journal_restart_unique(sd_journal *j) {
3c1668da
LP
2389 if (!j)
2390 return;
2391
2392 j->unique_file = NULL;
2393 j->unique_offset = 0;
2394}
85210bff
LP
2395
2396_public_ int sd_journal_reliable_fd(sd_journal *j) {
2397 if (!j)
2398 return -EINVAL;
2399
2400 return !j->on_network;
2401}
d4205751
LP
2402
2403static char *lookup_field(const char *field, void *userdata) {
2404 sd_journal *j = userdata;
2405 const void *data;
2406 size_t size, d;
2407 int r;
2408
2409 assert(field);
2410 assert(j);
2411
2412 r = sd_journal_get_data(j, field, &data, &size);
2413 if (r < 0 ||
2414 size > REPLACE_VAR_MAX)
2415 return strdup(field);
2416
2417 d = strlen(field) + 1;
2418
2419 return strndup((const char*) data + d, size - d);
2420}
2421
2422_public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
2423 const void *data;
2424 size_t size;
2425 sd_id128_t id;
2426 _cleanup_free_ char *text = NULL, *cid = NULL;
2427 char *t;
2428 int r;
2429
2430 if (!j)
2431 return -EINVAL;
2432 if (!ret)
2433 return -EINVAL;
2434
2435 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
2436 if (r < 0)
2437 return r;
2438
2439 cid = strndup((const char*) data + 11, size - 11);
2440 if (!cid)
2441 return -ENOMEM;
2442
2443 r = sd_id128_from_string(cid, &id);
2444 if (r < 0)
2445 return r;
2446
2447 r = catalog_get(id, &text);
2448 if (r < 0)
2449 return r;
2450
2451 t = replace_var(text, lookup_field, j);
2452 if (!t)
2453 return -ENOMEM;
2454
2455 *ret = t;
2456 return 0;
2457}
8f1e860f
LP
2458
2459_public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
2460 if (!ret)
2461 return -EINVAL;
2462
2463 return catalog_get(id, ret);
2464}
93b73b06
LP
2465
2466_public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
2467 if (!j)
2468 return -EINVAL;
2469
2470 j->data_threshold = sz;
2471 return 0;
2472}
2473
2474_public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
2475 if (!j)
2476 return -EINVAL;
2477 if (!sz)
2478 return -EINVAL;
2479
2480 *sz = j->data_threshold;
2481 return 0;
2482}