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