]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/sd-journal.c
journal: expose and make use of cutoff times of journal
[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>
87d2c1ff
LP
27
28#include "sd-journal.h"
29#include "journal-def.h"
cec736d2 30#include "journal-file.h"
260a2be4 31#include "hashmap.h"
cec736d2 32#include "list.h"
9eb977db 33#include "path-util.h"
de7b95cd 34#include "lookup3.h"
807e17f0 35#include "compress.h"
cf244689 36#include "journal-internal.h"
87d2c1ff 37
cab8ac60
LP
38#define JOURNAL_FILES_MAX 1024
39
de190aef 40static void detach_location(sd_journal *j) {
8f9b6cd9
LP
41 Iterator i;
42 JournalFile *f;
43
44 assert(j);
45
46 j->current_file = NULL;
47 j->current_field = 0;
48
49 HASHMAP_FOREACH(f, j->files, i)
50 f->current_offset = 0;
51}
52
de190aef
LP
53static void reset_location(sd_journal *j) {
54 assert(j);
55
56 detach_location(j);
57 zero(j->current_location);
58}
59
60static void init_location(Location *l, JournalFile *f, Object *o) {
61 assert(l);
62 assert(f);
63 assert(o->object.type == OBJECT_ENTRY);
64
65 l->type = LOCATION_DISCRETE;
66 l->seqnum = le64toh(o->entry.seqnum);
67 l->seqnum_id = f->header->seqnum_id;
68 l->realtime = le64toh(o->entry.realtime);
69 l->monotonic = le64toh(o->entry.monotonic);
ce3fd7e7 70 l->boot_id = o->entry.boot_id;
de190aef
LP
71 l->xor_hash = le64toh(o->entry.xor_hash);
72
73 l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
74}
75
76static void set_location(sd_journal *j, JournalFile *f, Object *o, uint64_t offset) {
77 assert(j);
78 assert(f);
79 assert(o);
80
81 init_location(&j->current_location, f, o);
82
83 j->current_file = f;
84 j->current_field = 0;
85
86 f->current_offset = offset;
87}
88
89static int same_field(const void *_a, size_t s, const void *_b, size_t t) {
90 const uint8_t *a = _a, *b = _b;
91 size_t j;
92 bool a_good = false, b_good = false, different = false;
93
94 for (j = 0; j < s && j < t; j++) {
95
96 if (a[j] == '=')
97 a_good = true;
98 if (b[j] == '=')
99 b_good = true;
100 if (a[j] != b[j])
101 different = true;
102
103 if (a_good && b_good)
104 return different ? 0 : 1;
105 }
106
107 return -EINVAL;
108}
109
a5344d2c 110_public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
de190aef 111 Match *m, *after = NULL;
4fd052ae 112 le64_t le_hash;
87d2c1ff 113
a5344d2c
LP
114 if (!j)
115 return -EINVAL;
116 if (!data)
117 return -EINVAL;
d94117a9
LP
118 if (size <= 1)
119 return -EINVAL;
120 if (!memchr(data, '=', size))
121 return -EINVAL;
122 if (*(char*) data == '=')
1cc101f1
LP
123 return -EINVAL;
124
ab4979d2
LP
125 /* FIXME: iterating with multiple matches is currently
126 * broken */
127 if (j->matches)
128 return -ENOTSUP;
129
de190aef
LP
130 le_hash = htole64(hash64(data, size));
131
132 LIST_FOREACH(matches, m, j->matches) {
133 int r;
134
135 if (m->le_hash == le_hash &&
136 m->size == size &&
137 memcmp(m->data, data, size) == 0)
138 return 0;
139
140 r = same_field(data, size, m->data, m->size);
141 if (r < 0)
142 return r;
143 else if (r > 0)
144 after = m;
145 }
146
cec736d2
LP
147 m = new0(Match, 1);
148 if (!m)
149 return -ENOMEM;
87d2c1ff 150
1cc101f1
LP
151 m->size = size;
152
cec736d2
LP
153 m->data = malloc(m->size);
154 if (!m->data) {
155 free(m);
156 return -ENOMEM;
87d2c1ff
LP
157 }
158
1cc101f1 159 memcpy(m->data, data, size);
de190aef 160 m->le_hash = le_hash;
87d2c1ff 161
de190aef
LP
162 /* Matches for the same fields we order adjacent to each
163 * other */
164 LIST_INSERT_AFTER(Match, matches, j->matches, after, m);
de7b95cd
LP
165 j->n_matches ++;
166
de190aef 167 detach_location(j);
8f9b6cd9 168
87d2c1ff
LP
169 return 0;
170}
171
a5344d2c
LP
172_public_ void sd_journal_flush_matches(sd_journal *j) {
173 if (!j)
174 return;
87d2c1ff 175
cec736d2
LP
176 while (j->matches) {
177 Match *m = j->matches;
87d2c1ff 178
cec736d2
LP
179 LIST_REMOVE(Match, matches, j->matches, m);
180 free(m->data);
181 free(m);
87d2c1ff 182 }
de7b95cd
LP
183
184 j->n_matches = 0;
8f9b6cd9 185
de190aef 186 detach_location(j);
87d2c1ff
LP
187}
188
de190aef
LP
189static int compare_order(JournalFile *af, Object *ao,
190 JournalFile *bf, Object *bo) {
87d2c1ff 191
cec736d2 192 uint64_t a, b;
87d2c1ff 193
de190aef
LP
194 assert(af);
195 assert(ao);
196 assert(bf);
197 assert(bo);
198
ae2cc8ef 199 /* We operate on two different files here, hence we can access
1cc101f1
LP
200 * two objects at the same time, which we normally can't.
201 *
202 * If contents and timestamps match, these entries are
203 * identical, even if the seqnum does not match */
204
205 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
206 ao->entry.monotonic == bo->entry.monotonic &&
207 ao->entry.realtime == bo->entry.realtime &&
208 ao->entry.xor_hash == bo->entry.xor_hash)
209 return 0;
ae2cc8ef 210
cec736d2 211 if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
87d2c1ff 212
cec736d2
LP
213 /* If this is from the same seqnum source, compare
214 * seqnums */
215 a = le64toh(ao->entry.seqnum);
216 b = le64toh(bo->entry.seqnum);
87d2c1ff 217
ae2cc8ef
LP
218 if (a < b)
219 return -1;
220 if (a > b)
221 return 1;
1cc101f1
LP
222
223 /* Wow! This is weird, different data but the same
224 * seqnums? Something is borked, but let's make the
225 * best of it and compare by time. */
ae2cc8ef 226 }
87d2c1ff 227
ae2cc8ef 228 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
87d2c1ff 229
cec736d2
LP
230 /* If the boot id matches compare monotonic time */
231 a = le64toh(ao->entry.monotonic);
232 b = le64toh(bo->entry.monotonic);
87d2c1ff 233
ae2cc8ef
LP
234 if (a < b)
235 return -1;
236 if (a > b)
237 return 1;
87d2c1ff
LP
238 }
239
ae2cc8ef
LP
240 /* Otherwise compare UTC time */
241 a = le64toh(ao->entry.realtime);
242 b = le64toh(ao->entry.realtime);
243
244 if (a < b)
245 return -1;
246 if (a > b)
247 return 1;
248
249 /* Finally, compare by contents */
250 a = le64toh(ao->entry.xor_hash);
251 b = le64toh(ao->entry.xor_hash);
252
253 if (a < b)
254 return -1;
255 if (a > b)
256 return 1;
257
258 return 0;
87d2c1ff
LP
259}
260
de190aef
LP
261static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
262 uint64_t a;
263
264 assert(af);
265 assert(ao);
266 assert(l);
267 assert(l->type == LOCATION_DISCRETE);
268
269 if (l->monotonic_set &&
270 sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
271 l->realtime_set &&
272 le64toh(ao->entry.realtime) == l->realtime &&
273 l->xor_hash_set &&
274 le64toh(ao->entry.xor_hash) == l->xor_hash)
275 return 0;
276
277 if (l->seqnum_set &&
278 sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
279
280 a = le64toh(ao->entry.seqnum);
281
282 if (a < l->seqnum)
283 return -1;
284 if (a > l->seqnum)
285 return 1;
286 }
287
288 if (l->monotonic_set &&
289 sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
290
291 a = le64toh(ao->entry.monotonic);
292
293 if (a < l->monotonic)
294 return -1;
295 if (a > l->monotonic)
296 return 1;
297 }
298
299 if (l->realtime_set) {
300
301 a = le64toh(ao->entry.realtime);
302
303 if (a < l->realtime)
304 return -1;
305 if (a > l->realtime)
306 return 1;
307 }
308
309 if (l->xor_hash_set) {
310 a = le64toh(ao->entry.xor_hash);
311
312 if (a < l->xor_hash)
313 return -1;
314 if (a > l->xor_hash)
315 return 1;
316 }
317
318 return 0;
319}
320
321static int find_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
322 Object *o = NULL;
323 uint64_t p = 0;
de7b95cd 324 int r;
de7b95cd
LP
325
326 assert(j);
de7b95cd
LP
327
328 if (!j->matches) {
de190aef
LP
329 /* No matches is simple */
330
331 if (j->current_location.type == LOCATION_HEAD)
332 r = journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, &o, &p);
333 else if (j->current_location.type == LOCATION_TAIL)
334 r = journal_file_next_entry(f, NULL, 0, DIRECTION_UP, &o, &p);
335 else if (j->current_location.seqnum_set &&
336 sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
337 r = journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, &o, &p);
6030831d 338 else if (j->current_location.monotonic_set) {
de190aef 339 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, &o, &p);
6030831d
LP
340
341 if (r == -ENOENT) {
342 /* boot id unknown in this file */
343 if (j->current_location.realtime_set)
344 r = journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, &o, &p);
345 else
346 r = journal_file_next_entry(f, NULL, 0, direction, &o, &p);
347 }
348 } else if (j->current_location.realtime_set)
de190aef
LP
349 r = journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, &o, &p);
350 else
351 r = journal_file_next_entry(f, NULL, 0, direction, &o, &p);
352
353 if (r <= 0)
354 return r;
355
356 } else {
357 Match *m, *term_match = NULL;
358 Object *to = NULL;
359 uint64_t tp = 0;
360
361 /* We have matches, first, let's jump to the monotonic
362 * position if we have any, since it implies a
363 * match. */
de7b95cd 364
de190aef
LP
365 if (j->current_location.type == LOCATION_DISCRETE &&
366 j->current_location.monotonic_set) {
367
368 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, &o, &p);
369 if (r <= 0)
9c4e3f26 370 return r == -ENOENT ? 0 : r;
de190aef
LP
371 }
372
373 LIST_FOREACH(matches, m, j->matches) {
374 Object *c, *d;
375 uint64_t cp, dp;
376
4fd052ae 377 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), &d, &dp);
de190aef
LP
378 if (r <= 0)
379 return r;
380
381 if (j->current_location.type == LOCATION_HEAD)
382 r = journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, &c, &cp);
383 else if (j->current_location.type == LOCATION_TAIL)
384 r = journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, &c, &cp);
385 else if (j->current_location.seqnum_set &&
386 sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
387 r = journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, &c, &cp);
388 else if (j->current_location.realtime_set)
389 r = journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, &c, &cp);
390 else
391 r = journal_file_next_entry_for_data(f, NULL, 0, dp, direction, &c, &cp);
392
b4e5f920
LP
393 if (r < 0)
394 return r;
395
de190aef
LP
396 if (!term_match) {
397 term_match = m;
398
399 if (r > 0) {
400 to = c;
401 tp = cp;
402 }
403 } else if (same_field(term_match->data, term_match->size, m->data, m->size)) {
404
405 /* Same field as previous match... */
406 if (r > 0) {
407
408 /* Find the earliest of the OR matches */
409
410 if (!to ||
411 (direction == DIRECTION_DOWN && cp < tp) ||
412 (direction == DIRECTION_UP && cp > tp)) {
413 to = c;
24b51289 414 tp = cp;
de190aef
LP
415 }
416
417 }
de7b95cd 418
de190aef
LP
419 } else {
420
421 /* Previous term is finished, did anything match? */
422 if (!to)
423 return 0;
424
425 /* Find the last of the AND matches */
426 if (!o ||
427 (direction == DIRECTION_DOWN && tp > p) ||
428 (direction == DIRECTION_UP && tp < p)) {
429 o = to;
430 p = tp;
431 }
432
433 term_match = m;
434
435 if (r > 0) {
436 to = c;
437 tp = cp;
438 } else {
439 to = NULL;
440 tp = 0;
441 }
442 }
443 }
444
445 /* Last term is finished, did anything match? */
446 if (!to)
447 return 0;
448
449 if (!o ||
450 (direction == DIRECTION_DOWN && tp > p) ||
451 (direction == DIRECTION_UP && tp < p)) {
452 o = to;
453 p = tp;
454 }
455
456 if (!o)
457 return 0;
de7b95cd
LP
458 }
459
de190aef
LP
460 if (ret)
461 *ret = o;
de7b95cd 462
de190aef
LP
463 if (offset)
464 *offset = p;
465
466 return 1;
467}
468
469static int next_with_matches(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
470 int r;
471 uint64_t cp;
472 Object *c;
473
474 assert(j);
475 assert(f);
476 assert(ret);
477 assert(offset);
478
479 c = *ret;
480 cp = *offset;
481
482 if (!j->matches) {
483 /* No matches is easy */
484
485 r = journal_file_next_entry(f, c, cp, direction, &c, &cp);
de7b95cd
LP
486 if (r <= 0)
487 return r;
488
de190aef
LP
489 if (ret)
490 *ret = c;
491 if (offset)
492 *offset = cp;
493 return 1;
de7b95cd
LP
494 }
495
de190aef
LP
496 /* So there are matches we have to adhere to, let's find the
497 * first entry that matches all of them */
498
de7b95cd
LP
499 for (;;) {
500 uint64_t np, n;
de190aef
LP
501 bool found, term_result = false;
502 Match *m, *term_match = NULL;
466ccd92 503 Object *npo = NULL;
de7b95cd
LP
504
505 n = journal_file_entry_n_items(c);
506
507 /* Make sure we don't match the entry we are starting
508 * from. */
cf5eb6a1 509 found = cp != *offset;
de7b95cd
LP
510
511 np = 0;
512 LIST_FOREACH(matches, m, j->matches) {
513 uint64_t q, k;
466ccd92 514 Object *qo = NULL;
de7b95cd 515
de190aef
LP
516 /* Let's check if this is the beginning of a
517 * new term, i.e. has a different field prefix
518 * as the preceeding match. */
519 if (!term_match) {
520 term_match = m;
521 term_result = false;
522 } else if (!same_field(term_match->data, term_match->size, m->data, m->size)) {
523 if (!term_result)
524 found = false;
525
526 term_match = m;
527 term_result = false;
528 }
529
de7b95cd
LP
530 for (k = 0; k < n; k++)
531 if (c->entry.items[k].hash == m->le_hash)
532 break;
533
534 if (k >= n) {
de190aef
LP
535 /* Hmm, didn't find any field that
536 * matched this rule, so ignore this
537 * match. Go on with next match */
de7b95cd
LP
538 continue;
539 }
540
de190aef
LP
541 term_result = true;
542
de7b95cd
LP
543 /* Hmm, so, this field matched, let's remember
544 * where we'd have to try next, in case the other
545 * matches are not OK */
e892bd17 546
466ccd92 547 r = journal_file_next_entry_for_data(f, c, cp, le64toh(c->entry.items[k].object_offset), direction, &qo, &q);
189f6d82
MR
548 /* This pointer is invalidated if the window was
549 * remapped. May need to re-fetch it later */
550 c = NULL;
4b067dc9
LP
551 if (r < 0)
552 return r;
553
de190aef 554 if (r > 0) {
e892bd17 555
de190aef 556 if (direction == DIRECTION_DOWN) {
466ccd92 557 if (q > np) {
de190aef 558 np = q;
466ccd92
LP
559 npo = qo;
560 }
de190aef 561 } else {
466ccd92 562 if (np == 0 || q < np) {
de190aef 563 np = q;
466ccd92
LP
564 npo = qo;
565 }
de190aef 566 }
e892bd17 567 }
de7b95cd
LP
568 }
569
de190aef 570 /* Check the last term */
466ccd92
LP
571 if (term_match && !term_result)
572 found = false;
de190aef 573
de7b95cd
LP
574 /* Did this entry match against all matches? */
575 if (found) {
189f6d82
MR
576 if (ret) {
577 if (c == NULL) {
578 /* Re-fetch the entry */
579 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
580 if (r < 0)
581 return r;
582 }
de190aef 583 *ret = c;
189f6d82 584 }
de190aef
LP
585 if (offset)
586 *offset = cp;
de7b95cd
LP
587 return 1;
588 }
589
590 /* Did we find a subsequent entry? */
591 if (np == 0)
592 return 0;
593
594 /* Hmm, ok, this entry only matched partially, so
595 * let's try another one */
596 cp = np;
466ccd92 597 c = npo;
de7b95cd
LP
598 }
599}
600
de190aef
LP
601static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
602 Object *c;
603 uint64_t cp;
604 int compare_value, r;
605
606 assert(j);
607 assert(f);
608
609 if (f->current_offset > 0) {
466ccd92
LP
610 cp = f->current_offset;
611
612 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
de190aef
LP
613 if (r < 0)
614 return r;
615
de190aef
LP
616 r = next_with_matches(j, f, direction, &c, &cp);
617 if (r <= 0)
618 return r;
619
620 compare_value = 1;
621 } else {
622 r = find_location(j, f, direction, &c, &cp);
623 if (r <= 0)
624 return r;
625
626 compare_value = 0;
627 }
628
629 for (;;) {
630 bool found;
631
632 if (j->current_location.type == LOCATION_DISCRETE) {
633 int k;
634
635 k = compare_with_location(f, c, &j->current_location);
636 if (direction == DIRECTION_DOWN)
637 found = k >= compare_value;
638 else
639 found = k <= -compare_value;
640 } else
641 found = true;
642
643 if (found) {
644 if (ret)
645 *ret = c;
646 if (offset)
647 *offset = cp;
648 return 1;
649 }
650
651 r = next_with_matches(j, f, direction, &c, &cp);
652 if (r <= 0)
653 return r;
654 }
655}
656
e892bd17 657static int real_journal_next(sd_journal *j, direction_t direction) {
cec736d2
LP
658 JournalFile *f, *new_current = NULL;
659 Iterator i;
87d2c1ff 660 int r;
cec736d2
LP
661 uint64_t new_offset = 0;
662 Object *new_entry = NULL;
87d2c1ff 663
a5344d2c
LP
664 if (!j)
665 return -EINVAL;
87d2c1ff 666
cec736d2
LP
667 HASHMAP_FOREACH(f, j->files, i) {
668 Object *o;
669 uint64_t p;
de190aef 670 bool found;
87d2c1ff 671
de190aef 672 r = next_beyond_location(j, f, direction, &o, &p);
87d2c1ff
LP
673 if (r < 0)
674 return r;
cec736d2
LP
675 else if (r == 0)
676 continue;
87d2c1ff 677
de190aef
LP
678 if (!new_current)
679 found = true;
680 else {
681 int k;
682
683 k = compare_order(f, o, new_current, new_entry);
684
685 if (direction == DIRECTION_DOWN)
686 found = k < 0;
687 else
688 found = k > 0;
689 }
690
691 if (found) {
cec736d2
LP
692 new_current = f;
693 new_entry = o;
694 new_offset = p;
87d2c1ff 695 }
87d2c1ff
LP
696 }
697
de190aef
LP
698 if (!new_current)
699 return 0;
ae2cc8ef 700
de190aef 701 set_location(j, new_current, new_entry, new_offset);
ae2cc8ef 702
de190aef
LP
703 return 1;
704}
ae2cc8ef 705
a5344d2c 706_public_ int sd_journal_next(sd_journal *j) {
de190aef
LP
707 return real_journal_next(j, DIRECTION_DOWN);
708}
ae2cc8ef 709
a5344d2c 710_public_ int sd_journal_previous(sd_journal *j) {
de190aef
LP
711 return real_journal_next(j, DIRECTION_UP);
712}
ae2cc8ef 713
6f003b43 714static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
de190aef 715 int c = 0, r;
ae2cc8ef 716
a5344d2c
LP
717 if (!j)
718 return -EINVAL;
de190aef 719
6f003b43
LP
720 if (skip == 0) {
721 /* If this is not a discrete skip, then at least
722 * resolve the current location */
723 if (j->current_location.type != LOCATION_DISCRETE)
724 return real_journal_next(j, direction);
725
726 return 0;
727 }
728
729 do {
730 r = real_journal_next(j, direction);
de190aef
LP
731 if (r < 0)
732 return r;
733
734 if (r == 0)
735 return c;
736
737 skip--;
738 c++;
6f003b43 739 } while (skip > 0);
87d2c1ff 740
de190aef 741 return c;
87d2c1ff
LP
742}
743
6f003b43
LP
744_public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
745 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
746}
de190aef 747
6f003b43
LP
748_public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
749 return real_journal_next_skip(j, DIRECTION_UP, skip);
87d2c1ff
LP
750}
751
a5344d2c 752_public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
cec736d2 753 Object *o;
87d2c1ff 754 int r;
3fbf9cbb 755 char bid[33], sid[33];
87d2c1ff 756
a5344d2c
LP
757 if (!j)
758 return -EINVAL;
759 if (!cursor)
760 return -EINVAL;
87d2c1ff 761
3fbf9cbb
LP
762 if (!j->current_file || j->current_file->current_offset <= 0)
763 return -EADDRNOTAVAIL;
87d2c1ff 764
de190aef 765 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
87d2c1ff
LP
766 if (r < 0)
767 return r;
768
3fbf9cbb
LP
769 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
770 sd_id128_to_string(o->entry.boot_id, bid);
87d2c1ff 771
3fbf9cbb
LP
772 if (asprintf(cursor,
773 "s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx;p=%s",
774 sid, (unsigned long long) le64toh(o->entry.seqnum),
775 bid, (unsigned long long) le64toh(o->entry.monotonic),
776 (unsigned long long) le64toh(o->entry.realtime),
777 (unsigned long long) le64toh(o->entry.xor_hash),
9eb977db 778 path_get_file_name(j->current_file->path)) < 0)
3fbf9cbb 779 return -ENOMEM;
87d2c1ff
LP
780
781 return 1;
782}
783
a5344d2c 784_public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
de190aef
LP
785 char *w;
786 size_t l;
787 char *state;
788 unsigned long long seqnum, monotonic, realtime, xor_hash;
789 bool
790 seqnum_id_set = false,
791 seqnum_set = false,
792 boot_id_set = false,
793 monotonic_set = false,
794 realtime_set = false,
795 xor_hash_set = false;
796 sd_id128_t seqnum_id, boot_id;
797
a5344d2c
LP
798 if (!j)
799 return -EINVAL;
800 if (!cursor)
801 return -EINVAL;
de190aef
LP
802
803 FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
804 char *item;
805 int k = 0;
806
807 if (l < 2 || w[1] != '=')
808 return -EINVAL;
809
810 item = strndup(w, l);
811 if (!item)
812 return -ENOMEM;
813
814 switch (w[0]) {
815
816 case 's':
817 seqnum_id_set = true;
818 k = sd_id128_from_string(w+2, &seqnum_id);
819 break;
820
821 case 'i':
822 seqnum_set = true;
823 if (sscanf(w+2, "%llx", &seqnum) != 1)
824 k = -EINVAL;
825 break;
826
827 case 'b':
828 boot_id_set = true;
829 k = sd_id128_from_string(w+2, &boot_id);
830 break;
831
832 case 'm':
833 monotonic_set = true;
834 if (sscanf(w+2, "%llx", &monotonic) != 1)
835 k = -EINVAL;
836 break;
837
838 case 't':
839 realtime_set = true;
840 if (sscanf(w+2, "%llx", &realtime) != 1)
841 k = -EINVAL;
842 break;
843
844 case 'x':
845 xor_hash_set = true;
846 if (sscanf(w+2, "%llx", &xor_hash) != 1)
847 k = -EINVAL;
848 break;
849 }
850
851 free(item);
852
853 if (k < 0)
854 return k;
855 }
856
857 if ((!seqnum_set || !seqnum_id_set) &&
858 (!monotonic_set || !boot_id_set) &&
859 !realtime_set)
860 return -EINVAL;
861
862 reset_location(j);
863
864 j->current_location.type = LOCATION_DISCRETE;
865
866 if (realtime_set) {
867 j->current_location.realtime = (uint64_t) realtime;
868 j->current_location.realtime_set = true;
869 }
870
871 if (seqnum_set && seqnum_id_set) {
872 j->current_location.seqnum = (uint64_t) seqnum;
873 j->current_location.seqnum_id = seqnum_id;
874 j->current_location.seqnum_set = true;
875 }
876
877 if (monotonic_set && boot_id_set) {
878 j->current_location.monotonic = (uint64_t) monotonic;
879 j->current_location.boot_id = boot_id;
880 j->current_location.monotonic_set = true;
881 }
882
883 if (xor_hash_set) {
884 j->current_location.xor_hash = (uint64_t) xor_hash;
885 j->current_location.xor_hash_set = true;
886 }
887
888 return 0;
889}
890
a5344d2c
LP
891_public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
892 if (!j)
893 return -EINVAL;
de190aef
LP
894
895 reset_location(j);
896 j->current_location.type = LOCATION_DISCRETE;
897 j->current_location.boot_id = boot_id;
898 j->current_location.monotonic = usec;
899 j->current_location.monotonic_set = true;
900
901 return 0;
902}
903
a5344d2c
LP
904_public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
905 if (!j)
906 return -EINVAL;
de190aef
LP
907
908 reset_location(j);
909 j->current_location.type = LOCATION_DISCRETE;
910 j->current_location.realtime = usec;
911 j->current_location.realtime_set = true;
912
913 return 0;
914}
915
a5344d2c
LP
916_public_ int sd_journal_seek_head(sd_journal *j) {
917 if (!j)
918 return -EINVAL;
de190aef
LP
919
920 reset_location(j);
921 j->current_location.type = LOCATION_HEAD;
922
923 return 0;
924}
925
a5344d2c
LP
926_public_ int sd_journal_seek_tail(sd_journal *j) {
927 if (!j)
928 return -EINVAL;
de190aef
LP
929
930 reset_location(j);
931 j->current_location.type = LOCATION_TAIL;
932
933 return 0;
87d2c1ff
LP
934}
935
3fbf9cbb
LP
936static int add_file(sd_journal *j, const char *prefix, const char *dir, const char *filename) {
937 char *fn;
938 int r;
939 JournalFile *f;
940
941 assert(j);
942 assert(prefix);
943 assert(filename);
944
cf244689
LP
945 if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) &&
946 !startswith(filename, "system.journal"))
947 return 0;
948
3fbf9cbb
LP
949 if (dir)
950 fn = join(prefix, "/", dir, "/", filename, NULL);
951 else
952 fn = join(prefix, "/", filename, NULL);
953
954 if (!fn)
955 return -ENOMEM;
956
50f20cfd
LP
957 if (hashmap_get(j->files, fn)) {
958 free(fn);
959 return 0;
960 }
961
962 if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
963 log_debug("Too many open journal files, not adding %s, ignoring.", fn);
964 free(fn);
965 return 0;
966 }
967
3fbf9cbb
LP
968 r = journal_file_open(fn, O_RDONLY, 0, NULL, &f);
969 free(fn);
970
971 if (r < 0) {
972 if (errno == ENOENT)
973 return 0;
974
975 return r;
976 }
977
72f59706 978 /* journal_file_dump(f); */
de190aef 979
3fbf9cbb
LP
980 r = hashmap_put(j->files, f->path, f);
981 if (r < 0) {
982 journal_file_close(f);
983 return r;
984 }
985
50f20cfd
LP
986 log_debug("File %s got added.", f->path);
987
988 return 0;
989}
990
991static int remove_file(sd_journal *j, const char *prefix, const char *dir, const char *filename) {
992 char *fn;
993 JournalFile *f;
994
995 assert(j);
996 assert(prefix);
997 assert(filename);
998
999 if (dir)
1000 fn = join(prefix, "/", dir, "/", filename, NULL);
1001 else
1002 fn = join(prefix, "/", filename, NULL);
1003
1004 if (!fn)
1005 return -ENOMEM;
1006
1007 f = hashmap_get(j->files, fn);
1008 free(fn);
1009
1010 if (!f)
1011 return 0;
1012
1013 hashmap_remove(j->files, f->path);
1014 journal_file_close(f);
1015
1016 log_debug("File %s got removed.", f->path);
3fbf9cbb
LP
1017 return 0;
1018}
1019
1020static int add_directory(sd_journal *j, const char *prefix, const char *dir) {
1021 char *fn;
1022 int r;
1023 DIR *d;
50f20cfd 1024 int wd;
cf244689 1025 sd_id128_t id, mid;
3fbf9cbb
LP
1026
1027 assert(j);
1028 assert(prefix);
1029 assert(dir);
1030
cf244689
LP
1031 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1032 (sd_id128_from_string(dir, &id) < 0 ||
1033 sd_id128_get_machine(&mid) < 0 ||
1034 !sd_id128_equal(id, mid)))
1035 return 0;
1036
3fbf9cbb
LP
1037 fn = join(prefix, "/", dir, NULL);
1038 if (!fn)
1039 return -ENOMEM;
1040
1041 d = opendir(fn);
3fbf9cbb
LP
1042
1043 if (!d) {
50f20cfd 1044 free(fn);
3fbf9cbb
LP
1045 if (errno == ENOENT)
1046 return 0;
1047
1048 return -errno;
1049 }
1050
50f20cfd
LP
1051 wd = inotify_add_watch(j->inotify_fd, fn,
1052 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1053 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|
1054 IN_DONT_FOLLOW|IN_ONLYDIR);
1055 if (wd > 0) {
1056 if (hashmap_put(j->inotify_wd_dirs, INT_TO_PTR(wd), fn) < 0)
1057 inotify_rm_watch(j->inotify_fd, wd);
1058 else
1059 fn = NULL;
1060 }
1061
1062 free(fn);
1063
3fbf9cbb
LP
1064 for (;;) {
1065 struct dirent buf, *de;
1066
1067 r = readdir_r(d, &buf, &de);
1068 if (r != 0 || !de)
1069 break;
1070
1071 if (!dirent_is_file_with_suffix(de, ".journal"))
1072 continue;
1073
1074 r = add_file(j, prefix, dir, de->d_name);
1075 if (r < 0)
1076 log_debug("Failed to add file %s/%s/%s: %s", prefix, dir, de->d_name, strerror(-r));
1077 }
1078
1079 closedir(d);
1080
50f20cfd
LP
1081 log_debug("Directory %s/%s got added.", prefix, dir);
1082
3fbf9cbb
LP
1083 return 0;
1084}
1085
50f20cfd
LP
1086static void remove_directory_wd(sd_journal *j, int wd) {
1087 char *p;
1088
1089 assert(j);
1090 assert(wd > 0);
1091
1092 if (j->inotify_fd >= 0)
1093 inotify_rm_watch(j->inotify_fd, wd);
1094
1095 p = hashmap_remove(j->inotify_wd_dirs, INT_TO_PTR(wd));
1096
1097 if (p) {
1098 log_debug("Directory %s got removed.", p);
1099 free(p);
1100 }
1101}
1102
1103static void add_root_wd(sd_journal *j, const char *p) {
1104 int wd;
1105 char *k;
1106
1107 assert(j);
1108 assert(p);
1109
1110 wd = inotify_add_watch(j->inotify_fd, p,
1111 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1112 IN_DONT_FOLLOW|IN_ONLYDIR);
1113 if (wd <= 0)
1114 return;
1115
1116 k = strdup(p);
1117 if (!k || hashmap_put(j->inotify_wd_roots, INT_TO_PTR(wd), k) < 0) {
1118 inotify_rm_watch(j->inotify_fd, wd);
1119 free(k);
1120 }
1121}
1122
1123static void remove_root_wd(sd_journal *j, int wd) {
1124 char *p;
1125
1126 assert(j);
1127 assert(wd > 0);
1128
1129 if (j->inotify_fd >= 0)
1130 inotify_rm_watch(j->inotify_fd, wd);
1131
1132 p = hashmap_remove(j->inotify_wd_roots, INT_TO_PTR(wd));
1133
1134 if (p) {
1135 log_debug("Root %s got removed.", p);
1136 free(p);
1137 }
1138}
1139
a5344d2c 1140_public_ int sd_journal_open(sd_journal **ret, int flags) {
87d2c1ff 1141 sd_journal *j;
87d2c1ff 1142 const char *p;
87d2c1ff
LP
1143 const char search_paths[] =
1144 "/run/log/journal\0"
1145 "/var/log/journal\0";
3fbf9cbb 1146 int r;
87d2c1ff 1147
a5344d2c
LP
1148 if (!ret)
1149 return -EINVAL;
1150
1151 if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
1152 SD_JOURNAL_RUNTIME_ONLY|
1153 SD_JOURNAL_SYSTEM_ONLY))
1154 return -EINVAL;
87d2c1ff
LP
1155
1156 j = new0(sd_journal, 1);
1157 if (!j)
1158 return -ENOMEM;
1159
cf244689
LP
1160 j->flags = flags;
1161
50f20cfd
LP
1162 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1163 if (j->inotify_fd < 0) {
1164 r = -errno;
1165 goto fail;
1166 }
1167
260a2be4 1168 j->files = hashmap_new(string_hash_func, string_compare_func);
3fbf9cbb
LP
1169 if (!j->files) {
1170 r = -ENOMEM;
260a2be4 1171 goto fail;
3fbf9cbb
LP
1172 }
1173
50f20cfd
LP
1174 j->inotify_wd_dirs = hashmap_new(trivial_hash_func, trivial_compare_func);
1175 j->inotify_wd_roots = hashmap_new(trivial_hash_func, trivial_compare_func);
1176
1177 if (!j->inotify_wd_dirs || !j->inotify_wd_roots) {
1178 r = -ENOMEM;
1179 goto fail;
1180 }
1181
3fbf9cbb
LP
1182 /* We ignore most errors here, since the idea is to only open
1183 * what's actually accessible, and ignore the rest. */
260a2be4 1184
87d2c1ff
LP
1185 NULSTR_FOREACH(p, search_paths) {
1186 DIR *d;
1187
cf244689
LP
1188 if ((flags & SD_JOURNAL_RUNTIME_ONLY) &&
1189 !path_startswith(p, "/run"))
1190 continue;
1191
87d2c1ff
LP
1192 d = opendir(p);
1193 if (!d) {
3fbf9cbb
LP
1194 if (errno != ENOENT)
1195 log_debug("Failed to open %s: %m", p);
87d2c1ff
LP
1196 continue;
1197 }
1198
50f20cfd
LP
1199 add_root_wd(j, p);
1200
87d2c1ff
LP
1201 for (;;) {
1202 struct dirent buf, *de;
3fbf9cbb 1203 sd_id128_t id;
87d2c1ff 1204
3fbf9cbb
LP
1205 r = readdir_r(d, &buf, &de);
1206 if (r != 0 || !de)
87d2c1ff
LP
1207 break;
1208
3fbf9cbb
LP
1209 if (dirent_is_file_with_suffix(de, ".journal")) {
1210 r = add_file(j, p, NULL, de->d_name);
1211 if (r < 0)
1212 log_debug("Failed to add file %s/%s: %s", p, de->d_name, strerror(-r));
87d2c1ff 1213
3fbf9cbb
LP
1214 } else if ((de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) &&
1215 sd_id128_from_string(de->d_name, &id) >= 0) {
87d2c1ff 1216
3fbf9cbb
LP
1217 r = add_directory(j, p, de->d_name);
1218 if (r < 0)
1219 log_debug("Failed to add directory %s/%s: %s", p, de->d_name, strerror(-r));
260a2be4
LP
1220 }
1221 }
3fbf9cbb
LP
1222
1223 closedir(d);
87d2c1ff
LP
1224 }
1225
1226 *ret = j;
1227 return 0;
1228
1229fail:
1230 sd_journal_close(j);
1231
1232 return r;
1233};
1234
a5344d2c
LP
1235_public_ void sd_journal_close(sd_journal *j) {
1236 if (!j)
1237 return;
87d2c1ff 1238
50f20cfd
LP
1239 if (j->inotify_wd_dirs) {
1240 void *k;
1241
1242 while ((k = hashmap_first_key(j->inotify_wd_dirs)))
1243 remove_directory_wd(j, PTR_TO_INT(k));
1244
1245 hashmap_free(j->inotify_wd_dirs);
1246 }
1247
1248 if (j->inotify_wd_roots) {
1249 void *k;
1250
1251 while ((k = hashmap_first_key(j->inotify_wd_roots)))
1252 remove_root_wd(j, PTR_TO_INT(k));
1253
1254 hashmap_free(j->inotify_wd_roots);
1255 }
1256
260a2be4
LP
1257 if (j->files) {
1258 JournalFile *f;
1259
1260 while ((f = hashmap_steal_first(j->files)))
1261 journal_file_close(f);
1262
1263 hashmap_free(j->files);
1264 }
87d2c1ff 1265
1cc101f1
LP
1266 sd_journal_flush_matches(j);
1267
50f20cfd
LP
1268 if (j->inotify_fd >= 0)
1269 close_nointr_nofail(j->inotify_fd);
1270
87d2c1ff
LP
1271 free(j);
1272}
3fbf9cbb 1273
a5344d2c 1274_public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
3fbf9cbb
LP
1275 Object *o;
1276 JournalFile *f;
1277 int r;
1278
a5344d2c
LP
1279 if (!j)
1280 return -EINVAL;
1281 if (!ret)
1282 return -EINVAL;
3fbf9cbb
LP
1283
1284 f = j->current_file;
1285 if (!f)
de190aef 1286 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1287
1288 if (f->current_offset <= 0)
de190aef 1289 return -EADDRNOTAVAIL;
3fbf9cbb 1290
de190aef 1291 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1292 if (r < 0)
1293 return r;
1294
1295 *ret = le64toh(o->entry.realtime);
de190aef 1296 return 0;
3fbf9cbb
LP
1297}
1298
a5344d2c 1299_public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
3fbf9cbb
LP
1300 Object *o;
1301 JournalFile *f;
1302 int r;
1303 sd_id128_t id;
1304
a5344d2c
LP
1305 if (!j)
1306 return -EINVAL;
1307 if (!ret)
1308 return -EINVAL;
3fbf9cbb
LP
1309
1310 f = j->current_file;
1311 if (!f)
de190aef 1312 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1313
1314 if (f->current_offset <= 0)
de190aef 1315 return -EADDRNOTAVAIL;
3fbf9cbb 1316
de190aef 1317 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1318 if (r < 0)
1319 return r;
1320
de190aef
LP
1321 if (ret_boot_id)
1322 *ret_boot_id = o->entry.boot_id;
1323 else {
1324 r = sd_id128_get_boot(&id);
1325 if (r < 0)
1326 return r;
3fbf9cbb 1327
de190aef 1328 if (!sd_id128_equal(id, o->entry.boot_id))
df50185b 1329 return -ESTALE;
de190aef 1330 }
3fbf9cbb
LP
1331
1332 *ret = le64toh(o->entry.monotonic);
de190aef 1333 return 0;
3fbf9cbb
LP
1334}
1335
a5344d2c 1336_public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
3fbf9cbb
LP
1337 JournalFile *f;
1338 uint64_t i, n;
1339 size_t field_length;
1340 int r;
1341 Object *o;
1342
a5344d2c
LP
1343 if (!j)
1344 return -EINVAL;
1345 if (!field)
1346 return -EINVAL;
1347 if (!data)
1348 return -EINVAL;
1349 if (!size)
1350 return -EINVAL;
3fbf9cbb
LP
1351
1352 if (isempty(field) || strchr(field, '='))
1353 return -EINVAL;
1354
1355 f = j->current_file;
1356 if (!f)
de190aef 1357 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1358
1359 if (f->current_offset <= 0)
de190aef 1360 return -EADDRNOTAVAIL;
3fbf9cbb 1361
de190aef 1362 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1363 if (r < 0)
1364 return r;
1365
1366 field_length = strlen(field);
1367
1368 n = journal_file_entry_n_items(o);
1369 for (i = 0; i < n; i++) {
4fd052ae
FC
1370 uint64_t p, l;
1371 le64_t le_hash;
3fbf9cbb
LP
1372 size_t t;
1373
1374 p = le64toh(o->entry.items[i].object_offset);
807e17f0 1375 le_hash = o->entry.items[i].hash;
de190aef 1376 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
3fbf9cbb
LP
1377 if (r < 0)
1378 return r;
1379
de190aef 1380 if (le_hash != o->data.hash)
de7b95cd
LP
1381 return -EBADMSG;
1382
3fbf9cbb
LP
1383 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1384
807e17f0
LP
1385 if (o->object.flags & OBJECT_COMPRESSED) {
1386
1387#ifdef HAVE_XZ
1388 if (uncompress_startswith(o->data.payload, l,
1389 &f->compress_buffer, &f->compress_buffer_size,
1390 field, field_length, '=')) {
1391
1392 uint64_t rsize;
1393
1394 if (!uncompress_blob(o->data.payload, l,
1395 &f->compress_buffer, &f->compress_buffer_size, &rsize))
1396 return -EBADMSG;
1397
1398 *data = f->compress_buffer;
1399 *size = (size_t) rsize;
1400
1401 return 0;
1402 }
1403#else
1404 return -EPROTONOSUPPORT;
1405#endif
1406
1407 } else if (l >= field_length+1 &&
1408 memcmp(o->data.payload, field, field_length) == 0 &&
1409 o->data.payload[field_length] == '=') {
3fbf9cbb 1410
161e54f8 1411 t = (size_t) l;
3fbf9cbb 1412
161e54f8
LP
1413 if ((uint64_t) t != l)
1414 return -E2BIG;
3fbf9cbb 1415
161e54f8
LP
1416 *data = o->data.payload;
1417 *size = t;
3fbf9cbb 1418
de190aef 1419 return 0;
161e54f8 1420 }
3fbf9cbb 1421
de190aef 1422 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
161e54f8
LP
1423 if (r < 0)
1424 return r;
3fbf9cbb
LP
1425 }
1426
de190aef 1427 return -ENOENT;
3fbf9cbb
LP
1428}
1429
a5344d2c 1430_public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
3fbf9cbb 1431 JournalFile *f;
4fd052ae
FC
1432 uint64_t p, l, n;
1433 le64_t le_hash;
3fbf9cbb
LP
1434 int r;
1435 Object *o;
de190aef 1436 size_t t;
3fbf9cbb 1437
a5344d2c
LP
1438 if (!j)
1439 return -EINVAL;
1440 if (!data)
1441 return -EINVAL;
1442 if (!size)
1443 return -EINVAL;
3fbf9cbb
LP
1444
1445 f = j->current_file;
1446 if (!f)
de190aef 1447 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1448
1449 if (f->current_offset <= 0)
de190aef 1450 return -EADDRNOTAVAIL;
3fbf9cbb 1451
de190aef 1452 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1453 if (r < 0)
1454 return r;
1455
1456 n = journal_file_entry_n_items(o);
7210bfb3 1457 if (j->current_field >= n)
3fbf9cbb
LP
1458 return 0;
1459
7210bfb3 1460 p = le64toh(o->entry.items[j->current_field].object_offset);
de190aef
LP
1461 le_hash = o->entry.items[j->current_field].hash;
1462 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
3fbf9cbb
LP
1463 if (r < 0)
1464 return r;
1465
de190aef 1466 if (le_hash != o->data.hash)
de7b95cd
LP
1467 return -EBADMSG;
1468
3fbf9cbb
LP
1469 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1470 t = (size_t) l;
1471
1472 /* We can't read objects larger than 4G on a 32bit machine */
1473 if ((uint64_t) t != l)
1474 return -E2BIG;
1475
807e17f0
LP
1476 if (o->object.flags & OBJECT_COMPRESSED) {
1477#ifdef HAVE_XZ
1478 uint64_t rsize;
1479
1480 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize))
1481 return -EBADMSG;
1482
1483 *data = f->compress_buffer;
1484 *size = (size_t) rsize;
1485#else
1486 return -EPROTONOSUPPORT;
1487#endif
1488 } else {
1489 *data = o->data.payload;
1490 *size = t;
1491 }
3fbf9cbb 1492
7210bfb3 1493 j->current_field ++;
3fbf9cbb
LP
1494
1495 return 1;
1496}
c2373f84 1497
a5344d2c
LP
1498_public_ void sd_journal_restart_data(sd_journal *j) {
1499 if (!j)
1500 return;
8725d60a
LP
1501
1502 j->current_field = 0;
c2373f84 1503}
50f20cfd 1504
a5344d2c
LP
1505_public_ int sd_journal_get_fd(sd_journal *j) {
1506 if (!j)
1507 return -EINVAL;
50f20cfd
LP
1508
1509 return j->inotify_fd;
1510}
1511
1512static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
1513 char *p;
1514 int r;
1515
1516 assert(j);
1517 assert(e);
1518
1519 /* Is this a subdirectory we watch? */
1520 p = hashmap_get(j->inotify_wd_dirs, INT_TO_PTR(e->wd));
1521 if (p) {
1522
1523 if (!(e->mask & IN_ISDIR) && e->len > 0 && endswith(e->name, ".journal")) {
1524
1525 /* Event for a journal file */
1526
1527 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1528 r = add_file(j, p, NULL, e->name);
1529 if (r < 0)
1530 log_debug("Failed to add file %s/%s: %s", p, e->name, strerror(-r));
1531 } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
1532
1533 r = remove_file(j, p, NULL, e->name);
1534 if (r < 0)
1535 log_debug("Failed to remove file %s/%s: %s", p, e->name, strerror(-r));
1536 }
1537
1538 } else if (e->len == 0) {
1539
1540 /* Event for the directory itself */
1541
1542 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT))
1543 remove_directory_wd(j, e->wd);
1544 }
1545
1546 return;
1547 }
1548
1549 /* Must be the root directory then? */
1550 p = hashmap_get(j->inotify_wd_roots, INT_TO_PTR(e->wd));
1551 if (p) {
1552 sd_id128_t id;
1553
1554 if (!(e->mask & IN_ISDIR) && e->len > 0 && endswith(e->name, ".journal")) {
1555
1556 /* Event for a journal file */
1557
1558 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1559 r = add_file(j, p, NULL, e->name);
1560 if (r < 0)
1561 log_debug("Failed to add file %s/%s: %s", p, e->name, strerror(-r));
1562 } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
1563
1564 r = remove_file(j, p, NULL, e->name);
1565 if (r < 0)
1566 log_debug("Failed to remove file %s/%s: %s", p, e->name, strerror(-r));
1567 }
1568
1569 } else if ((e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
1570
1571 /* Event for subdirectory */
1572
1573 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1574
1575 r = add_directory(j, p, e->name);
1576 if (r < 0)
1577 log_debug("Failed to add directory %s/%s: %s", p, e->name, strerror(-r));
1578 }
1579 }
1580
1581 return;
1582 }
1583
1584 if (e->mask & IN_IGNORED)
1585 return;
1586
1587 log_warning("Unknown inotify event.");
1588}
1589
a5344d2c 1590_public_ int sd_journal_process(sd_journal *j) {
50f20cfd
LP
1591 uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX];
1592
a5344d2c
LP
1593 if (!j)
1594 return -EINVAL;
50f20cfd
LP
1595
1596 for (;;) {
1597 struct inotify_event *e;
1598 ssize_t l;
1599
1600 l = read(j->inotify_fd, buffer, sizeof(buffer));
1601 if (l < 0) {
1602 if (errno == EINTR || errno == EAGAIN)
1603 return 0;
1604
1605 return -errno;
1606 }
1607
1608 e = (struct inotify_event*) buffer;
1609 while (l > 0) {
1610 size_t step;
1611
1612 process_inotify_event(j, e);
1613
1614 step = sizeof(struct inotify_event) + e->len;
1615 assert(step <= (size_t) l);
1616
1617 e = (struct inotify_event*) ((uint8_t*) e + step);
1618 l -= step;
1619 }
1620 }
1621}
6ad1d1c3 1622
08984293
LP
1623_public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
1624 Iterator i;
1625 JournalFile *f;
1626 bool first = true;
1627 int r;
1628
1629 if (!j)
1630 return -EINVAL;
1631 if (!from && !to)
1632 return -EINVAL;
1633
1634 HASHMAP_FOREACH(f, j->files, i) {
1635 usec_t fr, t;
1636
1637 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
1638 if (r < 0)
1639 return r;
1640 if (r == 0)
1641 continue;
1642
1643 if (first) {
1644 if (from)
1645 *from = fr;
1646 if (to)
1647 *to = t;
1648 first = false;
1649 } else {
1650 if (from)
1651 *from = MIN(fr, *from);
1652 if (to)
1653 *to = MIN(t, *to);
1654 }
1655 }
1656
1657 return first ? 0 : 1;
1658}
1659
1660_public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
1661 Iterator i;
1662 JournalFile *f;
1663 bool first = true;
1664 int r;
1665
1666 if (!j)
1667 return -EINVAL;
1668 if (!from && !to)
1669 return -EINVAL;
1670
1671 HASHMAP_FOREACH(f, j->files, i) {
1672 usec_t fr, t;
1673
1674 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
1675 if (r < 0)
1676 return r;
1677 if (r == 0)
1678 continue;
1679
1680 if (first) {
1681 if (from)
1682 *from = fr;
1683 if (to)
1684 *to = t;
1685 first = false;
1686 } else {
1687 if (from)
1688 *from = MIN(fr, *from);
1689 if (to)
1690 *to = MIN(t, *to);
1691 }
1692 }
1693
1694 return first ? 0 : 1;
1695}
1696
1697
19a2bd80
LP
1698/* _public_ int sd_journal_query_unique(sd_journal *j, const char *field) { */
1699/* if (!j) */
1700/* return -EINVAL; */
1701/* if (!field) */
1702/* return -EINVAL; */
1703
1704/* return -ENOTSUP; */
1705/* } */
1706
1707/* _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { */
1708/* if (!j) */
1709/* return -EINVAL; */
1710/* if (!data) */
1711/* return -EINVAL; */
1712/* if (!l) */
1713/* return -EINVAL; */
1714
1715/* return -ENOTSUP; */
1716/* } */
1717
1718/* _public_ void sd_journal_restart_unique(sd_journal *j) { */
1719/* if (!j) */
1720/* return; */
1721/* } */