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