]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/sd-journal.c
Spelling fixes.
[thirdparty/systemd.git] / src / journal / sd-journal.c
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
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
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
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <stddef.h>
25 #include <unistd.h>
26 #include <sys/inotify.h>
27 #include <sys/poll.h>
28
29 #include "sd-journal.h"
30 #include "journal-def.h"
31 #include "journal-file.h"
32 #include "hashmap.h"
33 #include "list.h"
34 #include "path-util.h"
35 #include "lookup3.h"
36 #include "compress.h"
37 #include "journal-internal.h"
38
39 #define JOURNAL_FILES_MAX 1024
40
41 static void detach_location(sd_journal *j) {
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
54 static void reset_location(sd_journal *j) {
55 assert(j);
56
57 detach_location(j);
58 zero(j->current_location);
59 }
60
61 static 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);
71 l->boot_id = o->entry.boot_id;
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
77 static 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
90 static int match_is_valid(const void *data, size_t size) {
91 const char *b, *p;
92
93 assert(data);
94
95 if (size < 2)
96 return false;
97
98 if (startswith(data, "__"))
99 return false;
100
101 b = data;
102 for (p = b; p < b + size; p++) {
103
104 if (*p == '=')
105 return p > b;
106
107 if (*p == '_')
108 continue;
109
110 if (*p >= 'A' && *p <= 'Z')
111 continue;
112
113 if (*p >= '0' && *p <= '9')
114 continue;
115
116 return false;
117 }
118
119 return false;
120 }
121
122 static bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
123 const uint8_t *a = _a, *b = _b;
124 size_t j;
125
126 for (j = 0; j < s && j < t; j++) {
127
128 if (a[j] != b[j])
129 return false;
130
131 if (a[j] == '=')
132 return true;
133 }
134
135 return true;
136 }
137
138 static Match *match_new(Match *p, MatchType t) {
139 Match *m;
140
141 m = new0(Match, 1);
142 if (!m)
143 return NULL;
144
145 m->type = t;
146
147 if (p) {
148 m->parent = p;
149 LIST_PREPEND(Match, matches, p->matches, m);
150 }
151
152 return m;
153 }
154
155 static void match_free(Match *m) {
156 assert(m);
157
158 while (m->matches)
159 match_free(m->matches);
160
161 if (m->parent)
162 LIST_REMOVE(Match, matches, m->parent->matches, m);
163
164 free(m->data);
165 free(m);
166 }
167
168 static void match_free_if_empty(Match *m) {
169 assert(m);
170
171 if (m->matches)
172 return;
173
174 match_free(m);
175 }
176
177 _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
178 Match *l2, *l3, *add_here = NULL, *m;
179 le64_t le_hash;
180
181 if (!j)
182 return -EINVAL;
183
184 if (!data)
185 return -EINVAL;
186
187 if (size == 0)
188 size = strlen(data);
189
190 if (!match_is_valid(data, size))
191 return -EINVAL;
192
193 /* level 0: OR term
194 * level 1: AND terms
195 * level 2: OR terms
196 * level 3: concrete matches */
197
198 if (!j->level0) {
199 j->level0 = match_new(NULL, MATCH_OR_TERM);
200 if (!j->level0)
201 return -ENOMEM;
202 }
203
204 if (!j->level1) {
205 j->level1 = match_new(j->level0, MATCH_AND_TERM);
206 if (!j->level1)
207 return -ENOMEM;
208 }
209
210 assert(j->level0->type == MATCH_OR_TERM);
211 assert(j->level1->type == MATCH_AND_TERM);
212
213 le_hash = htole64(hash64(data, size));
214
215 LIST_FOREACH(matches, l2, j->level1->matches) {
216 assert(l2->type == MATCH_OR_TERM);
217
218 LIST_FOREACH(matches, l3, l2->matches) {
219 assert(l3->type == MATCH_DISCRETE);
220
221 /* Exactly the same match already? Then ignore
222 * this addition */
223 if (l3->le_hash == le_hash &&
224 l3->size == size &&
225 memcmp(l3->data, data, size) == 0)
226 return 0;
227
228 /* Same field? Then let's add this to this OR term */
229 if (same_field(data, size, l3->data, l3->size)) {
230 add_here = l2;
231 break;
232 }
233 }
234
235 if (add_here)
236 break;
237 }
238
239 if (!add_here) {
240 add_here = match_new(j->level1, MATCH_OR_TERM);
241 if (!add_here)
242 goto fail;
243 }
244
245 m = match_new(add_here, MATCH_DISCRETE);
246 if (!m)
247 goto fail;
248
249 m->le_hash = le_hash;
250 m->size = size;
251 m->data = memdup(data, size);
252 if (!m->data)
253 goto fail;
254
255 detach_location(j);
256
257 return 0;
258
259 fail:
260 if (add_here)
261 match_free_if_empty(add_here);
262
263 if (j->level1)
264 match_free_if_empty(j->level1);
265
266 if (j->level0)
267 match_free_if_empty(j->level0);
268
269 return -ENOMEM;
270 }
271
272 _public_ int sd_journal_add_disjunction(sd_journal *j) {
273 Match *m;
274
275 assert(j);
276
277 if (!j->level0)
278 return 0;
279
280 if (!j->level1)
281 return 0;
282
283 if (!j->level1->matches)
284 return 0;
285
286 m = match_new(j->level0, MATCH_AND_TERM);
287 if (!m)
288 return -ENOMEM;
289
290 j->level1 = m;
291 return 0;
292 }
293
294 static char *match_make_string(Match *m) {
295 char *p, *r;
296 Match *i;
297 bool enclose = false;
298
299 if (!m)
300 return strdup("");
301
302 if (m->type == MATCH_DISCRETE)
303 return strndup(m->data, m->size);
304
305 p = NULL;
306 LIST_FOREACH(matches, i, m->matches) {
307 char *t, *k;
308
309 t = match_make_string(i);
310 if (!t) {
311 free(p);
312 return NULL;
313 }
314
315 if (p) {
316 k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t, NULL);
317 free(p);
318 free(t);
319
320 if (!k)
321 return NULL;
322
323 p = k;
324
325 enclose = true;
326 } else {
327 free(p);
328 p = t;
329 }
330 }
331
332 if (enclose) {
333 r = strjoin("(", p, ")", NULL);
334 free(p);
335 return r;
336 }
337
338 return p;
339 }
340
341 char *journal_make_match_string(sd_journal *j) {
342 assert(j);
343
344 return match_make_string(j->level0);
345 }
346
347 _public_ void sd_journal_flush_matches(sd_journal *j) {
348
349 if (!j)
350 return;
351
352 if (j->level0)
353 match_free(j->level0);
354
355 j->level0 = j->level1 = NULL;
356
357 detach_location(j);
358 }
359
360 static int compare_order(JournalFile *af, Object *ao,
361 JournalFile *bf, Object *bo) {
362
363 uint64_t a, b;
364
365 assert(af);
366 assert(ao);
367 assert(bf);
368 assert(bo);
369
370 /* We operate on two different files here, hence we can access
371 * two objects at the same time, which we normally can't.
372 *
373 * If contents and timestamps match, these entries are
374 * identical, even if the seqnum does not match */
375
376 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
377 ao->entry.monotonic == bo->entry.monotonic &&
378 ao->entry.realtime == bo->entry.realtime &&
379 ao->entry.xor_hash == bo->entry.xor_hash)
380 return 0;
381
382 if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
383
384 /* If this is from the same seqnum source, compare
385 * seqnums */
386 a = le64toh(ao->entry.seqnum);
387 b = le64toh(bo->entry.seqnum);
388
389 if (a < b)
390 return -1;
391 if (a > b)
392 return 1;
393
394 /* Wow! This is weird, different data but the same
395 * seqnums? Something is borked, but let's make the
396 * best of it and compare by time. */
397 }
398
399 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
400
401 /* If the boot id matches compare monotonic time */
402 a = le64toh(ao->entry.monotonic);
403 b = le64toh(bo->entry.monotonic);
404
405 if (a < b)
406 return -1;
407 if (a > b)
408 return 1;
409 }
410
411 /* Otherwise compare UTC time */
412 a = le64toh(ao->entry.realtime);
413 b = le64toh(bo->entry.realtime);
414
415 if (a < b)
416 return -1;
417 if (a > b)
418 return 1;
419
420 /* Finally, compare by contents */
421 a = le64toh(ao->entry.xor_hash);
422 b = le64toh(bo->entry.xor_hash);
423
424 if (a < b)
425 return -1;
426 if (a > b)
427 return 1;
428
429 return 0;
430 }
431
432 static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
433 uint64_t a;
434
435 assert(af);
436 assert(ao);
437 assert(l);
438 assert(l->type == LOCATION_DISCRETE);
439
440 if (l->monotonic_set &&
441 sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
442 l->realtime_set &&
443 le64toh(ao->entry.realtime) == l->realtime &&
444 l->xor_hash_set &&
445 le64toh(ao->entry.xor_hash) == l->xor_hash)
446 return 0;
447
448 if (l->seqnum_set &&
449 sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
450
451 a = le64toh(ao->entry.seqnum);
452
453 if (a < l->seqnum)
454 return -1;
455 if (a > l->seqnum)
456 return 1;
457 }
458
459 if (l->monotonic_set &&
460 sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
461
462 a = le64toh(ao->entry.monotonic);
463
464 if (a < l->monotonic)
465 return -1;
466 if (a > l->monotonic)
467 return 1;
468 }
469
470 if (l->realtime_set) {
471
472 a = le64toh(ao->entry.realtime);
473
474 if (a < l->realtime)
475 return -1;
476 if (a > l->realtime)
477 return 1;
478 }
479
480 if (l->xor_hash_set) {
481 a = le64toh(ao->entry.xor_hash);
482
483 if (a < l->xor_hash)
484 return -1;
485 if (a > l->xor_hash)
486 return 1;
487 }
488
489 return 0;
490 }
491
492 static int next_for_match(
493 sd_journal *j,
494 Match *m,
495 JournalFile *f,
496 uint64_t after_offset,
497 direction_t direction,
498 Object **ret,
499 uint64_t *offset) {
500
501 int r;
502 uint64_t np = 0;
503 Object *n;
504
505 assert(j);
506 assert(m);
507 assert(f);
508
509 if (m->type == MATCH_DISCRETE) {
510 uint64_t dp;
511
512 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
513 if (r <= 0)
514 return r;
515
516 return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
517
518 } else if (m->type == MATCH_OR_TERM) {
519 Match *i;
520
521 /* Find the earliest match beyond after_offset */
522
523 LIST_FOREACH(matches, i, m->matches) {
524 uint64_t cp;
525
526 r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
527 if (r < 0)
528 return r;
529 else if (r > 0) {
530 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
531 np = cp;
532 }
533 }
534
535 } else if (m->type == MATCH_AND_TERM) {
536 Match *i;
537 bool continue_looking;
538
539 /* Always jump to the next matching entry and repeat
540 * this until we fine and offset that matches for all
541 * matches. */
542
543 if (!m->matches)
544 return 0;
545
546 np = 0;
547 do {
548 continue_looking = false;
549
550 LIST_FOREACH(matches, i, m->matches) {
551 uint64_t cp, limit;
552
553 if (np == 0)
554 limit = after_offset;
555 else if (direction == DIRECTION_DOWN)
556 limit = MAX(np, after_offset);
557 else
558 limit = MIN(np, after_offset);
559
560 r = next_for_match(j, i, f, limit, direction, NULL, &cp);
561 if (r <= 0)
562 return r;
563
564 if ((direction == DIRECTION_DOWN ? cp >= after_offset : cp <= after_offset) &&
565 (np == 0 || (direction == DIRECTION_DOWN ? cp > np : np < cp))) {
566 np = cp;
567 continue_looking = true;
568 }
569 }
570
571 } while (continue_looking);
572 }
573
574 if (np == 0)
575 return 0;
576
577 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
578 if (r < 0)
579 return r;
580
581 if (ret)
582 *ret = n;
583 if (offset)
584 *offset = np;
585
586 return 1;
587 }
588
589 static int find_location_for_match(
590 sd_journal *j,
591 Match *m,
592 JournalFile *f,
593 direction_t direction,
594 Object **ret,
595 uint64_t *offset) {
596
597 int r;
598
599 assert(j);
600 assert(m);
601 assert(f);
602
603 if (m->type == MATCH_DISCRETE) {
604 uint64_t dp;
605
606 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
607 if (r <= 0)
608 return r;
609
610 /* FIXME: missing: find by monotonic */
611
612 if (j->current_location.type == LOCATION_HEAD)
613 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset);
614 if (j->current_location.type == LOCATION_TAIL)
615 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, ret, offset);
616 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
617 return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset);
618 if (j->current_location.monotonic_set) {
619 r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
620 if (r != -ENOENT)
621 return r;
622 }
623 if (j->current_location.realtime_set)
624 return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
625
626 return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset);
627
628 } else if (m->type == MATCH_OR_TERM) {
629 uint64_t np = 0;
630 Object *n;
631 Match *i;
632
633 /* Find the earliest match */
634
635 LIST_FOREACH(matches, i, m->matches) {
636 uint64_t cp;
637
638 r = find_location_for_match(j, i, f, direction, NULL, &cp);
639 if (r < 0)
640 return r;
641 else if (r > 0) {
642 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
643 np = cp;
644 }
645 }
646
647 if (np == 0)
648 return 0;
649
650 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
651 if (r < 0)
652 return r;
653
654 if (ret)
655 *ret = n;
656 if (offset)
657 *offset = np;
658
659 return 1;
660
661 } else {
662 Match *i;
663 uint64_t np = 0;
664
665 assert(m->type == MATCH_AND_TERM);
666
667 /* First jump to the last match, and then find the
668 * next one where all matches match */
669
670 if (!m->matches)
671 return 0;
672
673 LIST_FOREACH(matches, i, m->matches) {
674 uint64_t cp;
675
676 r = find_location_for_match(j, i, f, direction, NULL, &cp);
677 if (r <= 0)
678 return r;
679
680 if (np == 0 || (direction == DIRECTION_DOWN ? np < cp : np > cp))
681 np = cp;
682 }
683
684 return next_for_match(j, m, f, np, direction, ret, offset);
685 }
686 }
687
688 static int find_location_with_matches(
689 sd_journal *j,
690 JournalFile *f,
691 direction_t direction,
692 Object **ret,
693 uint64_t *offset) {
694
695 int r;
696
697 assert(j);
698 assert(f);
699 assert(ret);
700 assert(offset);
701
702 if (!j->level0) {
703 /* No matches is simple */
704
705 if (j->current_location.type == LOCATION_HEAD)
706 return journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, ret, offset);
707 if (j->current_location.type == LOCATION_TAIL)
708 return journal_file_next_entry(f, NULL, 0, DIRECTION_UP, ret, offset);
709 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
710 return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
711 if (j->current_location.monotonic_set) {
712 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
713 if (r != -ENOENT)
714 return r;
715 }
716 if (j->current_location.realtime_set)
717 return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
718
719 return journal_file_next_entry(f, NULL, 0, direction, ret, offset);
720 } else
721 return find_location_for_match(j, j->level0, f, direction, ret, offset);
722 }
723
724 static int next_with_matches(
725 sd_journal *j,
726 JournalFile *f,
727 direction_t direction,
728 Object **ret,
729 uint64_t *offset) {
730
731 Object *c;
732 uint64_t cp;
733
734 assert(j);
735 assert(f);
736 assert(ret);
737 assert(offset);
738
739 c = *ret;
740 cp = *offset;
741
742 /* No matches is easy. We simple advance the file
743 * pointer by one. */
744 if (!j->level0)
745 return journal_file_next_entry(f, c, cp, direction, ret, offset);
746
747 /* If we have a match then we look for the next matching entry
748 * with an offset at least one step larger */
749 return next_for_match(j, j->level0, f, direction == DIRECTION_DOWN ? cp+1 : cp-1, direction, ret, offset);
750 }
751
752 static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
753 Object *c;
754 uint64_t cp;
755 int r;
756
757 assert(j);
758 assert(f);
759
760 if (f->current_offset > 0) {
761 cp = f->current_offset;
762
763 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
764 if (r < 0)
765 return r;
766
767 r = next_with_matches(j, f, direction, &c, &cp);
768 if (r <= 0)
769 return r;
770 } else {
771 r = find_location_with_matches(j, f, direction, &c, &cp);
772 if (r <= 0)
773 return r;
774 }
775
776 /* OK, we found the spot, now let's advance until to an entry
777 * that is actually different from what we were previously
778 * looking at. This is necessary to handle entries which exist
779 * in two (or more) journal files, and which shall all be
780 * suppressed but one. */
781
782 for (;;) {
783 bool found;
784
785 if (j->current_location.type == LOCATION_DISCRETE) {
786 int k;
787
788 k = compare_with_location(f, c, &j->current_location);
789 if (direction == DIRECTION_DOWN)
790 found = k > 0;
791 else
792 found = k < 0;
793 } else
794 found = true;
795
796 if (found) {
797 if (ret)
798 *ret = c;
799 if (offset)
800 *offset = cp;
801 return 1;
802 }
803
804 r = next_with_matches(j, f, direction, &c, &cp);
805 if (r <= 0)
806 return r;
807 }
808 }
809
810 static int real_journal_next(sd_journal *j, direction_t direction) {
811 JournalFile *f, *new_current = NULL;
812 Iterator i;
813 int r;
814 uint64_t new_offset = 0;
815 Object *new_entry = NULL;
816
817 if (!j)
818 return -EINVAL;
819
820 HASHMAP_FOREACH(f, j->files, i) {
821 Object *o;
822 uint64_t p;
823 bool found;
824
825 r = next_beyond_location(j, f, direction, &o, &p);
826 if (r < 0) {
827 log_debug("Can't iterate through %s, ignoring: %s", f->path, strerror(-r));
828 continue;
829 } else if (r == 0)
830 continue;
831
832 if (!new_current)
833 found = true;
834 else {
835 int k;
836
837 k = compare_order(f, o, new_current, new_entry);
838
839 if (direction == DIRECTION_DOWN)
840 found = k < 0;
841 else
842 found = k > 0;
843 }
844
845 if (found) {
846 new_current = f;
847 new_entry = o;
848 new_offset = p;
849 }
850 }
851
852 if (!new_current)
853 return 0;
854
855 set_location(j, new_current, new_entry, new_offset);
856
857 return 1;
858 }
859
860 _public_ int sd_journal_next(sd_journal *j) {
861 return real_journal_next(j, DIRECTION_DOWN);
862 }
863
864 _public_ int sd_journal_previous(sd_journal *j) {
865 return real_journal_next(j, DIRECTION_UP);
866 }
867
868 static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
869 int c = 0, r;
870
871 if (!j)
872 return -EINVAL;
873
874 if (skip == 0) {
875 /* If this is not a discrete skip, then at least
876 * resolve the current location */
877 if (j->current_location.type != LOCATION_DISCRETE)
878 return real_journal_next(j, direction);
879
880 return 0;
881 }
882
883 do {
884 r = real_journal_next(j, direction);
885 if (r < 0)
886 return r;
887
888 if (r == 0)
889 return c;
890
891 skip--;
892 c++;
893 } while (skip > 0);
894
895 return c;
896 }
897
898 _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
899 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
900 }
901
902 _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
903 return real_journal_next_skip(j, DIRECTION_UP, skip);
904 }
905
906 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
907 Object *o;
908 int r;
909 char bid[33], sid[33];
910
911 if (!j)
912 return -EINVAL;
913 if (!cursor)
914 return -EINVAL;
915
916 if (!j->current_file || j->current_file->current_offset <= 0)
917 return -EADDRNOTAVAIL;
918
919 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
920 if (r < 0)
921 return r;
922
923 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
924 sd_id128_to_string(o->entry.boot_id, bid);
925
926 if (asprintf(cursor,
927 "s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx;p=%s",
928 sid, (unsigned long long) le64toh(o->entry.seqnum),
929 bid, (unsigned long long) le64toh(o->entry.monotonic),
930 (unsigned long long) le64toh(o->entry.realtime),
931 (unsigned long long) le64toh(o->entry.xor_hash),
932 path_get_file_name(j->current_file->path)) < 0)
933 return -ENOMEM;
934
935 return 1;
936 }
937
938 _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
939 char *w;
940 size_t l;
941 char *state;
942 unsigned long long seqnum, monotonic, realtime, xor_hash;
943 bool
944 seqnum_id_set = false,
945 seqnum_set = false,
946 boot_id_set = false,
947 monotonic_set = false,
948 realtime_set = false,
949 xor_hash_set = false;
950 sd_id128_t seqnum_id, boot_id;
951
952 if (!j)
953 return -EINVAL;
954 if (!cursor)
955 return -EINVAL;
956
957 FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
958 char *item;
959 int k = 0;
960
961 if (l < 2 || w[1] != '=')
962 return -EINVAL;
963
964 item = strndup(w, l);
965 if (!item)
966 return -ENOMEM;
967
968 switch (w[0]) {
969
970 case 's':
971 seqnum_id_set = true;
972 k = sd_id128_from_string(w+2, &seqnum_id);
973 break;
974
975 case 'i':
976 seqnum_set = true;
977 if (sscanf(w+2, "%llx", &seqnum) != 1)
978 k = -EINVAL;
979 break;
980
981 case 'b':
982 boot_id_set = true;
983 k = sd_id128_from_string(w+2, &boot_id);
984 break;
985
986 case 'm':
987 monotonic_set = true;
988 if (sscanf(w+2, "%llx", &monotonic) != 1)
989 k = -EINVAL;
990 break;
991
992 case 't':
993 realtime_set = true;
994 if (sscanf(w+2, "%llx", &realtime) != 1)
995 k = -EINVAL;
996 break;
997
998 case 'x':
999 xor_hash_set = true;
1000 if (sscanf(w+2, "%llx", &xor_hash) != 1)
1001 k = -EINVAL;
1002 break;
1003 }
1004
1005 free(item);
1006
1007 if (k < 0)
1008 return k;
1009 }
1010
1011 if ((!seqnum_set || !seqnum_id_set) &&
1012 (!monotonic_set || !boot_id_set) &&
1013 !realtime_set)
1014 return -EINVAL;
1015
1016 reset_location(j);
1017
1018 j->current_location.type = LOCATION_DISCRETE;
1019
1020 if (realtime_set) {
1021 j->current_location.realtime = (uint64_t) realtime;
1022 j->current_location.realtime_set = true;
1023 }
1024
1025 if (seqnum_set && seqnum_id_set) {
1026 j->current_location.seqnum = (uint64_t) seqnum;
1027 j->current_location.seqnum_id = seqnum_id;
1028 j->current_location.seqnum_set = true;
1029 }
1030
1031 if (monotonic_set && boot_id_set) {
1032 j->current_location.monotonic = (uint64_t) monotonic;
1033 j->current_location.boot_id = boot_id;
1034 j->current_location.monotonic_set = true;
1035 }
1036
1037 if (xor_hash_set) {
1038 j->current_location.xor_hash = (uint64_t) xor_hash;
1039 j->current_location.xor_hash_set = true;
1040 }
1041
1042 return 0;
1043 }
1044
1045 _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1046 if (!j)
1047 return -EINVAL;
1048
1049 reset_location(j);
1050 j->current_location.type = LOCATION_DISCRETE;
1051 j->current_location.boot_id = boot_id;
1052 j->current_location.monotonic = usec;
1053 j->current_location.monotonic_set = true;
1054
1055 return 0;
1056 }
1057
1058 _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1059 if (!j)
1060 return -EINVAL;
1061
1062 reset_location(j);
1063 j->current_location.type = LOCATION_DISCRETE;
1064 j->current_location.realtime = usec;
1065 j->current_location.realtime_set = true;
1066
1067 return 0;
1068 }
1069
1070 _public_ int sd_journal_seek_head(sd_journal *j) {
1071 if (!j)
1072 return -EINVAL;
1073
1074 reset_location(j);
1075 j->current_location.type = LOCATION_HEAD;
1076
1077 return 0;
1078 }
1079
1080 _public_ int sd_journal_seek_tail(sd_journal *j) {
1081 if (!j)
1082 return -EINVAL;
1083
1084 reset_location(j);
1085 j->current_location.type = LOCATION_TAIL;
1086
1087 return 0;
1088 }
1089
1090 static int add_file(sd_journal *j, const char *prefix, const char *filename) {
1091 char *path;
1092 int r;
1093 JournalFile *f;
1094
1095 assert(j);
1096 assert(prefix);
1097 assert(filename);
1098
1099 if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) &&
1100 !(streq(filename, "system.journal") ||
1101 (startswith(filename, "system@") && endswith(filename, ".journal"))))
1102 return 0;
1103
1104 path = strjoin(prefix, "/", filename, NULL);
1105 if (!path)
1106 return -ENOMEM;
1107
1108 if (hashmap_get(j->files, path)) {
1109 free(path);
1110 return 0;
1111 }
1112
1113 if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
1114 log_debug("Too many open journal files, not adding %s, ignoring.", path);
1115 free(path);
1116 return 0;
1117 }
1118
1119 r = journal_file_open(path, O_RDONLY, 0, NULL, &f);
1120 free(path);
1121
1122 if (r < 0) {
1123 if (errno == ENOENT)
1124 return 0;
1125
1126 return r;
1127 }
1128
1129 /* journal_file_dump(f); */
1130
1131 r = hashmap_put(j->files, f->path, f);
1132 if (r < 0) {
1133 journal_file_close(f);
1134 return r;
1135 }
1136
1137 j->current_invalidate_counter ++;
1138
1139 log_debug("File %s got added.", f->path);
1140
1141 return 0;
1142 }
1143
1144 static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
1145 char *path;
1146 JournalFile *f;
1147
1148 assert(j);
1149 assert(prefix);
1150 assert(filename);
1151
1152 path = strjoin(prefix, "/", filename, NULL);
1153 if (!path)
1154 return -ENOMEM;
1155
1156 f = hashmap_get(j->files, path);
1157 free(path);
1158 if (!f)
1159 return 0;
1160
1161 hashmap_remove(j->files, f->path);
1162 journal_file_close(f);
1163
1164 j->current_invalidate_counter ++;
1165
1166 log_debug("File %s got removed.", f->path);
1167 return 0;
1168 }
1169
1170 static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1171 char *path;
1172 int r;
1173 DIR *d;
1174 sd_id128_t id, mid;
1175 Directory *m;
1176
1177 assert(j);
1178 assert(prefix);
1179 assert(dirname);
1180
1181 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1182 (sd_id128_from_string(dirname, &id) < 0 ||
1183 sd_id128_get_machine(&mid) < 0 ||
1184 !sd_id128_equal(id, mid)))
1185 return 0;
1186
1187 path = strjoin(prefix, "/", dirname, NULL);
1188 if (!path)
1189 return -ENOMEM;
1190
1191 d = opendir(path);
1192 if (!d) {
1193 log_debug("Failed to open %s: %m", path);
1194 free(path);
1195
1196 if (errno == ENOENT)
1197 return 0;
1198 return -errno;
1199 }
1200
1201 m = hashmap_get(j->directories_by_path, path);
1202 if (!m) {
1203 m = new0(Directory, 1);
1204 if (!m) {
1205 closedir(d);
1206 free(path);
1207 return -ENOMEM;
1208 }
1209
1210 m->is_root = false;
1211 m->path = path;
1212
1213 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1214 closedir(d);
1215 free(m->path);
1216 free(m);
1217 return -ENOMEM;
1218 }
1219
1220 j->current_invalidate_counter ++;
1221
1222 log_debug("Directory %s got added.", m->path);
1223
1224 } else if (m->is_root) {
1225 free (path);
1226 closedir(d);
1227 return 0;
1228 } else
1229 free(path);
1230
1231 if (m->wd <= 0 && j->inotify_fd >= 0) {
1232
1233 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1234 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1235 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|
1236 IN_DONT_FOLLOW|IN_ONLYDIR);
1237
1238 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1239 inotify_rm_watch(j->inotify_fd, m->wd);
1240 }
1241
1242 for (;;) {
1243 struct dirent buf, *de;
1244
1245 r = readdir_r(d, &buf, &de);
1246 if (r != 0 || !de)
1247 break;
1248
1249 if (dirent_is_file_with_suffix(de, ".journal")) {
1250 r = add_file(j, m->path, de->d_name);
1251 if (r < 0)
1252 log_debug("Failed to add file %s/%s: %s", m->path, de->d_name, strerror(-r));
1253 }
1254 }
1255
1256 closedir(d);
1257
1258 return 0;
1259 }
1260
1261 static int add_root_directory(sd_journal *j, const char *p) {
1262 DIR *d;
1263 Directory *m;
1264 int r;
1265
1266 assert(j);
1267 assert(p);
1268
1269 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1270 !path_startswith(p, "/run"))
1271 return -EINVAL;
1272
1273 d = opendir(p);
1274 if (!d)
1275 return -errno;
1276
1277 m = hashmap_get(j->directories_by_path, p);
1278 if (!m) {
1279 m = new0(Directory, 1);
1280 if (!m) {
1281 closedir(d);
1282 return -ENOMEM;
1283 }
1284
1285 m->is_root = true;
1286 m->path = strdup(p);
1287 if (!m->path) {
1288 closedir(d);
1289 free(m);
1290 return -ENOMEM;
1291 }
1292
1293 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
1294 closedir(d);
1295 free(m->path);
1296 free(m);
1297 return -ENOMEM;
1298 }
1299
1300 j->current_invalidate_counter ++;
1301
1302 log_debug("Root directory %s got added.", m->path);
1303
1304 } else if (!m->is_root) {
1305 closedir(d);
1306 return 0;
1307 }
1308
1309 if (m->wd <= 0 && j->inotify_fd >= 0) {
1310
1311 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1312 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1313 IN_DONT_FOLLOW|IN_ONLYDIR);
1314
1315 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1316 inotify_rm_watch(j->inotify_fd, m->wd);
1317 }
1318
1319 for (;;) {
1320 struct dirent buf, *de;
1321 sd_id128_t id;
1322
1323 r = readdir_r(d, &buf, &de);
1324 if (r != 0 || !de)
1325 break;
1326
1327 if (dirent_is_file_with_suffix(de, ".journal")) {
1328 r = add_file(j, m->path, de->d_name);
1329 if (r < 0)
1330 log_debug("Failed to add file %s/%s: %s", m->path, de->d_name, strerror(-r));
1331
1332 } else if ((de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) &&
1333 sd_id128_from_string(de->d_name, &id) >= 0) {
1334
1335 r = add_directory(j, m->path, de->d_name);
1336 if (r < 0)
1337 log_debug("Failed to add directory %s/%s: %s", m->path, de->d_name, strerror(-r));
1338 }
1339 }
1340
1341 closedir(d);
1342
1343 return 0;
1344 }
1345
1346 static int remove_directory(sd_journal *j, Directory *d) {
1347 assert(j);
1348
1349 if (d->wd > 0) {
1350 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1351
1352 if (j->inotify_fd >= 0)
1353 inotify_rm_watch(j->inotify_fd, d->wd);
1354 }
1355
1356 hashmap_remove(j->directories_by_path, d->path);
1357
1358 if (d->is_root)
1359 log_debug("Root directory %s got removed.", d->path);
1360 else
1361 log_debug("Directory %s got removed.", d->path);
1362
1363 free(d->path);
1364 free(d);
1365
1366 return 0;
1367 }
1368
1369 static int add_search_paths(sd_journal *j) {
1370
1371 const char search_paths[] =
1372 "/run/log/journal\0"
1373 "/var/log/journal\0";
1374 const char *p;
1375
1376 assert(j);
1377
1378 /* We ignore most errors here, since the idea is to only open
1379 * what's actually accessible, and ignore the rest. */
1380
1381 NULSTR_FOREACH(p, search_paths)
1382 add_root_directory(j, p);
1383
1384 return 0;
1385 }
1386
1387 static int allocate_inotify(sd_journal *j) {
1388 assert(j);
1389
1390 if (j->inotify_fd < 0) {
1391 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1392 if (j->inotify_fd < 0)
1393 return -errno;
1394 }
1395
1396 if (!j->directories_by_wd) {
1397 j->directories_by_wd = hashmap_new(trivial_hash_func, trivial_compare_func);
1398 if (!j->directories_by_wd)
1399 return -ENOMEM;
1400 }
1401
1402 return 0;
1403 }
1404
1405 static sd_journal *journal_new(int flags) {
1406 sd_journal *j;
1407
1408 j = new0(sd_journal, 1);
1409 if (!j)
1410 return NULL;
1411
1412 j->inotify_fd = -1;
1413 j->flags = flags;
1414
1415 j->files = hashmap_new(string_hash_func, string_compare_func);
1416 if (!j->files) {
1417 free(j);
1418 return NULL;
1419 }
1420
1421 j->directories_by_path = hashmap_new(string_hash_func, string_compare_func);
1422 if (!j->directories_by_path) {
1423 hashmap_free(j->files);
1424 free(j);
1425 return NULL;
1426 }
1427
1428 return j;
1429 }
1430
1431 _public_ int sd_journal_open(sd_journal **ret, int flags) {
1432 sd_journal *j;
1433 int r;
1434
1435 if (!ret)
1436 return -EINVAL;
1437
1438 if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
1439 SD_JOURNAL_RUNTIME_ONLY|
1440 SD_JOURNAL_SYSTEM_ONLY))
1441 return -EINVAL;
1442
1443 j = journal_new(flags);
1444 if (!j)
1445 return -ENOMEM;
1446
1447 r = add_search_paths(j);
1448 if (r < 0)
1449 goto fail;
1450
1451 *ret = j;
1452 return 0;
1453
1454 fail:
1455 sd_journal_close(j);
1456
1457 return r;
1458 }
1459
1460 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1461 sd_journal *j;
1462 int r;
1463
1464 if (!ret)
1465 return -EINVAL;
1466
1467 if (!path || !path_is_absolute(path))
1468 return -EINVAL;
1469
1470 if (flags != 0)
1471 return -EINVAL;
1472
1473 j = journal_new(flags);
1474 if (!j)
1475 return -ENOMEM;
1476
1477 r = add_root_directory(j, path);
1478 if (r < 0)
1479 goto fail;
1480
1481 *ret = j;
1482 return 0;
1483
1484 fail:
1485 sd_journal_close(j);
1486
1487 return r;
1488 }
1489
1490 _public_ void sd_journal_close(sd_journal *j) {
1491 Directory *d;
1492 JournalFile *f;
1493
1494 if (!j)
1495 return;
1496
1497 while ((f = hashmap_steal_first(j->files)))
1498 journal_file_close(f);
1499
1500 hashmap_free(j->files);
1501
1502 while ((d = hashmap_first(j->directories_by_path)))
1503 remove_directory(j, d);
1504
1505 while ((d = hashmap_first(j->directories_by_wd)))
1506 remove_directory(j, d);
1507
1508 hashmap_free(j->directories_by_path);
1509 hashmap_free(j->directories_by_wd);
1510
1511 if (j->inotify_fd >= 0)
1512 close_nointr_nofail(j->inotify_fd);
1513
1514 sd_journal_flush_matches(j);
1515
1516 free(j);
1517 }
1518
1519 _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1520 Object *o;
1521 JournalFile *f;
1522 int r;
1523
1524 if (!j)
1525 return -EINVAL;
1526 if (!ret)
1527 return -EINVAL;
1528
1529 f = j->current_file;
1530 if (!f)
1531 return -EADDRNOTAVAIL;
1532
1533 if (f->current_offset <= 0)
1534 return -EADDRNOTAVAIL;
1535
1536 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1537 if (r < 0)
1538 return r;
1539
1540 *ret = le64toh(o->entry.realtime);
1541 return 0;
1542 }
1543
1544 _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1545 Object *o;
1546 JournalFile *f;
1547 int r;
1548 sd_id128_t id;
1549
1550 if (!j)
1551 return -EINVAL;
1552
1553 f = j->current_file;
1554 if (!f)
1555 return -EADDRNOTAVAIL;
1556
1557 if (f->current_offset <= 0)
1558 return -EADDRNOTAVAIL;
1559
1560 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1561 if (r < 0)
1562 return r;
1563
1564 if (ret_boot_id)
1565 *ret_boot_id = o->entry.boot_id;
1566 else {
1567 r = sd_id128_get_boot(&id);
1568 if (r < 0)
1569 return r;
1570
1571 if (!sd_id128_equal(id, o->entry.boot_id))
1572 return -ESTALE;
1573 }
1574
1575 if (ret)
1576 *ret = le64toh(o->entry.monotonic);
1577
1578 return 0;
1579 }
1580
1581 static bool field_is_valid(const char *field) {
1582 const char *p;
1583
1584 assert(field);
1585
1586 if (isempty(field))
1587 return false;
1588
1589 if (startswith(field, "__"))
1590 return false;
1591
1592 for (p = field; *p; p++) {
1593
1594 if (*p == '_')
1595 continue;
1596
1597 if (*p >= 'A' && *p <= 'Z')
1598 continue;
1599
1600 if (*p >= '0' && *p <= '9')
1601 continue;
1602
1603 return false;
1604 }
1605
1606 return true;
1607 }
1608
1609 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1610 JournalFile *f;
1611 uint64_t i, n;
1612 size_t field_length;
1613 int r;
1614 Object *o;
1615
1616 if (!j)
1617 return -EINVAL;
1618 if (!field)
1619 return -EINVAL;
1620 if (!data)
1621 return -EINVAL;
1622 if (!size)
1623 return -EINVAL;
1624
1625 if (!field_is_valid(field))
1626 return -EINVAL;
1627
1628 f = j->current_file;
1629 if (!f)
1630 return -EADDRNOTAVAIL;
1631
1632 if (f->current_offset <= 0)
1633 return -EADDRNOTAVAIL;
1634
1635 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1636 if (r < 0)
1637 return r;
1638
1639 field_length = strlen(field);
1640
1641 n = journal_file_entry_n_items(o);
1642 for (i = 0; i < n; i++) {
1643 uint64_t p, l;
1644 le64_t le_hash;
1645 size_t t;
1646
1647 p = le64toh(o->entry.items[i].object_offset);
1648 le_hash = o->entry.items[i].hash;
1649 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1650 if (r < 0)
1651 return r;
1652
1653 if (le_hash != o->data.hash)
1654 return -EBADMSG;
1655
1656 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1657
1658 if (o->object.flags & OBJECT_COMPRESSED) {
1659
1660 #ifdef HAVE_XZ
1661 if (uncompress_startswith(o->data.payload, l,
1662 &f->compress_buffer, &f->compress_buffer_size,
1663 field, field_length, '=')) {
1664
1665 uint64_t rsize;
1666
1667 if (!uncompress_blob(o->data.payload, l,
1668 &f->compress_buffer, &f->compress_buffer_size, &rsize))
1669 return -EBADMSG;
1670
1671 *data = f->compress_buffer;
1672 *size = (size_t) rsize;
1673
1674 return 0;
1675 }
1676 #else
1677 return -EPROTONOSUPPORT;
1678 #endif
1679
1680 } else if (l >= field_length+1 &&
1681 memcmp(o->data.payload, field, field_length) == 0 &&
1682 o->data.payload[field_length] == '=') {
1683
1684 t = (size_t) l;
1685
1686 if ((uint64_t) t != l)
1687 return -E2BIG;
1688
1689 *data = o->data.payload;
1690 *size = t;
1691
1692 return 0;
1693 }
1694
1695 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1696 if (r < 0)
1697 return r;
1698 }
1699
1700 return -ENOENT;
1701 }
1702
1703 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
1704 JournalFile *f;
1705 uint64_t p, l, n;
1706 le64_t le_hash;
1707 int r;
1708 Object *o;
1709 size_t t;
1710
1711 if (!j)
1712 return -EINVAL;
1713 if (!data)
1714 return -EINVAL;
1715 if (!size)
1716 return -EINVAL;
1717
1718 f = j->current_file;
1719 if (!f)
1720 return -EADDRNOTAVAIL;
1721
1722 if (f->current_offset <= 0)
1723 return -EADDRNOTAVAIL;
1724
1725 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1726 if (r < 0)
1727 return r;
1728
1729 n = journal_file_entry_n_items(o);
1730 if (j->current_field >= n)
1731 return 0;
1732
1733 p = le64toh(o->entry.items[j->current_field].object_offset);
1734 le_hash = o->entry.items[j->current_field].hash;
1735 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1736 if (r < 0)
1737 return r;
1738
1739 if (le_hash != o->data.hash)
1740 return -EBADMSG;
1741
1742 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1743 t = (size_t) l;
1744
1745 /* We can't read objects larger than 4G on a 32bit machine */
1746 if ((uint64_t) t != l)
1747 return -E2BIG;
1748
1749 if (o->object.flags & OBJECT_COMPRESSED) {
1750 #ifdef HAVE_XZ
1751 uint64_t rsize;
1752
1753 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize))
1754 return -EBADMSG;
1755
1756 *data = f->compress_buffer;
1757 *size = (size_t) rsize;
1758 #else
1759 return -EPROTONOSUPPORT;
1760 #endif
1761 } else {
1762 *data = o->data.payload;
1763 *size = t;
1764 }
1765
1766 j->current_field ++;
1767
1768 return 1;
1769 }
1770
1771 _public_ void sd_journal_restart_data(sd_journal *j) {
1772 if (!j)
1773 return;
1774
1775 j->current_field = 0;
1776 }
1777
1778 _public_ int sd_journal_get_fd(sd_journal *j) {
1779 int r;
1780
1781 if (!j)
1782 return -EINVAL;
1783
1784 if (j->inotify_fd >= 0)
1785 return j->inotify_fd;
1786
1787 r = allocate_inotify(j);
1788 if (r < 0)
1789 return r;
1790
1791 /* Iterate through all dirs again, to add them to the
1792 * inotify */
1793 r = add_search_paths(j);
1794 if (r < 0)
1795 return r;
1796
1797 return j->inotify_fd;
1798 }
1799
1800 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
1801 Directory *d;
1802 int r;
1803
1804 assert(j);
1805 assert(e);
1806
1807 /* Is this a subdirectory we watch? */
1808 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
1809 if (d) {
1810 sd_id128_t id;
1811
1812 if (!(e->mask & IN_ISDIR) && e->len > 0 && endswith(e->name, ".journal")) {
1813
1814 /* Event for a journal file */
1815
1816 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1817 r = add_file(j, d->path, e->name);
1818 if (r < 0)
1819 log_debug("Failed to add file %s/%s: %s", d->path, e->name, strerror(-r));
1820
1821 } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
1822
1823 r = remove_file(j, d->path, e->name);
1824 if (r < 0)
1825 log_debug("Failed to remove file %s/%s: %s", d->path, e->name, strerror(-r));
1826 }
1827
1828 } else if (!d->is_root && e->len == 0) {
1829
1830 /* Event for a subdirectory */
1831
1832 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
1833 r = remove_directory(j, d);
1834 if (r < 0)
1835 log_debug("Failed to remove directory %s: %s", d->path, strerror(-r));
1836 }
1837
1838
1839 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
1840
1841 /* Event for root directory */
1842
1843 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1844 r = add_directory(j, d->path, e->name);
1845 if (r < 0)
1846 log_debug("Failed to add directory %s/%s: %s", d->path, e->name, strerror(-r));
1847 }
1848 }
1849
1850 return;
1851 }
1852
1853 if (e->mask & IN_IGNORED)
1854 return;
1855
1856 log_warning("Unknown inotify event.");
1857 }
1858
1859 static int determine_change(sd_journal *j) {
1860 bool b;
1861
1862 assert(j);
1863
1864 b = j->current_invalidate_counter != j->last_invalidate_counter;
1865 j->last_invalidate_counter = j->current_invalidate_counter;
1866
1867 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
1868 }
1869
1870 _public_ int sd_journal_process(sd_journal *j) {
1871 uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX] _alignas_(struct inotify_event);
1872 bool got_something = false;
1873
1874 if (!j)
1875 return -EINVAL;
1876
1877 for (;;) {
1878 struct inotify_event *e;
1879 ssize_t l;
1880
1881 l = read(j->inotify_fd, buffer, sizeof(buffer));
1882 if (l < 0) {
1883 if (errno == EAGAIN || errno == EINTR)
1884 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
1885
1886 return -errno;
1887 }
1888
1889 got_something = true;
1890
1891 e = (struct inotify_event*) buffer;
1892 while (l > 0) {
1893 size_t step;
1894
1895 process_inotify_event(j, e);
1896
1897 step = sizeof(struct inotify_event) + e->len;
1898 assert(step <= (size_t) l);
1899
1900 e = (struct inotify_event*) ((uint8_t*) e + step);
1901 l -= step;
1902 }
1903 }
1904
1905 return determine_change(j);
1906 }
1907
1908 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
1909 int r;
1910
1911 assert(j);
1912
1913 if (j->inotify_fd < 0) {
1914
1915 /* This is the first invocation, hence create the
1916 * inotify watch */
1917 r = sd_journal_get_fd(j);
1918 if (r < 0)
1919 return r;
1920
1921 /* The journal might have changed since the context
1922 * object was created and we weren't watching before,
1923 * hence don't wait for anything, and return
1924 * immediately. */
1925 return determine_change(j);
1926 }
1927
1928 do {
1929 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
1930 } while (r == -EINTR);
1931
1932 if (r < 0)
1933 return r;
1934
1935 return sd_journal_process(j);
1936 }
1937
1938 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
1939 Iterator i;
1940 JournalFile *f;
1941 bool first = true;
1942 int r;
1943
1944 if (!j)
1945 return -EINVAL;
1946 if (!from && !to)
1947 return -EINVAL;
1948
1949 HASHMAP_FOREACH(f, j->files, i) {
1950 usec_t fr, t;
1951
1952 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
1953 if (r < 0)
1954 return r;
1955 if (r == 0)
1956 continue;
1957
1958 if (first) {
1959 if (from)
1960 *from = fr;
1961 if (to)
1962 *to = t;
1963 first = false;
1964 } else {
1965 if (from)
1966 *from = MIN(fr, *from);
1967 if (to)
1968 *to = MIN(t, *to);
1969 }
1970 }
1971
1972 return first ? 0 : 1;
1973 }
1974
1975 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
1976 Iterator i;
1977 JournalFile *f;
1978 bool first = true;
1979 int r;
1980
1981 if (!j)
1982 return -EINVAL;
1983 if (!from && !to)
1984 return -EINVAL;
1985
1986 HASHMAP_FOREACH(f, j->files, i) {
1987 usec_t fr, t;
1988
1989 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
1990 if (r < 0)
1991 return r;
1992 if (r == 0)
1993 continue;
1994
1995 if (first) {
1996 if (from)
1997 *from = fr;
1998 if (to)
1999 *to = t;
2000 first = false;
2001 } else {
2002 if (from)
2003 *from = MIN(fr, *from);
2004 if (to)
2005 *to = MIN(t, *to);
2006 }
2007 }
2008
2009 return first ? 0 : 1;
2010 }
2011
2012
2013 /* _public_ int sd_journal_query_unique(sd_journal *j, const char *field) { */
2014 /* if (!j) */
2015 /* return -EINVAL; */
2016 /* if (!field) */
2017 /* return -EINVAL; */
2018
2019 /* return -ENOTSUP; */
2020 /* } */
2021
2022 /* _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { */
2023 /* if (!j) */
2024 /* return -EINVAL; */
2025 /* if (!data) */
2026 /* return -EINVAL; */
2027 /* if (!l) */
2028 /* return -EINVAL; */
2029
2030 /* return -ENOTSUP; */
2031 /* } */
2032
2033 /* _public_ void sd_journal_restart_unique(sd_journal *j) { */
2034 /* if (!j) */
2035 /* return; */
2036 /* } */