chiark / gitweb /
journalctl: add a marker to log output for reboots
[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(ao->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(ao->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 _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
1430         JournalFile *f;
1431         uint64_t i, n;
1432         size_t field_length;
1433         int r;
1434         Object *o;
1435
1436         if (!j)
1437                 return -EINVAL;
1438         if (!field)
1439                 return -EINVAL;
1440         if (!data)
1441                 return -EINVAL;
1442         if (!size)
1443                 return -EINVAL;
1444
1445         if (isempty(field) || strchr(field, '='))
1446                 return -EINVAL;
1447
1448         f = j->current_file;
1449         if (!f)
1450                 return -EADDRNOTAVAIL;
1451
1452         if (f->current_offset <= 0)
1453                 return -EADDRNOTAVAIL;
1454
1455         r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1456         if (r < 0)
1457                 return r;
1458
1459         field_length = strlen(field);
1460
1461         n = journal_file_entry_n_items(o);
1462         for (i = 0; i < n; i++) {
1463                 uint64_t p, l;
1464                 le64_t le_hash;
1465                 size_t t;
1466
1467                 p = le64toh(o->entry.items[i].object_offset);
1468                 le_hash = o->entry.items[i].hash;
1469                 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1470                 if (r < 0)
1471                         return r;
1472
1473                 if (le_hash != o->data.hash)
1474                         return -EBADMSG;
1475
1476                 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1477
1478                 if (o->object.flags & OBJECT_COMPRESSED) {
1479
1480 #ifdef HAVE_XZ
1481                         if (uncompress_startswith(o->data.payload, l,
1482                                                   &f->compress_buffer, &f->compress_buffer_size,
1483                                                   field, field_length, '=')) {
1484
1485                                 uint64_t rsize;
1486
1487                                 if (!uncompress_blob(o->data.payload, l,
1488                                                      &f->compress_buffer, &f->compress_buffer_size, &rsize))
1489                                         return -EBADMSG;
1490
1491                                 *data = f->compress_buffer;
1492                                 *size = (size_t) rsize;
1493
1494                                 return 0;
1495                         }
1496 #else
1497                         return -EPROTONOSUPPORT;
1498 #endif
1499
1500                 } else if (l >= field_length+1 &&
1501                            memcmp(o->data.payload, field, field_length) == 0 &&
1502                            o->data.payload[field_length] == '=') {
1503
1504                         t = (size_t) l;
1505
1506                         if ((uint64_t) t != l)
1507                                 return -E2BIG;
1508
1509                         *data = o->data.payload;
1510                         *size = t;
1511
1512                         return 0;
1513                 }
1514
1515                 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1516                 if (r < 0)
1517                         return r;
1518         }
1519
1520         return -ENOENT;
1521 }
1522
1523 _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
1524         JournalFile *f;
1525         uint64_t p, l, n;
1526         le64_t le_hash;
1527         int r;
1528         Object *o;
1529         size_t t;
1530
1531         if (!j)
1532                 return -EINVAL;
1533         if (!data)
1534                 return -EINVAL;
1535         if (!size)
1536                 return -EINVAL;
1537
1538         f = j->current_file;
1539         if (!f)
1540                 return -EADDRNOTAVAIL;
1541
1542         if (f->current_offset <= 0)
1543                 return -EADDRNOTAVAIL;
1544
1545         r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
1546         if (r < 0)
1547                 return r;
1548
1549         n = journal_file_entry_n_items(o);
1550         if (j->current_field >= n)
1551                 return 0;
1552
1553         p = le64toh(o->entry.items[j->current_field].object_offset);
1554         le_hash = o->entry.items[j->current_field].hash;
1555         r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1556         if (r < 0)
1557                 return r;
1558
1559         if (le_hash != o->data.hash)
1560                 return -EBADMSG;
1561
1562         l = le64toh(o->object.size) - offsetof(Object, data.payload);
1563         t = (size_t) l;
1564
1565         /* We can't read objects larger than 4G on a 32bit machine */
1566         if ((uint64_t) t != l)
1567                 return -E2BIG;
1568
1569         if (o->object.flags & OBJECT_COMPRESSED) {
1570 #ifdef HAVE_XZ
1571                 uint64_t rsize;
1572
1573                 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize))
1574                         return -EBADMSG;
1575
1576                 *data = f->compress_buffer;
1577                 *size = (size_t) rsize;
1578 #else
1579                 return -EPROTONOSUPPORT;
1580 #endif
1581         } else {
1582                 *data = o->data.payload;
1583                 *size = t;
1584         }
1585
1586         j->current_field ++;
1587
1588         return 1;
1589 }
1590
1591 _public_ void sd_journal_restart_data(sd_journal *j) {
1592         if (!j)
1593                 return;
1594
1595         j->current_field = 0;
1596 }
1597
1598 _public_ int sd_journal_get_fd(sd_journal *j) {
1599         int r;
1600
1601         if (!j)
1602                 return -EINVAL;
1603
1604         if (j->inotify_fd >= 0)
1605                 return j->inotify_fd;
1606
1607         r = allocate_inotify(j);
1608         if (r < 0)
1609                 return r;
1610
1611         /* Iterate through all dirs again, to add them to the
1612          * inotify */
1613         r = add_search_paths(j);
1614         if (r < 0)
1615                 return r;
1616
1617         return j->inotify_fd;
1618 }
1619
1620 static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
1621         Directory *d;
1622         int r;
1623
1624         assert(j);
1625         assert(e);
1626
1627         /* Is this a subdirectory we watch? */
1628         d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
1629         if (d) {
1630                 sd_id128_t id;
1631
1632                 if (!(e->mask & IN_ISDIR) && e->len > 0 && endswith(e->name, ".journal")) {
1633
1634                         /* Event for a journal file */
1635
1636                         if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1637                                 r = add_file(j, d->path, e->name);
1638                                 if (r < 0)
1639                                         log_debug("Failed to add file %s/%s: %s", d->path, e->name, strerror(-r));
1640
1641                         } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
1642
1643                                 r = remove_file(j, d->path, e->name);
1644                                 if (r < 0)
1645                                         log_debug("Failed to remove file %s/%s: %s", d->path, e->name, strerror(-r));
1646                         }
1647
1648                 } else if (!d->is_root && e->len == 0) {
1649
1650                         /* Event for a subdirectory */
1651
1652                         if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
1653                                 r = remove_directory(j, d);
1654                                 if (r < 0)
1655                                         log_debug("Failed to remove directory %s: %s", d->path, strerror(-r));
1656                         }
1657
1658
1659                 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
1660
1661                         /* Event for root directory */
1662
1663                         if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
1664                                 r = add_directory(j, d->path, e->name);
1665                                 if (r < 0)
1666                                         log_debug("Failed to add directory %s/%s: %s", d->path, e->name, strerror(-r));
1667                         }
1668                 }
1669
1670                 return;
1671         }
1672
1673         if (e->mask & IN_IGNORED)
1674                 return;
1675
1676         log_warning("Unknown inotify event.");
1677 }
1678
1679 static int determine_change(sd_journal *j) {
1680         bool b;
1681
1682         assert(j);
1683
1684         b = j->current_invalidate_counter != j->last_invalidate_counter;
1685         j->last_invalidate_counter = j->current_invalidate_counter;
1686
1687         return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
1688 }
1689
1690 _public_ int sd_journal_process(sd_journal *j) {
1691         uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX];
1692         bool got_something = false;
1693
1694         if (!j)
1695                 return -EINVAL;
1696
1697         for (;;) {
1698                 struct inotify_event *e;
1699                 ssize_t l;
1700
1701                 l = read(j->inotify_fd, buffer, sizeof(buffer));
1702                 if (l < 0) {
1703                         if (errno == EAGAIN || errno == EINTR)
1704                                 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
1705
1706                         return -errno;
1707                 }
1708
1709                 got_something = true;
1710
1711                 e = (struct inotify_event*) buffer;
1712                 while (l > 0) {
1713                         size_t step;
1714
1715                         process_inotify_event(j, e);
1716
1717                         step = sizeof(struct inotify_event) + e->len;
1718                         assert(step <= (size_t) l);
1719
1720                         e = (struct inotify_event*) ((uint8_t*) e + step);
1721                         l -= step;
1722                 }
1723         }
1724
1725         return determine_change(j);
1726 }
1727
1728 _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
1729         int r;
1730
1731         assert(j);
1732
1733         if (j->inotify_fd < 0) {
1734
1735                 /* This is the first invocation, hence create the
1736                  * inotify watch */
1737                 r = sd_journal_get_fd(j);
1738                 if (r < 0)
1739                         return r;
1740
1741                 /* The journal might have changed since the context
1742                  * object was created and we weren't watching before,
1743                  * hence don't wait for anything, and return
1744                  * immediately. */
1745                 return determine_change(j);
1746         }
1747
1748         do {
1749                 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
1750         } while (r == -EINTR);
1751
1752         if (r < 0)
1753                 return r;
1754
1755         return sd_journal_process(j);
1756 }
1757
1758 _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
1759         Iterator i;
1760         JournalFile *f;
1761         bool first = true;
1762         int r;
1763
1764         if (!j)
1765                 return -EINVAL;
1766         if (!from && !to)
1767                 return -EINVAL;
1768
1769         HASHMAP_FOREACH(f, j->files, i) {
1770                 usec_t fr, t;
1771
1772                 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
1773                 if (r < 0)
1774                         return r;
1775                 if (r == 0)
1776                         continue;
1777
1778                 if (first) {
1779                         if (from)
1780                                 *from = fr;
1781                         if (to)
1782                                 *to = t;
1783                         first = false;
1784                 } else {
1785                         if (from)
1786                                 *from = MIN(fr, *from);
1787                         if (to)
1788                                 *to = MIN(t, *to);
1789                 }
1790         }
1791
1792         return first ? 0 : 1;
1793 }
1794
1795 _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
1796         Iterator i;
1797         JournalFile *f;
1798         bool first = true;
1799         int r;
1800
1801         if (!j)
1802                 return -EINVAL;
1803         if (!from && !to)
1804                 return -EINVAL;
1805
1806         HASHMAP_FOREACH(f, j->files, i) {
1807                 usec_t fr, t;
1808
1809                 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
1810                 if (r < 0)
1811                         return r;
1812                 if (r == 0)
1813                         continue;
1814
1815                 if (first) {
1816                         if (from)
1817                                 *from = fr;
1818                         if (to)
1819                                 *to = t;
1820                         first = false;
1821                 } else {
1822                         if (from)
1823                                 *from = MIN(fr, *from);
1824                         if (to)
1825                                 *to = MIN(t, *to);
1826                 }
1827         }
1828
1829         return first ? 0 : 1;
1830 }
1831
1832
1833 /* _public_ int sd_journal_query_unique(sd_journal *j, const char *field) { */
1834 /*         if (!j) */
1835 /*                 return -EINVAL; */
1836 /*         if (!field) */
1837 /*                 return -EINVAL; */
1838
1839 /*         return -ENOTSUP; */
1840 /* } */
1841
1842 /* _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { */
1843 /*         if (!j) */
1844 /*                 return -EINVAL; */
1845 /*         if (!data) */
1846 /*                 return -EINVAL; */
1847 /*         if (!l) */
1848 /*                 return -EINVAL; */
1849
1850 /*         return -ENOTSUP; */
1851 /* } */
1852
1853 /* _public_ void sd_journal_restart_unique(sd_journal *j) { */
1854 /*         if (!j) */
1855 /*                 return; */
1856 /* } */