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