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