]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/sd-journal.c
journal: check fields we search for more carefully
[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);
c4aff78b 243 b = le64toh(bo->entry.realtime);
ae2cc8ef
LP
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);
c4aff78b 252 b = le64toh(bo->entry.xor_hash);
ae2cc8ef
LP
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
a963990f
LP
938static int add_file(sd_journal *j, const char *prefix, const char *filename) {
939 char *path;
3fbf9cbb
LP
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
a963990f
LP
952 path = join(prefix, "/", filename, NULL);
953 if (!path)
3fbf9cbb
LP
954 return -ENOMEM;
955
a963990f
LP
956 if (hashmap_get(j->files, path)) {
957 free(path);
50f20cfd
LP
958 return 0;
959 }
960
961 if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
a963990f
LP
962 log_debug("Too many open journal files, not adding %s, ignoring.", path);
963 free(path);
50f20cfd
LP
964 return 0;
965 }
966
a963990f
LP
967 r = journal_file_open(path, O_RDONLY, 0, NULL, &f);
968 free(path);
3fbf9cbb
LP
969
970 if (r < 0) {
971 if (errno == ENOENT)
972 return 0;
973
974 return r;
975 }
976
72f59706 977 /* journal_file_dump(f); */
de190aef 978
3fbf9cbb
LP
979 r = hashmap_put(j->files, f->path, f);
980 if (r < 0) {
981 journal_file_close(f);
982 return r;
983 }
984
a963990f
LP
985 j->current_invalidate_counter ++;
986
50f20cfd
LP
987 log_debug("File %s got added.", f->path);
988
989 return 0;
990}
991
a963990f
LP
992static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
993 char *path;
50f20cfd
LP
994 JournalFile *f;
995
996 assert(j);
997 assert(prefix);
998 assert(filename);
999
a963990f
LP
1000 path = join(prefix, "/", filename, NULL);
1001 if (!path)
50f20cfd
LP
1002 return -ENOMEM;
1003
a963990f
LP
1004 f = hashmap_get(j->files, path);
1005 free(path);
50f20cfd
LP
1006 if (!f)
1007 return 0;
1008
1009 hashmap_remove(j->files, f->path);
1010 journal_file_close(f);
1011
a963990f
LP
1012 j->current_invalidate_counter ++;
1013
50f20cfd 1014 log_debug("File %s got removed.", f->path);
3fbf9cbb
LP
1015 return 0;
1016}
1017
a963990f
LP
1018static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1019 char *path;
3fbf9cbb
LP
1020 int r;
1021 DIR *d;
cf244689 1022 sd_id128_t id, mid;
a963990f 1023 Directory *m;
3fbf9cbb
LP
1024
1025 assert(j);
1026 assert(prefix);
a963990f 1027 assert(dirname);
3fbf9cbb 1028
cf244689 1029 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
a963990f 1030 (sd_id128_from_string(dirname, &id) < 0 ||
cf244689
LP
1031 sd_id128_get_machine(&mid) < 0 ||
1032 !sd_id128_equal(id, mid)))
1033 return 0;
1034
a963990f
LP
1035 path = join(prefix, "/", dirname, NULL);
1036 if (!path)
3fbf9cbb
LP
1037 return -ENOMEM;
1038
a963990f 1039 d = opendir(path);
3fbf9cbb 1040 if (!d) {
a963990f
LP
1041 log_debug("Failed to open %s: %m", path);
1042 free(path);
1043
3fbf9cbb
LP
1044 if (errno == ENOENT)
1045 return 0;
3fbf9cbb
LP
1046 return -errno;
1047 }
1048
a963990f
LP
1049 m = hashmap_get(j->directories_by_path, path);
1050 if (!m) {
1051 m = new0(Directory, 1);
1052 if (!m) {
1053 closedir(d);
1054 free(path);
1055 return -ENOMEM;
1056 }
1057
1058 m->is_root = false;
1059 m->path = path;
1060
1061 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1062 closedir(d);
1063 free(m->path);
1064 free(m);
1065 return -ENOMEM;
1066 }
1067
1068 j->current_invalidate_counter ++;
1069
1070 log_debug("Directory %s got added.", m->path);
1071
1072 } else if (m->is_root) {
1073 free (path);
1074 closedir(d);
1075 return 0;
1076 } else
1077 free(path);
1078
1079 if (m->wd <= 0 && j->inotify_fd >= 0) {
1080
1081 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1082 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1083 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|
1084 IN_DONT_FOLLOW|IN_ONLYDIR);
1085
1086 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1087 inotify_rm_watch(j->inotify_fd, m->wd);
1088 }
1089
1090 for (;;) {
1091 struct dirent buf, *de;
1092
1093 r = readdir_r(d, &buf, &de);
1094 if (r != 0 || !de)
1095 break;
1096
1097 if (dirent_is_file_with_suffix(de, ".journal")) {
1098 r = add_file(j, m->path, de->d_name);
1099 if (r < 0)
1100 log_debug("Failed to add file %s/%s: %s", m->path, de->d_name, strerror(-r));
1101 }
1102 }
1103
1104 closedir(d);
1105
1106 return 0;
1107}
1108
1109static int add_root_directory(sd_journal *j, const char *p) {
1110 DIR *d;
1111 Directory *m;
1112 int r;
1113
1114 assert(j);
1115 assert(p);
1116
1117 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1118 !path_startswith(p, "/run"))
1119 return -EINVAL;
1120
1121 d = opendir(p);
1122 if (!d)
1123 return -errno;
1124
1125 m = hashmap_get(j->directories_by_path, p);
1126 if (!m) {
1127 m = new0(Directory, 1);
1128 if (!m) {
1129 closedir(d);
1130 return -ENOMEM;
1131 }
1132
1133 m->is_root = true;
1134 m->path = strdup(p);
1135 if (!m->path) {
1136 closedir(d);
1137 free(m);
1138 return -ENOMEM;
1139 }
1140
1141 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1142 closedir(d);
1143 free(m->path);
1144 free(m);
1145 return -ENOMEM;
1146 }
1147
1148 j->current_invalidate_counter ++;
1149
1150 log_debug("Root directory %s got added.", m->path);
1151
1152 } else if (!m->is_root) {
1153 closedir(d);
1154 return 0;
50f20cfd
LP
1155 }
1156
a963990f
LP
1157 if (m->wd <= 0 && j->inotify_fd >= 0) {
1158
1159 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1160 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1161 IN_DONT_FOLLOW|IN_ONLYDIR);
1162
1163 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1164 inotify_rm_watch(j->inotify_fd, m->wd);
1165 }
50f20cfd 1166
3fbf9cbb
LP
1167 for (;;) {
1168 struct dirent buf, *de;
a963990f 1169 sd_id128_t id;
3fbf9cbb
LP
1170
1171 r = readdir_r(d, &buf, &de);
1172 if (r != 0 || !de)
1173 break;
1174
a963990f
LP
1175 if (dirent_is_file_with_suffix(de, ".journal")) {
1176 r = add_file(j, m->path, de->d_name);
1177 if (r < 0)
1178 log_debug("Failed to add file %s/%s: %s", m->path, de->d_name, strerror(-r));
3fbf9cbb 1179
a963990f
LP
1180 } else if ((de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) &&
1181 sd_id128_from_string(de->d_name, &id) >= 0) {
1182
1183 r = add_directory(j, m->path, de->d_name);
1184 if (r < 0)
1185 log_debug("Failed to add directory %s/%s: %s", m->path, de->d_name, strerror(-r));
1186 }
3fbf9cbb
LP
1187 }
1188
1189 closedir(d);
1190
a963990f
LP
1191 return 0;
1192}
1193
1194static int remove_directory(sd_journal *j, Directory *d) {
1195 assert(j);
1196
1197 if (d->wd > 0) {
1198 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1199
1200 if (j->inotify_fd >= 0)
1201 inotify_rm_watch(j->inotify_fd, d->wd);
1202 }
1203
1204 hashmap_remove(j->directories_by_path, d->path);
1205
1206 if (d->is_root)
1207 log_debug("Root directory %s got removed.", d->path);
1208 else
1209 log_debug("Directory %s got removed.", d->path);
1210
1211 free(d->path);
1212 free(d);
50f20cfd 1213
3fbf9cbb
LP
1214 return 0;
1215}
1216
a963990f
LP
1217static int add_search_paths(sd_journal *j) {
1218
1219 const char search_paths[] =
1220 "/run/log/journal\0"
1221 "/var/log/journal\0";
1222 const char *p;
50f20cfd
LP
1223
1224 assert(j);
50f20cfd 1225
a963990f
LP
1226 /* We ignore most errors here, since the idea is to only open
1227 * what's actually accessible, and ignore the rest. */
50f20cfd 1228
a963990f
LP
1229 NULSTR_FOREACH(p, search_paths)
1230 add_root_directory(j, p);
50f20cfd 1231
a963990f 1232 return 0;
50f20cfd
LP
1233}
1234
a963990f 1235static int allocate_inotify(sd_journal *j) {
50f20cfd 1236 assert(j);
50f20cfd 1237
a963990f
LP
1238 if (j->inotify_fd < 0) {
1239 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1240 if (j->inotify_fd < 0)
1241 return -errno;
1242 }
50f20cfd 1243
a963990f
LP
1244 if (!j->directories_by_wd) {
1245 j->directories_by_wd = hashmap_new(trivial_hash_func, trivial_compare_func);
1246 if (!j->directories_by_wd)
1247 return -ENOMEM;
50f20cfd 1248 }
a963990f
LP
1249
1250 return 0;
50f20cfd
LP
1251}
1252
a963990f
LP
1253static sd_journal *journal_new(int flags) {
1254 sd_journal *j;
50f20cfd 1255
a963990f
LP
1256 j = new0(sd_journal, 1);
1257 if (!j)
1258 return NULL;
50f20cfd 1259
a963990f
LP
1260 j->inotify_fd = -1;
1261 j->flags = flags;
50f20cfd 1262
a963990f
LP
1263 j->files = hashmap_new(string_hash_func, string_compare_func);
1264 if (!j->files) {
1265 free(j);
1266 return NULL;
1267 }
50f20cfd 1268
a963990f
LP
1269 j->directories_by_path = hashmap_new(string_hash_func, string_compare_func);
1270 if (!j->directories_by_path) {
1271 hashmap_free(j->files);
1272 free(j);
1273 return NULL;
50f20cfd 1274 }
a963990f
LP
1275
1276 return j;
50f20cfd
LP
1277}
1278
a5344d2c 1279_public_ int sd_journal_open(sd_journal **ret, int flags) {
87d2c1ff 1280 sd_journal *j;
3fbf9cbb 1281 int r;
87d2c1ff 1282
a5344d2c
LP
1283 if (!ret)
1284 return -EINVAL;
1285
1286 if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
1287 SD_JOURNAL_RUNTIME_ONLY|
1288 SD_JOURNAL_SYSTEM_ONLY))
1289 return -EINVAL;
87d2c1ff 1290
a963990f 1291 j = journal_new(flags);
87d2c1ff
LP
1292 if (!j)
1293 return -ENOMEM;
1294
a963990f
LP
1295 r = add_search_paths(j);
1296 if (r < 0)
50f20cfd 1297 goto fail;
50f20cfd 1298
a963990f
LP
1299 *ret = j;
1300 return 0;
cf244689 1301
a963990f
LP
1302fail:
1303 sd_journal_close(j);
87d2c1ff 1304
a963990f
LP
1305 return r;
1306}
50f20cfd 1307
a963990f
LP
1308_public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1309 sd_journal *j;
1310 int r;
87d2c1ff 1311
a963990f
LP
1312 if (!ret)
1313 return -EINVAL;
87d2c1ff 1314
a963990f
LP
1315 if (!path || !path_is_absolute(path))
1316 return -EINVAL;
87d2c1ff 1317
a963990f
LP
1318 if (flags != 0)
1319 return -EINVAL;
87d2c1ff 1320
a963990f
LP
1321 j = journal_new(flags);
1322 if (!j)
1323 return -ENOMEM;
3fbf9cbb 1324
a963990f
LP
1325 r = add_root_directory(j, path);
1326 if (r < 0)
1327 goto fail;
87d2c1ff
LP
1328
1329 *ret = j;
1330 return 0;
1331
1332fail:
1333 sd_journal_close(j);
1334
1335 return r;
a963990f 1336}
87d2c1ff 1337
a5344d2c 1338_public_ void sd_journal_close(sd_journal *j) {
a963990f
LP
1339 Directory *d;
1340 JournalFile *f;
1341
a5344d2c
LP
1342 if (!j)
1343 return;
87d2c1ff 1344
a963990f
LP
1345 while ((f = hashmap_steal_first(j->files)))
1346 journal_file_close(f);
50f20cfd 1347
a963990f 1348 hashmap_free(j->files);
260a2be4 1349
a963990f
LP
1350 while ((d = hashmap_first(j->directories_by_path)))
1351 remove_directory(j, d);
260a2be4 1352
a963990f
LP
1353 while ((d = hashmap_first(j->directories_by_wd)))
1354 remove_directory(j, d);
87d2c1ff 1355
a963990f
LP
1356 hashmap_free(j->directories_by_path);
1357 hashmap_free(j->directories_by_wd);
1cc101f1 1358
50f20cfd
LP
1359 if (j->inotify_fd >= 0)
1360 close_nointr_nofail(j->inotify_fd);
1361
a963990f
LP
1362 sd_journal_flush_matches(j);
1363
87d2c1ff
LP
1364 free(j);
1365}
3fbf9cbb 1366
a5344d2c 1367_public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
3fbf9cbb
LP
1368 Object *o;
1369 JournalFile *f;
1370 int r;
1371
a5344d2c
LP
1372 if (!j)
1373 return -EINVAL;
1374 if (!ret)
1375 return -EINVAL;
3fbf9cbb
LP
1376
1377 f = j->current_file;
1378 if (!f)
de190aef 1379 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1380
1381 if (f->current_offset <= 0)
de190aef 1382 return -EADDRNOTAVAIL;
3fbf9cbb 1383
de190aef 1384 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1385 if (r < 0)
1386 return r;
1387
1388 *ret = le64toh(o->entry.realtime);
de190aef 1389 return 0;
3fbf9cbb
LP
1390}
1391
a5344d2c 1392_public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
3fbf9cbb
LP
1393 Object *o;
1394 JournalFile *f;
1395 int r;
1396 sd_id128_t id;
1397
a5344d2c
LP
1398 if (!j)
1399 return -EINVAL;
3fbf9cbb
LP
1400
1401 f = j->current_file;
1402 if (!f)
de190aef 1403 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1404
1405 if (f->current_offset <= 0)
de190aef 1406 return -EADDRNOTAVAIL;
3fbf9cbb 1407
de190aef 1408 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1409 if (r < 0)
1410 return r;
1411
de190aef
LP
1412 if (ret_boot_id)
1413 *ret_boot_id = o->entry.boot_id;
1414 else {
1415 r = sd_id128_get_boot(&id);
1416 if (r < 0)
1417 return r;
3fbf9cbb 1418
de190aef 1419 if (!sd_id128_equal(id, o->entry.boot_id))
df50185b 1420 return -ESTALE;
de190aef 1421 }
3fbf9cbb 1422
14a65d65
LP
1423 if (ret)
1424 *ret = le64toh(o->entry.monotonic);
1425
de190aef 1426 return 0;
3fbf9cbb
LP
1427}
1428
362a3f81
LP
1429static bool field_is_valid(const char *field) {
1430 const char *p;
1431
1432 assert(field);
1433
1434 if (isempty(field))
1435 return false;
1436
1437 if (startswith(field, "__"))
1438 return false;
1439
1440 for (p = field; *p; p++) {
1441
1442 if (*p == '_')
1443 continue;
1444
1445 if (*p >= 'A' && *p <= 'Z')
1446 continue;
1447
1448 if (*p >= '0' && *p <= '9')
1449 continue;
1450
1451 return false;
1452 }
1453
1454 return true;
1455}
1456
a5344d2c 1457_public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
3fbf9cbb
LP
1458 JournalFile *f;
1459 uint64_t i, n;
1460 size_t field_length;
1461 int r;
1462 Object *o;
1463
a5344d2c
LP
1464 if (!j)
1465 return -EINVAL;
1466 if (!field)
1467 return -EINVAL;
1468 if (!data)
1469 return -EINVAL;
1470 if (!size)
1471 return -EINVAL;
3fbf9cbb 1472
362a3f81 1473 if (!field_is_valid(field))
3fbf9cbb
LP
1474 return -EINVAL;
1475
1476 f = j->current_file;
1477 if (!f)
de190aef 1478 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1479
1480 if (f->current_offset <= 0)
de190aef 1481 return -EADDRNOTAVAIL;
3fbf9cbb 1482
de190aef 1483 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1484 if (r < 0)
1485 return r;
1486
1487 field_length = strlen(field);
1488
1489 n = journal_file_entry_n_items(o);
1490 for (i = 0; i < n; i++) {
4fd052ae
FC
1491 uint64_t p, l;
1492 le64_t le_hash;
3fbf9cbb
LP
1493 size_t t;
1494
1495 p = le64toh(o->entry.items[i].object_offset);
807e17f0 1496 le_hash = o->entry.items[i].hash;
de190aef 1497 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
3fbf9cbb
LP
1498 if (r < 0)
1499 return r;
1500
de190aef 1501 if (le_hash != o->data.hash)
de7b95cd
LP
1502 return -EBADMSG;
1503
3fbf9cbb
LP
1504 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1505
807e17f0
LP
1506 if (o->object.flags & OBJECT_COMPRESSED) {
1507
1508#ifdef HAVE_XZ
1509 if (uncompress_startswith(o->data.payload, l,
1510 &f->compress_buffer, &f->compress_buffer_size,
1511 field, field_length, '=')) {
1512
1513 uint64_t rsize;
1514
1515 if (!uncompress_blob(o->data.payload, l,
1516 &f->compress_buffer, &f->compress_buffer_size, &rsize))
1517 return -EBADMSG;
1518
1519 *data = f->compress_buffer;
1520 *size = (size_t) rsize;
1521
1522 return 0;
1523 }
1524#else
1525 return -EPROTONOSUPPORT;
1526#endif
1527
1528 } else if (l >= field_length+1 &&
1529 memcmp(o->data.payload, field, field_length) == 0 &&
1530 o->data.payload[field_length] == '=') {
3fbf9cbb 1531
161e54f8 1532 t = (size_t) l;
3fbf9cbb 1533
161e54f8
LP
1534 if ((uint64_t) t != l)
1535 return -E2BIG;
3fbf9cbb 1536
161e54f8
LP
1537 *data = o->data.payload;
1538 *size = t;
3fbf9cbb 1539
de190aef 1540 return 0;
161e54f8 1541 }
3fbf9cbb 1542
de190aef 1543 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
161e54f8
LP
1544 if (r < 0)
1545 return r;
3fbf9cbb
LP
1546 }
1547
de190aef 1548 return -ENOENT;
3fbf9cbb
LP
1549}
1550
a5344d2c 1551_public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
3fbf9cbb 1552 JournalFile *f;
4fd052ae
FC
1553 uint64_t p, l, n;
1554 le64_t le_hash;
3fbf9cbb
LP
1555 int r;
1556 Object *o;
de190aef 1557 size_t t;
3fbf9cbb 1558
a5344d2c
LP
1559 if (!j)
1560 return -EINVAL;
1561 if (!data)
1562 return -EINVAL;
1563 if (!size)
1564 return -EINVAL;
3fbf9cbb
LP
1565
1566 f = j->current_file;
1567 if (!f)
de190aef 1568 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1569
1570 if (f->current_offset <= 0)
de190aef 1571 return -EADDRNOTAVAIL;
3fbf9cbb 1572
de190aef 1573 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1574 if (r < 0)
1575 return r;
1576
1577 n = journal_file_entry_n_items(o);
7210bfb3 1578 if (j->current_field >= n)
3fbf9cbb
LP
1579 return 0;
1580
7210bfb3 1581 p = le64toh(o->entry.items[j->current_field].object_offset);
de190aef
LP
1582 le_hash = o->entry.items[j->current_field].hash;
1583 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
3fbf9cbb
LP
1584 if (r < 0)
1585 return r;
1586
de190aef 1587 if (le_hash != o->data.hash)
de7b95cd
LP
1588 return -EBADMSG;
1589
3fbf9cbb
LP
1590 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1591 t = (size_t) l;
1592
1593 /* We can't read objects larger than 4G on a 32bit machine */
1594 if ((uint64_t) t != l)
1595 return -E2BIG;
1596
807e17f0
LP
1597 if (o->object.flags & OBJECT_COMPRESSED) {
1598#ifdef HAVE_XZ
1599 uint64_t rsize;
1600
1601 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize))
1602 return -EBADMSG;
1603
1604 *data = f->compress_buffer;
1605 *size = (size_t) rsize;
1606#else
1607 return -EPROTONOSUPPORT;
1608#endif
1609 } else {
1610 *data = o->data.payload;
1611 *size = t;
1612 }
3fbf9cbb 1613
7210bfb3 1614 j->current_field ++;
3fbf9cbb
LP
1615
1616 return 1;
1617}
c2373f84 1618
a5344d2c
LP
1619_public_ void sd_journal_restart_data(sd_journal *j) {
1620 if (!j)
1621 return;
8725d60a
LP
1622
1623 j->current_field = 0;
c2373f84 1624}
50f20cfd 1625
a5344d2c 1626_public_ int sd_journal_get_fd(sd_journal *j) {
a963990f
LP
1627 int r;
1628
a5344d2c
LP
1629 if (!j)
1630 return -EINVAL;
50f20cfd 1631
a963990f
LP
1632 if (j->inotify_fd >= 0)
1633 return j->inotify_fd;
1634
1635 r = allocate_inotify(j);
1636 if (r < 0)
1637 return r;
1638
1639 /* Iterate through all dirs again, to add them to the
1640 * inotify */
1641 r = add_search_paths(j);
1642 if (r < 0)
1643 return r;
1644
50f20cfd
LP
1645 return j->inotify_fd;
1646}
1647
1648static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
a963990f 1649 Directory *d;
50f20cfd
LP
1650 int r;
1651
1652 assert(j);
1653 assert(e);
1654
1655 /* Is this a subdirectory we watch? */
a963990f
LP
1656 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
1657 if (d) {
1658 sd_id128_t id;
50f20cfd
LP
1659
1660 if (!(e->mask & IN_ISDIR) && e->len > 0 && endswith(e->name, ".journal")) {
1661
1662 /* Event for a journal file */
1663
1664 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
a963990f 1665 r = add_file(j, d->path, e->name);
50f20cfd 1666 if (r < 0)
a963990f
LP
1667 log_debug("Failed to add file %s/%s: %s", d->path, e->name, strerror(-r));
1668
50f20cfd
LP
1669 } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
1670
a963990f 1671 r = remove_file(j, d->path, e->name);
50f20cfd 1672 if (r < 0)
a963990f 1673 log_debug("Failed to remove file %s/%s: %s", d->path, e->name, strerror(-r));
50f20cfd
LP
1674 }
1675
a963990f 1676 } else if (!d->is_root && e->len == 0) {
50f20cfd 1677
a963990f 1678 /* Event for a subdirectory */
50f20cfd 1679
a963990f
LP
1680 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
1681 r = remove_directory(j, d);
50f20cfd 1682 if (r < 0)
a963990f 1683 log_debug("Failed to remove directory %s: %s", d->path, strerror(-r));
50f20cfd
LP
1684 }
1685
50f20cfd 1686
a963990f 1687 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
50f20cfd 1688
a963990f 1689 /* Event for root directory */
50f20cfd 1690
a963990f
LP
1691 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1692 r = add_directory(j, d->path, e->name);
50f20cfd 1693 if (r < 0)
a963990f 1694 log_debug("Failed to add directory %s/%s: %s", d->path, e->name, strerror(-r));
50f20cfd
LP
1695 }
1696 }
1697
1698 return;
1699 }
1700
1701 if (e->mask & IN_IGNORED)
1702 return;
1703
1704 log_warning("Unknown inotify event.");
1705}
1706
a963990f
LP
1707static int determine_change(sd_journal *j) {
1708 bool b;
1709
1710 assert(j);
1711
1712 b = j->current_invalidate_counter != j->last_invalidate_counter;
1713 j->last_invalidate_counter = j->current_invalidate_counter;
1714
1715 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
1716}
1717
a5344d2c 1718_public_ int sd_journal_process(sd_journal *j) {
50f20cfd 1719 uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX];
a963990f 1720 bool got_something = false;
50f20cfd 1721
a5344d2c
LP
1722 if (!j)
1723 return -EINVAL;
50f20cfd
LP
1724
1725 for (;;) {
1726 struct inotify_event *e;
1727 ssize_t l;
1728
1729 l = read(j->inotify_fd, buffer, sizeof(buffer));
1730 if (l < 0) {
a963990f
LP
1731 if (errno == EAGAIN || errno == EINTR)
1732 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
50f20cfd
LP
1733
1734 return -errno;
1735 }
1736
a963990f
LP
1737 got_something = true;
1738
50f20cfd
LP
1739 e = (struct inotify_event*) buffer;
1740 while (l > 0) {
1741 size_t step;
1742
1743 process_inotify_event(j, e);
1744
1745 step = sizeof(struct inotify_event) + e->len;
1746 assert(step <= (size_t) l);
1747
1748 e = (struct inotify_event*) ((uint8_t*) e + step);
1749 l -= step;
1750 }
1751 }
a963990f
LP
1752
1753 return determine_change(j);
50f20cfd 1754}
6ad1d1c3 1755
e02d1cf7 1756_public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
a963990f 1757 int r;
e02d1cf7
LP
1758
1759 assert(j);
1760
a963990f
LP
1761 if (j->inotify_fd < 0) {
1762
1763 /* This is the first invocation, hence create the
1764 * inotify watch */
1765 r = sd_journal_get_fd(j);
1766 if (r < 0)
1767 return r;
1768
1769 /* The journal might have changed since the context
1770 * object was created and we weren't watching before,
1771 * hence don't wait for anything, and return
1772 * immediately. */
1773 return determine_change(j);
1774 }
1775
1776 do {
1777 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
1778 } while (r == -EINTR);
e02d1cf7
LP
1779
1780 if (r < 0)
1781 return r;
1782
a963990f 1783 return sd_journal_process(j);
e02d1cf7
LP
1784}
1785
08984293
LP
1786_public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
1787 Iterator i;
1788 JournalFile *f;
1789 bool first = true;
1790 int r;
1791
1792 if (!j)
1793 return -EINVAL;
1794 if (!from && !to)
1795 return -EINVAL;
1796
1797 HASHMAP_FOREACH(f, j->files, i) {
1798 usec_t fr, t;
1799
1800 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
1801 if (r < 0)
1802 return r;
1803 if (r == 0)
1804 continue;
1805
1806 if (first) {
1807 if (from)
1808 *from = fr;
1809 if (to)
1810 *to = t;
1811 first = false;
1812 } else {
1813 if (from)
1814 *from = MIN(fr, *from);
1815 if (to)
1816 *to = MIN(t, *to);
1817 }
1818 }
1819
1820 return first ? 0 : 1;
1821}
1822
1823_public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
1824 Iterator i;
1825 JournalFile *f;
1826 bool first = true;
1827 int r;
1828
1829 if (!j)
1830 return -EINVAL;
1831 if (!from && !to)
1832 return -EINVAL;
1833
1834 HASHMAP_FOREACH(f, j->files, i) {
1835 usec_t fr, t;
1836
1837 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
1838 if (r < 0)
1839 return r;
1840 if (r == 0)
1841 continue;
1842
1843 if (first) {
1844 if (from)
1845 *from = fr;
1846 if (to)
1847 *to = t;
1848 first = false;
1849 } else {
1850 if (from)
1851 *from = MIN(fr, *from);
1852 if (to)
1853 *to = MIN(t, *to);
1854 }
1855 }
1856
1857 return first ? 0 : 1;
1858}
1859
1860
19a2bd80
LP
1861/* _public_ int sd_journal_query_unique(sd_journal *j, const char *field) { */
1862/* if (!j) */
1863/* return -EINVAL; */
1864/* if (!field) */
1865/* return -EINVAL; */
1866
1867/* return -ENOTSUP; */
1868/* } */
1869
1870/* _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { */
1871/* if (!j) */
1872/* return -EINVAL; */
1873/* if (!data) */
1874/* return -EINVAL; */
1875/* if (!l) */
1876/* return -EINVAL; */
1877
1878/* return -ENOTSUP; */
1879/* } */
1880
1881/* _public_ void sd_journal_restart_unique(sd_journal *j) { */
1882/* if (!j) */
1883/* return; */
1884/* } */