chiark / gitweb /
bcfcbfb9e1f6dd2ce1e7f8c86dd206969581e32f
[elogind.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 General Public License as published by
10   the Free Software Foundation; either version 2 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   General Public License for more details.
17
18   You should have received a copy of the GNU 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
26 #include "sd-journal.h"
27 #include "journal-def.h"
28 #include "journal-file.h"
29 #include "hashmap.h"
30 #include "list.h"
31 #include "lookup3.h"
32
33 #define JOURNAL_FILES_MAX 1024
34
35 typedef struct Match Match;
36
37 struct Match {
38         char *data;
39         size_t size;
40         uint64_t le_hash;
41
42         LIST_FIELDS(Match, matches);
43 };
44
45 typedef enum location_type {
46         LOCATION_HEAD,
47         LOCATION_TAIL,
48         LOCATION_DISCRETE
49 } location_type_t;
50
51 typedef struct Location {
52         location_type_t type;
53
54         uint64_t seqnum;
55         sd_id128_t seqnum_id;
56         bool seqnum_set;
57
58         uint64_t realtime;
59         bool realtime_set;
60
61         uint64_t monotonic;
62         sd_id128_t boot_id;
63         bool monotonic_set;
64
65         uint64_t xor_hash;
66         bool xor_hash_set;
67 } Location;
68
69 struct sd_journal {
70         Hashmap *files;
71
72         Location current_location;
73         JournalFile *current_file;
74         uint64_t current_field;
75
76         LIST_HEAD(Match, matches);
77         unsigned n_matches;
78 };
79
80 static void detach_location(sd_journal *j) {
81         Iterator i;
82         JournalFile *f;
83
84         assert(j);
85
86         j->current_file = NULL;
87         j->current_field = 0;
88
89         HASHMAP_FOREACH(f, j->files, i)
90                 f->current_offset = 0;
91 }
92
93 static void reset_location(sd_journal *j) {
94         assert(j);
95
96         detach_location(j);
97         zero(j->current_location);
98 }
99
100 static void init_location(Location *l, JournalFile *f, Object *o) {
101         assert(l);
102         assert(f);
103         assert(o->object.type == OBJECT_ENTRY);
104
105         l->type = LOCATION_DISCRETE;
106         l->seqnum = le64toh(o->entry.seqnum);
107         l->seqnum_id = f->header->seqnum_id;
108         l->realtime = le64toh(o->entry.realtime);
109         l->monotonic = le64toh(o->entry.monotonic);
110         l->boot_id = le64toh(o->entry.boot_id);
111         l->xor_hash = le64toh(o->entry.xor_hash);
112
113         l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
114 }
115
116 static void set_location(sd_journal *j, JournalFile *f, Object *o, uint64_t offset) {
117         assert(j);
118         assert(f);
119         assert(o);
120
121         init_location(&j->current_location, f, o);
122
123         j->current_file = f;
124         j->current_field = 0;
125
126         f->current_offset = offset;
127 }
128
129 static int same_field(const void *_a, size_t s, const void *_b, size_t t) {
130         const uint8_t *a = _a, *b = _b;
131         size_t j;
132         bool a_good = false, b_good = false, different = false;
133
134         for (j = 0; j < s && j < t; j++) {
135
136                 if (a[j] == '=')
137                         a_good = true;
138                 if (b[j] == '=')
139                         b_good = true;
140                 if (a[j] != b[j])
141                         different = true;
142
143                 if (a_good && b_good)
144                         return different ? 0 : 1;
145         }
146
147         return -EINVAL;
148 }
149
150 int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
151         Match *m, *after = NULL;
152         uint64_t le_hash;
153
154         assert(j);
155
156         if (size <= 0)
157                 return -EINVAL;
158
159         assert(data);
160
161         le_hash = htole64(hash64(data, size));
162
163         LIST_FOREACH(matches, m, j->matches) {
164                 int r;
165
166                 if (m->le_hash == le_hash &&
167                     m->size == size &&
168                     memcmp(m->data, data, size) == 0)
169                         return 0;
170
171                 r = same_field(data, size, m->data, m->size);
172                 if (r < 0)
173                         return r;
174                 else if (r > 0)
175                         after = m;
176         }
177
178         m = new0(Match, 1);
179         if (!m)
180                 return -ENOMEM;
181
182         m->size = size;
183
184         m->data = malloc(m->size);
185         if (!m->data) {
186                 free(m);
187                 return -ENOMEM;
188         }
189
190         memcpy(m->data, data, size);
191         m->le_hash = le_hash;
192
193         /* Matches for the same fields we order adjacent to each
194          * other */
195         LIST_INSERT_AFTER(Match, matches, j->matches, after, m);
196         j->n_matches ++;
197
198         detach_location(j);
199
200         return 0;
201 }
202
203 void sd_journal_flush_matches(sd_journal *j) {
204         assert(j);
205
206         while (j->matches) {
207                 Match *m = j->matches;
208
209                 LIST_REMOVE(Match, matches, j->matches, m);
210                 free(m->data);
211                 free(m);
212         }
213
214         j->n_matches = 0;
215
216         detach_location(j);
217 }
218
219 static int compare_order(JournalFile *af, Object *ao,
220                          JournalFile *bf, Object *bo) {
221
222         uint64_t a, b;
223
224         assert(af);
225         assert(ao);
226         assert(bf);
227         assert(bo);
228
229         /* We operate on two different files here, hence we can access
230          * two objects at the same time, which we normally can't.
231          *
232          * If contents and timestamps match, these entries are
233          * identical, even if the seqnum does not match */
234
235         if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
236             ao->entry.monotonic == bo->entry.monotonic &&
237             ao->entry.realtime == bo->entry.realtime &&
238             ao->entry.xor_hash == bo->entry.xor_hash)
239                 return 0;
240
241         if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
242
243                 /* If this is from the same seqnum source, compare
244                  * seqnums */
245                 a = le64toh(ao->entry.seqnum);
246                 b = le64toh(bo->entry.seqnum);
247
248                 if (a < b)
249                         return -1;
250                 if (a > b)
251                         return 1;
252
253                 /* Wow! This is weird, different data but the same
254                  * seqnums? Something is borked, but let's make the
255                  * best of it and compare by time. */
256         }
257
258         if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
259
260                 /* If the boot id matches compare monotonic time */
261                 a = le64toh(ao->entry.monotonic);
262                 b = le64toh(bo->entry.monotonic);
263
264                 if (a < b)
265                         return -1;
266                 if (a > b)
267                         return 1;
268         }
269
270         /* Otherwise compare UTC time */
271         a = le64toh(ao->entry.realtime);
272         b = le64toh(ao->entry.realtime);
273
274         if (a < b)
275                 return -1;
276         if (a > b)
277                 return 1;
278
279         /* Finally, compare by contents */
280         a = le64toh(ao->entry.xor_hash);
281         b = le64toh(ao->entry.xor_hash);
282
283         if (a < b)
284                 return -1;
285         if (a > b)
286                 return 1;
287
288         return 0;
289 }
290
291 static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
292         uint64_t a;
293
294         assert(af);
295         assert(ao);
296         assert(l);
297         assert(l->type == LOCATION_DISCRETE);
298
299         if (l->monotonic_set &&
300             sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
301             l->realtime_set &&
302             le64toh(ao->entry.realtime) == l->realtime &&
303             l->xor_hash_set &&
304             le64toh(ao->entry.xor_hash) == l->xor_hash)
305                 return 0;
306
307         if (l->seqnum_set &&
308             sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
309
310                 a = le64toh(ao->entry.seqnum);
311
312                 if (a < l->seqnum)
313                         return -1;
314                 if (a > l->seqnum)
315                         return 1;
316         }
317
318         if (l->monotonic_set &&
319             sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
320
321                 a = le64toh(ao->entry.monotonic);
322
323                 if (a < l->monotonic)
324                         return -1;
325                 if (a > l->monotonic)
326                         return 1;
327         }
328
329         if (l->realtime_set) {
330
331                 a = le64toh(ao->entry.realtime);
332
333                 if (a < l->realtime)
334                         return -1;
335                 if (a > l->realtime)
336                         return 1;
337         }
338
339         if (l->xor_hash_set) {
340                 a = le64toh(ao->entry.xor_hash);
341
342                 if (a < l->xor_hash)
343                         return -1;
344                 if (a > l->xor_hash)
345                         return 1;
346         }
347
348         return 0;
349 }
350
351 static int find_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
352         Object *o = NULL;
353         uint64_t p = 0;
354         int r;
355
356         assert(j);
357
358         if (!j->matches) {
359                 /* No matches is simple */
360
361                 if (j->current_location.type == LOCATION_HEAD)
362                         r = journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, &o, &p);
363                 else if (j->current_location.type == LOCATION_TAIL)
364                         r = journal_file_next_entry(f, NULL, 0, DIRECTION_UP, &o, &p);
365                 else if (j->current_location.seqnum_set &&
366                          sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
367                         r = journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, &o, &p);
368                 else if (j->current_location.monotonic_set)
369                         r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, &o, &p);
370                 else if (j->current_location.realtime_set)
371                         r = journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, &o, &p);
372                 else
373                         r = journal_file_next_entry(f, NULL, 0, direction, &o, &p);
374
375                 if (r <= 0)
376                         return r;
377
378         } else  {
379                 Match *m, *term_match = NULL;
380                 Object *to = NULL;
381                 uint64_t tp = 0;
382
383                 /* We have matches, first, let's jump to the monotonic
384                  * position if we have any, since it implies a
385                  * match. */
386
387                 if (j->current_location.type == LOCATION_DISCRETE &&
388                     j->current_location.monotonic_set) {
389
390                         r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, &o, &p);
391                         if (r <= 0)
392                                 return r;
393                 }
394
395                 LIST_FOREACH(matches, m, j->matches) {
396                         Object *c, *d;
397                         uint64_t cp, dp;
398
399                         r = journal_file_find_data_object_with_hash(f, m->data, m->size, m->le_hash, &d, &dp);
400                         if (r <= 0)
401                                 return r;
402
403                         if (j->current_location.type == LOCATION_HEAD)
404                                 r = journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, &c, &cp);
405                         else if (j->current_location.type == LOCATION_TAIL)
406                                 r = journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, &c, &cp);
407                         else if (j->current_location.seqnum_set &&
408                                  sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
409                                 r = journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, &c, &cp);
410                         else if (j->current_location.realtime_set)
411                                 r = journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, &c, &cp);
412                         else
413                                 r = journal_file_next_entry_for_data(f, NULL, 0, dp, direction, &c, &cp);
414
415                         if (!term_match) {
416                                 term_match = m;
417
418                                 if (r > 0) {
419                                         to = c;
420                                         tp = cp;
421                                 }
422                         } else if (same_field(term_match->data, term_match->size, m->data, m->size)) {
423
424                                 /* Same field as previous match... */
425                                 if (r > 0) {
426
427                                         /* Find the earliest of the OR matches */
428
429                                         if (!to ||
430                                             (direction == DIRECTION_DOWN && cp < tp) ||
431                                             (direction == DIRECTION_UP && cp > tp)) {
432                                                 to = c;
433                                                 tp = tp;
434                                         }
435
436                                 }
437
438                         } else {
439
440                                 /* Previous term is finished, did anything match? */
441                                 if (!to)
442                                         return 0;
443
444                                 /* Find the last of the AND matches */
445                                 if (!o ||
446                                     (direction == DIRECTION_DOWN && tp > p) ||
447                                     (direction == DIRECTION_UP && tp < p)) {
448                                         o = to;
449                                         p = tp;
450                                 }
451
452                                 term_match = m;
453
454                                 if (r > 0) {
455                                         to = c;
456                                         tp = cp;
457                                 } else {
458                                         to = NULL;
459                                         tp = 0;
460                                 }
461                         }
462                 }
463
464                 /* Last term is finished, did anything match? */
465                 if (!to)
466                         return 0;
467
468                 if (!o ||
469                     (direction == DIRECTION_DOWN && tp > p) ||
470                     (direction == DIRECTION_UP && tp < p)) {
471                         o = to;
472                         p = tp;
473                 }
474
475                 if (!o)
476                         return 0;
477         }
478
479         if (ret)
480                 *ret = o;
481
482         if (offset)
483                 *offset = p;
484
485         return 1;
486 }
487
488 static int next_with_matches(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
489         int r;
490         uint64_t cp;
491         Object *c;
492
493         assert(j);
494         assert(f);
495         assert(ret);
496         assert(offset);
497
498         c = *ret;
499         cp = *offset;
500
501         if (!j->matches) {
502                 /* No matches is easy */
503
504                 r = journal_file_next_entry(f, c, cp, direction, &c, &cp);
505                 if (r <= 0)
506                         return r;
507
508                 if (ret)
509                         *ret = c;
510                 if (offset)
511                         *offset = cp;
512                 return 1;
513         }
514
515         /* So there are matches we have to adhere to, let's find the
516          * first entry that matches all of them */
517
518         for (;;) {
519                 uint64_t np, n;
520                 bool found, term_result = false;
521                 Match *m, *term_match = NULL;
522
523                 n = journal_file_entry_n_items(c);
524
525                 /* Make sure we don't match the entry we are starting
526                  * from. */
527                 found = cp > *offset;
528
529                 np = 0;
530                 LIST_FOREACH(matches, m, j->matches) {
531                         uint64_t q, k;
532
533                         /* Let's check if this is the beginning of a
534                          * new term, i.e. has a different field prefix
535                          * as the preceeding match. */
536                         if (!term_match) {
537                                 term_match = m;
538                                 term_result = false;
539                         } else if (!same_field(term_match->data, term_match->size, m->data, m->size)) {
540                                 if (!term_result)
541                                         found = false;
542
543                                 term_match = m;
544                                 term_result = false;
545                         }
546
547                         for (k = 0; k < n; k++)
548                                 if (c->entry.items[k].hash == m->le_hash)
549                                         break;
550
551                         if (k >= n) {
552                                 /* Hmm, didn't find any field that
553                                  * matched this rule, so ignore this
554                                  * match. Go on with next match */
555                                 continue;
556                         }
557
558                         term_result = true;
559
560                         /* Hmm, so, this field matched, let's remember
561                          * where we'd have to try next, in case the other
562                          * matches are not OK */
563
564                         r = journal_file_next_entry_for_data(f, c, cp, le64toh(c->entry.items[k].object_offset), direction, NULL, &q);
565                         if (r > 0) {
566
567                                 if (direction == DIRECTION_DOWN) {
568                                         if (q > np)
569                                                 np = q;
570                                 } else {
571                                         if (np == 0 || q < np)
572                                                 np = q;
573                                 }
574                         }
575                 }
576
577                 /* Check the last term */
578                 if (term_match && term_result)
579                         found = true;
580
581                 /* Did this entry match against all matches? */
582                 if (found) {
583                         if (ret)
584                                 *ret = c;
585                         if (offset)
586                                 *offset = cp;
587                         return 1;
588                 }
589
590                 /* Did we find a subsequent entry? */
591                 if (np == 0)
592                         return 0;
593
594                 /* Hmm, ok, this entry only matched partially, so
595                  * let's try another one */
596                 cp = np;
597         }
598 }
599
600 static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
601         Object *c;
602         uint64_t cp;
603         int compare_value, r;
604
605         assert(j);
606         assert(f);
607
608         if (f->current_offset > 0) {
609                 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &c);
610                 if (r < 0)
611                         return r;
612
613                 cp = f->current_offset;
614
615                 r = next_with_matches(j, f, direction, &c, &cp);
616                 if (r <= 0)
617                         return r;
618
619                 compare_value = 1;
620         } else {
621                 r = find_location(j, f, direction, &c, &cp);
622                 if (r <= 0)
623                         return r;
624
625                 compare_value = 0;
626         }
627
628         for (;;) {
629                 bool found;
630
631                 if (j->current_location.type == LOCATION_DISCRETE) {
632                         int k;
633
634                         k = compare_with_location(f, c, &j->current_location);
635                         if (direction == DIRECTION_DOWN)
636                                 found = k >= compare_value;
637                         else
638                                 found = k <= -compare_value;
639                 } else
640                         found = true;
641
642                 if (found) {
643                         if (ret)
644                                 *ret = c;
645                         if (offset)
646                                 *offset = cp;
647                         return 1;
648                 }
649
650                 r = next_with_matches(j, f, direction, &c, &cp);
651                 if (r <= 0)
652                         return r;
653         }
654 }
655
656 static int real_journal_next(sd_journal *j, direction_t direction) {
657         JournalFile *f, *new_current = NULL;
658         Iterator i;
659         int r;
660         uint64_t new_offset = 0;
661         Object *new_entry = NULL;
662
663         assert(j);
664
665         HASHMAP_FOREACH(f, j->files, i) {
666                 Object *o;
667                 uint64_t p;
668                 bool found;
669
670                 r = next_beyond_location(j, f, direction, &o, &p);
671                 if (r < 0)
672                         return r;
673                 else if (r == 0)
674                         continue;
675
676                 if (!new_current)
677                         found = true;
678                 else {
679                         int k;
680
681                         k = compare_order(f, o, new_current, new_entry);
682
683                         if (direction == DIRECTION_DOWN)
684                                 found = k < 0;
685                         else
686                                 found = k > 0;
687                 }
688
689                 if (found) {
690                         new_current = f;
691                         new_entry = o;
692                         new_offset = p;
693                 }
694         }
695
696         if (!new_current)
697                 return 0;
698
699         set_location(j, new_current, new_entry, new_offset);
700
701         return 1;
702 }
703
704 int sd_journal_next(sd_journal *j) {
705         return real_journal_next(j, DIRECTION_DOWN);
706 }
707
708 int sd_journal_previous(sd_journal *j) {
709         return real_journal_next(j, DIRECTION_UP);
710 }
711
712 int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
713         int c = 0, r;
714
715         assert(j);
716
717         while (skip > 0) {
718                 r = sd_journal_next(j);
719                 if (r < 0)
720                         return r;
721
722                 if (r == 0)
723                         return c;
724
725                 skip--;
726                 c++;
727         }
728
729         return c;
730 }
731
732 int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
733         int c = 0, r;
734
735         assert(j);
736
737         while (skip > 0) {
738                 r = sd_journal_previous(j);
739                 if (r < 0)
740                         return r;
741
742                 if (r == 0)
743                         return c;
744
745                 skip--;
746                 c++;
747         }
748
749         return 1;
750 }
751
752 int sd_journal_get_cursor(sd_journal *j, char **cursor) {
753         Object *o;
754         int r;
755         char bid[33], sid[33];
756
757         assert(j);
758         assert(cursor);
759
760         if (!j->current_file || j->current_file->current_offset <= 0)
761                 return -EADDRNOTAVAIL;
762
763         r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
764         if (r < 0)
765                 return r;
766
767         sd_id128_to_string(j->current_file->header->seqnum_id, sid);
768         sd_id128_to_string(o->entry.boot_id, bid);
769
770         if (asprintf(cursor,
771                      "s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx;p=%s",
772                      sid, (unsigned long long) le64toh(o->entry.seqnum),
773                      bid, (unsigned long long) le64toh(o->entry.monotonic),
774                      (unsigned long long) le64toh(o->entry.realtime),
775                      (unsigned long long) le64toh(o->entry.xor_hash),
776                      file_name_from_path(j->current_file->path)) < 0)
777                 return -ENOMEM;
778
779         return 1;
780 }
781
782 int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
783         char *w;
784         size_t l;
785         char *state;
786         unsigned long long seqnum, monotonic, realtime, xor_hash;
787         bool
788                 seqnum_id_set = false,
789                 seqnum_set = false,
790                 boot_id_set = false,
791                 monotonic_set = false,
792                 realtime_set = false,
793                 xor_hash_set = false;
794         sd_id128_t seqnum_id, boot_id;
795
796         assert(j);
797         assert(cursor);
798
799         FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
800                 char *item;
801                 int k = 0;
802
803                 if (l < 2 || w[1] != '=')
804                         return -EINVAL;
805
806                 item = strndup(w, l);
807                 if (!item)
808                         return -ENOMEM;
809
810                 switch (w[0]) {
811
812                 case 's':
813                         seqnum_id_set = true;
814                         k = sd_id128_from_string(w+2, &seqnum_id);
815                         break;
816
817                 case 'i':
818                         seqnum_set = true;
819                         if (sscanf(w+2, "%llx", &seqnum) != 1)
820                                 k = -EINVAL;
821                         break;
822
823                 case 'b':
824                         boot_id_set = true;
825                         k = sd_id128_from_string(w+2, &boot_id);
826                         break;
827
828                 case 'm':
829                         monotonic_set = true;
830                         if (sscanf(w+2, "%llx", &monotonic) != 1)
831                                 k = -EINVAL;
832                         break;
833
834                 case 't':
835                         realtime_set = true;
836                         if (sscanf(w+2, "%llx", &realtime) != 1)
837                                 k = -EINVAL;
838                         break;
839
840                 case 'x':
841                         xor_hash_set = true;
842                         if (sscanf(w+2, "%llx", &xor_hash) != 1)
843                                 k = -EINVAL;
844                         break;
845                 }
846
847                 free(item);
848
849                 if (k < 0)
850                         return k;
851         }
852
853         if ((!seqnum_set || !seqnum_id_set) &&
854             (!monotonic_set || !boot_id_set) &&
855             !realtime_set)
856                 return -EINVAL;
857
858         reset_location(j);
859
860         j->current_location.type = LOCATION_DISCRETE;
861
862         if (realtime_set) {
863                 j->current_location.realtime = (uint64_t) realtime;
864                 j->current_location.realtime_set = true;
865         }
866
867         if (seqnum_set && seqnum_id_set) {
868                 j->current_location.seqnum = (uint64_t) seqnum;
869                 j->current_location.seqnum_id = seqnum_id;
870                 j->current_location.seqnum_set = true;
871         }
872
873         if (monotonic_set && boot_id_set) {
874                 j->current_location.monotonic = (uint64_t) monotonic;
875                 j->current_location.boot_id = boot_id;
876                 j->current_location.monotonic_set = true;
877         }
878
879         if (xor_hash_set) {
880                 j->current_location.xor_hash = (uint64_t) xor_hash;
881                 j->current_location.xor_hash_set = true;
882         }
883
884         return 0;
885 }
886
887 int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
888         assert(j);
889
890         reset_location(j);
891         j->current_location.type = LOCATION_DISCRETE;
892         j->current_location.boot_id = boot_id;
893         j->current_location.monotonic = usec;
894         j->current_location.monotonic_set = true;
895
896         return 0;
897 }
898
899 int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
900         assert(j);
901
902         reset_location(j);
903         j->current_location.type = LOCATION_DISCRETE;
904         j->current_location.realtime = usec;
905         j->current_location.realtime_set = true;
906
907         return 0;
908 }
909
910 int sd_journal_seek_head(sd_journal *j) {
911         assert(j);
912
913         reset_location(j);
914         j->current_location.type = LOCATION_HEAD;
915
916         return 0;
917 }
918
919 int sd_journal_seek_tail(sd_journal *j) {
920         assert(j);
921
922         reset_location(j);
923         j->current_location.type = LOCATION_TAIL;
924
925         return 0;
926 }
927
928 static int add_file(sd_journal *j, const char *prefix, const char *dir, const char *filename) {
929         char *fn;
930         int r;
931         JournalFile *f;
932
933         assert(j);
934         assert(prefix);
935         assert(filename);
936
937         if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
938                 log_debug("Too many open journal files, ignoring.");
939                 return 0;
940         }
941
942         if (dir)
943                 fn = join(prefix, "/", dir, "/", filename, NULL);
944         else
945                 fn = join(prefix, "/", filename, NULL);
946
947         if (!fn)
948                 return -ENOMEM;
949
950         r = journal_file_open(fn, O_RDONLY, 0, NULL, &f);
951         free(fn);
952
953         if (r < 0) {
954                 if (errno == ENOENT)
955                         return 0;
956
957                 return r;
958         }
959
960         journal_file_dump(f);
961
962         r = hashmap_put(j->files, f->path, f);
963         if (r < 0) {
964                 journal_file_close(f);
965                 return r;
966         }
967
968         return 0;
969 }
970
971 static int add_directory(sd_journal *j, const char *prefix, const char *dir) {
972         char *fn;
973         int r;
974         DIR *d;
975
976         assert(j);
977         assert(prefix);
978         assert(dir);
979
980         fn = join(prefix, "/", dir, NULL);
981         if (!fn)
982                 return -ENOMEM;
983
984         d = opendir(fn);
985         free(fn);
986
987         if (!d) {
988                 if (errno == ENOENT)
989                         return 0;
990
991                 return -errno;
992         }
993
994         for (;;) {
995                 struct dirent buf, *de;
996
997                 r = readdir_r(d, &buf, &de);
998                 if (r != 0 || !de)
999                         break;
1000
1001                 if (!dirent_is_file_with_suffix(de, ".journal"))
1002                         continue;
1003
1004                 r = add_file(j, prefix, dir, de->d_name);
1005                 if (r < 0)
1006                         log_debug("Failed to add file %s/%s/%s: %s", prefix, dir, de->d_name, strerror(-r));
1007         }
1008
1009         closedir(d);
1010
1011         return 0;
1012 }
1013
1014 int sd_journal_open(sd_journal **ret) {
1015         sd_journal *j;
1016         const char *p;
1017         const char search_paths[] =
1018                 "/run/log/journal\0"
1019                 "/var/log/journal\0";
1020         int r;
1021
1022         assert(ret);
1023
1024         j = new0(sd_journal, 1);
1025         if (!j)
1026                 return -ENOMEM;
1027
1028         j->files = hashmap_new(string_hash_func, string_compare_func);
1029         if (!j->files) {
1030                 r = -ENOMEM;
1031                 goto fail;
1032         }
1033
1034         /* We ignore most errors here, since the idea is to only open
1035          * what's actually accessible, and ignore the rest. */
1036
1037         NULSTR_FOREACH(p, search_paths) {
1038                 DIR *d;
1039
1040                 d = opendir(p);
1041                 if (!d) {
1042                         if (errno != ENOENT)
1043                                 log_debug("Failed to open %s: %m", p);
1044                         continue;
1045                 }
1046
1047                 for (;;) {
1048                         struct dirent buf, *de;
1049                         sd_id128_t id;
1050
1051                         r = readdir_r(d, &buf, &de);
1052                         if (r != 0 || !de)
1053                                 break;
1054
1055                         if (dirent_is_file_with_suffix(de, ".journal")) {
1056                                 r = add_file(j, p, NULL, de->d_name);
1057                                 if (r < 0)
1058                                         log_debug("Failed to add file %s/%s: %s", p, de->d_name, strerror(-r));
1059
1060                         } else if ((de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) &&
1061                                    sd_id128_from_string(de->d_name, &id) >= 0) {
1062
1063                                 r = add_directory(j, p, de->d_name);
1064                                 if (r < 0)
1065                                         log_debug("Failed to add directory %s/%s: %s", p, de->d_name, strerror(-r));
1066                         }
1067                 }
1068
1069                 closedir(d);
1070         }
1071
1072         *ret = j;
1073         return 0;
1074
1075 fail:
1076         sd_journal_close(j);
1077
1078         return r;
1079 };
1080
1081 void sd_journal_close(sd_journal *j) {
1082         assert(j);
1083
1084         if (j->files) {
1085                 JournalFile *f;
1086
1087                 while ((f = hashmap_steal_first(j->files)))
1088                         journal_file_close(f);
1089
1090                 hashmap_free(j->files);
1091         }
1092
1093         sd_journal_flush_matches(j);
1094
1095         free(j);
1096 }
1097
1098 int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
1099         Object *o;
1100         JournalFile *f;
1101         int r;
1102
1103         assert(j);
1104         assert(ret);
1105
1106         f = j->current_file;
1107         if (!f)
1108                 return -EADDRNOTAVAIL;
1109
1110         if (f->current_offset <= 0)
1111                 return -EADDRNOTAVAIL;
1112
1113         r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1114         if (r < 0)
1115                 return r;
1116
1117         *ret = le64toh(o->entry.realtime);
1118         return 0;
1119 }
1120
1121 int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
1122         Object *o;
1123         JournalFile *f;
1124         int r;
1125         sd_id128_t id;
1126
1127         assert(j);
1128         assert(ret);
1129
1130         f = j->current_file;
1131         if (!f)
1132                 return -EADDRNOTAVAIL;
1133
1134         if (f->current_offset <= 0)
1135                 return -EADDRNOTAVAIL;
1136
1137         r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1138         if (r < 0)
1139                 return r;
1140
1141         if (ret_boot_id)
1142                 *ret_boot_id = o->entry.boot_id;
1143         else {
1144                 r = sd_id128_get_boot(&id);
1145                 if (r < 0)
1146                         return r;
1147
1148                 if (!sd_id128_equal(id, o->entry.boot_id))
1149                         return -ENOENT;
1150         }
1151
1152         *ret = le64toh(o->entry.monotonic);
1153         return 0;
1154 }
1155
1156 int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1157         JournalFile *f;
1158         uint64_t i, n;
1159         size_t field_length;
1160         int r;
1161         Object *o;
1162
1163         assert(j);
1164         assert(field);
1165         assert(data);
1166         assert(size);
1167
1168         if (isempty(field) || strchr(field, '='))
1169                 return -EINVAL;
1170
1171         f = j->current_file;
1172         if (!f)
1173                 return -EADDRNOTAVAIL;
1174
1175         if (f->current_offset <= 0)
1176                 return -EADDRNOTAVAIL;
1177
1178         r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1179         if (r < 0)
1180                 return r;
1181
1182         field_length = strlen(field);
1183
1184         n = journal_file_entry_n_items(o);
1185         for (i = 0; i < n; i++) {
1186                 uint64_t p, l, le_hash;
1187                 size_t t;
1188
1189                 p = le64toh(o->entry.items[i].object_offset);
1190                 le_hash = o->entry.items[j->current_field].hash;
1191                 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1192                 if (r < 0)
1193                         return r;
1194
1195                 if (le_hash != o->data.hash)
1196                         return -EBADMSG;
1197
1198                 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1199
1200                 if (l >= field_length+1 &&
1201                     memcmp(o->data.payload, field, field_length) == 0 &&
1202                     o->data.payload[field_length] == '=') {
1203
1204                         t = (size_t) l;
1205
1206                         if ((uint64_t) t != l)
1207                                 return -E2BIG;
1208
1209                         *data = o->data.payload;
1210                         *size = t;
1211
1212                         return 0;
1213                 }
1214
1215                 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1216                 if (r < 0)
1217                         return r;
1218         }
1219
1220         return -ENOENT;
1221 }
1222
1223 int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
1224         JournalFile *f;
1225         uint64_t p, l, n, le_hash;
1226         int r;
1227         Object *o;
1228         size_t t;
1229
1230         assert(j);
1231         assert(data);
1232         assert(size);
1233
1234         f = j->current_file;
1235         if (!f)
1236                 return -EADDRNOTAVAIL;
1237
1238         if (f->current_offset <= 0)
1239                 return -EADDRNOTAVAIL;
1240
1241         r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1242         if (r < 0)
1243                 return r;
1244
1245         n = journal_file_entry_n_items(o);
1246         if (j->current_field >= n)
1247                 return 0;
1248
1249         p = le64toh(o->entry.items[j->current_field].object_offset);
1250         le_hash = o->entry.items[j->current_field].hash;
1251         r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1252         if (r < 0)
1253                 return r;
1254
1255         if (le_hash != o->data.hash)
1256                 return -EBADMSG;
1257
1258         l = le64toh(o->object.size) - offsetof(Object, data.payload);
1259         t = (size_t) l;
1260
1261         /* We can't read objects larger than 4G on a 32bit machine */
1262         if ((uint64_t) t != l)
1263                 return -E2BIG;
1264
1265         *data = o->data.payload;
1266         *size = t;
1267
1268         j->current_field ++;
1269
1270         return 1;
1271 }
1272
1273 void sd_journal_restart_data(sd_journal *j) {
1274         assert(j);
1275
1276         j->current_field = 0;
1277 }