]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/sd-journal.c
journal: make table const
[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
16e9f408
LP
1834 if (j->mmap)
1835 mmap_cache_unref(j->mmap);
1836
7827b1a1 1837 free(j->path);
3c1668da 1838 free(j->unique_field);
6fe391c5 1839 set_free(j->errors);
87d2c1ff
LP
1840 free(j);
1841}
3fbf9cbb 1842
a5344d2c 1843_public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
3fbf9cbb
LP
1844 Object *o;
1845 JournalFile *f;
1846 int r;
1847
a5344d2c
LP
1848 if (!j)
1849 return -EINVAL;
a65f06bb
ZJS
1850 if (journal_pid_changed(j))
1851 return -ECHILD;
a5344d2c
LP
1852 if (!ret)
1853 return -EINVAL;
3fbf9cbb
LP
1854
1855 f = j->current_file;
1856 if (!f)
de190aef 1857 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1858
1859 if (f->current_offset <= 0)
de190aef 1860 return -EADDRNOTAVAIL;
3fbf9cbb 1861
de190aef 1862 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1863 if (r < 0)
1864 return r;
1865
1866 *ret = le64toh(o->entry.realtime);
de190aef 1867 return 0;
3fbf9cbb
LP
1868}
1869
a5344d2c 1870_public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
3fbf9cbb
LP
1871 Object *o;
1872 JournalFile *f;
1873 int r;
1874 sd_id128_t id;
1875
a5344d2c
LP
1876 if (!j)
1877 return -EINVAL;
a65f06bb
ZJS
1878 if (journal_pid_changed(j))
1879 return -ECHILD;
3fbf9cbb
LP
1880
1881 f = j->current_file;
1882 if (!f)
de190aef 1883 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1884
1885 if (f->current_offset <= 0)
de190aef 1886 return -EADDRNOTAVAIL;
3fbf9cbb 1887
de190aef 1888 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1889 if (r < 0)
1890 return r;
1891
de190aef
LP
1892 if (ret_boot_id)
1893 *ret_boot_id = o->entry.boot_id;
1894 else {
1895 r = sd_id128_get_boot(&id);
1896 if (r < 0)
1897 return r;
3fbf9cbb 1898
de190aef 1899 if (!sd_id128_equal(id, o->entry.boot_id))
df50185b 1900 return -ESTALE;
de190aef 1901 }
3fbf9cbb 1902
14a65d65
LP
1903 if (ret)
1904 *ret = le64toh(o->entry.monotonic);
1905
de190aef 1906 return 0;
3fbf9cbb
LP
1907}
1908
362a3f81
LP
1909static bool field_is_valid(const char *field) {
1910 const char *p;
1911
1912 assert(field);
1913
1914 if (isempty(field))
1915 return false;
1916
1917 if (startswith(field, "__"))
1918 return false;
1919
1920 for (p = field; *p; p++) {
1921
1922 if (*p == '_')
1923 continue;
1924
1925 if (*p >= 'A' && *p <= 'Z')
1926 continue;
1927
1928 if (*p >= '0' && *p <= '9')
1929 continue;
1930
1931 return false;
1932 }
1933
1934 return true;
1935}
1936
a5344d2c 1937_public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
3fbf9cbb
LP
1938 JournalFile *f;
1939 uint64_t i, n;
1940 size_t field_length;
1941 int r;
1942 Object *o;
1943
a5344d2c
LP
1944 if (!j)
1945 return -EINVAL;
a65f06bb
ZJS
1946 if (journal_pid_changed(j))
1947 return -ECHILD;
a5344d2c
LP
1948 if (!field)
1949 return -EINVAL;
1950 if (!data)
1951 return -EINVAL;
1952 if (!size)
1953 return -EINVAL;
3fbf9cbb 1954
362a3f81 1955 if (!field_is_valid(field))
3fbf9cbb
LP
1956 return -EINVAL;
1957
1958 f = j->current_file;
1959 if (!f)
de190aef 1960 return -EADDRNOTAVAIL;
3fbf9cbb
LP
1961
1962 if (f->current_offset <= 0)
de190aef 1963 return -EADDRNOTAVAIL;
3fbf9cbb 1964
de190aef 1965 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
1966 if (r < 0)
1967 return r;
1968
1969 field_length = strlen(field);
1970
1971 n = journal_file_entry_n_items(o);
1972 for (i = 0; i < n; i++) {
4fd052ae
FC
1973 uint64_t p, l;
1974 le64_t le_hash;
3fbf9cbb
LP
1975 size_t t;
1976
1977 p = le64toh(o->entry.items[i].object_offset);
807e17f0 1978 le_hash = o->entry.items[i].hash;
de190aef 1979 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
3fbf9cbb
LP
1980 if (r < 0)
1981 return r;
1982
de190aef 1983 if (le_hash != o->data.hash)
de7b95cd
LP
1984 return -EBADMSG;
1985
3fbf9cbb
LP
1986 l = le64toh(o->object.size) - offsetof(Object, data.payload);
1987
807e17f0
LP
1988 if (o->object.flags & OBJECT_COMPRESSED) {
1989
1990#ifdef HAVE_XZ
1991 if (uncompress_startswith(o->data.payload, l,
1992 &f->compress_buffer, &f->compress_buffer_size,
1993 field, field_length, '=')) {
1994
1995 uint64_t rsize;
1996
1997 if (!uncompress_blob(o->data.payload, l,
93b73b06
LP
1998 &f->compress_buffer, &f->compress_buffer_size, &rsize,
1999 j->data_threshold))
807e17f0
LP
2000 return -EBADMSG;
2001
2002 *data = f->compress_buffer;
2003 *size = (size_t) rsize;
2004
2005 return 0;
2006 }
2007#else
2008 return -EPROTONOSUPPORT;
2009#endif
2010
2011 } else if (l >= field_length+1 &&
2012 memcmp(o->data.payload, field, field_length) == 0 &&
2013 o->data.payload[field_length] == '=') {
3fbf9cbb 2014
161e54f8 2015 t = (size_t) l;
3fbf9cbb 2016
161e54f8
LP
2017 if ((uint64_t) t != l)
2018 return -E2BIG;
3fbf9cbb 2019
161e54f8
LP
2020 *data = o->data.payload;
2021 *size = t;
3fbf9cbb 2022
99613ec5 2023 return 0;
161e54f8 2024 }
3fbf9cbb 2025
de190aef 2026 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
161e54f8
LP
2027 if (r < 0)
2028 return r;
3fbf9cbb
LP
2029 }
2030
de190aef 2031 return -ENOENT;
3fbf9cbb
LP
2032}
2033
93b73b06 2034static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
3c1668da
LP
2035 size_t t;
2036 uint64_t l;
2037
2038 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2039 t = (size_t) l;
2040
2041 /* We can't read objects larger than 4G on a 32bit machine */
2042 if ((uint64_t) t != l)
2043 return -E2BIG;
2044
2045 if (o->object.flags & OBJECT_COMPRESSED) {
2046#ifdef HAVE_XZ
2047 uint64_t rsize;
2048
93b73b06 2049 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize, j->data_threshold))
3c1668da
LP
2050 return -EBADMSG;
2051
2052 *data = f->compress_buffer;
2053 *size = (size_t) rsize;
2054#else
2055 return -EPROTONOSUPPORT;
2056#endif
2057 } else {
2058 *data = o->data.payload;
2059 *size = t;
2060 }
2061
2062 return 0;
2063}
2064
a5344d2c 2065_public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
3fbf9cbb 2066 JournalFile *f;
3c1668da 2067 uint64_t p, n;
4fd052ae 2068 le64_t le_hash;
3fbf9cbb
LP
2069 int r;
2070 Object *o;
2071
a5344d2c
LP
2072 if (!j)
2073 return -EINVAL;
a65f06bb
ZJS
2074 if (journal_pid_changed(j))
2075 return -ECHILD;
a5344d2c
LP
2076 if (!data)
2077 return -EINVAL;
2078 if (!size)
2079 return -EINVAL;
3fbf9cbb
LP
2080
2081 f = j->current_file;
2082 if (!f)
de190aef 2083 return -EADDRNOTAVAIL;
3fbf9cbb
LP
2084
2085 if (f->current_offset <= 0)
de190aef 2086 return -EADDRNOTAVAIL;
3fbf9cbb 2087
de190aef 2088 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
2089 if (r < 0)
2090 return r;
2091
2092 n = journal_file_entry_n_items(o);
7210bfb3 2093 if (j->current_field >= n)
3fbf9cbb
LP
2094 return 0;
2095
7210bfb3 2096 p = le64toh(o->entry.items[j->current_field].object_offset);
de190aef
LP
2097 le_hash = o->entry.items[j->current_field].hash;
2098 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
3fbf9cbb
LP
2099 if (r < 0)
2100 return r;
2101
de190aef 2102 if (le_hash != o->data.hash)
de7b95cd
LP
2103 return -EBADMSG;
2104
93b73b06 2105 r = return_data(j, f, o, data, size);
3c1668da
LP
2106 if (r < 0)
2107 return r;
3fbf9cbb 2108
7210bfb3 2109 j->current_field ++;
3fbf9cbb
LP
2110
2111 return 1;
2112}
c2373f84 2113
a5344d2c
LP
2114_public_ void sd_journal_restart_data(sd_journal *j) {
2115 if (!j)
2116 return;
8725d60a
LP
2117
2118 j->current_field = 0;
c2373f84 2119}
50f20cfd 2120
a5344d2c 2121_public_ int sd_journal_get_fd(sd_journal *j) {
a963990f
LP
2122 int r;
2123
a5344d2c
LP
2124 if (!j)
2125 return -EINVAL;
a65f06bb
ZJS
2126 if (journal_pid_changed(j))
2127 return -ECHILD;
50f20cfd 2128
a963990f
LP
2129 if (j->inotify_fd >= 0)
2130 return j->inotify_fd;
2131
2132 r = allocate_inotify(j);
2133 if (r < 0)
2134 return r;
2135
2136 /* Iterate through all dirs again, to add them to the
2137 * inotify */
5302ebe1
ZJS
2138 if (j->no_new_files)
2139 r = add_current_paths(j);
2140 else if (j->path)
7827b1a1
LP
2141 r = add_root_directory(j, j->path);
2142 else
2143 r = add_search_paths(j);
a963990f
LP
2144 if (r < 0)
2145 return r;
2146
50f20cfd
LP
2147 return j->inotify_fd;
2148}
2149
ee531d94
LP
2150_public_ int sd_journal_get_events(sd_journal *j) {
2151 int fd;
2152
2153 if (!j)
2154 return -EINVAL;
a65f06bb
ZJS
2155 if (journal_pid_changed(j))
2156 return -ECHILD;
ee531d94
LP
2157
2158 fd = sd_journal_get_fd(j);
2159 if (fd < 0)
2160 return fd;
2161
2162 return POLLIN;
2163}
2164
39c155ea
LP
2165_public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2166 int fd;
2167
2168 if (!j)
2169 return -EINVAL;
a65f06bb
ZJS
2170 if (journal_pid_changed(j))
2171 return -ECHILD;
39c155ea
LP
2172 if (!timeout_usec)
2173 return -EINVAL;
2174
2175 fd = sd_journal_get_fd(j);
2176 if (fd < 0)
2177 return fd;
2178
2179 if (!j->on_network) {
2180 *timeout_usec = (uint64_t) -1;
2181 return 0;
2182 }
2183
2184 /* If we are on the network we need to regularly check for
2185 * changes manually */
2186
2187 *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2188 return 1;
2189}
2190
50f20cfd 2191static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
a963990f 2192 Directory *d;
50f20cfd
LP
2193 int r;
2194
2195 assert(j);
2196 assert(e);
2197
2198 /* Is this a subdirectory we watch? */
a963990f
LP
2199 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2200 if (d) {
2201 sd_id128_t id;
50f20cfd 2202
de2c3907
LP
2203 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2204 (endswith(e->name, ".journal") ||
2205 endswith(e->name, ".journal~"))) {
50f20cfd
LP
2206
2207 /* Event for a journal file */
2208
2209 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
a963990f 2210 r = add_file(j, d->path, e->name);
6fe391c5
ZJS
2211 if (r < 0) {
2212 log_debug("Failed to add file %s/%s: %s",
2213 d->path, e->name, strerror(-r));
3ac251b8 2214 set_put_error(j, r);
6fe391c5 2215 }
a963990f 2216
5e6870ea 2217 } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
50f20cfd 2218
a963990f 2219 r = remove_file(j, d->path, e->name);
50f20cfd 2220 if (r < 0)
a963990f 2221 log_debug("Failed to remove file %s/%s: %s", d->path, e->name, strerror(-r));
50f20cfd
LP
2222 }
2223
a963990f 2224 } else if (!d->is_root && e->len == 0) {
50f20cfd 2225
a963990f 2226 /* Event for a subdirectory */
50f20cfd 2227
a963990f
LP
2228 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
2229 r = remove_directory(j, d);
50f20cfd 2230 if (r < 0)
a963990f 2231 log_debug("Failed to remove directory %s: %s", d->path, strerror(-r));
50f20cfd
LP
2232 }
2233
50f20cfd 2234
a963990f 2235 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
50f20cfd 2236
a963990f 2237 /* Event for root directory */
50f20cfd 2238
a963990f
LP
2239 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
2240 r = add_directory(j, d->path, e->name);
50f20cfd 2241 if (r < 0)
a963990f 2242 log_debug("Failed to add directory %s/%s: %s", d->path, e->name, strerror(-r));
50f20cfd
LP
2243 }
2244 }
2245
2246 return;
2247 }
2248
2249 if (e->mask & IN_IGNORED)
2250 return;
2251
2252 log_warning("Unknown inotify event.");
2253}
2254
a963990f
LP
2255static int determine_change(sd_journal *j) {
2256 bool b;
2257
2258 assert(j);
2259
2260 b = j->current_invalidate_counter != j->last_invalidate_counter;
2261 j->last_invalidate_counter = j->current_invalidate_counter;
2262
2263 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2264}
2265
a5344d2c 2266_public_ int sd_journal_process(sd_journal *j) {
19d1e4ee 2267 uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX] _alignas_(struct inotify_event);
a963990f 2268 bool got_something = false;
50f20cfd 2269
a5344d2c
LP
2270 if (!j)
2271 return -EINVAL;
a65f06bb
ZJS
2272 if (journal_pid_changed(j))
2273 return -ECHILD;
50f20cfd 2274
39c155ea
LP
2275 j->last_process_usec = now(CLOCK_MONOTONIC);
2276
50f20cfd
LP
2277 for (;;) {
2278 struct inotify_event *e;
2279 ssize_t l;
2280
2281 l = read(j->inotify_fd, buffer, sizeof(buffer));
2282 if (l < 0) {
a963990f
LP
2283 if (errno == EAGAIN || errno == EINTR)
2284 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
50f20cfd
LP
2285
2286 return -errno;
2287 }
2288
a963990f
LP
2289 got_something = true;
2290
50f20cfd
LP
2291 e = (struct inotify_event*) buffer;
2292 while (l > 0) {
2293 size_t step;
2294
2295 process_inotify_event(j, e);
2296
2297 step = sizeof(struct inotify_event) + e->len;
2298 assert(step <= (size_t) l);
2299
2300 e = (struct inotify_event*) ((uint8_t*) e + step);
2301 l -= step;
2302 }
2303 }
a963990f
LP
2304
2305 return determine_change(j);
50f20cfd 2306}
6ad1d1c3 2307
e02d1cf7 2308_public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
a963990f 2309 int r;
39c155ea 2310 uint64_t t;
e02d1cf7 2311
a65f06bb
ZJS
2312 if (!j)
2313 return -EINVAL;
2314 if (journal_pid_changed(j))
2315 return -ECHILD;
e02d1cf7 2316
a963990f
LP
2317 if (j->inotify_fd < 0) {
2318
2319 /* This is the first invocation, hence create the
2320 * inotify watch */
2321 r = sd_journal_get_fd(j);
2322 if (r < 0)
2323 return r;
2324
2325 /* The journal might have changed since the context
2326 * object was created and we weren't watching before,
2327 * hence don't wait for anything, and return
2328 * immediately. */
2329 return determine_change(j);
2330 }
2331
39c155ea
LP
2332 r = sd_journal_get_timeout(j, &t);
2333 if (r < 0)
2334 return r;
2335
2336 if (t != (uint64_t) -1) {
2337 usec_t n;
2338
2339 n = now(CLOCK_MONOTONIC);
2340 t = t > n ? t - n : 0;
85210bff 2341
39c155ea
LP
2342 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2343 timeout_usec = t;
85210bff
LP
2344 }
2345
a963990f
LP
2346 do {
2347 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2348 } while (r == -EINTR);
e02d1cf7
LP
2349
2350 if (r < 0)
2351 return r;
2352
a963990f 2353 return sd_journal_process(j);
e02d1cf7
LP
2354}
2355
08984293
LP
2356_public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2357 Iterator i;
2358 JournalFile *f;
2359 bool first = true;
2360 int r;
2361
2362 if (!j)
2363 return -EINVAL;
a65f06bb
ZJS
2364 if (journal_pid_changed(j))
2365 return -ECHILD;
08984293
LP
2366 if (!from && !to)
2367 return -EINVAL;
39887731
TA
2368 if (from == to)
2369 return -EINVAL;
08984293
LP
2370
2371 HASHMAP_FOREACH(f, j->files, i) {
2372 usec_t fr, t;
2373
2374 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
9f8d2983
LP
2375 if (r == -ENOENT)
2376 continue;
08984293
LP
2377 if (r < 0)
2378 return r;
2379 if (r == 0)
2380 continue;
2381
2382 if (first) {
2383 if (from)
2384 *from = fr;
2385 if (to)
2386 *to = t;
2387 first = false;
2388 } else {
2389 if (from)
2390 *from = MIN(fr, *from);
2391 if (to)
0f91dd87 2392 *to = MAX(t, *to);
08984293
LP
2393 }
2394 }
2395
2396 return first ? 0 : 1;
2397}
2398
2399_public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2400 Iterator i;
2401 JournalFile *f;
2402 bool first = true;
2403 int r;
2404
2405 if (!j)
2406 return -EINVAL;
a65f06bb
ZJS
2407 if (journal_pid_changed(j))
2408 return -ECHILD;
08984293
LP
2409 if (!from && !to)
2410 return -EINVAL;
39887731
TA
2411 if (from == to)
2412 return -EINVAL;
08984293
LP
2413
2414 HASHMAP_FOREACH(f, j->files, i) {
2415 usec_t fr, t;
2416
2417 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
9f8d2983
LP
2418 if (r == -ENOENT)
2419 continue;
08984293
LP
2420 if (r < 0)
2421 return r;
2422 if (r == 0)
2423 continue;
2424
2425 if (first) {
2426 if (from)
2427 *from = fr;
2428 if (to)
2429 *to = t;
2430 first = false;
2431 } else {
2432 if (from)
2433 *from = MIN(fr, *from);
2434 if (to)
0f91dd87 2435 *to = MAX(t, *to);
08984293
LP
2436 }
2437 }
2438
2439 return first ? 0 : 1;
2440}
2441
dca6219e
LP
2442void journal_print_header(sd_journal *j) {
2443 Iterator i;
2444 JournalFile *f;
2445 bool newline = false;
2446
2447 assert(j);
2448
2449 HASHMAP_FOREACH(f, j->files, i) {
2450 if (newline)
2451 putchar('\n');
2452 else
2453 newline = true;
2454
2455 journal_file_print_header(f);
2456 }
2457}
08984293 2458
a1a03e30
LP
2459_public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2460 Iterator i;
2461 JournalFile *f;
2462 uint64_t sum = 0;
2463
2464 if (!j)
2465 return -EINVAL;
a65f06bb
ZJS
2466 if (journal_pid_changed(j))
2467 return -ECHILD;
a1a03e30
LP
2468 if (!bytes)
2469 return -EINVAL;
2470
2471 HASHMAP_FOREACH(f, j->files, i) {
2472 struct stat st;
2473
2474 if (fstat(f->fd, &st) < 0)
2475 return -errno;
2476
2477 sum += (uint64_t) st.st_blocks * 512ULL;
2478 }
2479
2480 *bytes = sum;
2481 return 0;
2482}
2483
3c1668da
LP
2484_public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2485 char *f;
2486
2487 if (!j)
2488 return -EINVAL;
a65f06bb
ZJS
2489 if (journal_pid_changed(j))
2490 return -ECHILD;
3c1668da
LP
2491 if (isempty(field))
2492 return -EINVAL;
d5c4ed62
LP
2493 if (!field_is_valid(field))
2494 return -EINVAL;
3c1668da
LP
2495
2496 f = strdup(field);
2497 if (!f)
2498 return -ENOMEM;
2499
2500 free(j->unique_field);
2501 j->unique_field = f;
2502 j->unique_file = NULL;
2503 j->unique_offset = 0;
2504
2505 return 0;
2506}
2507
2508_public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
2509 Object *o;
2510 size_t k;
2511 int r;
19a2bd80 2512
3c1668da
LP
2513 if (!j)
2514 return -EINVAL;
a65f06bb
ZJS
2515 if (journal_pid_changed(j))
2516 return -ECHILD;
3c1668da
LP
2517 if (!data)
2518 return -EINVAL;
2519 if (!l)
2520 return -EINVAL;
2521 if (!j->unique_field)
2522 return -EINVAL;
19a2bd80 2523
3c1668da 2524 k = strlen(j->unique_field);
19a2bd80 2525
3c1668da
LP
2526 if (!j->unique_file) {
2527 j->unique_file = hashmap_first(j->files);
2528 if (!j->unique_file)
2529 return 0;
2530 j->unique_offset = 0;
2531 }
19a2bd80 2532
3c1668da
LP
2533 for (;;) {
2534 JournalFile *of;
2535 Iterator i;
2536 const void *odata;
2537 size_t ol;
2538 bool found;
2539
bdc02927 2540 /* Proceed to next data object in the field's linked list */
3c1668da
LP
2541 if (j->unique_offset == 0) {
2542 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2543 if (r < 0)
2544 return r;
2545
2546 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2547 } else {
2548 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2549 if (r < 0)
2550 return r;
2551
2552 j->unique_offset = le64toh(o->data.next_field_offset);
2553 }
2554
2555 /* We reached the end of the list? Then start again, with the next file */
2556 if (j->unique_offset == 0) {
2557 JournalFile *n;
2558
2559 n = hashmap_next(j->files, j->unique_file->path);
2560 if (!n)
2561 return 0;
2562
2563 j->unique_file = n;
2564 continue;
2565 }
2566
2567 /* We do not use the type context here, but 0 instead,
2568 * so that we can look at this data object at the same
2569 * time as one on another file */
2570 r = journal_file_move_to_object(j->unique_file, 0, j->unique_offset, &o);
2571 if (r < 0)
2572 return r;
2573
2574 /* Let's do the type check by hand, since we used 0 context above. */
2575 if (o->object.type != OBJECT_DATA)
2576 return -EBADMSG;
2577
93b73b06 2578 r = return_data(j, j->unique_file, o, &odata, &ol);
3c1668da
LP
2579 if (r < 0)
2580 return r;
2581
2582 /* OK, now let's see if we already returned this data
2583 * object by checking if it exists in the earlier
2584 * traversed files. */
2585 found = false;
2586 HASHMAP_FOREACH(of, j->files, i) {
2587 Object *oo;
2588 uint64_t op;
2589
2590 if (of == j->unique_file)
2591 break;
2592
2593 /* Skip this file it didn't have any fields
2594 * indexed */
2595 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
2596 le64toh(of->header->n_fields) <= 0)
2597 continue;
2598
2599 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
2600 if (r < 0)
2601 return r;
2602
2603 if (r > 0)
2604 found = true;
2605 }
2606
2607 if (found)
2608 continue;
2609
93b73b06 2610 r = return_data(j, j->unique_file, o, data, l);
3c1668da
LP
2611 if (r < 0)
2612 return r;
2613
2614 return 1;
2615 }
2616}
2617
115646c7 2618_public_ void sd_journal_restart_unique(sd_journal *j) {
3c1668da
LP
2619 if (!j)
2620 return;
2621
2622 j->unique_file = NULL;
2623 j->unique_offset = 0;
2624}
85210bff
LP
2625
2626_public_ int sd_journal_reliable_fd(sd_journal *j) {
2627 if (!j)
2628 return -EINVAL;
a65f06bb
ZJS
2629 if (journal_pid_changed(j))
2630 return -ECHILD;
85210bff
LP
2631
2632 return !j->on_network;
2633}
d4205751
LP
2634
2635static char *lookup_field(const char *field, void *userdata) {
2636 sd_journal *j = userdata;
2637 const void *data;
2638 size_t size, d;
2639 int r;
2640
2641 assert(field);
2642 assert(j);
2643
2644 r = sd_journal_get_data(j, field, &data, &size);
2645 if (r < 0 ||
2646 size > REPLACE_VAR_MAX)
2647 return strdup(field);
2648
2649 d = strlen(field) + 1;
2650
2651 return strndup((const char*) data + d, size - d);
2652}
2653
2654_public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
2655 const void *data;
2656 size_t size;
2657 sd_id128_t id;
2658 _cleanup_free_ char *text = NULL, *cid = NULL;
2659 char *t;
2660 int r;
2661
2662 if (!j)
2663 return -EINVAL;
a65f06bb
ZJS
2664 if (journal_pid_changed(j))
2665 return -ECHILD;
d4205751
LP
2666 if (!ret)
2667 return -EINVAL;
2668
2669 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
2670 if (r < 0)
2671 return r;
2672
2673 cid = strndup((const char*) data + 11, size - 11);
2674 if (!cid)
2675 return -ENOMEM;
2676
2677 r = sd_id128_from_string(cid, &id);
2678 if (r < 0)
2679 return r;
2680
844ec79b 2681 r = catalog_get(CATALOG_DATABASE, id, &text);
d4205751
LP
2682 if (r < 0)
2683 return r;
2684
2685 t = replace_var(text, lookup_field, j);
2686 if (!t)
2687 return -ENOMEM;
2688
2689 *ret = t;
2690 return 0;
2691}
8f1e860f
LP
2692
2693_public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
2694 if (!ret)
2695 return -EINVAL;
2696
844ec79b 2697 return catalog_get(CATALOG_DATABASE, id, ret);
8f1e860f 2698}
93b73b06
LP
2699
2700_public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
2701 if (!j)
2702 return -EINVAL;
a65f06bb
ZJS
2703 if (journal_pid_changed(j))
2704 return -ECHILD;
93b73b06
LP
2705
2706 j->data_threshold = sz;
2707 return 0;
2708}
2709
2710_public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
2711 if (!j)
2712 return -EINVAL;
a65f06bb
ZJS
2713 if (journal_pid_changed(j))
2714 return -ECHILD;
93b73b06
LP
2715 if (!sz)
2716 return -EINVAL;
2717
2718 *sz = j->data_threshold;
2719 return 0;
2720}