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