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