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