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