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