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