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