]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/sd-journal.c
units: cleanup agetty command line
[thirdparty/systemd.git] / src / journal / sd-journal.c
CommitLineData
87d2c1ff
LP
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
5430f7f2
LP
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
87d2c1ff
LP
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
5430f7f2 16 Lesser General Public License for more details.
87d2c1ff 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
87d2c1ff
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
87d2c1ff 22#include <errno.h>
87d2c1ff 23#include <fcntl.h>
3fbf9cbb 24#include <stddef.h>
50f20cfd
LP
25#include <unistd.h>
26#include <sys/inotify.h>
e02d1cf7 27#include <sys/poll.h>
85210bff
LP
28#include <sys/vfs.h>
29#include <linux/magic.h>
87d2c1ff
LP
30
31#include "sd-journal.h"
32#include "journal-def.h"
cec736d2 33#include "journal-file.h"
260a2be4 34#include "hashmap.h"
cec736d2 35#include "list.h"
9eb977db 36#include "path-util.h"
de7b95cd 37#include "lookup3.h"
807e17f0 38#include "compress.h"
cf244689 39#include "journal-internal.h"
85210bff 40#include "missing.h"
d4205751
LP
41#include "catalog.h"
42#include "replace-var.h"
87d2c1ff 43
cab8ac60
LP
44#define JOURNAL_FILES_MAX 1024
45
85210bff
LP
46#define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC)
47
d4205751
LP
48#define REPLACE_VAR_MAX 256
49
93b73b06
LP
50#define DEFAULT_DATA_THRESHOLD (64*1024)
51
6fe391c5
ZJS
52/* We return an error here only if we didn't manage to
53 memorize the real error. */
3ac251b8
LP
54static int set_put_error(sd_journal *j, int r) {
55 int k;
56
6fe391c5
ZJS
57 if (r >= 0)
58 return r;
59
3ac251b8
LP
60 k = set_ensure_allocated(&j->errors, trivial_hash_func, trivial_compare_func);
61 if (k < 0)
62 return k;
63
64 return set_put(j->errors, INT_TO_PTR(r));
6fe391c5
ZJS
65}
66
de190aef 67static void detach_location(sd_journal *j) {
8f9b6cd9
LP
68 Iterator i;
69 JournalFile *f;
70
71 assert(j);
72
73 j->current_file = NULL;
74 j->current_field = 0;
75
76 HASHMAP_FOREACH(f, j->files, i)
77 f->current_offset = 0;
78}
79
de190aef
LP
80static void reset_location(sd_journal *j) {
81 assert(j);
82
83 detach_location(j);
84 zero(j->current_location);
85}
86
a87247dd 87static void init_location(Location *l, LocationType type, JournalFile *f, Object *o) {
de190aef 88 assert(l);
a87247dd 89 assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK);
de190aef
LP
90 assert(f);
91 assert(o->object.type == OBJECT_ENTRY);
92
a87247dd 93 l->type = type;
de190aef
LP
94 l->seqnum = le64toh(o->entry.seqnum);
95 l->seqnum_id = f->header->seqnum_id;
96 l->realtime = le64toh(o->entry.realtime);
97 l->monotonic = le64toh(o->entry.monotonic);
ce3fd7e7 98 l->boot_id = o->entry.boot_id;
de190aef
LP
99 l->xor_hash = le64toh(o->entry.xor_hash);
100
101 l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
102}
103
a87247dd 104static void set_location(sd_journal *j, LocationType type, JournalFile *f, Object *o, uint64_t offset) {
de190aef 105 assert(j);
a87247dd 106 assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK);
de190aef
LP
107 assert(f);
108 assert(o);
109
a87247dd 110 init_location(&j->current_location, type, f, o);
de190aef 111
a3e6f050
MV
112 if (j->current_file)
113 j->current_file->current_offset = 0;
114
de190aef
LP
115 j->current_file = f;
116 j->current_field = 0;
117
118 f->current_offset = offset;
119}
120
cbdca852
LP
121static int match_is_valid(const void *data, size_t size) {
122 const char *b, *p;
123
124 assert(data);
125
126 if (size < 2)
127 return false;
128
129 if (startswith(data, "__"))
130 return false;
131
132 b = data;
133 for (p = b; p < b + size; p++) {
134
135 if (*p == '=')
136 return p > b;
137
138 if (*p == '_')
139 continue;
140
141 if (*p >= 'A' && *p <= 'Z')
142 continue;
143
144 if (*p >= '0' && *p <= '9')
145 continue;
146
147 return false;
148 }
149
150 return false;
151}
152
153static bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
de190aef
LP
154 const uint8_t *a = _a, *b = _b;
155 size_t j;
de190aef
LP
156
157 for (j = 0; j < s && j < t; j++) {
158
de190aef 159 if (a[j] != b[j])
cbdca852 160 return false;
de190aef 161
cbdca852
LP
162 if (a[j] == '=')
163 return true;
de190aef
LP
164 }
165
cbdca852
LP
166 return true;
167}
168
169static Match *match_new(Match *p, MatchType t) {
170 Match *m;
171
172 m = new0(Match, 1);
173 if (!m)
174 return NULL;
175
176 m->type = t;
177
178 if (p) {
179 m->parent = p;
180 LIST_PREPEND(Match, matches, p->matches, m);
181 }
182
183 return m;
184}
185
186static void match_free(Match *m) {
187 assert(m);
188
189 while (m->matches)
190 match_free(m->matches);
191
192 if (m->parent)
193 LIST_REMOVE(Match, matches, m->parent->matches, m);
194
195 free(m->data);
196 free(m);
197}
198
199static void match_free_if_empty(Match *m) {
200 assert(m);
201
202 if (m->matches)
203 return;
204
205 match_free(m);
de190aef
LP
206}
207
a5344d2c 208_public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
cd34b3c6 209 Match *l3, *l4, *add_here = NULL, *m;
4fd052ae 210 le64_t le_hash;
87d2c1ff 211
a5344d2c
LP
212 if (!j)
213 return -EINVAL;
cbdca852 214
a5344d2c
LP
215 if (!data)
216 return -EINVAL;
cbdca852
LP
217
218 if (size == 0)
219 size = strlen(data);
220
221 if (!match_is_valid(data, size))
1cc101f1
LP
222 return -EINVAL;
223
cd34b3c6
HH
224 /* level 0: AND term
225 * level 1: OR terms
226 * level 2: AND terms
227 * level 3: OR terms
228 * level 4: concrete matches */
cbdca852
LP
229
230 if (!j->level0) {
cd34b3c6 231 j->level0 = match_new(NULL, MATCH_AND_TERM);
cbdca852
LP
232 if (!j->level0)
233 return -ENOMEM;
234 }
235
236 if (!j->level1) {
cd34b3c6 237 j->level1 = match_new(j->level0, MATCH_OR_TERM);
cbdca852
LP
238 if (!j->level1)
239 return -ENOMEM;
240 }
241
cd34b3c6
HH
242 if (!j->level2) {
243 j->level2 = match_new(j->level1, MATCH_AND_TERM);
244 if (!j->level2)
245 return -ENOMEM;
246 }
247
248 assert(j->level0->type == MATCH_AND_TERM);
249 assert(j->level1->type == MATCH_OR_TERM);
250 assert(j->level2->type == MATCH_AND_TERM);
ab4979d2 251
de190aef
LP
252 le_hash = htole64(hash64(data, size));
253
cd34b3c6
HH
254 LIST_FOREACH(matches, l3, j->level2->matches) {
255 assert(l3->type == MATCH_OR_TERM);
de190aef 256
cd34b3c6
HH
257 LIST_FOREACH(matches, l4, l3->matches) {
258 assert(l4->type == MATCH_DISCRETE);
de190aef 259
cbdca852
LP
260 /* Exactly the same match already? Then ignore
261 * this addition */
cd34b3c6
HH
262 if (l4->le_hash == le_hash &&
263 l4->size == size &&
264 memcmp(l4->data, data, size) == 0)
cbdca852
LP
265 return 0;
266
267 /* Same field? Then let's add this to this OR term */
cd34b3c6
HH
268 if (same_field(data, size, l4->data, l4->size)) {
269 add_here = l3;
cbdca852
LP
270 break;
271 }
272 }
273
274 if (add_here)
275 break;
de190aef
LP
276 }
277
cbdca852 278 if (!add_here) {
cd34b3c6 279 add_here = match_new(j->level2, MATCH_OR_TERM);
cbdca852
LP
280 if (!add_here)
281 goto fail;
282 }
283
284 m = match_new(add_here, MATCH_DISCRETE);
cec736d2 285 if (!m)
cbdca852 286 goto fail;
87d2c1ff 287
cbdca852 288 m->le_hash = le_hash;
1cc101f1 289 m->size = size;
cbdca852
LP
290 m->data = memdup(data, size);
291 if (!m->data)
292 goto fail;
293
294 detach_location(j);
295
296 return 0;
297
298fail:
299 if (add_here)
300 match_free_if_empty(add_here);
301
cd34b3c6
HH
302 if (j->level2)
303 match_free_if_empty(j->level2);
304
cbdca852
LP
305 if (j->level1)
306 match_free_if_empty(j->level1);
307
308 if (j->level0)
309 match_free_if_empty(j->level0);
310
311 return -ENOMEM;
312}
313
cd34b3c6 314_public_ int sd_journal_add_conjunction(sd_journal *j) {
cbdca852 315 assert(j);
1cc101f1 316
cbdca852
LP
317 if (!j->level0)
318 return 0;
319
320 if (!j->level1)
321 return 0;
322
323 if (!j->level1->matches)
324 return 0;
325
cd34b3c6
HH
326 j->level1 = NULL;
327 j->level2 = NULL;
328
329 return 0;
330}
331
332_public_ int sd_journal_add_disjunction(sd_journal *j) {
333 assert(j);
334
335 if (!j->level0)
336 return 0;
337
338 if (!j->level1)
339 return 0;
340
341 if (!j->level2)
342 return 0;
343
344 if (!j->level2->matches)
345 return 0;
cbdca852 346
cd34b3c6 347 j->level2 = NULL;
cbdca852
LP
348 return 0;
349}
350
351static char *match_make_string(Match *m) {
352 char *p, *r;
353 Match *i;
354 bool enclose = false;
355
356 if (!m)
357 return strdup("");
358
359 if (m->type == MATCH_DISCRETE)
360 return strndup(m->data, m->size);
361
362 p = NULL;
363 LIST_FOREACH(matches, i, m->matches) {
364 char *t, *k;
365
366 t = match_make_string(i);
367 if (!t) {
368 free(p);
369 return NULL;
370 }
371
372 if (p) {
b7def684 373 k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t, NULL);
cbdca852
LP
374 free(p);
375 free(t);
376
377 if (!k)
378 return NULL;
379
380 p = k;
381
382 enclose = true;
383 } else {
384 free(p);
385 p = t;
386 }
87d2c1ff
LP
387 }
388
cbdca852 389 if (enclose) {
b7def684 390 r = strjoin("(", p, ")", NULL);
cbdca852
LP
391 free(p);
392 return r;
393 }
87d2c1ff 394
cbdca852
LP
395 return p;
396}
de7b95cd 397
cbdca852
LP
398char *journal_make_match_string(sd_journal *j) {
399 assert(j);
8f9b6cd9 400
cbdca852 401 return match_make_string(j->level0);
87d2c1ff
LP
402}
403
a5344d2c 404_public_ void sd_journal_flush_matches(sd_journal *j) {
cbdca852 405
a5344d2c
LP
406 if (!j)
407 return;
87d2c1ff 408
cbdca852
LP
409 if (j->level0)
410 match_free(j->level0);
de7b95cd 411
cd34b3c6 412 j->level0 = j->level1 = j->level2 = NULL;
8f9b6cd9 413
de190aef 414 detach_location(j);
87d2c1ff
LP
415}
416
468b21de 417static int compare_entry_order(JournalFile *af, Object *_ao,
5afbe712 418 JournalFile *bf, uint64_t bp) {
87d2c1ff 419
cec736d2 420 uint64_t a, b;
468b21de
LP
421 Object *ao, *bo;
422 int r;
87d2c1ff 423
de190aef 424 assert(af);
de190aef 425 assert(bf);
468b21de
LP
426 assert(_ao);
427
428 /* The mmap cache might invalidate the object from the first
429 * file if we look at the one from the second file. Hence
430 * temporarily copy the header of the first one, and look at
431 * that only. */
432 ao = alloca(offsetof(EntryObject, items));
433 memcpy(ao, _ao, offsetof(EntryObject, items));
434
435 r = journal_file_move_to_object(bf, OBJECT_ENTRY, bp, &bo);
436 if (r < 0)
437 return strcmp(af->path, bf->path);
de190aef 438
ae2cc8ef 439 /* We operate on two different files here, hence we can access
1cc101f1
LP
440 * two objects at the same time, which we normally can't.
441 *
442 * If contents and timestamps match, these entries are
443 * identical, even if the seqnum does not match */
444
445 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
446 ao->entry.monotonic == bo->entry.monotonic &&
447 ao->entry.realtime == bo->entry.realtime &&
448 ao->entry.xor_hash == bo->entry.xor_hash)
449 return 0;
ae2cc8ef 450
cec736d2 451 if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
87d2c1ff 452
cec736d2
LP
453 /* If this is from the same seqnum source, compare
454 * seqnums */
455 a = le64toh(ao->entry.seqnum);
456 b = le64toh(bo->entry.seqnum);
87d2c1ff 457
ae2cc8ef
LP
458 if (a < b)
459 return -1;
460 if (a > b)
461 return 1;
1cc101f1
LP
462
463 /* Wow! This is weird, different data but the same
464 * seqnums? Something is borked, but let's make the
465 * best of it and compare by time. */
ae2cc8ef 466 }
87d2c1ff 467
ae2cc8ef 468 if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
87d2c1ff 469
cec736d2
LP
470 /* If the boot id matches compare monotonic time */
471 a = le64toh(ao->entry.monotonic);
472 b = le64toh(bo->entry.monotonic);
87d2c1ff 473
ae2cc8ef
LP
474 if (a < b)
475 return -1;
476 if (a > b)
477 return 1;
87d2c1ff
LP
478 }
479
ae2cc8ef
LP
480 /* Otherwise compare UTC time */
481 a = le64toh(ao->entry.realtime);
c4aff78b 482 b = le64toh(bo->entry.realtime);
ae2cc8ef
LP
483
484 if (a < b)
485 return -1;
486 if (a > b)
487 return 1;
488
489 /* Finally, compare by contents */
490 a = le64toh(ao->entry.xor_hash);
c4aff78b 491 b = le64toh(bo->entry.xor_hash);
ae2cc8ef
LP
492
493 if (a < b)
494 return -1;
495 if (a > b)
496 return 1;
497
498 return 0;
87d2c1ff
LP
499}
500
44a6b1b6 501_pure_ static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
de190aef
LP
502 uint64_t a;
503
504 assert(af);
505 assert(ao);
506 assert(l);
a87247dd 507 assert(l->type == LOCATION_DISCRETE || l->type == LOCATION_SEEK);
de190aef
LP
508
509 if (l->monotonic_set &&
510 sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
511 l->realtime_set &&
512 le64toh(ao->entry.realtime) == l->realtime &&
513 l->xor_hash_set &&
514 le64toh(ao->entry.xor_hash) == l->xor_hash)
515 return 0;
516
517 if (l->seqnum_set &&
518 sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
519
520 a = le64toh(ao->entry.seqnum);
521
522 if (a < l->seqnum)
523 return -1;
524 if (a > l->seqnum)
525 return 1;
526 }
527
528 if (l->monotonic_set &&
529 sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
530
531 a = le64toh(ao->entry.monotonic);
532
533 if (a < l->monotonic)
534 return -1;
535 if (a > l->monotonic)
536 return 1;
537 }
538
539 if (l->realtime_set) {
540
541 a = le64toh(ao->entry.realtime);
542
543 if (a < l->realtime)
544 return -1;
545 if (a > l->realtime)
546 return 1;
547 }
548
549 if (l->xor_hash_set) {
550 a = le64toh(ao->entry.xor_hash);
551
552 if (a < l->xor_hash)
553 return -1;
554 if (a > l->xor_hash)
555 return 1;
556 }
557
558 return 0;
559}
560
cbdca852
LP
561static int next_for_match(
562 sd_journal *j,
563 Match *m,
564 JournalFile *f,
565 uint64_t after_offset,
566 direction_t direction,
567 Object **ret,
568 uint64_t *offset) {
569
de7b95cd 570 int r;
cbdca852
LP
571 uint64_t np = 0;
572 Object *n;
de7b95cd
LP
573
574 assert(j);
cbdca852
LP
575 assert(m);
576 assert(f);
de7b95cd 577
cbdca852
LP
578 if (m->type == MATCH_DISCRETE) {
579 uint64_t dp;
de190aef 580
cbdca852 581 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
de190aef
LP
582 if (r <= 0)
583 return r;
584
cbdca852 585 return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
de190aef 586
cbdca852
LP
587 } else if (m->type == MATCH_OR_TERM) {
588 Match *i;
de7b95cd 589
cbdca852 590 /* Find the earliest match beyond after_offset */
de190aef 591
cbdca852
LP
592 LIST_FOREACH(matches, i, m->matches) {
593 uint64_t cp;
de190aef 594
cbdca852 595 r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
b4e5f920
LP
596 if (r < 0)
597 return r;
cbdca852
LP
598 else if (r > 0) {
599 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
600 np = cp;
601 }
602 }
b4e5f920 603
cbdca852
LP
604 } else if (m->type == MATCH_AND_TERM) {
605 Match *i;
606 bool continue_looking;
de190aef 607
cbdca852
LP
608 /* Always jump to the next matching entry and repeat
609 * this until we fine and offset that matches for all
610 * matches. */
de190aef 611
cbdca852
LP
612 if (!m->matches)
613 return 0;
de7b95cd 614
cbdca852
LP
615 np = 0;
616 do {
617 continue_looking = false;
de190aef 618
cbdca852
LP
619 LIST_FOREACH(matches, i, m->matches) {
620 uint64_t cp, limit;
de190aef 621
cbdca852
LP
622 if (np == 0)
623 limit = after_offset;
624 else if (direction == DIRECTION_DOWN)
625 limit = MAX(np, after_offset);
626 else
627 limit = MIN(np, after_offset);
de190aef 628
cbdca852
LP
629 r = next_for_match(j, i, f, limit, direction, NULL, &cp);
630 if (r <= 0)
631 return r;
de190aef 632
cbdca852 633 if ((direction == DIRECTION_DOWN ? cp >= after_offset : cp <= after_offset) &&
23e97f7d 634 (np == 0 || (direction == DIRECTION_DOWN ? cp > np : cp < np))) {
cbdca852
LP
635 np = cp;
636 continue_looking = true;
de190aef
LP
637 }
638 }
de190aef 639
cbdca852
LP
640 } while (continue_looking);
641 }
de190aef 642
cbdca852
LP
643 if (np == 0)
644 return 0;
de190aef 645
cbdca852
LP
646 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
647 if (r < 0)
648 return r;
de7b95cd 649
de190aef 650 if (ret)
cbdca852 651 *ret = n;
de190aef 652 if (offset)
cbdca852 653 *offset = np;
de190aef
LP
654
655 return 1;
656}
657
cbdca852
LP
658static int find_location_for_match(
659 sd_journal *j,
660 Match *m,
661 JournalFile *f,
662 direction_t direction,
663 Object **ret,
664 uint64_t *offset) {
665
de190aef 666 int r;
de190aef
LP
667
668 assert(j);
cbdca852 669 assert(m);
de190aef 670 assert(f);
de190aef 671
cbdca852
LP
672 if (m->type == MATCH_DISCRETE) {
673 uint64_t dp;
de190aef 674
cbdca852 675 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
de7b95cd
LP
676 if (r <= 0)
677 return r;
678
cbdca852 679 /* FIXME: missing: find by monotonic */
de7b95cd 680
cbdca852
LP
681 if (j->current_location.type == LOCATION_HEAD)
682 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset);
683 if (j->current_location.type == LOCATION_TAIL)
684 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, ret, offset);
685 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
686 return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset);
687 if (j->current_location.monotonic_set) {
688 r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
689 if (r != -ENOENT)
690 return r;
691 }
692 if (j->current_location.realtime_set)
693 return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
de190aef 694
cbdca852 695 return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset);
de7b95cd 696
cbdca852
LP
697 } else if (m->type == MATCH_OR_TERM) {
698 uint64_t np = 0;
699 Object *n;
700 Match *i;
de7b95cd 701
cbdca852 702 /* Find the earliest match */
de7b95cd 703
cbdca852
LP
704 LIST_FOREACH(matches, i, m->matches) {
705 uint64_t cp;
706
707 r = find_location_for_match(j, i, f, direction, NULL, &cp);
708 if (r < 0)
709 return r;
710 else if (r > 0) {
711 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
712 np = cp;
de190aef 713 }
cbdca852 714 }
de190aef 715
cbdca852
LP
716 if (np == 0)
717 return 0;
de7b95cd 718
cbdca852
LP
719 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
720 if (r < 0)
721 return r;
de7b95cd 722
cbdca852
LP
723 if (ret)
724 *ret = n;
725 if (offset)
726 *offset = np;
de190aef 727
cbdca852 728 return 1;
e892bd17 729
cbdca852
LP
730 } else {
731 Match *i;
732 uint64_t np = 0;
733
734 assert(m->type == MATCH_AND_TERM);
735
736 /* First jump to the last match, and then find the
737 * next one where all matches match */
738
739 if (!m->matches)
740 return 0;
741
742 LIST_FOREACH(matches, i, m->matches) {
743 uint64_t cp;
744
745 r = find_location_for_match(j, i, f, direction, NULL, &cp);
746 if (r <= 0)
4b067dc9
LP
747 return r;
748
cbdca852
LP
749 if (np == 0 || (direction == DIRECTION_DOWN ? np < cp : np > cp))
750 np = cp;
de7b95cd
LP
751 }
752
cbdca852
LP
753 return next_for_match(j, m, f, np, direction, ret, offset);
754 }
755}
de190aef 756
cbdca852
LP
757static int find_location_with_matches(
758 sd_journal *j,
759 JournalFile *f,
760 direction_t direction,
761 Object **ret,
762 uint64_t *offset) {
763
764 int r;
765
766 assert(j);
767 assert(f);
768 assert(ret);
769 assert(offset);
770
771 if (!j->level0) {
772 /* No matches is simple */
773
774 if (j->current_location.type == LOCATION_HEAD)
775 return journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, ret, offset);
776 if (j->current_location.type == LOCATION_TAIL)
777 return journal_file_next_entry(f, NULL, 0, DIRECTION_UP, ret, offset);
778 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
779 return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
780 if (j->current_location.monotonic_set) {
781 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
782 if (r != -ENOENT)
783 return r;
de7b95cd 784 }
cbdca852
LP
785 if (j->current_location.realtime_set)
786 return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
de7b95cd 787
cbdca852
LP
788 return journal_file_next_entry(f, NULL, 0, direction, ret, offset);
789 } else
790 return find_location_for_match(j, j->level0, f, direction, ret, offset);
791}
de7b95cd 792
cbdca852
LP
793static int next_with_matches(
794 sd_journal *j,
795 JournalFile *f,
796 direction_t direction,
797 Object **ret,
798 uint64_t *offset) {
799
800 Object *c;
801 uint64_t cp;
802
803 assert(j);
804 assert(f);
805 assert(ret);
806 assert(offset);
807
808 c = *ret;
809 cp = *offset;
810
811 /* No matches is easy. We simple advance the file
812 * pointer by one. */
813 if (!j->level0)
814 return journal_file_next_entry(f, c, cp, direction, ret, offset);
815
816 /* If we have a match then we look for the next matching entry
49f43d5f 817 * with an offset at least one step larger */
cbdca852 818 return next_for_match(j, j->level0, f, direction == DIRECTION_DOWN ? cp+1 : cp-1, direction, ret, offset);
de7b95cd
LP
819}
820
de190aef
LP
821static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
822 Object *c;
823 uint64_t cp;
cbdca852 824 int r;
de190aef
LP
825
826 assert(j);
827 assert(f);
828
829 if (f->current_offset > 0) {
466ccd92
LP
830 cp = f->current_offset;
831
832 r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
de190aef
LP
833 if (r < 0)
834 return r;
835
de190aef
LP
836 r = next_with_matches(j, f, direction, &c, &cp);
837 if (r <= 0)
838 return r;
de190aef 839 } else {
cbdca852 840 r = find_location_with_matches(j, f, direction, &c, &cp);
de190aef
LP
841 if (r <= 0)
842 return r;
de190aef
LP
843 }
844
cbdca852
LP
845 /* OK, we found the spot, now let's advance until to an entry
846 * that is actually different from what we were previously
847 * looking at. This is necessary to handle entries which exist
848 * in two (or more) journal files, and which shall all be
849 * suppressed but one. */
850
de190aef
LP
851 for (;;) {
852 bool found;
853
854 if (j->current_location.type == LOCATION_DISCRETE) {
855 int k;
856
857 k = compare_with_location(f, c, &j->current_location);
858 if (direction == DIRECTION_DOWN)
cbdca852 859 found = k > 0;
de190aef 860 else
cbdca852 861 found = k < 0;
de190aef
LP
862 } else
863 found = true;
864
865 if (found) {
866 if (ret)
867 *ret = c;
868 if (offset)
869 *offset = cp;
870 return 1;
871 }
872
873 r = next_with_matches(j, f, direction, &c, &cp);
874 if (r <= 0)
875 return r;
876 }
877}
878
e892bd17 879static int real_journal_next(sd_journal *j, direction_t direction) {
468b21de
LP
880 JournalFile *f, *new_file = NULL;
881 uint64_t new_offset = 0;
882 Object *o;
883 uint64_t p;
cec736d2 884 Iterator i;
87d2c1ff
LP
885 int r;
886
a5344d2c
LP
887 if (!j)
888 return -EINVAL;
87d2c1ff 889
cec736d2 890 HASHMAP_FOREACH(f, j->files, i) {
de190aef 891 bool found;
87d2c1ff 892
de190aef 893 r = next_beyond_location(j, f, direction, &o, &p);
e590af26
LP
894 if (r < 0) {
895 log_debug("Can't iterate through %s, ignoring: %s", f->path, strerror(-r));
896 continue;
897 } else if (r == 0)
cec736d2 898 continue;
87d2c1ff 899
468b21de 900 if (!new_file)
de190aef
LP
901 found = true;
902 else {
903 int k;
904
468b21de 905 k = compare_entry_order(f, o, new_file, new_offset);
de190aef
LP
906
907 if (direction == DIRECTION_DOWN)
908 found = k < 0;
909 else
910 found = k > 0;
911 }
912
913 if (found) {
468b21de 914 new_file = f;
cec736d2 915 new_offset = p;
87d2c1ff 916 }
87d2c1ff
LP
917 }
918
468b21de 919 if (!new_file)
de190aef 920 return 0;
ae2cc8ef 921
468b21de
LP
922 r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_offset, &o);
923 if (r < 0)
924 return r;
925
a87247dd 926 set_location(j, LOCATION_DISCRETE, new_file, o, new_offset);
ae2cc8ef 927
de190aef
LP
928 return 1;
929}
ae2cc8ef 930
a5344d2c 931_public_ int sd_journal_next(sd_journal *j) {
de190aef
LP
932 return real_journal_next(j, DIRECTION_DOWN);
933}
ae2cc8ef 934
a5344d2c 935_public_ int sd_journal_previous(sd_journal *j) {
de190aef
LP
936 return real_journal_next(j, DIRECTION_UP);
937}
ae2cc8ef 938
6f003b43 939static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
de190aef 940 int c = 0, r;
ae2cc8ef 941
a5344d2c
LP
942 if (!j)
943 return -EINVAL;
de190aef 944
6f003b43
LP
945 if (skip == 0) {
946 /* If this is not a discrete skip, then at least
947 * resolve the current location */
948 if (j->current_location.type != LOCATION_DISCRETE)
949 return real_journal_next(j, direction);
950
951 return 0;
952 }
953
954 do {
955 r = real_journal_next(j, direction);
de190aef
LP
956 if (r < 0)
957 return r;
958
959 if (r == 0)
960 return c;
961
962 skip--;
963 c++;
6f003b43 964 } while (skip > 0);
87d2c1ff 965
de190aef 966 return c;
87d2c1ff
LP
967}
968
6f003b43
LP
969_public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
970 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
971}
de190aef 972
6f003b43
LP
973_public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
974 return real_journal_next_skip(j, DIRECTION_UP, skip);
87d2c1ff
LP
975}
976
a5344d2c 977_public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
cec736d2 978 Object *o;
87d2c1ff 979 int r;
3fbf9cbb 980 char bid[33], sid[33];
87d2c1ff 981
a5344d2c
LP
982 if (!j)
983 return -EINVAL;
984 if (!cursor)
985 return -EINVAL;
87d2c1ff 986
3fbf9cbb
LP
987 if (!j->current_file || j->current_file->current_offset <= 0)
988 return -EADDRNOTAVAIL;
87d2c1ff 989
de190aef 990 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
87d2c1ff
LP
991 if (r < 0)
992 return r;
993
3fbf9cbb
LP
994 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
995 sd_id128_to_string(o->entry.boot_id, bid);
87d2c1ff 996
3fbf9cbb 997 if (asprintf(cursor,
934a316c 998 "s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx",
3fbf9cbb
LP
999 sid, (unsigned long long) le64toh(o->entry.seqnum),
1000 bid, (unsigned long long) le64toh(o->entry.monotonic),
1001 (unsigned long long) le64toh(o->entry.realtime),
934a316c 1002 (unsigned long long) le64toh(o->entry.xor_hash)) < 0)
3fbf9cbb 1003 return -ENOMEM;
87d2c1ff 1004
6f47ad30 1005 return 0;
87d2c1ff
LP
1006}
1007
a5344d2c 1008_public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
c6511e85 1009 char *w, *state;
de190aef 1010 size_t l;
de190aef
LP
1011 unsigned long long seqnum, monotonic, realtime, xor_hash;
1012 bool
1013 seqnum_id_set = false,
1014 seqnum_set = false,
1015 boot_id_set = false,
1016 monotonic_set = false,
1017 realtime_set = false,
1018 xor_hash_set = false;
1019 sd_id128_t seqnum_id, boot_id;
1020
a5344d2c
LP
1021 if (!j)
1022 return -EINVAL;
c6511e85 1023 if (isempty(cursor))
a5344d2c 1024 return -EINVAL;
de190aef
LP
1025
1026 FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
1027 char *item;
1028 int k = 0;
1029
1030 if (l < 2 || w[1] != '=')
1031 return -EINVAL;
1032
1033 item = strndup(w, l);
1034 if (!item)
1035 return -ENOMEM;
1036
1037 switch (w[0]) {
1038
1039 case 's':
1040 seqnum_id_set = true;
be3ea5ea 1041 k = sd_id128_from_string(item+2, &seqnum_id);
de190aef
LP
1042 break;
1043
1044 case 'i':
1045 seqnum_set = true;
be3ea5ea 1046 if (sscanf(item+2, "%llx", &seqnum) != 1)
de190aef
LP
1047 k = -EINVAL;
1048 break;
1049
1050 case 'b':
1051 boot_id_set = true;
be3ea5ea 1052 k = sd_id128_from_string(item+2, &boot_id);
de190aef
LP
1053 break;
1054
1055 case 'm':
1056 monotonic_set = true;
be3ea5ea 1057 if (sscanf(item+2, "%llx", &monotonic) != 1)
de190aef
LP
1058 k = -EINVAL;
1059 break;
1060
1061 case 't':
1062 realtime_set = true;
be3ea5ea 1063 if (sscanf(item+2, "%llx", &realtime) != 1)
de190aef
LP
1064 k = -EINVAL;
1065 break;
1066
1067 case 'x':
1068 xor_hash_set = true;
be3ea5ea 1069 if (sscanf(item+2, "%llx", &xor_hash) != 1)
de190aef
LP
1070 k = -EINVAL;
1071 break;
1072 }
1073
1074 free(item);
1075
1076 if (k < 0)
1077 return k;
1078 }
1079
1080 if ((!seqnum_set || !seqnum_id_set) &&
1081 (!monotonic_set || !boot_id_set) &&
1082 !realtime_set)
1083 return -EINVAL;
1084
1085 reset_location(j);
1086
a87247dd 1087 j->current_location.type = LOCATION_SEEK;
de190aef
LP
1088
1089 if (realtime_set) {
1090 j->current_location.realtime = (uint64_t) realtime;
1091 j->current_location.realtime_set = true;
1092 }
1093
1094 if (seqnum_set && seqnum_id_set) {
1095 j->current_location.seqnum = (uint64_t) seqnum;
1096 j->current_location.seqnum_id = seqnum_id;
1097 j->current_location.seqnum_set = true;
1098 }
1099
1100 if (monotonic_set && boot_id_set) {
1101 j->current_location.monotonic = (uint64_t) monotonic;
1102 j->current_location.boot_id = boot_id;
1103 j->current_location.monotonic_set = true;
1104 }
1105
1106 if (xor_hash_set) {
1107 j->current_location.xor_hash = (uint64_t) xor_hash;
1108 j->current_location.xor_hash_set = true;
1109 }
1110
1111 return 0;
1112}
1113
c6511e85
LP
1114_public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
1115 int r;
1116 char *w, *state;
1117 size_t l;
1118 Object *o;
1119
1120 if (!j)
1121 return -EINVAL;
1122 if (isempty(cursor))
1123 return -EINVAL;
1124
1125 if (!j->current_file || j->current_file->current_offset <= 0)
1126 return -EADDRNOTAVAIL;
1127
1128 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1129 if (r < 0)
1130 return r;
1131
1132 FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
1133 _cleanup_free_ char *item = NULL;
1134 sd_id128_t id;
1135 unsigned long long ll;
1136 int k = 0;
1137
1138 if (l < 2 || w[1] != '=')
1139 return -EINVAL;
1140
1141 item = strndup(w, l);
1142 if (!item)
1143 return -ENOMEM;
1144
1145 switch (w[0]) {
1146
1147 case 's':
1148 k = sd_id128_from_string(item+2, &id);
1149 if (k < 0)
1150 return k;
1151 if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
1152 return 0;
1153 break;
1154
1155 case 'i':
1156 if (sscanf(item+2, "%llx", &ll) != 1)
1157 return -EINVAL;
1158 if (ll != le64toh(o->entry.seqnum))
1159 return 0;
1160 break;
1161
1162 case 'b':
1163 k = sd_id128_from_string(item+2, &id);
1164 if (k < 0)
1165 return k;
1166 if (!sd_id128_equal(id, o->entry.boot_id))
1167 return 0;
1168 break;
1169
1170 case 'm':
1171 if (sscanf(item+2, "%llx", &ll) != 1)
1172 return -EINVAL;
1173 if (ll != le64toh(o->entry.monotonic))
1174 return 0;
1175 break;
1176
1177 case 't':
1178 if (sscanf(item+2, "%llx", &ll) != 1)
1179 return -EINVAL;
1180 if (ll != le64toh(o->entry.realtime))
1181 return 0;
1182 break;
1183
1184 case 'x':
1185 if (sscanf(item+2, "%llx", &ll) != 1)
1186 return -EINVAL;
1187 if (ll != le64toh(o->entry.xor_hash))
1188 return 0;
1189 break;
1190 }
1191 }
1192
1193 return 1;
1194}
1195
1196
a5344d2c
LP
1197_public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1198 if (!j)
1199 return -EINVAL;
de190aef
LP
1200
1201 reset_location(j);
a87247dd 1202 j->current_location.type = LOCATION_SEEK;
de190aef
LP
1203 j->current_location.boot_id = boot_id;
1204 j->current_location.monotonic = usec;
1205 j->current_location.monotonic_set = true;
1206
1207 return 0;
1208}
1209
a5344d2c
LP
1210_public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1211 if (!j)
1212 return -EINVAL;
de190aef
LP
1213
1214 reset_location(j);
a87247dd 1215 j->current_location.type = LOCATION_SEEK;
de190aef
LP
1216 j->current_location.realtime = usec;
1217 j->current_location.realtime_set = true;
1218
1219 return 0;
1220}
1221
a5344d2c
LP
1222_public_ int sd_journal_seek_head(sd_journal *j) {
1223 if (!j)
1224 return -EINVAL;
de190aef
LP
1225
1226 reset_location(j);
1227 j->current_location.type = LOCATION_HEAD;
1228
1229 return 0;
1230}
1231
a5344d2c
LP
1232_public_ int sd_journal_seek_tail(sd_journal *j) {
1233 if (!j)
1234 return -EINVAL;
de190aef
LP
1235
1236 reset_location(j);
1237 j->current_location.type = LOCATION_TAIL;
1238
1239 return 0;
87d2c1ff
LP
1240}
1241
85210bff
LP
1242static void check_network(sd_journal *j, int fd) {
1243 struct statfs sfs;
1244
1245 assert(j);
1246
1247 if (j->on_network)
1248 return;
1249
1250 if (fstatfs(fd, &sfs) < 0)
1251 return;
1252
1253 j->on_network =
bdd29249
HH
1254 F_TYPE_CMP(sfs.f_type, CIFS_MAGIC_NUMBER) ||
1255 F_TYPE_CMP(sfs.f_type, CODA_SUPER_MAGIC) ||
1256 F_TYPE_CMP(sfs.f_type, NCP_SUPER_MAGIC) ||
1257 F_TYPE_CMP(sfs.f_type, NFS_SUPER_MAGIC) ||
1258 F_TYPE_CMP(sfs.f_type, SMB_SUPER_MAGIC);
85210bff
LP
1259}
1260
a963990f 1261static int add_file(sd_journal *j, const char *prefix, const char *filename) {
7fd1b19b 1262 _cleanup_free_ char *path = NULL;
3fbf9cbb
LP
1263 int r;
1264 JournalFile *f;
1265
1266 assert(j);
1267 assert(prefix);
1268 assert(filename);
1269
cf244689 1270 if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) &&
38a6db16 1271 !(streq(filename, "system.journal") ||
de2c3907
LP
1272 streq(filename, "system.journal~") ||
1273 (startswith(filename, "system@") &&
1274 (endswith(filename, ".journal") || endswith(filename, ".journal~")))))
cf244689
LP
1275 return 0;
1276
b7def684 1277 path = strjoin(prefix, "/", filename, NULL);
a963990f 1278 if (!path)
3fbf9cbb
LP
1279 return -ENOMEM;
1280
a50d7d43 1281 if (hashmap_get(j->files, path))
50f20cfd 1282 return 0;
50f20cfd
LP
1283
1284 if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
a963990f 1285 log_debug("Too many open journal files, not adding %s, ignoring.", path);
3ac251b8 1286 return set_put_error(j, -ETOOMANYREFS);
50f20cfd
LP
1287 }
1288
16e9f408 1289 r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
3fbf9cbb
LP
1290 if (r < 0) {
1291 if (errno == ENOENT)
1292 return 0;
1293
1294 return r;
1295 }
1296
72f59706 1297 /* journal_file_dump(f); */
de190aef 1298
3fbf9cbb
LP
1299 r = hashmap_put(j->files, f->path, f);
1300 if (r < 0) {
1301 journal_file_close(f);
1302 return r;
1303 }
1304
a50d7d43
ZJS
1305 log_debug("File %s got added.", f->path);
1306
85210bff
LP
1307 check_network(j, f->fd);
1308
a963990f
LP
1309 j->current_invalidate_counter ++;
1310
50f20cfd
LP
1311 return 0;
1312}
1313
a963990f
LP
1314static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
1315 char *path;
50f20cfd
LP
1316 JournalFile *f;
1317
1318 assert(j);
1319 assert(prefix);
1320 assert(filename);
1321
b7def684 1322 path = strjoin(prefix, "/", filename, NULL);
a963990f 1323 if (!path)
50f20cfd
LP
1324 return -ENOMEM;
1325
a963990f
LP
1326 f = hashmap_get(j->files, path);
1327 free(path);
50f20cfd
LP
1328 if (!f)
1329 return 0;
1330
1331 hashmap_remove(j->files, f->path);
44a5fa34
LP
1332
1333 log_debug("File %s got removed.", f->path);
1334
3c1668da
LP
1335 if (j->current_file == f) {
1336 j->current_file = NULL;
1337 j->current_field = 0;
1338 }
1339
1340 if (j->unique_file == f) {
1341 j->unique_file = NULL;
1342 j->unique_offset = 0;
1343 }
1344
50f20cfd
LP
1345 journal_file_close(f);
1346
a963990f
LP
1347 j->current_invalidate_counter ++;
1348
3fbf9cbb
LP
1349 return 0;
1350}
1351
a963990f 1352static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
7fd1b19b 1353 _cleanup_free_ char *path = NULL;
3fbf9cbb 1354 int r;
7fd1b19b 1355 _cleanup_closedir_ DIR *d = NULL;
cf244689 1356 sd_id128_t id, mid;
a963990f 1357 Directory *m;
3fbf9cbb
LP
1358
1359 assert(j);
1360 assert(prefix);
a963990f 1361 assert(dirname);
3fbf9cbb 1362
d95b1fb3
ZJS
1363 log_debug("Considering %s/%s.", prefix, dirname);
1364
cf244689 1365 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
a963990f 1366 (sd_id128_from_string(dirname, &id) < 0 ||
cf244689 1367 sd_id128_get_machine(&mid) < 0 ||
d95b1fb3 1368 !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
cf244689
LP
1369 return 0;
1370
b7def684 1371 path = strjoin(prefix, "/", dirname, NULL);
a963990f 1372 if (!path)
3fbf9cbb
LP
1373 return -ENOMEM;
1374
a963990f 1375 d = opendir(path);
3fbf9cbb 1376 if (!d) {
a963990f 1377 log_debug("Failed to open %s: %m", path);
3fbf9cbb
LP
1378 if (errno == ENOENT)
1379 return 0;
3fbf9cbb
LP
1380 return -errno;
1381 }
1382
a963990f
LP
1383 m = hashmap_get(j->directories_by_path, path);
1384 if (!m) {
1385 m = new0(Directory, 1);
a50d7d43 1386 if (!m)
a963990f 1387 return -ENOMEM;
a963990f
LP
1388
1389 m->is_root = false;
1390 m->path = path;
1391
1392 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
a963990f
LP
1393 free(m);
1394 return -ENOMEM;
1395 }
1396
a50d7d43 1397 path = NULL; /* avoid freeing in cleanup */
a963990f
LP
1398 j->current_invalidate_counter ++;
1399
1400 log_debug("Directory %s got added.", m->path);
1401
a50d7d43 1402 } else if (m->is_root)
a963990f 1403 return 0;
a963990f
LP
1404
1405 if (m->wd <= 0 && j->inotify_fd >= 0) {
1406
1407 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1408 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
5e6870ea 1409 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
4a842cad 1410 IN_ONLYDIR);
a963990f
LP
1411
1412 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1413 inotify_rm_watch(j->inotify_fd, m->wd);
1414 }
1415
1416 for (;;) {
7d5e9c0f
LP
1417 struct dirent *de;
1418 union dirent_storage buf;
a963990f 1419
7d5e9c0f 1420 r = readdir_r(d, &buf.de, &de);
a963990f
LP
1421 if (r != 0 || !de)
1422 break;
1423
de2c3907
LP
1424 if (dirent_is_file_with_suffix(de, ".journal") ||
1425 dirent_is_file_with_suffix(de, ".journal~")) {
a963990f 1426 r = add_file(j, m->path, de->d_name);
6fe391c5
ZJS
1427 if (r < 0) {
1428 log_debug("Failed to add file %s/%s: %s",
1429 m->path, de->d_name, strerror(-r));
3ac251b8 1430 r = set_put_error(j, r);
6fe391c5
ZJS
1431 if (r < 0)
1432 return r;
1433 }
a963990f
LP
1434 }
1435 }
1436
85210bff
LP
1437 check_network(j, dirfd(d));
1438
a963990f
LP
1439 return 0;
1440}
1441
1442static int add_root_directory(sd_journal *j, const char *p) {
7fd1b19b 1443 _cleanup_closedir_ DIR *d = NULL;
a963990f
LP
1444 Directory *m;
1445 int r;
1446
1447 assert(j);
1448 assert(p);
1449
1450 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1451 !path_startswith(p, "/run"))
1452 return -EINVAL;
1453
1454 d = opendir(p);
1455 if (!d)
1456 return -errno;
1457
1458 m = hashmap_get(j->directories_by_path, p);
1459 if (!m) {
1460 m = new0(Directory, 1);
a50d7d43 1461 if (!m)
a963990f 1462 return -ENOMEM;
a963990f
LP
1463
1464 m->is_root = true;
1465 m->path = strdup(p);
1466 if (!m->path) {
a963990f
LP
1467 free(m);
1468 return -ENOMEM;
1469 }
1470
1471 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
a963990f
LP
1472 free(m->path);
1473 free(m);
1474 return -ENOMEM;
1475 }
1476
1477 j->current_invalidate_counter ++;
1478
1479 log_debug("Root directory %s got added.", m->path);
1480
a50d7d43 1481 } else if (!m->is_root)
a963990f 1482 return 0;
50f20cfd 1483
a963990f
LP
1484 if (m->wd <= 0 && j->inotify_fd >= 0) {
1485
1486 m->wd = inotify_add_watch(j->inotify_fd, m->path,
1487 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
4a842cad 1488 IN_ONLYDIR);
a963990f
LP
1489
1490 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1491 inotify_rm_watch(j->inotify_fd, m->wd);
1492 }
50f20cfd 1493
3fbf9cbb 1494 for (;;) {
7d5e9c0f
LP
1495 struct dirent *de;
1496 union dirent_storage buf;
a963990f 1497 sd_id128_t id;
3fbf9cbb 1498
7d5e9c0f 1499 r = readdir_r(d, &buf.de, &de);
3fbf9cbb
LP
1500 if (r != 0 || !de)
1501 break;
1502
de2c3907
LP
1503 if (dirent_is_file_with_suffix(de, ".journal") ||
1504 dirent_is_file_with_suffix(de, ".journal~")) {
a963990f 1505 r = add_file(j, m->path, de->d_name);
6fe391c5
ZJS
1506 if (r < 0) {
1507 log_debug("Failed to add file %s/%s: %s",
1508 m->path, de->d_name, strerror(-r));
3ac251b8 1509 r = set_put_error(j, r);
6fe391c5
ZJS
1510 if (r < 0)
1511 return r;
1512 }
6f5878a2 1513 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
a963990f
LP
1514 sd_id128_from_string(de->d_name, &id) >= 0) {
1515
1516 r = add_directory(j, m->path, de->d_name);
1517 if (r < 0)
1518 log_debug("Failed to add directory %s/%s: %s", m->path, de->d_name, strerror(-r));
1519 }
3fbf9cbb
LP
1520 }
1521
85210bff
LP
1522 check_network(j, dirfd(d));
1523
a963990f
LP
1524 return 0;
1525}
1526
1527static int remove_directory(sd_journal *j, Directory *d) {
1528 assert(j);
1529
1530 if (d->wd > 0) {
1531 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1532
1533 if (j->inotify_fd >= 0)
1534 inotify_rm_watch(j->inotify_fd, d->wd);
1535 }
1536
1537 hashmap_remove(j->directories_by_path, d->path);
1538
1539 if (d->is_root)
1540 log_debug("Root directory %s got removed.", d->path);
1541 else
1542 log_debug("Directory %s got removed.", d->path);
1543
1544 free(d->path);
1545 free(d);
50f20cfd 1546
3fbf9cbb
LP
1547 return 0;
1548}
1549
a963990f 1550static int add_search_paths(sd_journal *j) {
6fe391c5 1551 int r;
a963990f
LP
1552 const char search_paths[] =
1553 "/run/log/journal\0"
1554 "/var/log/journal\0";
1555 const char *p;
50f20cfd
LP
1556
1557 assert(j);
50f20cfd 1558
a963990f
LP
1559 /* We ignore most errors here, since the idea is to only open
1560 * what's actually accessible, and ignore the rest. */
50f20cfd 1561
6fe391c5
ZJS
1562 NULSTR_FOREACH(p, search_paths) {
1563 r = add_root_directory(j, p);
3ac251b8
LP
1564 if (r < 0 && r != -ENOENT) {
1565 r = set_put_error(j, r);
1566 if (r < 0)
1567 return r;
1568 }
6fe391c5 1569 }
50f20cfd 1570
a963990f 1571 return 0;
50f20cfd
LP
1572}
1573
a963990f 1574static int allocate_inotify(sd_journal *j) {
50f20cfd 1575 assert(j);
50f20cfd 1576
a963990f
LP
1577 if (j->inotify_fd < 0) {
1578 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1579 if (j->inotify_fd < 0)
1580 return -errno;
1581 }
50f20cfd 1582
a963990f
LP
1583 if (!j->directories_by_wd) {
1584 j->directories_by_wd = hashmap_new(trivial_hash_func, trivial_compare_func);
1585 if (!j->directories_by_wd)
1586 return -ENOMEM;
50f20cfd 1587 }
a963990f
LP
1588
1589 return 0;
50f20cfd
LP
1590}
1591
7827b1a1 1592static sd_journal *journal_new(int flags, const char *path) {
a963990f 1593 sd_journal *j;
50f20cfd 1594
a963990f
LP
1595 j = new0(sd_journal, 1);
1596 if (!j)
1597 return NULL;
50f20cfd 1598
a963990f
LP
1599 j->inotify_fd = -1;
1600 j->flags = flags;
93b73b06 1601 j->data_threshold = DEFAULT_DATA_THRESHOLD;
50f20cfd 1602
7827b1a1
LP
1603 if (path) {
1604 j->path = strdup(path);
6180fc61
ZJS
1605 if (!j->path)
1606 goto fail;
7827b1a1
LP
1607 }
1608
a963990f 1609 j->files = hashmap_new(string_hash_func, string_compare_func);
a963990f 1610 j->directories_by_path = hashmap_new(string_hash_func, string_compare_func);
84168d80 1611 j->mmap = mmap_cache_new();
3ac251b8 1612 if (!j->files || !j->directories_by_path || !j->mmap)
6180fc61 1613 goto fail;
16e9f408 1614
a963990f 1615 return j;
6180fc61
ZJS
1616
1617fail:
1618 sd_journal_close(j);
1619 return NULL;
50f20cfd
LP
1620}
1621
a5344d2c 1622_public_ int sd_journal_open(sd_journal **ret, int flags) {
87d2c1ff 1623 sd_journal *j;
3fbf9cbb 1624 int r;
87d2c1ff 1625
a5344d2c
LP
1626 if (!ret)
1627 return -EINVAL;
1628
1629 if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
1630 SD_JOURNAL_RUNTIME_ONLY|
1631 SD_JOURNAL_SYSTEM_ONLY))
1632 return -EINVAL;
87d2c1ff 1633
7827b1a1 1634 j = journal_new(flags, NULL);
87d2c1ff
LP
1635 if (!j)
1636 return -ENOMEM;
1637
a963990f
LP
1638 r = add_search_paths(j);
1639 if (r < 0)
50f20cfd 1640 goto fail;
50f20cfd 1641
a963990f
LP
1642 *ret = j;
1643 return 0;
cf244689 1644
a963990f
LP
1645fail:
1646 sd_journal_close(j);
87d2c1ff 1647
a963990f
LP
1648 return r;
1649}
50f20cfd 1650
a963990f
LP
1651_public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1652 sd_journal *j;
1653 int r;
87d2c1ff 1654
a963990f
LP
1655 if (!ret)
1656 return -EINVAL;
87d2c1ff 1657
8cb17a6d 1658 if (!path)
a963990f 1659 return -EINVAL;
87d2c1ff 1660
a963990f
LP
1661 if (flags != 0)
1662 return -EINVAL;
87d2c1ff 1663
7827b1a1 1664 j = journal_new(flags, path);
a963990f
LP
1665 if (!j)
1666 return -ENOMEM;
3fbf9cbb 1667
a963990f 1668 r = add_root_directory(j, path);
6fe391c5 1669 if (r < 0) {
3ac251b8 1670 set_put_error(j, r);
a963990f 1671 goto fail;
6fe391c5 1672 }
87d2c1ff
LP
1673
1674 *ret = j;
1675 return 0;
1676
1677fail:
1678 sd_journal_close(j);
1679
1680 return r;
a963990f 1681}
87d2c1ff 1682
a5344d2c 1683_public_ void sd_journal_close(sd_journal *j) {
a963990f
LP
1684 Directory *d;
1685 JournalFile *f;
1686
a5344d2c
LP
1687 if (!j)
1688 return;
87d2c1ff 1689
54b1da83
LP
1690 sd_journal_flush_matches(j);
1691
a963990f
LP
1692 while ((f = hashmap_steal_first(j->files)))
1693 journal_file_close(f);
50f20cfd 1694
a963990f 1695 hashmap_free(j->files);
260a2be4 1696
a963990f
LP
1697 while ((d = hashmap_first(j->directories_by_path)))
1698 remove_directory(j, d);
260a2be4 1699
a963990f
LP
1700 while ((d = hashmap_first(j->directories_by_wd)))
1701 remove_directory(j, d);
87d2c1ff 1702
a963990f
LP
1703 hashmap_free(j->directories_by_path);
1704 hashmap_free(j->directories_by_wd);
1cc101f1 1705
50f20cfd
LP
1706 if (j->inotify_fd >= 0)
1707 close_nointr_nofail(j->inotify_fd);
1708
16e9f408
LP
1709 if (j->mmap)
1710 mmap_cache_unref(j->mmap);
1711
7827b1a1 1712 free(j->path);
3c1668da 1713 free(j->unique_field);
6fe391c5 1714 set_free(j->errors);
87d2c1ff
LP
1715 free(j);
1716}
3fbf9cbb 1717
a5344d2c 1718_public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
3fbf9cbb
LP
1719 Object *o;
1720 JournalFile *f;
1721 int r;
1722
a5344d2c
LP
1723 if (!j)
1724 return -EINVAL;
1725 if (!ret)
1726 return -EINVAL;
3fbf9cbb
LP
1727
1728 f = j->current_file;
1729 if (!f)
de190aef 1730 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1731
1732 if (f->current_offset <= 0)
de190aef 1733 return -EADDRNOTAVAIL;
3fbf9cbb 1734
de190aef 1735 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1736 if (r < 0)
1737 return r;
1738
1739 *ret = le64toh(o->entry.realtime);
de190aef 1740 return 0;
3fbf9cbb
LP
1741}
1742
a5344d2c 1743_public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
3fbf9cbb
LP
1744 Object *o;
1745 JournalFile *f;
1746 int r;
1747 sd_id128_t id;
1748
a5344d2c
LP
1749 if (!j)
1750 return -EINVAL;
3fbf9cbb
LP
1751
1752 f = j->current_file;
1753 if (!f)
de190aef 1754 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1755
1756 if (f->current_offset <= 0)
de190aef 1757 return -EADDRNOTAVAIL;
3fbf9cbb 1758
de190aef 1759 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1760 if (r < 0)
1761 return r;
1762
de190aef
LP
1763 if (ret_boot_id)
1764 *ret_boot_id = o->entry.boot_id;
1765 else {
1766 r = sd_id128_get_boot(&id);
1767 if (r < 0)
1768 return r;
3fbf9cbb 1769
de190aef 1770 if (!sd_id128_equal(id, o->entry.boot_id))
df50185b 1771 return -ESTALE;
de190aef 1772 }
3fbf9cbb 1773
14a65d65
LP
1774 if (ret)
1775 *ret = le64toh(o->entry.monotonic);
1776
de190aef 1777 return 0;
3fbf9cbb
LP
1778}
1779
362a3f81
LP
1780static bool field_is_valid(const char *field) {
1781 const char *p;
1782
1783 assert(field);
1784
1785 if (isempty(field))
1786 return false;
1787
1788 if (startswith(field, "__"))
1789 return false;
1790
1791 for (p = field; *p; p++) {
1792
1793 if (*p == '_')
1794 continue;
1795
1796 if (*p >= 'A' && *p <= 'Z')
1797 continue;
1798
1799 if (*p >= '0' && *p <= '9')
1800 continue;
1801
1802 return false;
1803 }
1804
1805 return true;
1806}
1807
a5344d2c 1808_public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
3fbf9cbb
LP
1809 JournalFile *f;
1810 uint64_t i, n;
1811 size_t field_length;
1812 int r;
1813 Object *o;
1814
a5344d2c
LP
1815 if (!j)
1816 return -EINVAL;
1817 if (!field)
1818 return -EINVAL;
1819 if (!data)
1820 return -EINVAL;
1821 if (!size)
1822 return -EINVAL;
3fbf9cbb 1823
362a3f81 1824 if (!field_is_valid(field))
3fbf9cbb
LP
1825 return -EINVAL;
1826
1827 f = j->current_file;
1828 if (!f)
de190aef 1829 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1830
1831 if (f->current_offset <= 0)
de190aef 1832 return -EADDRNOTAVAIL;
3fbf9cbb 1833
de190aef 1834 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1835 if (r < 0)
1836 return r;
1837
1838 field_length = strlen(field);
1839
1840 n = journal_file_entry_n_items(o);
1841 for (i = 0; i < n; i++) {
4fd052ae
FC
1842 uint64_t p, l;
1843 le64_t le_hash;
3fbf9cbb
LP
1844 size_t t;
1845
1846 p = le64toh(o->entry.items[i].object_offset);
807e17f0 1847 le_hash = o->entry.items[i].hash;
de190aef 1848 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
3fbf9cbb
LP
1849 if (r < 0)
1850 return r;
1851
de190aef 1852 if (le_hash != o->data.hash)
de7b95cd
LP
1853 return -EBADMSG;
1854
3fbf9cbb
LP
1855 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1856
807e17f0
LP
1857 if (o->object.flags & OBJECT_COMPRESSED) {
1858
1859#ifdef HAVE_XZ
1860 if (uncompress_startswith(o->data.payload, l,
1861 &f->compress_buffer, &f->compress_buffer_size,
1862 field, field_length, '=')) {
1863
1864 uint64_t rsize;
1865
1866 if (!uncompress_blob(o->data.payload, l,
93b73b06
LP
1867 &f->compress_buffer, &f->compress_buffer_size, &rsize,
1868 j->data_threshold))
807e17f0
LP
1869 return -EBADMSG;
1870
1871 *data = f->compress_buffer;
1872 *size = (size_t) rsize;
1873
1874 return 0;
1875 }
1876#else
1877 return -EPROTONOSUPPORT;
1878#endif
1879
1880 } else if (l >= field_length+1 &&
1881 memcmp(o->data.payload, field, field_length) == 0 &&
1882 o->data.payload[field_length] == '=') {
3fbf9cbb 1883
161e54f8 1884 t = (size_t) l;
3fbf9cbb 1885
161e54f8
LP
1886 if ((uint64_t) t != l)
1887 return -E2BIG;
3fbf9cbb 1888
161e54f8
LP
1889 *data = o->data.payload;
1890 *size = t;
3fbf9cbb 1891
99613ec5 1892 return 0;
161e54f8 1893 }
3fbf9cbb 1894
de190aef 1895 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
161e54f8
LP
1896 if (r < 0)
1897 return r;
3fbf9cbb
LP
1898 }
1899
de190aef 1900 return -ENOENT;
3fbf9cbb
LP
1901}
1902
93b73b06 1903static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
3c1668da
LP
1904 size_t t;
1905 uint64_t l;
1906
1907 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1908 t = (size_t) l;
1909
1910 /* We can't read objects larger than 4G on a 32bit machine */
1911 if ((uint64_t) t != l)
1912 return -E2BIG;
1913
1914 if (o->object.flags & OBJECT_COMPRESSED) {
1915#ifdef HAVE_XZ
1916 uint64_t rsize;
1917
93b73b06 1918 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize, j->data_threshold))
3c1668da
LP
1919 return -EBADMSG;
1920
1921 *data = f->compress_buffer;
1922 *size = (size_t) rsize;
1923#else
1924 return -EPROTONOSUPPORT;
1925#endif
1926 } else {
1927 *data = o->data.payload;
1928 *size = t;
1929 }
1930
1931 return 0;
1932}
1933
a5344d2c 1934_public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
3fbf9cbb 1935 JournalFile *f;
3c1668da 1936 uint64_t p, n;
4fd052ae 1937 le64_t le_hash;
3fbf9cbb
LP
1938 int r;
1939 Object *o;
1940
a5344d2c
LP
1941 if (!j)
1942 return -EINVAL;
1943 if (!data)
1944 return -EINVAL;
1945 if (!size)
1946 return -EINVAL;
3fbf9cbb
LP
1947
1948 f = j->current_file;
1949 if (!f)
de190aef 1950 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1951
1952 if (f->current_offset <= 0)
de190aef 1953 return -EADDRNOTAVAIL;
3fbf9cbb 1954
de190aef 1955 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1956 if (r < 0)
1957 return r;
1958
1959 n = journal_file_entry_n_items(o);
7210bfb3 1960 if (j->current_field >= n)
3fbf9cbb
LP
1961 return 0;
1962
7210bfb3 1963 p = le64toh(o->entry.items[j->current_field].object_offset);
de190aef
LP
1964 le_hash = o->entry.items[j->current_field].hash;
1965 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
3fbf9cbb
LP
1966 if (r < 0)
1967 return r;
1968
de190aef 1969 if (le_hash != o->data.hash)
de7b95cd
LP
1970 return -EBADMSG;
1971
93b73b06 1972 r = return_data(j, f, o, data, size);
3c1668da
LP
1973 if (r < 0)
1974 return r;
3fbf9cbb 1975
7210bfb3 1976 j->current_field ++;
3fbf9cbb
LP
1977
1978 return 1;
1979}
c2373f84 1980
a5344d2c
LP
1981_public_ void sd_journal_restart_data(sd_journal *j) {
1982 if (!j)
1983 return;
8725d60a
LP
1984
1985 j->current_field = 0;
c2373f84 1986}
50f20cfd 1987
a5344d2c 1988_public_ int sd_journal_get_fd(sd_journal *j) {
a963990f
LP
1989 int r;
1990
a5344d2c
LP
1991 if (!j)
1992 return -EINVAL;
50f20cfd 1993
a963990f
LP
1994 if (j->inotify_fd >= 0)
1995 return j->inotify_fd;
1996
1997 r = allocate_inotify(j);
1998 if (r < 0)
1999 return r;
2000
2001 /* Iterate through all dirs again, to add them to the
2002 * inotify */
7827b1a1
LP
2003 if (j->path)
2004 r = add_root_directory(j, j->path);
2005 else
2006 r = add_search_paths(j);
a963990f
LP
2007 if (r < 0)
2008 return r;
2009
50f20cfd
LP
2010 return j->inotify_fd;
2011}
2012
ee531d94
LP
2013_public_ int sd_journal_get_events(sd_journal *j) {
2014 int fd;
2015
2016 if (!j)
2017 return -EINVAL;
2018
2019 fd = sd_journal_get_fd(j);
2020 if (fd < 0)
2021 return fd;
2022
2023 return POLLIN;
2024}
2025
39c155ea
LP
2026_public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2027 int fd;
2028
2029 if (!j)
2030 return -EINVAL;
2031 if (!timeout_usec)
2032 return -EINVAL;
2033
2034 fd = sd_journal_get_fd(j);
2035 if (fd < 0)
2036 return fd;
2037
2038 if (!j->on_network) {
2039 *timeout_usec = (uint64_t) -1;
2040 return 0;
2041 }
2042
2043 /* If we are on the network we need to regularly check for
2044 * changes manually */
2045
2046 *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2047 return 1;
2048}
2049
50f20cfd 2050static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
a963990f 2051 Directory *d;
50f20cfd
LP
2052 int r;
2053
2054 assert(j);
2055 assert(e);
2056
2057 /* Is this a subdirectory we watch? */
a963990f
LP
2058 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2059 if (d) {
2060 sd_id128_t id;
50f20cfd 2061
de2c3907
LP
2062 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2063 (endswith(e->name, ".journal") ||
2064 endswith(e->name, ".journal~"))) {
50f20cfd
LP
2065
2066 /* Event for a journal file */
2067
2068 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
a963990f 2069 r = add_file(j, d->path, e->name);
6fe391c5
ZJS
2070 if (r < 0) {
2071 log_debug("Failed to add file %s/%s: %s",
2072 d->path, e->name, strerror(-r));
3ac251b8 2073 set_put_error(j, r);
6fe391c5 2074 }
a963990f 2075
5e6870ea 2076 } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
50f20cfd 2077
a963990f 2078 r = remove_file(j, d->path, e->name);
50f20cfd 2079 if (r < 0)
a963990f 2080 log_debug("Failed to remove file %s/%s: %s", d->path, e->name, strerror(-r));
50f20cfd
LP
2081 }
2082
a963990f 2083 } else if (!d->is_root && e->len == 0) {
50f20cfd 2084
a963990f 2085 /* Event for a subdirectory */
50f20cfd 2086
a963990f
LP
2087 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
2088 r = remove_directory(j, d);
50f20cfd 2089 if (r < 0)
a963990f 2090 log_debug("Failed to remove directory %s: %s", d->path, strerror(-r));
50f20cfd
LP
2091 }
2092
50f20cfd 2093
a963990f 2094 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
50f20cfd 2095
a963990f 2096 /* Event for root directory */
50f20cfd 2097
a963990f
LP
2098 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2099 r = add_directory(j, d->path, e->name);
50f20cfd 2100 if (r < 0)
a963990f 2101 log_debug("Failed to add directory %s/%s: %s", d->path, e->name, strerror(-r));
50f20cfd
LP
2102 }
2103 }
2104
2105 return;
2106 }
2107
2108 if (e->mask & IN_IGNORED)
2109 return;
2110
2111 log_warning("Unknown inotify event.");
2112}
2113
a963990f
LP
2114static int determine_change(sd_journal *j) {
2115 bool b;
2116
2117 assert(j);
2118
2119 b = j->current_invalidate_counter != j->last_invalidate_counter;
2120 j->last_invalidate_counter = j->current_invalidate_counter;
2121
2122 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2123}
2124
a5344d2c 2125_public_ int sd_journal_process(sd_journal *j) {
19d1e4ee 2126 uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX] _alignas_(struct inotify_event);
a963990f 2127 bool got_something = false;
50f20cfd 2128
a5344d2c
LP
2129 if (!j)
2130 return -EINVAL;
50f20cfd 2131
39c155ea
LP
2132 j->last_process_usec = now(CLOCK_MONOTONIC);
2133
50f20cfd
LP
2134 for (;;) {
2135 struct inotify_event *e;
2136 ssize_t l;
2137
2138 l = read(j->inotify_fd, buffer, sizeof(buffer));
2139 if (l < 0) {
a963990f
LP
2140 if (errno == EAGAIN || errno == EINTR)
2141 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
50f20cfd
LP
2142
2143 return -errno;
2144 }
2145
a963990f
LP
2146 got_something = true;
2147
50f20cfd
LP
2148 e = (struct inotify_event*) buffer;
2149 while (l > 0) {
2150 size_t step;
2151
2152 process_inotify_event(j, e);
2153
2154 step = sizeof(struct inotify_event) + e->len;
2155 assert(step <= (size_t) l);
2156
2157 e = (struct inotify_event*) ((uint8_t*) e + step);
2158 l -= step;
2159 }
2160 }
a963990f
LP
2161
2162 return determine_change(j);
50f20cfd 2163}
6ad1d1c3 2164
e02d1cf7 2165_public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
a963990f 2166 int r;
39c155ea 2167 uint64_t t;
e02d1cf7
LP
2168
2169 assert(j);
2170
a963990f
LP
2171 if (j->inotify_fd < 0) {
2172
2173 /* This is the first invocation, hence create the
2174 * inotify watch */
2175 r = sd_journal_get_fd(j);
2176 if (r < 0)
2177 return r;
2178
2179 /* The journal might have changed since the context
2180 * object was created and we weren't watching before,
2181 * hence don't wait for anything, and return
2182 * immediately. */
2183 return determine_change(j);
2184 }
2185
39c155ea
LP
2186 r = sd_journal_get_timeout(j, &t);
2187 if (r < 0)
2188 return r;
2189
2190 if (t != (uint64_t) -1) {
2191 usec_t n;
2192
2193 n = now(CLOCK_MONOTONIC);
2194 t = t > n ? t - n : 0;
85210bff 2195
39c155ea
LP
2196 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2197 timeout_usec = t;
85210bff
LP
2198 }
2199
a963990f
LP
2200 do {
2201 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2202 } while (r == -EINTR);
e02d1cf7
LP
2203
2204 if (r < 0)
2205 return r;
2206
a963990f 2207 return sd_journal_process(j);
e02d1cf7
LP
2208}
2209
08984293
LP
2210_public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2211 Iterator i;
2212 JournalFile *f;
2213 bool first = true;
2214 int r;
2215
2216 if (!j)
2217 return -EINVAL;
2218 if (!from && !to)
2219 return -EINVAL;
39887731
TA
2220 if (from == to)
2221 return -EINVAL;
08984293
LP
2222
2223 HASHMAP_FOREACH(f, j->files, i) {
2224 usec_t fr, t;
2225
2226 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
9f8d2983
LP
2227 if (r == -ENOENT)
2228 continue;
08984293
LP
2229 if (r < 0)
2230 return r;
2231 if (r == 0)
2232 continue;
2233
2234 if (first) {
2235 if (from)
2236 *from = fr;
2237 if (to)
2238 *to = t;
2239 first = false;
2240 } else {
2241 if (from)
2242 *from = MIN(fr, *from);
2243 if (to)
0f91dd87 2244 *to = MAX(t, *to);
08984293
LP
2245 }
2246 }
2247
2248 return first ? 0 : 1;
2249}
2250
2251_public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2252 Iterator i;
2253 JournalFile *f;
2254 bool first = true;
2255 int r;
2256
2257 if (!j)
2258 return -EINVAL;
2259 if (!from && !to)
2260 return -EINVAL;
39887731
TA
2261 if (from == to)
2262 return -EINVAL;
08984293
LP
2263
2264 HASHMAP_FOREACH(f, j->files, i) {
2265 usec_t fr, t;
2266
2267 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
9f8d2983
LP
2268 if (r == -ENOENT)
2269 continue;
08984293
LP
2270 if (r < 0)
2271 return r;
2272 if (r == 0)
2273 continue;
2274
2275 if (first) {
2276 if (from)
2277 *from = fr;
2278 if (to)
2279 *to = t;
2280 first = false;
2281 } else {
2282 if (from)
2283 *from = MIN(fr, *from);
2284 if (to)
0f91dd87 2285 *to = MAX(t, *to);
08984293
LP
2286 }
2287 }
2288
2289 return first ? 0 : 1;
2290}
2291
dca6219e
LP
2292void journal_print_header(sd_journal *j) {
2293 Iterator i;
2294 JournalFile *f;
2295 bool newline = false;
2296
2297 assert(j);
2298
2299 HASHMAP_FOREACH(f, j->files, i) {
2300 if (newline)
2301 putchar('\n');
2302 else
2303 newline = true;
2304
2305 journal_file_print_header(f);
2306 }
2307}
08984293 2308
a1a03e30
LP
2309_public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2310 Iterator i;
2311 JournalFile *f;
2312 uint64_t sum = 0;
2313
2314 if (!j)
2315 return -EINVAL;
2316 if (!bytes)
2317 return -EINVAL;
2318
2319 HASHMAP_FOREACH(f, j->files, i) {
2320 struct stat st;
2321
2322 if (fstat(f->fd, &st) < 0)
2323 return -errno;
2324
2325 sum += (uint64_t) st.st_blocks * 512ULL;
2326 }
2327
2328 *bytes = sum;
2329 return 0;
2330}
2331
3c1668da
LP
2332_public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2333 char *f;
2334
2335 if (!j)
2336 return -EINVAL;
2337 if (isempty(field))
2338 return -EINVAL;
d5c4ed62
LP
2339 if (!field_is_valid(field))
2340 return -EINVAL;
3c1668da
LP
2341
2342 f = strdup(field);
2343 if (!f)
2344 return -ENOMEM;
2345
2346 free(j->unique_field);
2347 j->unique_field = f;
2348 j->unique_file = NULL;
2349 j->unique_offset = 0;
2350
2351 return 0;
2352}
2353
2354_public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
2355 Object *o;
2356 size_t k;
2357 int r;
19a2bd80 2358
3c1668da
LP
2359 if (!j)
2360 return -EINVAL;
2361 if (!data)
2362 return -EINVAL;
2363 if (!l)
2364 return -EINVAL;
2365 if (!j->unique_field)
2366 return -EINVAL;
19a2bd80 2367
3c1668da 2368 k = strlen(j->unique_field);
19a2bd80 2369
3c1668da
LP
2370 if (!j->unique_file) {
2371 j->unique_file = hashmap_first(j->files);
2372 if (!j->unique_file)
2373 return 0;
2374 j->unique_offset = 0;
2375 }
19a2bd80 2376
3c1668da
LP
2377 for (;;) {
2378 JournalFile *of;
2379 Iterator i;
2380 const void *odata;
2381 size_t ol;
2382 bool found;
2383
bdc02927 2384 /* Proceed to next data object in the field's linked list */
3c1668da
LP
2385 if (j->unique_offset == 0) {
2386 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2387 if (r < 0)
2388 return r;
2389
2390 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2391 } else {
2392 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2393 if (r < 0)
2394 return r;
2395
2396 j->unique_offset = le64toh(o->data.next_field_offset);
2397 }
2398
2399 /* We reached the end of the list? Then start again, with the next file */
2400 if (j->unique_offset == 0) {
2401 JournalFile *n;
2402
2403 n = hashmap_next(j->files, j->unique_file->path);
2404 if (!n)
2405 return 0;
2406
2407 j->unique_file = n;
2408 continue;
2409 }
2410
2411 /* We do not use the type context here, but 0 instead,
2412 * so that we can look at this data object at the same
2413 * time as one on another file */
2414 r = journal_file_move_to_object(j->unique_file, 0, j->unique_offset, &o);
2415 if (r < 0)
2416 return r;
2417
2418 /* Let's do the type check by hand, since we used 0 context above. */
2419 if (o->object.type != OBJECT_DATA)
2420 return -EBADMSG;
2421
93b73b06 2422 r = return_data(j, j->unique_file, o, &odata, &ol);
3c1668da
LP
2423 if (r < 0)
2424 return r;
2425
2426 /* OK, now let's see if we already returned this data
2427 * object by checking if it exists in the earlier
2428 * traversed files. */
2429 found = false;
2430 HASHMAP_FOREACH(of, j->files, i) {
2431 Object *oo;
2432 uint64_t op;
2433
2434 if (of == j->unique_file)
2435 break;
2436
2437 /* Skip this file it didn't have any fields
2438 * indexed */
2439 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2440 le64toh(of->header->n_fields) <= 0)
2441 continue;
2442
2443 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2444 if (r < 0)
2445 return r;
2446
2447 if (r > 0)
2448 found = true;
2449 }
2450
2451 if (found)
2452 continue;
2453
93b73b06 2454 r = return_data(j, j->unique_file, o, data, l);
3c1668da
LP
2455 if (r < 0)
2456 return r;
2457
2458 return 1;
2459 }
2460}
2461
115646c7 2462_public_ void sd_journal_restart_unique(sd_journal *j) {
3c1668da
LP
2463 if (!j)
2464 return;
2465
2466 j->unique_file = NULL;
2467 j->unique_offset = 0;
2468}
85210bff
LP
2469
2470_public_ int sd_journal_reliable_fd(sd_journal *j) {
2471 if (!j)
2472 return -EINVAL;
2473
2474 return !j->on_network;
2475}
d4205751
LP
2476
2477static char *lookup_field(const char *field, void *userdata) {
2478 sd_journal *j = userdata;
2479 const void *data;
2480 size_t size, d;
2481 int r;
2482
2483 assert(field);
2484 assert(j);
2485
2486 r = sd_journal_get_data(j, field, &data, &size);
2487 if (r < 0 ||
2488 size > REPLACE_VAR_MAX)
2489 return strdup(field);
2490
2491 d = strlen(field) + 1;
2492
2493 return strndup((const char*) data + d, size - d);
2494}
2495
2496_public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
2497 const void *data;
2498 size_t size;
2499 sd_id128_t id;
2500 _cleanup_free_ char *text = NULL, *cid = NULL;
2501 char *t;
2502 int r;
2503
2504 if (!j)
2505 return -EINVAL;
2506 if (!ret)
2507 return -EINVAL;
2508
2509 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
2510 if (r < 0)
2511 return r;
2512
2513 cid = strndup((const char*) data + 11, size - 11);
2514 if (!cid)
2515 return -ENOMEM;
2516
2517 r = sd_id128_from_string(cid, &id);
2518 if (r < 0)
2519 return r;
2520
844ec79b 2521 r = catalog_get(CATALOG_DATABASE, id, &text);
d4205751
LP
2522 if (r < 0)
2523 return r;
2524
2525 t = replace_var(text, lookup_field, j);
2526 if (!t)
2527 return -ENOMEM;
2528
2529 *ret = t;
2530 return 0;
2531}
8f1e860f
LP
2532
2533_public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
2534 if (!ret)
2535 return -EINVAL;
2536
844ec79b 2537 return catalog_get(CATALOG_DATABASE, id, ret);
8f1e860f 2538}
93b73b06
LP
2539
2540_public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
2541 if (!j)
2542 return -EINVAL;
2543
2544 j->data_threshold = sz;
2545 return 0;
2546}
2547
2548_public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
2549 if (!j)
2550 return -EINVAL;
2551 if (!sz)
2552 return -EINVAL;
2553
2554 *sz = j->data_threshold;
2555 return 0;
2556}