]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/sd-journal.c
journal: compare candidate entries using JournalFiles' locations
[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
de190aef
LP
730static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
731 Object *c;
732 uint64_t cp;
cbdca852 733 int r;
de190aef
LP
734
735 assert(j);
736 assert(f);
737
87011c25 738 if (f->last_direction == direction && f->current_offset > 0) {
466ccd92
LP
739 cp = f->current_offset;
740
741 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
de190aef
LP
742 if (r < 0)
743 return r;
744
de190aef
LP
745 r = next_with_matches(j, f, direction, &c, &cp);
746 if (r <= 0)
747 return r;
de190aef 748 } else {
cbdca852 749 r = find_location_with_matches(j, f, direction, &c, &cp);
de190aef
LP
750 if (r <= 0)
751 return r;
de190aef
LP
752 }
753
bc302926 754 /* OK, we found the spot, now let's advance until an entry
cbdca852
LP
755 * that is actually different from what we were previously
756 * looking at. This is necessary to handle entries which exist
757 * in two (or more) journal files, and which shall all be
758 * suppressed but one. */
759
de190aef
LP
760 for (;;) {
761 bool found;
762
763 if (j->current_location.type == LOCATION_DISCRETE) {
764 int k;
765
766 k = compare_with_location(f, c, &j->current_location);
1cdf7175
CH
767
768 found = direction == DIRECTION_DOWN ? k > 0 : k < 0;
de190aef
LP
769 } else
770 found = true;
771
772 if (found) {
6573ef05
MS
773 journal_file_save_location(f, direction, c, cp);
774
de190aef
LP
775 if (ret)
776 *ret = c;
777 if (offset)
778 *offset = cp;
779 return 1;
780 }
781
782 r = next_with_matches(j, f, direction, &c, &cp);
783 if (r <= 0)
784 return r;
785 }
786}
787
e892bd17 788static int real_journal_next(sd_journal *j, direction_t direction) {
468b21de
LP
789 JournalFile *f, *new_file = NULL;
790 uint64_t new_offset = 0;
a002d44b 791 uint64_t p = 0;
cec736d2 792 Iterator i;
a002d44b 793 Object *o;
87d2c1ff
LP
794 int r;
795
1ae464e0
TA
796 assert_return(j, -EINVAL);
797 assert_return(!journal_pid_changed(j), -ECHILD);
87d2c1ff 798
c1f906bd 799 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
de190aef 800 bool found;
87d2c1ff 801
de190aef 802 r = next_beyond_location(j, f, direction, &o, &p);
e590af26 803 if (r < 0) {
da927ba9 804 log_debug_errno(r, "Can't iterate through %s, ignoring: %m", f->path);
a9a245c1 805 remove_file_real(j, f);
e590af26 806 continue;
6573ef05
MS
807 } else if (r == 0) {
808 f->location_type = LOCATION_TAIL;
cec736d2 809 continue;
6573ef05 810 }
87d2c1ff 811
468b21de 812 if (!new_file)
de190aef
LP
813 found = true;
814 else {
815 int k;
816
d8ae66d7 817 k = journal_file_compare_locations(f, new_file);
de190aef 818
bc302926 819 found = direction == DIRECTION_DOWN ? k < 0 : k > 0;
de190aef
LP
820 }
821
822 if (found) {
468b21de 823 new_file = f;
cec736d2 824 new_offset = p;
87d2c1ff 825 }
87d2c1ff
LP
826 }
827
468b21de 828 if (!new_file)
de190aef 829 return 0;
ae2cc8ef 830
468b21de
LP
831 r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_offset, &o);
832 if (r < 0)
833 return r;
834
1eb6332d 835 set_location(j, new_file, o);
ae2cc8ef 836
de190aef
LP
837 return 1;
838}
ae2cc8ef 839
a5344d2c 840_public_ int sd_journal_next(sd_journal *j) {
de190aef
LP
841 return real_journal_next(j, DIRECTION_DOWN);
842}
ae2cc8ef 843
a5344d2c 844_public_ int sd_journal_previous(sd_journal *j) {
de190aef
LP
845 return real_journal_next(j, DIRECTION_UP);
846}
ae2cc8ef 847
6f003b43 848static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
de190aef 849 int c = 0, r;
ae2cc8ef 850
1ae464e0
TA
851 assert_return(j, -EINVAL);
852 assert_return(!journal_pid_changed(j), -ECHILD);
de190aef 853
6f003b43
LP
854 if (skip == 0) {
855 /* If this is not a discrete skip, then at least
856 * resolve the current location */
857 if (j->current_location.type != LOCATION_DISCRETE)
858 return real_journal_next(j, direction);
859
860 return 0;
861 }
862
863 do {
864 r = real_journal_next(j, direction);
de190aef
LP
865 if (r < 0)
866 return r;
867
868 if (r == 0)
869 return c;
870
871 skip--;
872 c++;
6f003b43 873 } while (skip > 0);
87d2c1ff 874
de190aef 875 return c;
87d2c1ff
LP
876}
877
6f003b43
LP
878_public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
879 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
880}
de190aef 881
6f003b43
LP
882_public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
883 return real_journal_next_skip(j, DIRECTION_UP, skip);
87d2c1ff
LP
884}
885
a5344d2c 886_public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
cec736d2 887 Object *o;
87d2c1ff 888 int r;
3fbf9cbb 889 char bid[33], sid[33];
87d2c1ff 890
1ae464e0
TA
891 assert_return(j, -EINVAL);
892 assert_return(!journal_pid_changed(j), -ECHILD);
893 assert_return(cursor, -EINVAL);
87d2c1ff 894
3fbf9cbb
LP
895 if (!j->current_file || j->current_file->current_offset <= 0)
896 return -EADDRNOTAVAIL;
87d2c1ff 897
de190aef 898 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
87d2c1ff
LP
899 if (r < 0)
900 return r;
901
3fbf9cbb
LP
902 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
903 sd_id128_to_string(o->entry.boot_id, bid);
87d2c1ff 904
3fbf9cbb 905 if (asprintf(cursor,
507f22bd
ZJS
906 "s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64,
907 sid, le64toh(o->entry.seqnum),
908 bid, le64toh(o->entry.monotonic),
909 le64toh(o->entry.realtime),
910 le64toh(o->entry.xor_hash)) < 0)
3fbf9cbb 911 return -ENOMEM;
87d2c1ff 912
6f47ad30 913 return 0;
87d2c1ff
LP
914}
915
a5344d2c 916_public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
a2a5291b 917 const char *word, *state;
de190aef 918 size_t l;
de190aef
LP
919 unsigned long long seqnum, monotonic, realtime, xor_hash;
920 bool
921 seqnum_id_set = false,
922 seqnum_set = false,
923 boot_id_set = false,
924 monotonic_set = false,
925 realtime_set = false,
926 xor_hash_set = false;
927 sd_id128_t seqnum_id, boot_id;
928
1ae464e0
TA
929 assert_return(j, -EINVAL);
930 assert_return(!journal_pid_changed(j), -ECHILD);
931 assert_return(!isempty(cursor), -EINVAL);
de190aef 932
a2a5291b 933 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
de190aef
LP
934 char *item;
935 int k = 0;
936
a2a5291b 937 if (l < 2 || word[1] != '=')
de190aef
LP
938 return -EINVAL;
939
a2a5291b 940 item = strndup(word, l);
de190aef
LP
941 if (!item)
942 return -ENOMEM;
943
a2a5291b 944 switch (word[0]) {
de190aef
LP
945
946 case 's':
947 seqnum_id_set = true;
be3ea5ea 948 k = sd_id128_from_string(item+2, &seqnum_id);
de190aef
LP
949 break;
950
951 case 'i':
952 seqnum_set = true;
be3ea5ea 953 if (sscanf(item+2, "%llx", &seqnum) != 1)
de190aef
LP
954 k = -EINVAL;
955 break;
956
957 case 'b':
958 boot_id_set = true;
be3ea5ea 959 k = sd_id128_from_string(item+2, &boot_id);
de190aef
LP
960 break;
961
962 case 'm':
963 monotonic_set = true;
be3ea5ea 964 if (sscanf(item+2, "%llx", &monotonic) != 1)
de190aef
LP
965 k = -EINVAL;
966 break;
967
968 case 't':
969 realtime_set = true;
be3ea5ea 970 if (sscanf(item+2, "%llx", &realtime) != 1)
de190aef
LP
971 k = -EINVAL;
972 break;
973
974 case 'x':
975 xor_hash_set = true;
be3ea5ea 976 if (sscanf(item+2, "%llx", &xor_hash) != 1)
de190aef
LP
977 k = -EINVAL;
978 break;
979 }
980
981 free(item);
982
983 if (k < 0)
984 return k;
985 }
986
987 if ((!seqnum_set || !seqnum_id_set) &&
988 (!monotonic_set || !boot_id_set) &&
989 !realtime_set)
990 return -EINVAL;
991
992 reset_location(j);
993
a87247dd 994 j->current_location.type = LOCATION_SEEK;
de190aef
LP
995
996 if (realtime_set) {
997 j->current_location.realtime = (uint64_t) realtime;
998 j->current_location.realtime_set = true;
999 }
1000
1001 if (seqnum_set && seqnum_id_set) {
1002 j->current_location.seqnum = (uint64_t) seqnum;
1003 j->current_location.seqnum_id = seqnum_id;
1004 j->current_location.seqnum_set = true;
1005 }
1006
1007 if (monotonic_set && boot_id_set) {
1008 j->current_location.monotonic = (uint64_t) monotonic;
1009 j->current_location.boot_id = boot_id;
1010 j->current_location.monotonic_set = true;
1011 }
1012
1013 if (xor_hash_set) {
1014 j->current_location.xor_hash = (uint64_t) xor_hash;
1015 j->current_location.xor_hash_set = true;
1016 }
1017
1018 return 0;
1019}
1020
c6511e85
LP
1021_public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
1022 int r;
a2a5291b 1023 const char *word, *state;
c6511e85
LP
1024 size_t l;
1025 Object *o;
1026
1ae464e0
TA
1027 assert_return(j, -EINVAL);
1028 assert_return(!journal_pid_changed(j), -ECHILD);
1029 assert_return(!isempty(cursor), -EINVAL);
c6511e85
LP
1030
1031 if (!j->current_file || j->current_file->current_offset <= 0)
1032 return -EADDRNOTAVAIL;
1033
1034 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1035 if (r < 0)
1036 return r;
1037
a2a5291b 1038 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
c6511e85
LP
1039 _cleanup_free_ char *item = NULL;
1040 sd_id128_t id;
1041 unsigned long long ll;
1042 int k = 0;
1043
a2a5291b 1044 if (l < 2 || word[1] != '=')
c6511e85
LP
1045 return -EINVAL;
1046
a2a5291b 1047 item = strndup(word, l);
c6511e85
LP
1048 if (!item)
1049 return -ENOMEM;
1050
a2a5291b 1051 switch (word[0]) {
c6511e85
LP
1052
1053 case 's':
1054 k = sd_id128_from_string(item+2, &id);
1055 if (k < 0)
1056 return k;
1057 if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
1058 return 0;
1059 break;
1060
1061 case 'i':
1062 if (sscanf(item+2, "%llx", &ll) != 1)
1063 return -EINVAL;
1064 if (ll != le64toh(o->entry.seqnum))
1065 return 0;
1066 break;
1067
1068 case 'b':
1069 k = sd_id128_from_string(item+2, &id);
1070 if (k < 0)
1071 return k;
1072 if (!sd_id128_equal(id, o->entry.boot_id))
1073 return 0;
1074 break;
1075
1076 case 'm':
1077 if (sscanf(item+2, "%llx", &ll) != 1)
1078 return -EINVAL;
1079 if (ll != le64toh(o->entry.monotonic))
1080 return 0;
1081 break;
1082
1083 case 't':
1084 if (sscanf(item+2, "%llx", &ll) != 1)
1085 return -EINVAL;
1086 if (ll != le64toh(o->entry.realtime))
1087 return 0;
1088 break;
1089
1090 case 'x':
1091 if (sscanf(item+2, "%llx", &ll) != 1)
1092 return -EINVAL;
1093 if (ll != le64toh(o->entry.xor_hash))
1094 return 0;
1095 break;
1096 }
1097 }
1098
1099 return 1;
1100}
1101
1102
a5344d2c 1103_public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1ae464e0
TA
1104 assert_return(j, -EINVAL);
1105 assert_return(!journal_pid_changed(j), -ECHILD);
de190aef
LP
1106
1107 reset_location(j);
a87247dd 1108 j->current_location.type = LOCATION_SEEK;
de190aef
LP
1109 j->current_location.boot_id = boot_id;
1110 j->current_location.monotonic = usec;
1111 j->current_location.monotonic_set = true;
1112
1113 return 0;
1114}
1115
a5344d2c 1116_public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1ae464e0
TA
1117 assert_return(j, -EINVAL);
1118 assert_return(!journal_pid_changed(j), -ECHILD);
de190aef
LP
1119
1120 reset_location(j);
a87247dd 1121 j->current_location.type = LOCATION_SEEK;
de190aef
LP
1122 j->current_location.realtime = usec;
1123 j->current_location.realtime_set = true;
1124
1125 return 0;
1126}
1127
a5344d2c 1128_public_ int sd_journal_seek_head(sd_journal *j) {
1ae464e0
TA
1129 assert_return(j, -EINVAL);
1130 assert_return(!journal_pid_changed(j), -ECHILD);
de190aef
LP
1131
1132 reset_location(j);
1133 j->current_location.type = LOCATION_HEAD;
1134
1135 return 0;
1136}
1137
a5344d2c 1138_public_ int sd_journal_seek_tail(sd_journal *j) {
1ae464e0
TA
1139 assert_return(j, -EINVAL);
1140 assert_return(!journal_pid_changed(j), -ECHILD);
de190aef
LP
1141
1142 reset_location(j);
1143 j->current_location.type = LOCATION_TAIL;
1144
1145 return 0;
87d2c1ff
LP
1146}
1147
85210bff
LP
1148static void check_network(sd_journal *j, int fd) {
1149 struct statfs sfs;
1150
1151 assert(j);
1152
1153 if (j->on_network)
1154 return;
1155
1156 if (fstatfs(fd, &sfs) < 0)
1157 return;
1158
1159 j->on_network =
c51cf056
ZJS
1160 F_TYPE_EQUAL(sfs.f_type, CIFS_MAGIC_NUMBER) ||
1161 F_TYPE_EQUAL(sfs.f_type, CODA_SUPER_MAGIC) ||
1162 F_TYPE_EQUAL(sfs.f_type, NCP_SUPER_MAGIC) ||
1163 F_TYPE_EQUAL(sfs.f_type, NFS_SUPER_MAGIC) ||
1164 F_TYPE_EQUAL(sfs.f_type, SMB_SUPER_MAGIC);
85210bff
LP
1165}
1166
a688baa8
ZJS
1167static bool file_has_type_prefix(const char *prefix, const char *filename) {
1168 const char *full, *tilded, *atted;
1169
baabc091 1170 full = strappenda(prefix, ".journal");
a688baa8
ZJS
1171 tilded = strappenda(full, "~");
1172 atted = strappenda(prefix, "@");
1173
1174 return streq(filename, full) ||
1175 streq(filename, tilded) ||
1176 startswith(filename, atted);
1177}
1178
1179static bool file_type_wanted(int flags, const char *filename) {
1180 if (!endswith(filename, ".journal") && !endswith(filename, ".journal~"))
1181 return false;
1182
1183 /* no flags set → every type is OK */
1184 if (!(flags & (SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)))
1185 return true;
1186
1187 if (flags & SD_JOURNAL_SYSTEM && file_has_type_prefix("system", filename))
1188 return true;
1189
1190 if (flags & SD_JOURNAL_CURRENT_USER) {
1191 char prefix[5 + DECIMAL_STR_MAX(uid_t) + 1];
1192
de0671ee 1193 assert_se(snprintf(prefix, sizeof(prefix), "user-"UID_FMT, getuid())
a688baa8
ZJS
1194 < (int) sizeof(prefix));
1195
1196 if (file_has_type_prefix(prefix, filename))
1197 return true;
1198 }
1199
1200 return false;
1201}
1202
5302ebe1 1203static int add_any_file(sd_journal *j, const char *path) {
39883f62 1204 JournalFile *f = NULL;
5302ebe1 1205 int r;
3fbf9cbb
LP
1206
1207 assert(j);
5302ebe1 1208 assert(path);
3fbf9cbb 1209
c1f906bd 1210 if (ordered_hashmap_get(j->files, path))
50f20cfd 1211 return 0;
50f20cfd 1212
c1f906bd 1213 if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
5302ebe1 1214 log_warning("Too many open journal files, not adding %s.", path);
3ac251b8 1215 return set_put_error(j, -ETOOMANYREFS);
50f20cfd
LP
1216 }
1217
16e9f408 1218 r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
5302ebe1 1219 if (r < 0)
3fbf9cbb 1220 return r;
3fbf9cbb 1221
72f59706 1222 /* journal_file_dump(f); */
de190aef 1223
c1f906bd 1224 r = ordered_hashmap_put(j->files, f->path, f);
3fbf9cbb
LP
1225 if (r < 0) {
1226 journal_file_close(f);
1227 return r;
1228 }
1229
5ec76417 1230 log_debug("File %s added.", f->path);
a50d7d43 1231
85210bff
LP
1232 check_network(j, f->fd);
1233
a963990f
LP
1234 j->current_invalidate_counter ++;
1235
50f20cfd
LP
1236 return 0;
1237}
1238
5302ebe1
ZJS
1239static int add_file(sd_journal *j, const char *prefix, const char *filename) {
1240 _cleanup_free_ char *path = NULL;
1241 int r;
1242
1243 assert(j);
1244 assert(prefix);
1245 assert(filename);
1246
1247 if (j->no_new_files ||
1248 !file_type_wanted(j->flags, filename))
1249 return 0;
1250
1251 path = strjoin(prefix, "/", filename, NULL);
1252 if (!path)
1253 return -ENOMEM;
1254
1255 r = add_any_file(j, path);
1256 if (r == -ENOENT)
1257 return 0;
1258 return 0;
1259}
1260
a963990f 1261static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
a9a245c1 1262 _cleanup_free_ char *path;
50f20cfd
LP
1263 JournalFile *f;
1264
1265 assert(j);
1266 assert(prefix);
1267 assert(filename);
1268
b7def684 1269 path = strjoin(prefix, "/", filename, NULL);
a963990f 1270 if (!path)
50f20cfd
LP
1271 return -ENOMEM;
1272
c1f906bd 1273 f = ordered_hashmap_get(j->files, path);
50f20cfd
LP
1274 if (!f)
1275 return 0;
1276
a9a245c1
ZJS
1277 remove_file_real(j, f);
1278 return 0;
1279}
1280
1281static void remove_file_real(sd_journal *j, JournalFile *f) {
1282 assert(j);
1283 assert(f);
1284
c1f906bd 1285 ordered_hashmap_remove(j->files, f->path);
44a5fa34 1286
5ec76417 1287 log_debug("File %s removed.", f->path);
44a5fa34 1288
3c1668da
LP
1289 if (j->current_file == f) {
1290 j->current_file = NULL;
1291 j->current_field = 0;
1292 }
1293
1294 if (j->unique_file == f) {
360af4cf 1295 /* Jump to the next unique_file or NULL if that one was last */
c1f906bd 1296 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
3c1668da 1297 j->unique_offset = 0;
360af4cf
ZJS
1298 if (!j->unique_file)
1299 j->unique_file_lost = true;
3c1668da
LP
1300 }
1301
50f20cfd
LP
1302 journal_file_close(f);
1303
a963990f 1304 j->current_invalidate_counter ++;
3fbf9cbb
LP
1305}
1306
a963990f 1307static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
7fd1b19b 1308 _cleanup_free_ char *path = NULL;
3fbf9cbb 1309 int r;
7fd1b19b 1310 _cleanup_closedir_ DIR *d = NULL;
cf244689 1311 sd_id128_t id, mid;
a963990f 1312 Directory *m;
3fbf9cbb
LP
1313
1314 assert(j);
1315 assert(prefix);
a963990f 1316 assert(dirname);
3fbf9cbb 1317
d95b1fb3
ZJS
1318 log_debug("Considering %s/%s.", prefix, dirname);
1319
cf244689 1320 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
a963990f 1321 (sd_id128_from_string(dirname, &id) < 0 ||
cf244689 1322 sd_id128_get_machine(&mid) < 0 ||
d95b1fb3 1323 !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
cf244689
LP
1324 return 0;
1325
b7def684 1326 path = strjoin(prefix, "/", dirname, NULL);
a963990f 1327 if (!path)
3fbf9cbb
LP
1328 return -ENOMEM;
1329
a963990f 1330 d = opendir(path);
3fbf9cbb 1331 if (!d) {
56f64d95 1332 log_debug_errno(errno, "Failed to open %s: %m", path);
3fbf9cbb
LP
1333 if (errno == ENOENT)
1334 return 0;
3fbf9cbb
LP
1335 return -errno;
1336 }
1337
a963990f
LP
1338 m = hashmap_get(j->directories_by_path, path);
1339 if (!m) {
1340 m = new0(Directory, 1);
a50d7d43 1341 if (!m)
a963990f 1342 return -ENOMEM;
a963990f
LP
1343
1344 m->is_root = false;
1345 m->path = path;
1346
1347 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
a963990f
LP
1348 free(m);
1349 return -ENOMEM;
1350 }
1351
a50d7d43 1352 path = NULL; /* avoid freeing in cleanup */
a963990f
LP
1353 j->current_invalidate_counter ++;
1354
5ec76417 1355 log_debug("Directory %s added.", m->path);
a963990f 1356
a50d7d43 1357 } else if (m->is_root)
a963990f 1358 return 0;
a963990f
LP
1359
1360 if (m->wd <= 0 && j->inotify_fd >= 0) {
1361
1362 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1363 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
5e6870ea 1364 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
4a842cad 1365 IN_ONLYDIR);
a963990f
LP
1366
1367 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1368 inotify_rm_watch(j->inotify_fd, m->wd);
1369 }
1370
1371 for (;;) {
7d5e9c0f 1372 struct dirent *de;
a963990f 1373
bde1fdd7
FW
1374 errno = 0;
1375 de = readdir(d);
1376 if (!de && errno != 0) {
1377 r = -errno;
56f64d95 1378 log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
bde1fdd7
FW
1379 return r;
1380 }
1381 if (!de)
a963990f
LP
1382 break;
1383
de2c3907
LP
1384 if (dirent_is_file_with_suffix(de, ".journal") ||
1385 dirent_is_file_with_suffix(de, ".journal~")) {
a963990f 1386 r = add_file(j, m->path, de->d_name);
6fe391c5 1387 if (r < 0) {
c33b3297
MS
1388 log_debug_errno(r, "Failed to add file %s/%s: %m",
1389 m->path, de->d_name);
3ac251b8 1390 r = set_put_error(j, r);
6fe391c5
ZJS
1391 if (r < 0)
1392 return r;
1393 }
a963990f
LP
1394 }
1395 }
1396
85210bff
LP
1397 check_network(j, dirfd(d));
1398
a963990f
LP
1399 return 0;
1400}
1401
89739579 1402static int add_root_directory(sd_journal *j, const char *p) {
7fd1b19b 1403 _cleanup_closedir_ DIR *d = NULL;
a963990f
LP
1404 Directory *m;
1405 int r;
1406
1407 assert(j);
1408 assert(p);
1409
1410 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1411 !path_startswith(p, "/run"))
1412 return -EINVAL;
1413
89739579
LP
1414 if (j->prefix)
1415 p = strappenda(j->prefix, p);
b6741478 1416
a963990f
LP
1417 d = opendir(p);
1418 if (!d)
1419 return -errno;
1420
1421 m = hashmap_get(j->directories_by_path, p);
1422 if (!m) {
1423 m = new0(Directory, 1);
a50d7d43 1424 if (!m)
a963990f 1425 return -ENOMEM;
a963990f
LP
1426
1427 m->is_root = true;
1428 m->path = strdup(p);
1429 if (!m->path) {
a963990f
LP
1430 free(m);
1431 return -ENOMEM;
1432 }
1433
1434 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
a963990f
LP
1435 free(m->path);
1436 free(m);
1437 return -ENOMEM;
1438 }
1439
1440 j->current_invalidate_counter ++;
1441
5ec76417 1442 log_debug("Root directory %s added.", m->path);
a963990f 1443
a50d7d43 1444 } else if (!m->is_root)
a963990f 1445 return 0;
50f20cfd 1446
a963990f
LP
1447 if (m->wd <= 0 && j->inotify_fd >= 0) {
1448
1449 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1450 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
4a842cad 1451 IN_ONLYDIR);
a963990f
LP
1452
1453 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1454 inotify_rm_watch(j->inotify_fd, m->wd);
1455 }
50f20cfd 1456
5302ebe1
ZJS
1457 if (j->no_new_files)
1458 return 0;
1459
3fbf9cbb 1460 for (;;) {
7d5e9c0f 1461 struct dirent *de;
a963990f 1462 sd_id128_t id;
3fbf9cbb 1463
bde1fdd7
FW
1464 errno = 0;
1465 de = readdir(d);
1466 if (!de && errno != 0) {
1467 r = -errno;
56f64d95 1468 log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
bde1fdd7
FW
1469 return r;
1470 }
1471 if (!de)
3fbf9cbb
LP
1472 break;
1473
de2c3907
LP
1474 if (dirent_is_file_with_suffix(de, ".journal") ||
1475 dirent_is_file_with_suffix(de, ".journal~")) {
a963990f 1476 r = add_file(j, m->path, de->d_name);
6fe391c5 1477 if (r < 0) {
c33b3297
MS
1478 log_debug_errno(r, "Failed to add file %s/%s: %m",
1479 m->path, de->d_name);
3ac251b8 1480 r = set_put_error(j, r);
6fe391c5
ZJS
1481 if (r < 0)
1482 return r;
1483 }
6f5878a2 1484 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
a963990f
LP
1485 sd_id128_from_string(de->d_name, &id) >= 0) {
1486
1487 r = add_directory(j, m->path, de->d_name);
1488 if (r < 0)
da927ba9 1489 log_debug_errno(r, "Failed to add directory %s/%s: %m", m->path, de->d_name);
a963990f 1490 }
3fbf9cbb
LP
1491 }
1492
85210bff
LP
1493 check_network(j, dirfd(d));
1494
a963990f
LP
1495 return 0;
1496}
1497
1498static int remove_directory(sd_journal *j, Directory *d) {
1499 assert(j);
1500
1501 if (d->wd > 0) {
1502 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1503
1504 if (j->inotify_fd >= 0)
1505 inotify_rm_watch(j->inotify_fd, d->wd);
1506 }
1507
1508 hashmap_remove(j->directories_by_path, d->path);
1509
1510 if (d->is_root)
5ec76417 1511 log_debug("Root directory %s removed.", d->path);
a963990f 1512 else
5ec76417 1513 log_debug("Directory %s removed.", d->path);
a963990f
LP
1514
1515 free(d->path);
1516 free(d);
50f20cfd 1517
3fbf9cbb
LP
1518 return 0;
1519}
1520
89739579 1521static int add_search_paths(sd_journal *j) {
6fe391c5 1522 int r;
a963990f
LP
1523 const char search_paths[] =
1524 "/run/log/journal\0"
1525 "/var/log/journal\0";
1526 const char *p;
50f20cfd
LP
1527
1528 assert(j);
50f20cfd 1529
a963990f
LP
1530 /* We ignore most errors here, since the idea is to only open
1531 * what's actually accessible, and ignore the rest. */
50f20cfd 1532
6fe391c5 1533 NULSTR_FOREACH(p, search_paths) {
89739579 1534 r = add_root_directory(j, p);
3ac251b8
LP
1535 if (r < 0 && r != -ENOENT) {
1536 r = set_put_error(j, r);
1537 if (r < 0)
1538 return r;
1539 }
6fe391c5 1540 }
50f20cfd 1541
a963990f 1542 return 0;
50f20cfd
LP
1543}
1544
5302ebe1
ZJS
1545static int add_current_paths(sd_journal *j) {
1546 Iterator i;
1547 JournalFile *f;
1548
1549 assert(j);
1550 assert(j->no_new_files);
1551
1552 /* Simply adds all directories for files we have open as
1553 * "root" directories. We don't expect errors here, so we
1554 * treat them as fatal. */
1555
c1f906bd 1556 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
5302ebe1 1557 _cleanup_free_ char *dir;
e9174f29 1558 int r;
5302ebe1
ZJS
1559
1560 dir = dirname_malloc(f->path);
1561 if (!dir)
1562 return -ENOMEM;
1563
89739579 1564 r = add_root_directory(j, dir);
5302ebe1
ZJS
1565 if (r < 0) {
1566 set_put_error(j, r);
1567 return r;
1568 }
1569 }
1570
1571 return 0;
1572}
1573
1574
a963990f 1575static int allocate_inotify(sd_journal *j) {
50f20cfd 1576 assert(j);
50f20cfd 1577
a963990f
LP
1578 if (j->inotify_fd < 0) {
1579 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1580 if (j->inotify_fd < 0)
1581 return -errno;
1582 }
50f20cfd 1583
a963990f 1584 if (!j->directories_by_wd) {
d5099efc 1585 j->directories_by_wd = hashmap_new(NULL);
a963990f
LP
1586 if (!j->directories_by_wd)
1587 return -ENOMEM;
50f20cfd 1588 }
a963990f
LP
1589
1590 return 0;
50f20cfd
LP
1591}
1592
7827b1a1 1593static sd_journal *journal_new(int flags, const char *path) {
a963990f 1594 sd_journal *j;
50f20cfd 1595
a963990f
LP
1596 j = new0(sd_journal, 1);
1597 if (!j)
1598 return NULL;
50f20cfd 1599
a65f06bb 1600 j->original_pid = getpid();
a963990f
LP
1601 j->inotify_fd = -1;
1602 j->flags = flags;
93b73b06 1603 j->data_threshold = DEFAULT_DATA_THRESHOLD;
50f20cfd 1604
7827b1a1
LP
1605 if (path) {
1606 j->path = strdup(path);
6180fc61
ZJS
1607 if (!j->path)
1608 goto fail;
7827b1a1
LP
1609 }
1610
c1f906bd 1611 j->files = ordered_hashmap_new(&string_hash_ops);
d5099efc 1612 j->directories_by_path = hashmap_new(&string_hash_ops);
84168d80 1613 j->mmap = mmap_cache_new();
3ac251b8 1614 if (!j->files || !j->directories_by_path || !j->mmap)
6180fc61 1615 goto fail;
16e9f408 1616
a963990f 1617 return j;
6180fc61
ZJS
1618
1619fail:
1620 sd_journal_close(j);
1621 return NULL;
50f20cfd
LP
1622}
1623
a5344d2c 1624_public_ int sd_journal_open(sd_journal **ret, int flags) {
87d2c1ff 1625 sd_journal *j;
3fbf9cbb 1626 int r;
87d2c1ff 1627
1ae464e0 1628 assert_return(ret, -EINVAL);
b6741478 1629 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL);
87d2c1ff 1630
7827b1a1 1631 j = journal_new(flags, NULL);
87d2c1ff
LP
1632 if (!j)
1633 return -ENOMEM;
1634
89739579 1635 r = add_search_paths(j);
a963990f 1636 if (r < 0)
50f20cfd 1637 goto fail;
50f20cfd 1638
a963990f
LP
1639 *ret = j;
1640 return 0;
cf244689 1641
a963990f
LP
1642fail:
1643 sd_journal_close(j);
87d2c1ff 1644
a963990f
LP
1645 return r;
1646}
50f20cfd 1647
b6741478
LP
1648_public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
1649 _cleanup_free_ char *root = NULL, *class = NULL;
1650 sd_journal *j;
1651 char *p;
1652 int r;
1653
1654 assert_return(machine, -EINVAL);
1655 assert_return(ret, -EINVAL);
1656 assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL);
affcf189 1657 assert_return(machine_name_is_valid(machine), -EINVAL);
b6741478
LP
1658
1659 p = strappenda("/run/systemd/machines/", machine);
1660 r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL);
1661 if (r == -ENOENT)
1662 return -EHOSTDOWN;
1663 if (r < 0)
1664 return r;
1665 if (!root)
1666 return -ENODATA;
1667
1668 if (!streq_ptr(class, "container"))
1669 return -EIO;
1670
1671 j = journal_new(flags, NULL);
1672 if (!j)
1673 return -ENOMEM;
1674
89739579
LP
1675 j->prefix = root;
1676 root = NULL;
1677
1678 r = add_search_paths(j);
b6741478
LP
1679 if (r < 0)
1680 goto fail;
1681
1682 *ret = j;
1683 return 0;
1684
1685fail:
1686 sd_journal_close(j);
1687 return r;
1688}
1689
a963990f
LP
1690_public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1691 sd_journal *j;
1692 int r;
87d2c1ff 1693
1ae464e0
TA
1694 assert_return(ret, -EINVAL);
1695 assert_return(path, -EINVAL);
1696 assert_return(flags == 0, -EINVAL);
87d2c1ff 1697
7827b1a1 1698 j = journal_new(flags, path);
a963990f
LP
1699 if (!j)
1700 return -ENOMEM;
3fbf9cbb 1701
89739579 1702 r = add_root_directory(j, path);
6fe391c5 1703 if (r < 0) {
3ac251b8 1704 set_put_error(j, r);
a963990f 1705 goto fail;
6fe391c5 1706 }
87d2c1ff
LP
1707
1708 *ret = j;
1709 return 0;
1710
1711fail:
1712 sd_journal_close(j);
1713
1714 return r;
a963990f 1715}
87d2c1ff 1716
5302ebe1
ZJS
1717_public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
1718 sd_journal *j;
1719 const char **path;
1720 int r;
1721
1ae464e0
TA
1722 assert_return(ret, -EINVAL);
1723 assert_return(flags == 0, -EINVAL);
5302ebe1
ZJS
1724
1725 j = journal_new(flags, NULL);
1726 if (!j)
1727 return -ENOMEM;
1728
1729 STRV_FOREACH(path, paths) {
1730 r = add_any_file(j, *path);
1731 if (r < 0) {
da927ba9 1732 log_error_errno(r, "Failed to open %s: %m", *path);
5302ebe1
ZJS
1733 goto fail;
1734 }
1735 }
1736
1737 j->no_new_files = true;
1738
1739 *ret = j;
1740 return 0;
1741
1742fail:
1743 sd_journal_close(j);
1744
1745 return r;
1746}
1747
a5344d2c 1748_public_ void sd_journal_close(sd_journal *j) {
a963990f
LP
1749 Directory *d;
1750 JournalFile *f;
1751
a5344d2c
LP
1752 if (!j)
1753 return;
87d2c1ff 1754
54b1da83
LP
1755 sd_journal_flush_matches(j);
1756
c1f906bd 1757 while ((f = ordered_hashmap_steal_first(j->files)))
a963990f 1758 journal_file_close(f);
50f20cfd 1759
c1f906bd 1760 ordered_hashmap_free(j->files);
260a2be4 1761
a963990f
LP
1762 while ((d = hashmap_first(j->directories_by_path)))
1763 remove_directory(j, d);
260a2be4 1764
a963990f
LP
1765 while ((d = hashmap_first(j->directories_by_wd)))
1766 remove_directory(j, d);
87d2c1ff 1767
a963990f
LP
1768 hashmap_free(j->directories_by_path);
1769 hashmap_free(j->directories_by_wd);
1cc101f1 1770
03e334a1 1771 safe_close(j->inotify_fd);
50f20cfd 1772
bf807d4d
LP
1773 if (j->mmap) {
1774 log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap));
16e9f408 1775 mmap_cache_unref(j->mmap);
bf807d4d 1776 }
16e9f408 1777
7827b1a1 1778 free(j->path);
89739579 1779 free(j->prefix);
3c1668da 1780 free(j->unique_field);
6fe391c5 1781 set_free(j->errors);
87d2c1ff
LP
1782 free(j);
1783}
3fbf9cbb 1784
a5344d2c 1785_public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
3fbf9cbb
LP
1786 Object *o;
1787 JournalFile *f;
1788 int r;
1789
1ae464e0
TA
1790 assert_return(j, -EINVAL);
1791 assert_return(!journal_pid_changed(j), -ECHILD);
1792 assert_return(ret, -EINVAL);
3fbf9cbb
LP
1793
1794 f = j->current_file;
1795 if (!f)
de190aef 1796 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1797
1798 if (f->current_offset <= 0)
de190aef 1799 return -EADDRNOTAVAIL;
3fbf9cbb 1800
de190aef 1801 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1802 if (r < 0)
1803 return r;
1804
1805 *ret = le64toh(o->entry.realtime);
de190aef 1806 return 0;
3fbf9cbb
LP
1807}
1808
a5344d2c 1809_public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
3fbf9cbb
LP
1810 Object *o;
1811 JournalFile *f;
1812 int r;
1813 sd_id128_t id;
1814
1ae464e0
TA
1815 assert_return(j, -EINVAL);
1816 assert_return(!journal_pid_changed(j), -ECHILD);
3fbf9cbb
LP
1817
1818 f = j->current_file;
1819 if (!f)
de190aef 1820 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1821
1822 if (f->current_offset <= 0)
de190aef 1823 return -EADDRNOTAVAIL;
3fbf9cbb 1824
de190aef 1825 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1826 if (r < 0)
1827 return r;
1828
de190aef
LP
1829 if (ret_boot_id)
1830 *ret_boot_id = o->entry.boot_id;
1831 else {
1832 r = sd_id128_get_boot(&id);
1833 if (r < 0)
1834 return r;
3fbf9cbb 1835
de190aef 1836 if (!sd_id128_equal(id, o->entry.boot_id))
df50185b 1837 return -ESTALE;
de190aef 1838 }
3fbf9cbb 1839
14a65d65
LP
1840 if (ret)
1841 *ret = le64toh(o->entry.monotonic);
1842
de190aef 1843 return 0;
3fbf9cbb
LP
1844}
1845
362a3f81
LP
1846static bool field_is_valid(const char *field) {
1847 const char *p;
1848
1849 assert(field);
1850
1851 if (isempty(field))
1852 return false;
1853
1854 if (startswith(field, "__"))
1855 return false;
1856
1857 for (p = field; *p; p++) {
1858
1859 if (*p == '_')
1860 continue;
1861
1862 if (*p >= 'A' && *p <= 'Z')
1863 continue;
1864
1865 if (*p >= '0' && *p <= '9')
1866 continue;
1867
1868 return false;
1869 }
1870
1871 return true;
1872}
1873
a5344d2c 1874_public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
3fbf9cbb
LP
1875 JournalFile *f;
1876 uint64_t i, n;
1877 size_t field_length;
1878 int r;
1879 Object *o;
1880
1ae464e0
TA
1881 assert_return(j, -EINVAL);
1882 assert_return(!journal_pid_changed(j), -ECHILD);
1883 assert_return(field, -EINVAL);
1884 assert_return(data, -EINVAL);
1885 assert_return(size, -EINVAL);
1886 assert_return(field_is_valid(field), -EINVAL);
3fbf9cbb
LP
1887
1888 f = j->current_file;
1889 if (!f)
de190aef 1890 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1891
1892 if (f->current_offset <= 0)
de190aef 1893 return -EADDRNOTAVAIL;
3fbf9cbb 1894
de190aef 1895 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1896 if (r < 0)
1897 return r;
1898
1899 field_length = strlen(field);
1900
1901 n = journal_file_entry_n_items(o);
1902 for (i = 0; i < n; i++) {
4fd052ae
FC
1903 uint64_t p, l;
1904 le64_t le_hash;
3fbf9cbb 1905 size_t t;
1ec7120e 1906 int compression;
3fbf9cbb
LP
1907
1908 p = le64toh(o->entry.items[i].object_offset);
807e17f0 1909 le_hash = o->entry.items[i].hash;
de190aef 1910 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
3fbf9cbb
LP
1911 if (r < 0)
1912 return r;
1913
de190aef 1914 if (le_hash != o->data.hash)
de7b95cd
LP
1915 return -EBADMSG;
1916
3fbf9cbb
LP
1917 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1918
1ec7120e 1919 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
3b1a55e1
ZJS
1920 if (compression) {
1921#if defined(HAVE_XZ) || defined(HAVE_LZ4)
1922 if (decompress_startswith(compression,
1923 o->data.payload, l,
1924 &f->compress_buffer, &f->compress_buffer_size,
1925 field, field_length, '=')) {
1926
fa1c4b51 1927 size_t rsize;
3b1a55e1
ZJS
1928
1929 r = decompress_blob(compression,
1930 o->data.payload, l,
1931 &f->compress_buffer, &f->compress_buffer_size, &rsize,
1932 j->data_threshold);
1933 if (r < 0)
1934 return r;
807e17f0 1935
3b1a55e1
ZJS
1936 *data = f->compress_buffer;
1937 *size = (size_t) rsize;
807e17f0 1938
3b1a55e1
ZJS
1939 return 0;
1940 }
1941#else
1942 return -EPROTONOSUPPORT;
1943#endif
807e17f0
LP
1944 } else if (l >= field_length+1 &&
1945 memcmp(o->data.payload, field, field_length) == 0 &&
1946 o->data.payload[field_length] == '=') {
3fbf9cbb 1947
161e54f8 1948 t = (size_t) l;
3fbf9cbb 1949
161e54f8
LP
1950 if ((uint64_t) t != l)
1951 return -E2BIG;
3fbf9cbb 1952
161e54f8
LP
1953 *data = o->data.payload;
1954 *size = t;
3fbf9cbb 1955
99613ec5 1956 return 0;
161e54f8 1957 }
3fbf9cbb 1958
de190aef 1959 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
161e54f8
LP
1960 if (r < 0)
1961 return r;
3fbf9cbb
LP
1962 }
1963
de190aef 1964 return -ENOENT;
3fbf9cbb
LP
1965}
1966
93b73b06 1967static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
3c1668da
LP
1968 size_t t;
1969 uint64_t l;
3b1a55e1 1970 int compression;
3c1668da
LP
1971
1972 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1973 t = (size_t) l;
1974
1975 /* We can't read objects larger than 4G on a 32bit machine */
1976 if ((uint64_t) t != l)
1977 return -E2BIG;
1978
1ec7120e
ZJS
1979 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
1980 if (compression) {
3b1a55e1 1981#if defined(HAVE_XZ) || defined(HAVE_LZ4)
fa1c4b51 1982 size_t rsize;
3b1a55e1 1983 int r;
3c1668da 1984
1ec7120e
ZJS
1985 r = decompress_blob(compression,
1986 o->data.payload, l, &f->compress_buffer,
1987 &f->compress_buffer_size, &rsize, j->data_threshold);
1988 if (r < 0)
1989 return r;
3c1668da
LP
1990
1991 *data = f->compress_buffer;
1992 *size = (size_t) rsize;
3b1a55e1
ZJS
1993#else
1994 return -EPROTONOSUPPORT;
1995#endif
3c1668da
LP
1996 } else {
1997 *data = o->data.payload;
1998 *size = t;
1999 }
2000
2001 return 0;
2002}
2003
a5344d2c 2004_public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
3fbf9cbb 2005 JournalFile *f;
3c1668da 2006 uint64_t p, n;
4fd052ae 2007 le64_t le_hash;
3fbf9cbb
LP
2008 int r;
2009 Object *o;
2010
1ae464e0
TA
2011 assert_return(j, -EINVAL);
2012 assert_return(!journal_pid_changed(j), -ECHILD);
2013 assert_return(data, -EINVAL);
2014 assert_return(size, -EINVAL);
3fbf9cbb
LP
2015
2016 f = j->current_file;
2017 if (!f)
de190aef 2018 return -EADDRNOTAVAIL;
3fbf9cbb
LP
2019
2020 if (f->current_offset <= 0)
de190aef 2021 return -EADDRNOTAVAIL;
3fbf9cbb 2022
de190aef 2023 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
2024 if (r < 0)
2025 return r;
2026
2027 n = journal_file_entry_n_items(o);
7210bfb3 2028 if (j->current_field >= n)
3fbf9cbb
LP
2029 return 0;
2030
7210bfb3 2031 p = le64toh(o->entry.items[j->current_field].object_offset);
de190aef
LP
2032 le_hash = o->entry.items[j->current_field].hash;
2033 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
3fbf9cbb
LP
2034 if (r < 0)
2035 return r;
2036
de190aef 2037 if (le_hash != o->data.hash)
de7b95cd
LP
2038 return -EBADMSG;
2039
93b73b06 2040 r = return_data(j, f, o, data, size);
3c1668da
LP
2041 if (r < 0)
2042 return r;
3fbf9cbb 2043
7210bfb3 2044 j->current_field ++;
3fbf9cbb
LP
2045
2046 return 1;
2047}
c2373f84 2048
a5344d2c
LP
2049_public_ void sd_journal_restart_data(sd_journal *j) {
2050 if (!j)
2051 return;
8725d60a
LP
2052
2053 j->current_field = 0;
c2373f84 2054}
50f20cfd 2055
a5344d2c 2056_public_ int sd_journal_get_fd(sd_journal *j) {
a963990f
LP
2057 int r;
2058
1ae464e0
TA
2059 assert_return(j, -EINVAL);
2060 assert_return(!journal_pid_changed(j), -ECHILD);
50f20cfd 2061
a963990f
LP
2062 if (j->inotify_fd >= 0)
2063 return j->inotify_fd;
2064
2065 r = allocate_inotify(j);
2066 if (r < 0)
2067 return r;
2068
2069 /* Iterate through all dirs again, to add them to the
2070 * inotify */
5302ebe1
ZJS
2071 if (j->no_new_files)
2072 r = add_current_paths(j);
2073 else if (j->path)
89739579 2074 r = add_root_directory(j, j->path);
7827b1a1 2075 else
89739579 2076 r = add_search_paths(j);
a963990f
LP
2077 if (r < 0)
2078 return r;
2079
50f20cfd
LP
2080 return j->inotify_fd;
2081}
2082
ee531d94
LP
2083_public_ int sd_journal_get_events(sd_journal *j) {
2084 int fd;
2085
1ae464e0
TA
2086 assert_return(j, -EINVAL);
2087 assert_return(!journal_pid_changed(j), -ECHILD);
ee531d94
LP
2088
2089 fd = sd_journal_get_fd(j);
2090 if (fd < 0)
2091 return fd;
2092
2093 return POLLIN;
2094}
2095
39c155ea
LP
2096_public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2097 int fd;
2098
1ae464e0
TA
2099 assert_return(j, -EINVAL);
2100 assert_return(!journal_pid_changed(j), -ECHILD);
2101 assert_return(timeout_usec, -EINVAL);
39c155ea
LP
2102
2103 fd = sd_journal_get_fd(j);
2104 if (fd < 0)
2105 return fd;
2106
2107 if (!j->on_network) {
2108 *timeout_usec = (uint64_t) -1;
2109 return 0;
2110 }
2111
2112 /* If we are on the network we need to regularly check for
2113 * changes manually */
2114
2115 *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2116 return 1;
2117}
2118
50f20cfd 2119static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
a963990f 2120 Directory *d;
50f20cfd
LP
2121 int r;
2122
2123 assert(j);
2124 assert(e);
2125
2126 /* Is this a subdirectory we watch? */
a963990f
LP
2127 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2128 if (d) {
2129 sd_id128_t id;
50f20cfd 2130
de2c3907
LP
2131 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2132 (endswith(e->name, ".journal") ||
2133 endswith(e->name, ".journal~"))) {
50f20cfd
LP
2134
2135 /* Event for a journal file */
2136
2137 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
a963990f 2138 r = add_file(j, d->path, e->name);
6fe391c5 2139 if (r < 0) {
c33b3297
MS
2140 log_debug_errno(r, "Failed to add file %s/%s: %m",
2141 d->path, e->name);
3ac251b8 2142 set_put_error(j, r);
6fe391c5 2143 }
a963990f 2144
5e6870ea 2145 } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
50f20cfd 2146
a963990f 2147 r = remove_file(j, d->path, e->name);
50f20cfd 2148 if (r < 0)
da927ba9 2149 log_debug_errno(r, "Failed to remove file %s/%s: %m", d->path, e->name);
50f20cfd
LP
2150 }
2151
a963990f 2152 } else if (!d->is_root && e->len == 0) {
50f20cfd 2153
a963990f 2154 /* Event for a subdirectory */
50f20cfd 2155
a963990f
LP
2156 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
2157 r = remove_directory(j, d);
50f20cfd 2158 if (r < 0)
da927ba9 2159 log_debug_errno(r, "Failed to remove directory %s: %m", d->path);
50f20cfd
LP
2160 }
2161
50f20cfd 2162
a963990f 2163 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
50f20cfd 2164
a963990f 2165 /* Event for root directory */
50f20cfd 2166
a963990f
LP
2167 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2168 r = add_directory(j, d->path, e->name);
50f20cfd 2169 if (r < 0)
da927ba9 2170 log_debug_errno(r, "Failed to add directory %s/%s: %m", d->path, e->name);
50f20cfd
LP
2171 }
2172 }
2173
2174 return;
2175 }
2176
2177 if (e->mask & IN_IGNORED)
2178 return;
2179
2180 log_warning("Unknown inotify event.");
2181}
2182
a963990f
LP
2183static int determine_change(sd_journal *j) {
2184 bool b;
2185
2186 assert(j);
2187
2188 b = j->current_invalidate_counter != j->last_invalidate_counter;
2189 j->last_invalidate_counter = j->current_invalidate_counter;
2190
2191 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2192}
2193
a5344d2c 2194_public_ int sd_journal_process(sd_journal *j) {
a963990f 2195 bool got_something = false;
50f20cfd 2196
1ae464e0
TA
2197 assert_return(j, -EINVAL);
2198 assert_return(!journal_pid_changed(j), -ECHILD);
50f20cfd 2199
39c155ea
LP
2200 j->last_process_usec = now(CLOCK_MONOTONIC);
2201
50f20cfd 2202 for (;;) {
f7c1ad4f 2203 uint8_t buffer[INOTIFY_EVENT_MAX] _alignas_(struct inotify_event);
50f20cfd
LP
2204 struct inotify_event *e;
2205 ssize_t l;
2206
2207 l = read(j->inotify_fd, buffer, sizeof(buffer));
2208 if (l < 0) {
a963990f
LP
2209 if (errno == EAGAIN || errno == EINTR)
2210 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
50f20cfd
LP
2211
2212 return -errno;
2213 }
2214
a963990f
LP
2215 got_something = true;
2216
f7c1ad4f 2217 FOREACH_INOTIFY_EVENT(e, buffer, l)
50f20cfd 2218 process_inotify_event(j, e);
50f20cfd
LP
2219 }
2220}
6ad1d1c3 2221
e02d1cf7 2222_public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
a963990f 2223 int r;
39c155ea 2224 uint64_t t;
e02d1cf7 2225
1ae464e0
TA
2226 assert_return(j, -EINVAL);
2227 assert_return(!journal_pid_changed(j), -ECHILD);
e02d1cf7 2228
a963990f
LP
2229 if (j->inotify_fd < 0) {
2230
2231 /* This is the first invocation, hence create the
2232 * inotify watch */
2233 r = sd_journal_get_fd(j);
2234 if (r < 0)
2235 return r;
2236
2237 /* The journal might have changed since the context
2238 * object was created and we weren't watching before,
2239 * hence don't wait for anything, and return
2240 * immediately. */
2241 return determine_change(j);
2242 }
2243
39c155ea
LP
2244 r = sd_journal_get_timeout(j, &t);
2245 if (r < 0)
2246 return r;
2247
2248 if (t != (uint64_t) -1) {
2249 usec_t n;
2250
2251 n = now(CLOCK_MONOTONIC);
2252 t = t > n ? t - n : 0;
85210bff 2253
39c155ea
LP
2254 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2255 timeout_usec = t;
85210bff
LP
2256 }
2257
a963990f
LP
2258 do {
2259 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2260 } while (r == -EINTR);
e02d1cf7
LP
2261
2262 if (r < 0)
2263 return r;
2264
a963990f 2265 return sd_journal_process(j);
e02d1cf7
LP
2266}
2267
08984293
LP
2268_public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2269 Iterator i;
2270 JournalFile *f;
2271 bool first = true;
581483bf 2272 uint64_t fmin = 0, tmax = 0;
08984293
LP
2273 int r;
2274
1ae464e0
TA
2275 assert_return(j, -EINVAL);
2276 assert_return(!journal_pid_changed(j), -ECHILD);
2277 assert_return(from || to, -EINVAL);
2278 assert_return(from != to, -EINVAL);
08984293 2279
c1f906bd 2280 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
08984293
LP
2281 usec_t fr, t;
2282
2283 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
9f8d2983
LP
2284 if (r == -ENOENT)
2285 continue;
08984293
LP
2286 if (r < 0)
2287 return r;
2288 if (r == 0)
2289 continue;
2290
2291 if (first) {
581483bf
LP
2292 fmin = fr;
2293 tmax = t;
08984293
LP
2294 first = false;
2295 } else {
581483bf
LP
2296 fmin = MIN(fr, fmin);
2297 tmax = MAX(t, tmax);
08984293
LP
2298 }
2299 }
2300
581483bf
LP
2301 if (from)
2302 *from = fmin;
2303 if (to)
2304 *to = tmax;
2305
08984293
LP
2306 return first ? 0 : 1;
2307}
2308
2309_public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2310 Iterator i;
2311 JournalFile *f;
1651e2c6 2312 bool found = false;
08984293
LP
2313 int r;
2314
1ae464e0
TA
2315 assert_return(j, -EINVAL);
2316 assert_return(!journal_pid_changed(j), -ECHILD);
2317 assert_return(from || to, -EINVAL);
2318 assert_return(from != to, -EINVAL);
08984293 2319
c1f906bd 2320 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
08984293
LP
2321 usec_t fr, t;
2322
2323 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
9f8d2983
LP
2324 if (r == -ENOENT)
2325 continue;
08984293
LP
2326 if (r < 0)
2327 return r;
2328 if (r == 0)
2329 continue;
2330
1651e2c6 2331 if (found) {
08984293 2332 if (from)
1651e2c6 2333 *from = MIN(fr, *from);
08984293 2334 if (to)
1651e2c6 2335 *to = MAX(t, *to);
08984293
LP
2336 } else {
2337 if (from)
1651e2c6 2338 *from = fr;
08984293 2339 if (to)
1651e2c6
ZJS
2340 *to = t;
2341 found = true;
08984293
LP
2342 }
2343 }
2344
1651e2c6 2345 return found;
08984293
LP
2346}
2347
dca6219e
LP
2348void journal_print_header(sd_journal *j) {
2349 Iterator i;
2350 JournalFile *f;
2351 bool newline = false;
2352
2353 assert(j);
2354
c1f906bd 2355 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
dca6219e
LP
2356 if (newline)
2357 putchar('\n');
2358 else
2359 newline = true;
2360
2361 journal_file_print_header(f);
2362 }
2363}
08984293 2364
a1a03e30
LP
2365_public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2366 Iterator i;
2367 JournalFile *f;
2368 uint64_t sum = 0;
2369
1ae464e0
TA
2370 assert_return(j, -EINVAL);
2371 assert_return(!journal_pid_changed(j), -ECHILD);
2372 assert_return(bytes, -EINVAL);
a1a03e30 2373
c1f906bd 2374 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
a1a03e30
LP
2375 struct stat st;
2376
2377 if (fstat(f->fd, &st) < 0)
2378 return -errno;
2379
2380 sum += (uint64_t) st.st_blocks * 512ULL;
2381 }
2382
2383 *bytes = sum;
2384 return 0;
2385}
2386
3c1668da
LP
2387_public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2388 char *f;
2389
1ae464e0
TA
2390 assert_return(j, -EINVAL);
2391 assert_return(!journal_pid_changed(j), -ECHILD);
2392 assert_return(!isempty(field), -EINVAL);
2393 assert_return(field_is_valid(field), -EINVAL);
3c1668da
LP
2394
2395 f = strdup(field);
2396 if (!f)
2397 return -ENOMEM;
2398
2399 free(j->unique_field);
2400 j->unique_field = f;
2401 j->unique_file = NULL;
2402 j->unique_offset = 0;
360af4cf 2403 j->unique_file_lost = false;
3c1668da
LP
2404
2405 return 0;
2406}
2407
2408_public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
3c1668da 2409 size_t k;
19a2bd80 2410
1ae464e0
TA
2411 assert_return(j, -EINVAL);
2412 assert_return(!journal_pid_changed(j), -ECHILD);
2413 assert_return(data, -EINVAL);
2414 assert_return(l, -EINVAL);
2415 assert_return(j->unique_field, -EINVAL);
19a2bd80 2416
3c1668da 2417 k = strlen(j->unique_field);
19a2bd80 2418
3c1668da 2419 if (!j->unique_file) {
360af4cf
ZJS
2420 if (j->unique_file_lost)
2421 return 0;
2422
c1f906bd 2423 j->unique_file = ordered_hashmap_first(j->files);
3c1668da
LP
2424 if (!j->unique_file)
2425 return 0;
360af4cf 2426
3c1668da
LP
2427 j->unique_offset = 0;
2428 }
19a2bd80 2429
3c1668da
LP
2430 for (;;) {
2431 JournalFile *of;
2432 Iterator i;
ae97089d 2433 Object *o;
3c1668da
LP
2434 const void *odata;
2435 size_t ol;
2436 bool found;
ae97089d 2437 int r;
3c1668da 2438
bdc02927 2439 /* Proceed to next data object in the field's linked list */
3c1668da
LP
2440 if (j->unique_offset == 0) {
2441 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2442 if (r < 0)
2443 return r;
2444
2445 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2446 } else {
2447 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2448 if (r < 0)
2449 return r;
2450
2451 j->unique_offset = le64toh(o->data.next_field_offset);
2452 }
2453
2454 /* We reached the end of the list? Then start again, with the next file */
2455 if (j->unique_offset == 0) {
c1f906bd 2456 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
360af4cf 2457 if (!j->unique_file)
3c1668da
LP
2458 return 0;
2459
3c1668da
LP
2460 continue;
2461 }
2462
d05089d8
MS
2463 /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED
2464 * instead, so that we can look at this data object at the same
3c1668da 2465 * time as one on another file */
d05089d8 2466 r = journal_file_move_to_object(j->unique_file, OBJECT_UNUSED, j->unique_offset, &o);
3c1668da
LP
2467 if (r < 0)
2468 return r;
2469
2470 /* Let's do the type check by hand, since we used 0 context above. */
ae97089d 2471 if (o->object.type != OBJECT_DATA) {
36202fd2 2472 log_debug("%s:offset " OFSfmt ": object has type %d, expected %d",
ae97089d
ZJS
2473 j->unique_file->path, j->unique_offset,
2474 o->object.type, OBJECT_DATA);
3c1668da 2475 return -EBADMSG;
ae97089d
ZJS
2476 }
2477
93b73b06 2478 r = return_data(j, j->unique_file, o, &odata, &ol);
3c1668da
LP
2479 if (r < 0)
2480 return r;
2481
0f99f74a
ZJS
2482 /* Check if we have at least the field name and "=". */
2483 if (ol <= k) {
2484 log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
2485 j->unique_file->path, j->unique_offset,
2486 ol, k + 1);
2487 return -EBADMSG;
2488 }
2489
2490 if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') {
2491 log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"",
2492 j->unique_file->path, j->unique_offset,
2493 j->unique_field);
2494 return -EBADMSG;
2495 }
2496
3c1668da
LP
2497 /* OK, now let's see if we already returned this data
2498 * object by checking if it exists in the earlier
2499 * traversed files. */
2500 found = false;
c1f906bd 2501 ORDERED_HASHMAP_FOREACH(of, j->files, i) {
3c1668da
LP
2502 Object *oo;
2503 uint64_t op;
2504
2505 if (of == j->unique_file)
2506 break;
2507
2508 /* Skip this file it didn't have any fields
2509 * indexed */
2510 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2511 le64toh(of->header->n_fields) <= 0)
2512 continue;
2513
2514 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2515 if (r < 0)
2516 return r;
2517
2518 if (r > 0)
2519 found = true;
2520 }
2521
06cc69d4
JJ
2522 if (found)
2523 continue;
2524
93b73b06 2525 r = return_data(j, j->unique_file, o, data, l);
3c1668da
LP
2526 if (r < 0)
2527 return r;
2528
2529 return 1;
2530 }
2531}
2532
115646c7 2533_public_ void sd_journal_restart_unique(sd_journal *j) {
3c1668da
LP
2534 if (!j)
2535 return;
2536
2537 j->unique_file = NULL;
2538 j->unique_offset = 0;
360af4cf 2539 j->unique_file_lost = false;
3c1668da 2540}
85210bff
LP
2541
2542_public_ int sd_journal_reliable_fd(sd_journal *j) {
1ae464e0
TA
2543 assert_return(j, -EINVAL);
2544 assert_return(!journal_pid_changed(j), -ECHILD);
85210bff
LP
2545
2546 return !j->on_network;
2547}
d4205751
LP
2548
2549static char *lookup_field(const char *field, void *userdata) {
2550 sd_journal *j = userdata;
2551 const void *data;
2552 size_t size, d;
2553 int r;
2554
2555 assert(field);
2556 assert(j);
2557
2558 r = sd_journal_get_data(j, field, &data, &size);
2559 if (r < 0 ||
2560 size > REPLACE_VAR_MAX)
2561 return strdup(field);
2562
2563 d = strlen(field) + 1;
2564
2565 return strndup((const char*) data + d, size - d);
2566}
2567
2568_public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
2569 const void *data;
2570 size_t size;
2571 sd_id128_t id;
2572 _cleanup_free_ char *text = NULL, *cid = NULL;
2573 char *t;
2574 int r;
2575
1ae464e0
TA
2576 assert_return(j, -EINVAL);
2577 assert_return(!journal_pid_changed(j), -ECHILD);
2578 assert_return(ret, -EINVAL);
d4205751
LP
2579
2580 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
2581 if (r < 0)
2582 return r;
2583
2584 cid = strndup((const char*) data + 11, size - 11);
2585 if (!cid)
2586 return -ENOMEM;
2587
2588 r = sd_id128_from_string(cid, &id);
2589 if (r < 0)
2590 return r;
2591
844ec79b 2592 r = catalog_get(CATALOG_DATABASE, id, &text);
d4205751
LP
2593 if (r < 0)
2594 return r;
2595
2596 t = replace_var(text, lookup_field, j);
2597 if (!t)
2598 return -ENOMEM;
2599
2600 *ret = t;
2601 return 0;
2602}
8f1e860f
LP
2603
2604_public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
1ae464e0 2605 assert_return(ret, -EINVAL);
8f1e860f 2606
844ec79b 2607 return catalog_get(CATALOG_DATABASE, id, ret);
8f1e860f 2608}
93b73b06
LP
2609
2610_public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
1ae464e0
TA
2611 assert_return(j, -EINVAL);
2612 assert_return(!journal_pid_changed(j), -ECHILD);
93b73b06
LP
2613
2614 j->data_threshold = sz;
2615 return 0;
2616}
2617
2618_public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
1ae464e0
TA
2619 assert_return(j, -EINVAL);
2620 assert_return(!journal_pid_changed(j), -ECHILD);
2621 assert_return(sz, -EINVAL);
93b73b06
LP
2622
2623 *sz = j->data_threshold;
2624 return 0;
2625}