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