]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/sd-journal.c
journal-file: refuse opening non-regular journal files
[thirdparty/systemd.git] / src / journal / sd-journal.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
87d2c1ff
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2011 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
87d2c1ff
LP
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 15 Lesser General Public License for more details.
87d2c1ff 16
5430f7f2 17 You should have received a copy of the GNU Lesser General Public License
87d2c1ff
LP
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
87d2c1ff 21#include <errno.h>
87d2c1ff 22#include <fcntl.h>
82e24b00 23#include <inttypes.h>
07630cea
LP
24#include <linux/magic.h>
25#include <poll.h>
3fbf9cbb 26#include <stddef.h>
50f20cfd 27#include <sys/inotify.h>
85210bff 28#include <sys/vfs.h>
07630cea 29#include <unistd.h>
87d2c1ff
LP
30
31#include "sd-journal.h"
c004493c 32
b5efdb8a 33#include "alloc-util.h"
07630cea
LP
34#include "catalog.h"
35#include "compress.h"
a0956174 36#include "dirent-util.h"
3ffd4af2 37#include "fd-util.h"
07630cea 38#include "fileio.h"
f97b34a6 39#include "format-util.h"
77601719 40#include "fs-util.h"
07630cea
LP
41#include "hashmap.h"
42#include "hostname-util.h"
a9be0692 43#include "id128-util.h"
c004493c 44#include "io-util.h"
87d2c1ff 45#include "journal-def.h"
cec736d2 46#include "journal-file.h"
07630cea 47#include "journal-internal.h"
cec736d2 48#include "list.h"
de7b95cd 49#include "lookup3.h"
85210bff 50#include "missing.h"
07630cea 51#include "path-util.h"
dccca82b 52#include "process-util.h"
d4205751 53#include "replace-var.h"
15a5e950 54#include "stat-util.h"
77f9fa3b 55#include "stat-util.h"
15a5e950 56#include "stdio-util.h"
07630cea
LP
57#include "string-util.h"
58#include "strv.h"
87d2c1ff 59
9e8abdf0
LP
60#define JOURNAL_FILES_MAX 7168
61
85210bff
LP
62#define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC)
63
d4205751
LP
64#define REPLACE_VAR_MAX 256
65
93b73b06
LP
66#define DEFAULT_DATA_THRESHOLD (64*1024)
67
a9a245c1
ZJS
68static void remove_file_real(sd_journal *j, JournalFile *f);
69
a65f06bb
ZJS
70static bool journal_pid_changed(sd_journal *j) {
71 assert(j);
72
73 /* We don't support people creating a journal object and
74 * keeping it around over a fork(). Let's complain. */
75
df0ff127 76 return j->original_pid != getpid_cached();
a65f06bb
ZJS
77}
78
5768d259
LP
79static int journal_put_error(sd_journal *j, int r, const char *path) {
80 char *copy;
3ac251b8
LP
81 int k;
82
5768d259
LP
83 /* Memorize an error we encountered, and store which
84 * file/directory it was generated from. Note that we store
85 * only *one* path per error code, as the error code is the
86 * key into the hashmap, and the path is the value. This means
87 * we keep track only of all error kinds, but not of all error
88 * locations. This has the benefit that the hashmap cannot
89 * grow beyond bounds.
90 *
91 * We return an error here only if we didn't manage to
92 * memorize the real error. */
93
6fe391c5
ZJS
94 if (r >= 0)
95 return r;
96
5768d259 97 k = hashmap_ensure_allocated(&j->errors, NULL);
3ac251b8
LP
98 if (k < 0)
99 return k;
100
5768d259
LP
101 if (path) {
102 copy = strdup(path);
103 if (!copy)
104 return -ENOMEM;
105 } else
106 copy = NULL;
107
108 k = hashmap_put(j->errors, INT_TO_PTR(r), copy);
109 if (k < 0) {
110 free(copy);
111
112 if (k == -EEXIST)
113 return 0;
114
115 return k;
116 }
117
118 return 0;
6fe391c5
ZJS
119}
120
de190aef 121static void detach_location(sd_journal *j) {
8f9b6cd9
LP
122 Iterator i;
123 JournalFile *f;
124
125 assert(j);
126
127 j->current_file = NULL;
128 j->current_field = 0;
129
c1f906bd 130 ORDERED_HASHMAP_FOREACH(f, j->files, i)
1fc605b0 131 journal_file_reset_location(f);
8f9b6cd9
LP
132}
133
de190aef
LP
134static void reset_location(sd_journal *j) {
135 assert(j);
136
137 detach_location(j);
138 zero(j->current_location);
139}
140
a87247dd 141static void init_location(Location *l, LocationType type, JournalFile *f, Object *o) {
de190aef 142 assert(l);
3742095b 143 assert(IN_SET(type, LOCATION_DISCRETE, LOCATION_SEEK));
de190aef
LP
144 assert(f);
145 assert(o->object.type == OBJECT_ENTRY);
146
a87247dd 147 l->type = type;
de190aef
LP
148 l->seqnum = le64toh(o->entry.seqnum);
149 l->seqnum_id = f->header->seqnum_id;
150 l->realtime = le64toh(o->entry.realtime);
151 l->monotonic = le64toh(o->entry.monotonic);
ce3fd7e7 152 l->boot_id = o->entry.boot_id;
de190aef
LP
153 l->xor_hash = le64toh(o->entry.xor_hash);
154
155 l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
156}
157
1eb6332d 158static void set_location(sd_journal *j, JournalFile *f, Object *o) {
de190aef
LP
159 assert(j);
160 assert(f);
161 assert(o);
162
1eb6332d 163 init_location(&j->current_location, LOCATION_DISCRETE, f, o);
de190aef
LP
164
165 j->current_file = f;
166 j->current_field = 0;
167
6573ef05
MS
168 /* Let f know its candidate entry was picked. */
169 assert(f->location_type == LOCATION_SEEK);
170 f->location_type = LOCATION_DISCRETE;
de190aef
LP
171}
172
cbdca852
LP
173static int match_is_valid(const void *data, size_t size) {
174 const char *b, *p;
175
176 assert(data);
177
178 if (size < 2)
179 return false;
180
181 if (startswith(data, "__"))
182 return false;
183
184 b = data;
185 for (p = b; p < b + size; p++) {
186
187 if (*p == '=')
188 return p > b;
189
190 if (*p == '_')
191 continue;
192
193 if (*p >= 'A' && *p <= 'Z')
194 continue;
195
196 if (*p >= '0' && *p <= '9')
197 continue;
198
199 return false;
200 }
201
202 return false;
203}
204
205static bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
de190aef
LP
206 const uint8_t *a = _a, *b = _b;
207 size_t j;
de190aef
LP
208
209 for (j = 0; j < s && j < t; j++) {
210
de190aef 211 if (a[j] != b[j])
cbdca852 212 return false;
de190aef 213
cbdca852
LP
214 if (a[j] == '=')
215 return true;
de190aef
LP
216 }
217
bc302926 218 assert_not_reached("\"=\" not found");
cbdca852
LP
219}
220
221static Match *match_new(Match *p, MatchType t) {
222 Match *m;
223
224 m = new0(Match, 1);
225 if (!m)
226 return NULL;
227
228 m->type = t;
229
230 if (p) {
231 m->parent = p;
71fda00f 232 LIST_PREPEND(matches, p->matches, m);
cbdca852
LP
233 }
234
235 return m;
236}
237
238static void match_free(Match *m) {
239 assert(m);
240
241 while (m->matches)
242 match_free(m->matches);
243
244 if (m->parent)
71fda00f 245 LIST_REMOVE(matches, m->parent->matches, m);
cbdca852
LP
246
247 free(m->data);
248 free(m);
249}
250
251static void match_free_if_empty(Match *m) {
c5a10d9c 252 if (!m || m->matches)
cbdca852
LP
253 return;
254
255 match_free(m);
de190aef
LP
256}
257
a5344d2c 258_public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
cd34b3c6 259 Match *l3, *l4, *add_here = NULL, *m;
4fd052ae 260 le64_t le_hash;
87d2c1ff 261
1ae464e0
TA
262 assert_return(j, -EINVAL);
263 assert_return(!journal_pid_changed(j), -ECHILD);
264 assert_return(data, -EINVAL);
cbdca852
LP
265
266 if (size == 0)
267 size = strlen(data);
268
1ae464e0 269 assert_return(match_is_valid(data, size), -EINVAL);
1cc101f1 270
cd34b3c6
HH
271 /* level 0: AND term
272 * level 1: OR terms
273 * level 2: AND terms
274 * level 3: OR terms
275 * level 4: concrete matches */
cbdca852
LP
276
277 if (!j->level0) {
cd34b3c6 278 j->level0 = match_new(NULL, MATCH_AND_TERM);
cbdca852
LP
279 if (!j->level0)
280 return -ENOMEM;
281 }
282
283 if (!j->level1) {
cd34b3c6 284 j->level1 = match_new(j->level0, MATCH_OR_TERM);
cbdca852
LP
285 if (!j->level1)
286 return -ENOMEM;
287 }
288
cd34b3c6
HH
289 if (!j->level2) {
290 j->level2 = match_new(j->level1, MATCH_AND_TERM);
291 if (!j->level2)
292 return -ENOMEM;
293 }
294
295 assert(j->level0->type == MATCH_AND_TERM);
296 assert(j->level1->type == MATCH_OR_TERM);
297 assert(j->level2->type == MATCH_AND_TERM);
ab4979d2 298
de190aef
LP
299 le_hash = htole64(hash64(data, size));
300
cd34b3c6
HH
301 LIST_FOREACH(matches, l3, j->level2->matches) {
302 assert(l3->type == MATCH_OR_TERM);
de190aef 303
cd34b3c6
HH
304 LIST_FOREACH(matches, l4, l3->matches) {
305 assert(l4->type == MATCH_DISCRETE);
de190aef 306
cbdca852
LP
307 /* Exactly the same match already? Then ignore
308 * this addition */
cd34b3c6
HH
309 if (l4->le_hash == le_hash &&
310 l4->size == size &&
311 memcmp(l4->data, data, size) == 0)
cbdca852
LP
312 return 0;
313
314 /* Same field? Then let's add this to this OR term */
cd34b3c6
HH
315 if (same_field(data, size, l4->data, l4->size)) {
316 add_here = l3;
cbdca852
LP
317 break;
318 }
319 }
320
321 if (add_here)
322 break;
de190aef
LP
323 }
324
cbdca852 325 if (!add_here) {
cd34b3c6 326 add_here = match_new(j->level2, MATCH_OR_TERM);
cbdca852
LP
327 if (!add_here)
328 goto fail;
329 }
330
331 m = match_new(add_here, MATCH_DISCRETE);
cec736d2 332 if (!m)
cbdca852 333 goto fail;
87d2c1ff 334
cbdca852 335 m->le_hash = le_hash;
1cc101f1 336 m->size = size;
cbdca852
LP
337 m->data = memdup(data, size);
338 if (!m->data)
339 goto fail;
340
341 detach_location(j);
342
343 return 0;
344
345fail:
c5a10d9c
ZJS
346 match_free_if_empty(add_here);
347 match_free_if_empty(j->level2);
348 match_free_if_empty(j->level1);
349 match_free_if_empty(j->level0);
cbdca852
LP
350
351 return -ENOMEM;
352}
353
cd34b3c6 354_public_ int sd_journal_add_conjunction(sd_journal *j) {
1ae464e0
TA
355 assert_return(j, -EINVAL);
356 assert_return(!journal_pid_changed(j), -ECHILD);
1cc101f1 357
cbdca852
LP
358 if (!j->level0)
359 return 0;
360
361 if (!j->level1)
362 return 0;
363
364 if (!j->level1->matches)
365 return 0;
366
cd34b3c6
HH
367 j->level1 = NULL;
368 j->level2 = NULL;
369
370 return 0;
371}
372
373_public_ int sd_journal_add_disjunction(sd_journal *j) {
1ae464e0
TA
374 assert_return(j, -EINVAL);
375 assert_return(!journal_pid_changed(j), -ECHILD);
cd34b3c6
HH
376
377 if (!j->level0)
378 return 0;
379
380 if (!j->level1)
381 return 0;
382
383 if (!j->level2)
384 return 0;
385
386 if (!j->level2->matches)
387 return 0;
cbdca852 388
cd34b3c6 389 j->level2 = NULL;
cbdca852
LP
390 return 0;
391}
392
393static char *match_make_string(Match *m) {
6b430fdb 394 char *p = NULL, *r;
cbdca852
LP
395 Match *i;
396 bool enclose = false;
397
398 if (!m)
4ad16808 399 return strdup("none");
cbdca852
LP
400
401 if (m->type == MATCH_DISCRETE)
402 return strndup(m->data, m->size);
403
cbdca852
LP
404 LIST_FOREACH(matches, i, m->matches) {
405 char *t, *k;
406
407 t = match_make_string(i);
6b430fdb
ZJS
408 if (!t)
409 return mfree(p);
cbdca852
LP
410
411 if (p) {
605405c6 412 k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t);
cbdca852
LP
413 free(p);
414 free(t);
415
416 if (!k)
417 return NULL;
418
419 p = k;
420
421 enclose = true;
bc302926 422 } else
cbdca852 423 p = t;
87d2c1ff
LP
424 }
425
cbdca852 426 if (enclose) {
605405c6 427 r = strjoin("(", p, ")");
cbdca852
LP
428 free(p);
429 return r;
430 }
87d2c1ff 431
cbdca852
LP
432 return p;
433}
de7b95cd 434
cbdca852
LP
435char *journal_make_match_string(sd_journal *j) {
436 assert(j);
8f9b6cd9 437
cbdca852 438 return match_make_string(j->level0);
87d2c1ff
LP
439}
440
a5344d2c
LP
441_public_ void sd_journal_flush_matches(sd_journal *j) {
442 if (!j)
443 return;
87d2c1ff 444
cbdca852
LP
445 if (j->level0)
446 match_free(j->level0);
de7b95cd 447
cd34b3c6 448 j->level0 = j->level1 = j->level2 = NULL;
8f9b6cd9 449
de190aef 450 detach_location(j);
87d2c1ff
LP
451}
452
487d3720
MS
453_pure_ static int compare_with_location(JournalFile *f, Location *l) {
454 assert(f);
de190aef 455 assert(l);
487d3720 456 assert(f->location_type == LOCATION_SEEK);
4c701096 457 assert(IN_SET(l->type, LOCATION_DISCRETE, LOCATION_SEEK));
de190aef
LP
458
459 if (l->monotonic_set &&
487d3720 460 sd_id128_equal(f->current_boot_id, l->boot_id) &&
de190aef 461 l->realtime_set &&
487d3720 462 f->current_realtime == l->realtime &&
de190aef 463 l->xor_hash_set &&
487d3720 464 f->current_xor_hash == l->xor_hash)
de190aef
LP
465 return 0;
466
467 if (l->seqnum_set &&
487d3720 468 sd_id128_equal(f->header->seqnum_id, l->seqnum_id)) {
de190aef 469
487d3720 470 if (f->current_seqnum < l->seqnum)
de190aef 471 return -1;
487d3720 472 if (f->current_seqnum > l->seqnum)
de190aef
LP
473 return 1;
474 }
475
476 if (l->monotonic_set &&
487d3720 477 sd_id128_equal(f->current_boot_id, l->boot_id)) {
de190aef 478
487d3720 479 if (f->current_monotonic < l->monotonic)
de190aef 480 return -1;
487d3720 481 if (f->current_monotonic > l->monotonic)
de190aef
LP
482 return 1;
483 }
484
485 if (l->realtime_set) {
486
487d3720 487 if (f->current_realtime < l->realtime)
de190aef 488 return -1;
487d3720 489 if (f->current_realtime > l->realtime)
de190aef
LP
490 return 1;
491 }
492
493 if (l->xor_hash_set) {
de190aef 494
487d3720 495 if (f->current_xor_hash < l->xor_hash)
de190aef 496 return -1;
487d3720 497 if (f->current_xor_hash > l->xor_hash)
de190aef
LP
498 return 1;
499 }
500
501 return 0;
502}
503
cbdca852
LP
504static int next_for_match(
505 sd_journal *j,
506 Match *m,
507 JournalFile *f,
508 uint64_t after_offset,
509 direction_t direction,
510 Object **ret,
511 uint64_t *offset) {
512
de7b95cd 513 int r;
cbdca852
LP
514 uint64_t np = 0;
515 Object *n;
de7b95cd
LP
516
517 assert(j);
cbdca852
LP
518 assert(m);
519 assert(f);
de7b95cd 520
cbdca852
LP
521 if (m->type == MATCH_DISCRETE) {
522 uint64_t dp;
de190aef 523
cbdca852 524 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
de190aef
LP
525 if (r <= 0)
526 return r;
527
cbdca852 528 return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
de190aef 529
cbdca852
LP
530 } else if (m->type == MATCH_OR_TERM) {
531 Match *i;
de7b95cd 532
cbdca852 533 /* Find the earliest match beyond after_offset */
de190aef 534
cbdca852
LP
535 LIST_FOREACH(matches, i, m->matches) {
536 uint64_t cp;
de190aef 537
cbdca852 538 r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
b4e5f920
LP
539 if (r < 0)
540 return r;
cbdca852 541 else if (r > 0) {
bc302926 542 if (np == 0 || (direction == DIRECTION_DOWN ? cp < np : cp > np))
cbdca852
LP
543 np = cp;
544 }
545 }
b4e5f920 546
bc302926
ZJS
547 if (np == 0)
548 return 0;
549
cbdca852 550 } else if (m->type == MATCH_AND_TERM) {
2bc8ca0c 551 Match *i, *last_moved;
de190aef 552
cbdca852 553 /* Always jump to the next matching entry and repeat
2bc8ca0c 554 * this until we find an offset that matches for all
cbdca852 555 * matches. */
de190aef 556
cbdca852
LP
557 if (!m->matches)
558 return 0;
de7b95cd 559
2bc8ca0c
ZJS
560 r = next_for_match(j, m->matches, f, after_offset, direction, NULL, &np);
561 if (r <= 0)
562 return r;
de190aef 563
2bc8ca0c
ZJS
564 assert(direction == DIRECTION_DOWN ? np >= after_offset : np <= after_offset);
565 last_moved = m->matches;
de190aef 566
2bc8ca0c
ZJS
567 LIST_LOOP_BUT_ONE(matches, i, m->matches, last_moved) {
568 uint64_t cp;
de190aef 569
2bc8ca0c
ZJS
570 r = next_for_match(j, i, f, np, direction, NULL, &cp);
571 if (r <= 0)
572 return r;
de190aef 573
2bc8ca0c
ZJS
574 assert(direction == DIRECTION_DOWN ? cp >= np : cp <= np);
575 if (direction == DIRECTION_DOWN ? cp > np : cp < np) {
576 np = cp;
577 last_moved = i;
de190aef 578 }
2bc8ca0c 579 }
cbdca852 580 }
de190aef 581
bc302926 582 assert(np > 0);
de190aef 583
cbdca852
LP
584 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
585 if (r < 0)
586 return r;
de7b95cd 587
de190aef 588 if (ret)
cbdca852 589 *ret = n;
de190aef 590 if (offset)
cbdca852 591 *offset = np;
de190aef
LP
592
593 return 1;
594}
595
cbdca852
LP
596static int find_location_for_match(
597 sd_journal *j,
598 Match *m,
599 JournalFile *f,
600 direction_t direction,
601 Object **ret,
602 uint64_t *offset) {
603
de190aef 604 int r;
de190aef
LP
605
606 assert(j);
cbdca852 607 assert(m);
de190aef 608 assert(f);
de190aef 609
cbdca852
LP
610 if (m->type == MATCH_DISCRETE) {
611 uint64_t dp;
de190aef 612
cbdca852 613 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
de7b95cd
LP
614 if (r <= 0)
615 return r;
616
cbdca852 617 /* FIXME: missing: find by monotonic */
de7b95cd 618
cbdca852
LP
619 if (j->current_location.type == LOCATION_HEAD)
620 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset);
621 if (j->current_location.type == LOCATION_TAIL)
622 return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, ret, offset);
623 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
624 return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset);
625 if (j->current_location.monotonic_set) {
626 r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
627 if (r != -ENOENT)
628 return r;
629 }
630 if (j->current_location.realtime_set)
631 return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
de190aef 632
cbdca852 633 return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset);
de7b95cd 634
cbdca852
LP
635 } else if (m->type == MATCH_OR_TERM) {
636 uint64_t np = 0;
637 Object *n;
638 Match *i;
de7b95cd 639
cbdca852 640 /* Find the earliest match */
de7b95cd 641
cbdca852
LP
642 LIST_FOREACH(matches, i, m->matches) {
643 uint64_t cp;
644
645 r = find_location_for_match(j, i, f, direction, NULL, &cp);
646 if (r < 0)
647 return r;
648 else if (r > 0) {
649 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
650 np = cp;
de190aef 651 }
cbdca852 652 }
de190aef 653
cbdca852
LP
654 if (np == 0)
655 return 0;
de7b95cd 656
cbdca852
LP
657 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
658 if (r < 0)
659 return r;
de7b95cd 660
cbdca852
LP
661 if (ret)
662 *ret = n;
663 if (offset)
664 *offset = np;
de190aef 665
cbdca852 666 return 1;
e892bd17 667
cbdca852
LP
668 } else {
669 Match *i;
670 uint64_t np = 0;
671
672 assert(m->type == MATCH_AND_TERM);
673
674 /* First jump to the last match, and then find the
675 * next one where all matches match */
676
677 if (!m->matches)
678 return 0;
679
680 LIST_FOREACH(matches, i, m->matches) {
681 uint64_t cp;
682
683 r = find_location_for_match(j, i, f, direction, NULL, &cp);
684 if (r <= 0)
4b067dc9
LP
685 return r;
686
bc302926 687 if (np == 0 || (direction == DIRECTION_DOWN ? cp > np : cp < np))
cbdca852 688 np = cp;
de7b95cd
LP
689 }
690
cbdca852
LP
691 return next_for_match(j, m, f, np, direction, ret, offset);
692 }
693}
de190aef 694
cbdca852
LP
695static int find_location_with_matches(
696 sd_journal *j,
697 JournalFile *f,
698 direction_t direction,
699 Object **ret,
700 uint64_t *offset) {
701
702 int r;
703
704 assert(j);
705 assert(f);
706 assert(ret);
707 assert(offset);
708
709 if (!j->level0) {
710 /* No matches is simple */
711
712 if (j->current_location.type == LOCATION_HEAD)
f534928a 713 return journal_file_next_entry(f, 0, DIRECTION_DOWN, ret, offset);
cbdca852 714 if (j->current_location.type == LOCATION_TAIL)
f534928a 715 return journal_file_next_entry(f, 0, DIRECTION_UP, ret, offset);
cbdca852
LP
716 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
717 return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
718 if (j->current_location.monotonic_set) {
719 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
720 if (r != -ENOENT)
721 return r;
de7b95cd 722 }
cbdca852
LP
723 if (j->current_location.realtime_set)
724 return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
de7b95cd 725
f534928a 726 return journal_file_next_entry(f, 0, direction, ret, offset);
cbdca852
LP
727 } else
728 return find_location_for_match(j, j->level0, f, direction, ret, offset);
729}
de7b95cd 730
cbdca852
LP
731static int next_with_matches(
732 sd_journal *j,
733 JournalFile *f,
734 direction_t direction,
735 Object **ret,
736 uint64_t *offset) {
737
cbdca852
LP
738 assert(j);
739 assert(f);
740 assert(ret);
741 assert(offset);
742
cbdca852
LP
743 /* No matches is easy. We simple advance the file
744 * pointer by one. */
745 if (!j->level0)
b29ddfcb 746 return journal_file_next_entry(f, f->current_offset, direction, ret, offset);
cbdca852
LP
747
748 /* If we have a match then we look for the next matching entry
49f43d5f 749 * with an offset at least one step larger */
b29ddfcb
MS
750 return next_for_match(j, j->level0, f,
751 direction == DIRECTION_DOWN ? f->current_offset + 1
752 : f->current_offset - 1,
753 direction, ret, offset);
de7b95cd
LP
754}
755
58439db4 756static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction) {
de190aef 757 Object *c;
6e693b42 758 uint64_t cp, n_entries;
cbdca852 759 int r;
de190aef
LP
760
761 assert(j);
762 assert(f);
763
950c07d4
MS
764 n_entries = le64toh(f->header->n_entries);
765
766 /* If we hit EOF before, we don't need to look into this file again
767 * unless direction changed or new entries appeared. */
768 if (f->last_direction == direction && f->location_type == LOCATION_TAIL &&
769 n_entries == f->last_n_entries)
770 return 0;
668c965a 771
950c07d4
MS
772 f->last_n_entries = n_entries;
773
774 if (f->last_direction == direction && f->current_offset > 0) {
7943f422
MS
775 /* LOCATION_SEEK here means we did the work in a previous
776 * iteration and the current location already points to a
777 * candidate entry. */
487d3720
MS
778 if (f->location_type != LOCATION_SEEK) {
779 r = next_with_matches(j, f, direction, &c, &cp);
780 if (r <= 0)
781 return r;
466ccd92 782
950c07d4 783 journal_file_save_location(f, c, cp);
487d3720 784 }
de190aef 785 } else {
950c07d4
MS
786 f->last_direction = direction;
787
cbdca852 788 r = find_location_with_matches(j, f, direction, &c, &cp);
de190aef
LP
789 if (r <= 0)
790 return r;
487d3720 791
950c07d4 792 journal_file_save_location(f, c, cp);
de190aef
LP
793 }
794
bc302926 795 /* OK, we found the spot, now let's advance until an entry
cbdca852
LP
796 * that is actually different from what we were previously
797 * looking at. This is necessary to handle entries which exist
798 * in two (or more) journal files, and which shall all be
799 * suppressed but one. */
800
de190aef
LP
801 for (;;) {
802 bool found;
803
804 if (j->current_location.type == LOCATION_DISCRETE) {
805 int k;
806
487d3720 807 k = compare_with_location(f, &j->current_location);
1cdf7175
CH
808
809 found = direction == DIRECTION_DOWN ? k > 0 : k < 0;
de190aef
LP
810 } else
811 found = true;
812
487d3720 813 if (found)
de190aef 814 return 1;
de190aef
LP
815
816 r = next_with_matches(j, f, direction, &c, &cp);
817 if (r <= 0)
818 return r;
487d3720 819
950c07d4 820 journal_file_save_location(f, c, cp);
de190aef
LP
821 }
822}
823
e892bd17 824static int real_journal_next(sd_journal *j, direction_t direction) {
5d4ba7f2
VC
825 JournalFile *new_file = NULL;
826 unsigned i, n_files;
827 const void **files;
a002d44b 828 Object *o;
87d2c1ff
LP
829 int r;
830
1ae464e0
TA
831 assert_return(j, -EINVAL);
832 assert_return(!journal_pid_changed(j), -ECHILD);
87d2c1ff 833
5d4ba7f2
VC
834 r = iterated_cache_get(j->files_cache, NULL, &files, &n_files);
835 if (r < 0)
836 return r;
837
838 for (i = 0; i < n_files; i++) {
839 JournalFile *f = (JournalFile *)files[i];
de190aef 840 bool found;
87d2c1ff 841
58439db4 842 r = next_beyond_location(j, f, direction);
e590af26 843 if (r < 0) {
da927ba9 844 log_debug_errno(r, "Can't iterate through %s, ignoring: %m", f->path);
a9a245c1 845 remove_file_real(j, f);
e590af26 846 continue;
6573ef05
MS
847 } else if (r == 0) {
848 f->location_type = LOCATION_TAIL;
cec736d2 849 continue;
6573ef05 850 }
87d2c1ff 851
468b21de 852 if (!new_file)
de190aef
LP
853 found = true;
854 else {
855 int k;
856
d8ae66d7 857 k = journal_file_compare_locations(f, new_file);
de190aef 858
bc302926 859 found = direction == DIRECTION_DOWN ? k < 0 : k > 0;
de190aef
LP
860 }
861
e499c999 862 if (found)
468b21de 863 new_file = f;
87d2c1ff
LP
864 }
865
468b21de 866 if (!new_file)
de190aef 867 return 0;
ae2cc8ef 868
e499c999 869 r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_file->current_offset, &o);
468b21de
LP
870 if (r < 0)
871 return r;
872
1eb6332d 873 set_location(j, new_file, o);
ae2cc8ef 874
de190aef
LP
875 return 1;
876}
ae2cc8ef 877
a5344d2c 878_public_ int sd_journal_next(sd_journal *j) {
de190aef
LP
879 return real_journal_next(j, DIRECTION_DOWN);
880}
ae2cc8ef 881
a5344d2c 882_public_ int sd_journal_previous(sd_journal *j) {
de190aef
LP
883 return real_journal_next(j, DIRECTION_UP);
884}
ae2cc8ef 885
6f003b43 886static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
de190aef 887 int c = 0, r;
ae2cc8ef 888
1ae464e0
TA
889 assert_return(j, -EINVAL);
890 assert_return(!journal_pid_changed(j), -ECHILD);
de190aef 891
6f003b43
LP
892 if (skip == 0) {
893 /* If this is not a discrete skip, then at least
894 * resolve the current location */
04884354
LK
895 if (j->current_location.type != LOCATION_DISCRETE) {
896 r = real_journal_next(j, direction);
897 if (r < 0)
898 return r;
899 }
6f003b43
LP
900
901 return 0;
902 }
903
904 do {
905 r = real_journal_next(j, direction);
de190aef
LP
906 if (r < 0)
907 return r;
908
909 if (r == 0)
910 return c;
911
912 skip--;
913 c++;
6f003b43 914 } while (skip > 0);
87d2c1ff 915
de190aef 916 return c;
87d2c1ff
LP
917}
918
6f003b43
LP
919_public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
920 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
921}
de190aef 922
6f003b43
LP
923_public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
924 return real_journal_next_skip(j, DIRECTION_UP, skip);
87d2c1ff
LP
925}
926
a5344d2c 927_public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
cec736d2 928 Object *o;
87d2c1ff 929 int r;
3fbf9cbb 930 char bid[33], sid[33];
87d2c1ff 931
1ae464e0
TA
932 assert_return(j, -EINVAL);
933 assert_return(!journal_pid_changed(j), -ECHILD);
934 assert_return(cursor, -EINVAL);
87d2c1ff 935
3fbf9cbb
LP
936 if (!j->current_file || j->current_file->current_offset <= 0)
937 return -EADDRNOTAVAIL;
87d2c1ff 938
de190aef 939 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
87d2c1ff
LP
940 if (r < 0)
941 return r;
942
3fbf9cbb
LP
943 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
944 sd_id128_to_string(o->entry.boot_id, bid);
87d2c1ff 945
3fbf9cbb 946 if (asprintf(cursor,
507f22bd
ZJS
947 "s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64,
948 sid, le64toh(o->entry.seqnum),
949 bid, le64toh(o->entry.monotonic),
950 le64toh(o->entry.realtime),
951 le64toh(o->entry.xor_hash)) < 0)
3fbf9cbb 952 return -ENOMEM;
87d2c1ff 953
6f47ad30 954 return 0;
87d2c1ff
LP
955}
956
a5344d2c 957_public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
a2a5291b 958 const char *word, *state;
de190aef 959 size_t l;
de190aef
LP
960 unsigned long long seqnum, monotonic, realtime, xor_hash;
961 bool
962 seqnum_id_set = false,
963 seqnum_set = false,
964 boot_id_set = false,
965 monotonic_set = false,
966 realtime_set = false,
967 xor_hash_set = false;
968 sd_id128_t seqnum_id, boot_id;
969
1ae464e0
TA
970 assert_return(j, -EINVAL);
971 assert_return(!journal_pid_changed(j), -ECHILD);
972 assert_return(!isempty(cursor), -EINVAL);
de190aef 973
a2a5291b 974 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
de190aef
LP
975 char *item;
976 int k = 0;
977
a2a5291b 978 if (l < 2 || word[1] != '=')
de190aef
LP
979 return -EINVAL;
980
a2a5291b 981 item = strndup(word, l);
de190aef
LP
982 if (!item)
983 return -ENOMEM;
984
a2a5291b 985 switch (word[0]) {
de190aef
LP
986
987 case 's':
988 seqnum_id_set = true;
be3ea5ea 989 k = sd_id128_from_string(item+2, &seqnum_id);
de190aef
LP
990 break;
991
992 case 'i':
993 seqnum_set = true;
be3ea5ea 994 if (sscanf(item+2, "%llx", &seqnum) != 1)
de190aef
LP
995 k = -EINVAL;
996 break;
997
998 case 'b':
999 boot_id_set = true;
be3ea5ea 1000 k = sd_id128_from_string(item+2, &boot_id);
de190aef
LP
1001 break;
1002
1003 case 'm':
1004 monotonic_set = true;
be3ea5ea 1005 if (sscanf(item+2, "%llx", &monotonic) != 1)
de190aef
LP
1006 k = -EINVAL;
1007 break;
1008
1009 case 't':
1010 realtime_set = true;
be3ea5ea 1011 if (sscanf(item+2, "%llx", &realtime) != 1)
de190aef
LP
1012 k = -EINVAL;
1013 break;
1014
1015 case 'x':
1016 xor_hash_set = true;
be3ea5ea 1017 if (sscanf(item+2, "%llx", &xor_hash) != 1)
de190aef
LP
1018 k = -EINVAL;
1019 break;
1020 }
1021
1022 free(item);
1023
1024 if (k < 0)
1025 return k;
1026 }
1027
1028 if ((!seqnum_set || !seqnum_id_set) &&
1029 (!monotonic_set || !boot_id_set) &&
1030 !realtime_set)
1031 return -EINVAL;
1032
1033 reset_location(j);
1034
a87247dd 1035 j->current_location.type = LOCATION_SEEK;
de190aef
LP
1036
1037 if (realtime_set) {
1038 j->current_location.realtime = (uint64_t) realtime;
1039 j->current_location.realtime_set = true;
1040 }
1041
1042 if (seqnum_set && seqnum_id_set) {
1043 j->current_location.seqnum = (uint64_t) seqnum;
1044 j->current_location.seqnum_id = seqnum_id;
1045 j->current_location.seqnum_set = true;
1046 }
1047
1048 if (monotonic_set && boot_id_set) {
1049 j->current_location.monotonic = (uint64_t) monotonic;
1050 j->current_location.boot_id = boot_id;
1051 j->current_location.monotonic_set = true;
1052 }
1053
1054 if (xor_hash_set) {
1055 j->current_location.xor_hash = (uint64_t) xor_hash;
1056 j->current_location.xor_hash_set = true;
1057 }
1058
1059 return 0;
1060}
1061
c6511e85
LP
1062_public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
1063 int r;
c6511e85
LP
1064 Object *o;
1065
1ae464e0
TA
1066 assert_return(j, -EINVAL);
1067 assert_return(!journal_pid_changed(j), -ECHILD);
1068 assert_return(!isempty(cursor), -EINVAL);
c6511e85
LP
1069
1070 if (!j->current_file || j->current_file->current_offset <= 0)
1071 return -EADDRNOTAVAIL;
1072
1073 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1074 if (r < 0)
1075 return r;
1076
9ed794a3 1077 for (;;) {
c6511e85 1078 _cleanup_free_ char *item = NULL;
c6511e85 1079 unsigned long long ll;
7294bb5b 1080 sd_id128_t id;
c6511e85
LP
1081 int k = 0;
1082
7294bb5b
SS
1083 r = extract_first_word(&cursor, &item, ";", EXTRACT_DONT_COALESCE_SEPARATORS);
1084 if (r < 0)
1085 return r;
c6511e85 1086
7294bb5b
SS
1087 if (r == 0)
1088 break;
c6511e85 1089
7294bb5b
SS
1090 if (strlen(item) < 2 || item[1] != '=')
1091 return -EINVAL;
1092
1093 switch (item[0]) {
c6511e85
LP
1094
1095 case 's':
1096 k = sd_id128_from_string(item+2, &id);
1097 if (k < 0)
1098 return k;
1099 if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
1100 return 0;
1101 break;
1102
1103 case 'i':
1104 if (sscanf(item+2, "%llx", &ll) != 1)
1105 return -EINVAL;
1106 if (ll != le64toh(o->entry.seqnum))
1107 return 0;
1108 break;
1109
1110 case 'b':
1111 k = sd_id128_from_string(item+2, &id);
1112 if (k < 0)
1113 return k;
1114 if (!sd_id128_equal(id, o->entry.boot_id))
1115 return 0;
1116 break;
1117
1118 case 'm':
1119 if (sscanf(item+2, "%llx", &ll) != 1)
1120 return -EINVAL;
1121 if (ll != le64toh(o->entry.monotonic))
1122 return 0;
1123 break;
1124
1125 case 't':
1126 if (sscanf(item+2, "%llx", &ll) != 1)
1127 return -EINVAL;
1128 if (ll != le64toh(o->entry.realtime))
1129 return 0;
1130 break;
1131
1132 case 'x':
1133 if (sscanf(item+2, "%llx", &ll) != 1)
1134 return -EINVAL;
1135 if (ll != le64toh(o->entry.xor_hash))
1136 return 0;
1137 break;
1138 }
1139 }
1140
1141 return 1;
1142}
1143
a5344d2c 1144_public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1ae464e0
TA
1145 assert_return(j, -EINVAL);
1146 assert_return(!journal_pid_changed(j), -ECHILD);
de190aef
LP
1147
1148 reset_location(j);
a87247dd 1149 j->current_location.type = LOCATION_SEEK;
de190aef
LP
1150 j->current_location.boot_id = boot_id;
1151 j->current_location.monotonic = usec;
1152 j->current_location.monotonic_set = true;
1153
1154 return 0;
1155}
1156
a5344d2c 1157_public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1ae464e0
TA
1158 assert_return(j, -EINVAL);
1159 assert_return(!journal_pid_changed(j), -ECHILD);
de190aef
LP
1160
1161 reset_location(j);
a87247dd 1162 j->current_location.type = LOCATION_SEEK;
de190aef
LP
1163 j->current_location.realtime = usec;
1164 j->current_location.realtime_set = true;
1165
1166 return 0;
1167}
1168
a5344d2c 1169_public_ int sd_journal_seek_head(sd_journal *j) {
1ae464e0
TA
1170 assert_return(j, -EINVAL);
1171 assert_return(!journal_pid_changed(j), -ECHILD);
de190aef
LP
1172
1173 reset_location(j);
1174 j->current_location.type = LOCATION_HEAD;
1175
1176 return 0;
1177}
1178
a5344d2c 1179_public_ int sd_journal_seek_tail(sd_journal *j) {
1ae464e0
TA
1180 assert_return(j, -EINVAL);
1181 assert_return(!journal_pid_changed(j), -ECHILD);
de190aef
LP
1182
1183 reset_location(j);
1184 j->current_location.type = LOCATION_TAIL;
1185
1186 return 0;
87d2c1ff
LP
1187}
1188
85210bff 1189static void check_network(sd_journal *j, int fd) {
85210bff
LP
1190 assert(j);
1191
1192 if (j->on_network)
1193 return;
1194
77f9fa3b 1195 j->on_network = fd_is_network_fs(fd);
85210bff
LP
1196}
1197
a688baa8
ZJS
1198static bool file_has_type_prefix(const char *prefix, const char *filename) {
1199 const char *full, *tilded, *atted;
1200
63c372cb
LP
1201 full = strjoina(prefix, ".journal");
1202 tilded = strjoina(full, "~");
1203 atted = strjoina(prefix, "@");
a688baa8
ZJS
1204
1205 return streq(filename, full) ||
1206 streq(filename, tilded) ||
1207 startswith(filename, atted);
1208}
1209
1210static bool file_type_wanted(int flags, const char *filename) {
d617408e
LP
1211 assert(filename);
1212
a688baa8
ZJS
1213 if (!endswith(filename, ".journal") && !endswith(filename, ".journal~"))
1214 return false;
1215
1216 /* no flags set → every type is OK */
1217 if (!(flags & (SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)))
1218 return true;
1219
1220 if (flags & SD_JOURNAL_SYSTEM && file_has_type_prefix("system", filename))
1221 return true;
1222
1223 if (flags & SD_JOURNAL_CURRENT_USER) {
1224 char prefix[5 + DECIMAL_STR_MAX(uid_t) + 1];
1225
5ffa8c81 1226 xsprintf(prefix, "user-"UID_FMT, getuid());
a688baa8
ZJS
1227
1228 if (file_has_type_prefix(prefix, filename))
1229 return true;
1230 }
1231
1232 return false;
1233}
1234
5d1ce257
LP
1235static bool path_has_prefix(sd_journal *j, const char *path, const char *prefix) {
1236 assert(j);
1237 assert(path);
1238 assert(prefix);
1239
1240 if (j->toplevel_fd >= 0)
1241 return false;
1242
1243 return path_startswith(path, prefix);
1244}
1245
1246static const char *skip_slash(const char *p) {
1247
1248 if (!p)
1249 return NULL;
1250
1251 while (*p == '/')
1252 p++;
1253
1254 return p;
1255}
1256
1257static int add_any_file(sd_journal *j, int fd, const char *path) {
39883f62 1258 JournalFile *f = NULL;
5d1ce257 1259 bool close_fd = false;
d617408e 1260 int r, k;
3fbf9cbb
LP
1261
1262 assert(j);
5d1ce257 1263 assert(fd >= 0 || path);
3fbf9cbb 1264
858749f7
LP
1265 if (path) {
1266 f = ordered_hashmap_get(j->files, path);
1267 if (f) {
1268 /* Mark this file as seen in this generation. This is used to GC old files in
1269 * process_q_overflow() to detect journal files that are still and discern them from those who
1270 * are gone. */
1271 f->last_seen_generation = j->generation;
1272 return 0;
1273 }
1274 }
50f20cfd 1275
9e8abdf0 1276 if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
d617408e
LP
1277 log_debug("Too many open journal files, not adding %s.", path);
1278 r = -ETOOMANYREFS;
1279 goto fail;
9e8abdf0
LP
1280 }
1281
5d1ce257
LP
1282 if (fd < 0 && j->toplevel_fd >= 0) {
1283
1284 /* If there's a top-level fd defined, open the file relative to this now. (Make the path relative,
1285 * explicitly, since otherwise openat() ignores the first argument.) */
1286
1287 fd = openat(j->toplevel_fd, skip_slash(path), O_RDONLY|O_CLOEXEC);
1288 if (fd < 0) {
1289 r = log_debug_errno(errno, "Failed to open journal file %s: %m", path);
1290 goto fail;
1291 }
1292
1293 close_fd = true;
1294 }
1295
1296 r = journal_file_open(fd, path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, NULL, &f);
d617408e 1297 if (r < 0) {
5d1ce257
LP
1298 if (close_fd)
1299 safe_close(fd);
d617408e
LP
1300 log_debug_errno(r, "Failed to open journal file %s: %m", path);
1301 goto fail;
1302 }
3fbf9cbb 1303
72f59706 1304 /* journal_file_dump(f); */
de190aef 1305
c1f906bd 1306 r = ordered_hashmap_put(j->files, f->path, f);
3fbf9cbb 1307 if (r < 0) {
5d1ce257 1308 f->close_fd = close_fd;
69a3a6fd 1309 (void) journal_file_close(f);
d617408e 1310 goto fail;
3fbf9cbb
LP
1311 }
1312
858749f7
LP
1313 f->last_seen_generation = j->generation;
1314
5d1ce257
LP
1315 if (!j->has_runtime_files && path_has_prefix(j, f->path, "/run"))
1316 j->has_runtime_files = true;
1317 else if (!j->has_persistent_files && path_has_prefix(j, f->path, "/var"))
1318 j->has_persistent_files = true;
1319
5ec76417 1320 log_debug("File %s added.", f->path);
a50d7d43 1321
85210bff
LP
1322 check_network(j, f->fd);
1323
313cefa1 1324 j->current_invalidate_counter++;
a963990f 1325
50f20cfd 1326 return 0;
d617408e
LP
1327
1328fail:
5768d259 1329 k = journal_put_error(j, r, path);
d617408e
LP
1330 if (k < 0)
1331 return k;
1332
1333 return r;
50f20cfd
LP
1334}
1335
5302ebe1 1336static int add_file(sd_journal *j, const char *prefix, const char *filename) {
d617408e 1337 const char *path;
5302ebe1
ZJS
1338
1339 assert(j);
1340 assert(prefix);
1341 assert(filename);
1342
5d1ce257 1343 if (j->no_new_files)
5302ebe1
ZJS
1344 return 0;
1345
5d1ce257
LP
1346 if (!file_type_wanted(j->flags, filename))
1347 return 0;
39fd5b08 1348
5d1ce257
LP
1349 path = strjoina(prefix, "/", filename);
1350 return add_any_file(j, -1, path);
5302ebe1
ZJS
1351}
1352
d617408e
LP
1353static void remove_file(sd_journal *j, const char *prefix, const char *filename) {
1354 const char *path;
50f20cfd
LP
1355 JournalFile *f;
1356
1357 assert(j);
1358 assert(prefix);
1359 assert(filename);
1360
d617408e 1361 path = strjoina(prefix, "/", filename);
c1f906bd 1362 f = ordered_hashmap_get(j->files, path);
50f20cfd 1363 if (!f)
d617408e 1364 return;
50f20cfd 1365
a9a245c1 1366 remove_file_real(j, f);
a9a245c1
ZJS
1367}
1368
1369static void remove_file_real(sd_journal *j, JournalFile *f) {
1370 assert(j);
1371 assert(f);
1372
c1f906bd 1373 ordered_hashmap_remove(j->files, f->path);
44a5fa34 1374
5ec76417 1375 log_debug("File %s removed.", f->path);
44a5fa34 1376
3c1668da
LP
1377 if (j->current_file == f) {
1378 j->current_file = NULL;
1379 j->current_field = 0;
1380 }
1381
1382 if (j->unique_file == f) {
360af4cf 1383 /* Jump to the next unique_file or NULL if that one was last */
c1f906bd 1384 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
3c1668da 1385 j->unique_offset = 0;
360af4cf
ZJS
1386 if (!j->unique_file)
1387 j->unique_file_lost = true;
3c1668da
LP
1388 }
1389
eb86030e
LP
1390 if (j->fields_file == f) {
1391 j->fields_file = ordered_hashmap_next(j->files, j->fields_file->path);
1392 j->fields_offset = 0;
1393 if (!j->fields_file)
1394 j->fields_file_lost = true;
1395 }
1396
69a3a6fd 1397 (void) journal_file_close(f);
50f20cfd 1398
313cefa1 1399 j->current_invalidate_counter++;
3fbf9cbb
LP
1400}
1401
d617408e
LP
1402static int dirname_is_machine_id(const char *fn) {
1403 sd_id128_t id, machine;
1404 int r;
1405
1406 r = sd_id128_get_machine(&machine);
1407 if (r < 0)
1408 return r;
1409
1410 r = sd_id128_from_string(fn, &id);
1411 if (r < 0)
1412 return r;
1413
1414 return sd_id128_equal(id, machine);
1415}
1416
858749f7
LP
1417static bool dirent_is_journal_file(const struct dirent *de) {
1418 assert(de);
1419
1420 if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN))
1421 return false;
1422
1423 return endswith(de->d_name, ".journal") ||
1424 endswith(de->d_name, ".journal~");
1425}
1426
1427static bool dirent_is_id128_subdir(const struct dirent *de) {
1428 assert(de);
1429
1430 if (!IN_SET(de->d_type, DT_DIR, DT_LNK, DT_UNKNOWN))
1431 return false;
1432
1433 return id128_is_valid(de->d_name);
1434}
1435
1436static int directory_open(sd_journal *j, const char *path, DIR **ret) {
1437 DIR *d;
1438
1439 assert(j);
1440 assert(path);
1441 assert(ret);
1442
1443 if (j->toplevel_fd < 0)
1444 d = opendir(path);
1445 else
1446 /* Open the specified directory relative to the toplevel fd. Enforce that the path specified is
1447 * relative, by dropping the initial slash */
1448 d = xopendirat(j->toplevel_fd, skip_slash(path), 0);
1449 if (!d)
1450 return -errno;
1451
1452 *ret = d;
1453 return 0;
1454}
1455
1456static int add_directory(sd_journal *j, const char *prefix, const char *dirname);
1457
1458static void directory_enumerate(sd_journal *j, Directory *m, DIR *d) {
1459 struct dirent *de;
1460
1461 assert(j);
1462 assert(m);
1463 assert(d);
1464
1465 FOREACH_DIRENT_ALL(de, d, goto fail) {
1466 if (dirent_is_journal_file(de))
1467 (void) add_file(j, m->path, de->d_name);
1468
1469 if (m->is_root && dirent_is_id128_subdir(de))
1470 (void) add_directory(j, m->path, de->d_name);
1471 }
1472
1473 return;
1474
1475fail:
1476 log_debug_errno(errno, "Failed to enumerate directory %s, ignoring: %m", m->path);
1477}
1478
1479static void directory_watch(sd_journal *j, Directory *m, int fd, uint32_t mask) {
1480 int r;
1481
1482 assert(j);
1483 assert(m);
1484 assert(fd >= 0);
1485
1486 /* Watch this directory if that's enabled and if it not being watched yet. */
1487
1488 if (m->wd > 0) /* Already have a watch? */
1489 return;
1490 if (j->inotify_fd < 0) /* Not watching at all? */
1491 return;
1492
1493 m->wd = inotify_add_watch_fd(j->inotify_fd, fd, mask);
1494 if (m->wd < 0) {
1495 log_debug_errno(errno, "Failed to watch journal directory '%s', ignoring: %m", m->path);
1496 return;
1497 }
1498
1499 r = hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m);
1500 if (r == -EEXIST)
1501 log_debug_errno(r, "Directory '%s' already being watched under a different path, ignoring: %m", m->path);
1502 if (r < 0) {
1503 log_debug_errno(r, "Failed to add watch for journal directory '%s' to hashmap, ignoring: %m", m->path);
1504 (void) inotify_rm_watch(j->inotify_fd, m->wd);
1505 m->wd = -1;
1506 }
1507}
1508
a963990f 1509static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
7fd1b19b 1510 _cleanup_free_ char *path = NULL;
7fd1b19b 1511 _cleanup_closedir_ DIR *d = NULL;
a963990f 1512 Directory *m;
d617408e 1513 int r, k;
3fbf9cbb
LP
1514
1515 assert(j);
1516 assert(prefix);
3fbf9cbb 1517
5d1ce257
LP
1518 /* Adds a journal file directory to watch. If the directory is already tracked this updates the inotify watch
1519 * and reenumerates directory contents */
d95b1fb3 1520
5d1ce257 1521 if (dirname)
605405c6 1522 path = strjoin(prefix, "/", dirname);
5d1ce257
LP
1523 else
1524 path = strdup(prefix);
d617408e
LP
1525 if (!path) {
1526 r = -ENOMEM;
1527 goto fail;
1528 }
3fbf9cbb 1529
858749f7 1530 log_debug("Considering directory '%s'.", path);
5d1ce257
LP
1531
1532 /* We consider everything local that is in a directory for the local machine ID, or that is stored in /run */
1533 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1534 !((dirname && dirname_is_machine_id(dirname) > 0) || path_has_prefix(j, path, "/run")))
858749f7 1535 return 0;
5d1ce257 1536
858749f7
LP
1537 r = directory_open(j, path, &d);
1538 if (r < 0) {
1539 log_debug_errno(r, "Failed to open directory '%s': %m", path);
d617408e 1540 goto fail;
3fbf9cbb
LP
1541 }
1542
a963990f
LP
1543 m = hashmap_get(j->directories_by_path, path);
1544 if (!m) {
1545 m = new0(Directory, 1);
d617408e
LP
1546 if (!m) {
1547 r = -ENOMEM;
1548 goto fail;
1549 }
a963990f
LP
1550
1551 m->is_root = false;
1552 m->path = path;
1553
1554 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
a963990f 1555 free(m);
d617408e
LP
1556 r = -ENOMEM;
1557 goto fail;
a963990f
LP
1558 }
1559
a50d7d43 1560 path = NULL; /* avoid freeing in cleanup */
313cefa1 1561 j->current_invalidate_counter++;
a963990f 1562
5ec76417 1563 log_debug("Directory %s added.", m->path);
a963990f 1564
a50d7d43 1565 } else if (m->is_root)
858749f7 1566 return 0; /* Don't 'downgrade' from root directory */
a963990f 1567
858749f7 1568 m->last_seen_generation = j->generation;
a963990f 1569
858749f7
LP
1570 directory_watch(j, m, dirfd(d),
1571 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1572 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
1573 IN_ONLYDIR);
a963990f 1574
858749f7
LP
1575 if (!j->no_new_files)
1576 directory_enumerate(j, m, d);
a963990f 1577
85210bff
LP
1578 check_network(j, dirfd(d));
1579
a963990f 1580 return 0;
d617408e
LP
1581
1582fail:
5d1ce257 1583 k = journal_put_error(j, r, path ?: prefix);
d617408e
LP
1584 if (k < 0)
1585 return k;
1586
1587 return r;
a963990f
LP
1588}
1589
d617408e 1590static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) {
5d1ce257 1591
7fd1b19b 1592 _cleanup_closedir_ DIR *d = NULL;
a963990f 1593 Directory *m;
d617408e 1594 int r, k;
a963990f
LP
1595
1596 assert(j);
a963990f 1597
5d1ce257
LP
1598 /* Adds a root directory to our set of directories to use. If the root directory is already in the set, we
1599 * update the inotify logic, and renumerate the directory entries. This call may hence be called to initially
1600 * populate the set, as well as to update it later. */
a963990f 1601
5d1ce257
LP
1602 if (p) {
1603 /* If there's a path specified, use it. */
b6741478 1604
858749f7
LP
1605 log_debug("Considering root directory '%s'.", p);
1606
5d1ce257
LP
1607 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1608 !path_has_prefix(j, p, "/run"))
1609 return -EINVAL;
d617408e 1610
5d1ce257
LP
1611 if (j->prefix)
1612 p = strjoina(j->prefix, p);
1613
858749f7
LP
1614 r = directory_open(j, p, &d);
1615 if (r == -ENOENT && missing_ok)
1616 return 0;
1617 if (r < 0) {
1618 log_debug_errno(r, "Failed to open root directory %s: %m", p);
5d1ce257
LP
1619 goto fail;
1620 }
1621 } else {
1622 int dfd;
1623
1624 /* If there's no path specified, then we use the top-level fd itself. We duplicate the fd here, since
1625 * opendir() will take possession of the fd, and close it, which we don't want. */
1626
1627 p = "."; /* store this as "." in the directories hashmap */
1628
1629 dfd = fcntl(j->toplevel_fd, F_DUPFD_CLOEXEC, 3);
1630 if (dfd < 0) {
1631 r = -errno;
1632 goto fail;
1633 }
1634
1635 d = fdopendir(dfd);
1636 if (!d) {
1637 r = -errno;
1638 safe_close(dfd);
1639 goto fail;
1640 }
1641
1642 rewinddir(d);
d617408e 1643 }
a963990f
LP
1644
1645 m = hashmap_get(j->directories_by_path, p);
1646 if (!m) {
1647 m = new0(Directory, 1);
d617408e
LP
1648 if (!m) {
1649 r = -ENOMEM;
1650 goto fail;
1651 }
a963990f
LP
1652
1653 m->is_root = true;
5d1ce257 1654
a963990f
LP
1655 m->path = strdup(p);
1656 if (!m->path) {
a963990f 1657 free(m);
d617408e
LP
1658 r = -ENOMEM;
1659 goto fail;
a963990f
LP
1660 }
1661
1662 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
a963990f
LP
1663 free(m->path);
1664 free(m);
d617408e
LP
1665 r = -ENOMEM;
1666 goto fail;
a963990f
LP
1667 }
1668
313cefa1 1669 j->current_invalidate_counter++;
a963990f 1670
5ec76417 1671 log_debug("Root directory %s added.", m->path);
a963990f 1672
a50d7d43 1673 } else if (!m->is_root)
a963990f 1674 return 0;
50f20cfd 1675
858749f7
LP
1676 directory_watch(j, m, dirfd(d),
1677 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1678 IN_ONLYDIR);
a963990f 1679
858749f7
LP
1680 if (!j->no_new_files)
1681 directory_enumerate(j, m, d);
3fbf9cbb 1682
85210bff
LP
1683 check_network(j, dirfd(d));
1684
a963990f 1685 return 0;
d617408e
LP
1686
1687fail:
5768d259 1688 k = journal_put_error(j, r, p);
d617408e
LP
1689 if (k < 0)
1690 return k;
1691
1692 return r;
a963990f
LP
1693}
1694
b2b46f91 1695static void remove_directory(sd_journal *j, Directory *d) {
a963990f
LP
1696 assert(j);
1697
1698 if (d->wd > 0) {
1699 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1700
1701 if (j->inotify_fd >= 0)
1702 inotify_rm_watch(j->inotify_fd, d->wd);
1703 }
1704
1705 hashmap_remove(j->directories_by_path, d->path);
1706
1707 if (d->is_root)
5ec76417 1708 log_debug("Root directory %s removed.", d->path);
a963990f 1709 else
5ec76417 1710 log_debug("Directory %s removed.", d->path);
a963990f
LP
1711
1712 free(d->path);
1713 free(d);
3fbf9cbb
LP
1714}
1715
89739579 1716static int add_search_paths(sd_journal *j) {
d617408e
LP
1717
1718 static const char search_paths[] =
a963990f
LP
1719 "/run/log/journal\0"
1720 "/var/log/journal\0";
1721 const char *p;
50f20cfd
LP
1722
1723 assert(j);
50f20cfd 1724
a963990f
LP
1725 /* We ignore most errors here, since the idea is to only open
1726 * what's actually accessible, and ignore the rest. */
50f20cfd 1727
d617408e
LP
1728 NULSTR_FOREACH(p, search_paths)
1729 (void) add_root_directory(j, p, true);
50f20cfd 1730
574b77ef
MB
1731 if (!(j->flags & SD_JOURNAL_LOCAL_ONLY))
1732 (void) add_root_directory(j, "/var/log/journal/remote", true);
1733
a963990f 1734 return 0;
50f20cfd
LP
1735}
1736
5302ebe1
ZJS
1737static int add_current_paths(sd_journal *j) {
1738 Iterator i;
1739 JournalFile *f;
1740
1741 assert(j);
1742 assert(j->no_new_files);
1743
5d1ce257 1744 /* Simply adds all directories for files we have open as directories. We don't expect errors here, so we
5302ebe1
ZJS
1745 * treat them as fatal. */
1746
c1f906bd 1747 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
5302ebe1 1748 _cleanup_free_ char *dir;
e9174f29 1749 int r;
5302ebe1
ZJS
1750
1751 dir = dirname_malloc(f->path);
1752 if (!dir)
1753 return -ENOMEM;
1754
5d1ce257 1755 r = add_directory(j, dir, NULL);
d617408e 1756 if (r < 0)
5302ebe1 1757 return r;
5302ebe1
ZJS
1758 }
1759
1760 return 0;
1761}
1762
a963990f 1763static int allocate_inotify(sd_journal *j) {
50f20cfd 1764 assert(j);
50f20cfd 1765
a963990f
LP
1766 if (j->inotify_fd < 0) {
1767 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1768 if (j->inotify_fd < 0)
1769 return -errno;
1770 }
50f20cfd 1771
cb306f5d 1772 return hashmap_ensure_allocated(&j->directories_by_wd, NULL);
50f20cfd
LP
1773}
1774
7827b1a1 1775static sd_journal *journal_new(int flags, const char *path) {
a963990f 1776 sd_journal *j;
50f20cfd 1777
a963990f
LP
1778 j = new0(sd_journal, 1);
1779 if (!j)
1780 return NULL;
50f20cfd 1781
df0ff127 1782 j->original_pid = getpid_cached();
5d1ce257 1783 j->toplevel_fd = -1;
a963990f
LP
1784 j->inotify_fd = -1;
1785 j->flags = flags;
93b73b06 1786 j->data_threshold = DEFAULT_DATA_THRESHOLD;
50f20cfd 1787
7827b1a1 1788 if (path) {
16fefe90
ZJS
1789 char *t;
1790
1791 t = strdup(path);
1792 if (!t)
6180fc61 1793 goto fail;
16fefe90
ZJS
1794
1795 if (flags & SD_JOURNAL_OS_ROOT)
1796 j->prefix = t;
1797 else
1798 j->path = t;
7827b1a1
LP
1799 }
1800
548f6937 1801 j->files = ordered_hashmap_new(&path_hash_ops);
5d4ba7f2
VC
1802 if (!j->files)
1803 goto fail;
1804
1805 j->files_cache = ordered_hashmap_iterated_cache_new(j->files);
548f6937 1806 j->directories_by_path = hashmap_new(&path_hash_ops);
84168d80 1807 j->mmap = mmap_cache_new();
5d4ba7f2 1808 if (!j->files_cache || !j->directories_by_path || !j->mmap)
6180fc61 1809 goto fail;
16e9f408 1810
a963990f 1811 return j;
6180fc61
ZJS
1812
1813fail:
1814 sd_journal_close(j);
1815 return NULL;
50f20cfd
LP
1816}
1817
1aaa68f5
ZJS
1818#define OPEN_ALLOWED_FLAGS \
1819 (SD_JOURNAL_LOCAL_ONLY | \
1820 SD_JOURNAL_RUNTIME_ONLY | \
1821 SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)
1822
a5344d2c 1823_public_ int sd_journal_open(sd_journal **ret, int flags) {
87d2c1ff 1824 sd_journal *j;
3fbf9cbb 1825 int r;
87d2c1ff 1826
1ae464e0 1827 assert_return(ret, -EINVAL);
1aaa68f5 1828 assert_return((flags & ~OPEN_ALLOWED_FLAGS) == 0, -EINVAL);
87d2c1ff 1829
7827b1a1 1830 j = journal_new(flags, NULL);
87d2c1ff
LP
1831 if (!j)
1832 return -ENOMEM;
1833
89739579 1834 r = add_search_paths(j);
a963990f 1835 if (r < 0)
50f20cfd 1836 goto fail;
50f20cfd 1837
a963990f
LP
1838 *ret = j;
1839 return 0;
cf244689 1840
a963990f
LP
1841fail:
1842 sd_journal_close(j);
87d2c1ff 1843
a963990f
LP
1844 return r;
1845}
50f20cfd 1846
1aaa68f5
ZJS
1847#define OPEN_CONTAINER_ALLOWED_FLAGS \
1848 (SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_SYSTEM)
1849
b6741478
LP
1850_public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
1851 _cleanup_free_ char *root = NULL, *class = NULL;
1852 sd_journal *j;
1853 char *p;
1854 int r;
1855
2daa9cbd
LP
1856 /* This is pretty much deprecated, people should use machined's OpenMachineRootDirectory() call instead in
1857 * combination with sd_journal_open_directory_fd(). */
1858
b6741478
LP
1859 assert_return(machine, -EINVAL);
1860 assert_return(ret, -EINVAL);
1aaa68f5 1861 assert_return((flags & ~OPEN_CONTAINER_ALLOWED_FLAGS) == 0, -EINVAL);
affcf189 1862 assert_return(machine_name_is_valid(machine), -EINVAL);
b6741478 1863
63c372cb 1864 p = strjoina("/run/systemd/machines/", machine);
b6741478
LP
1865 r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL);
1866 if (r == -ENOENT)
1867 return -EHOSTDOWN;
1868 if (r < 0)
1869 return r;
1870 if (!root)
1871 return -ENODATA;
1872
1873 if (!streq_ptr(class, "container"))
1874 return -EIO;
1875
16fefe90 1876 j = journal_new(flags, root);
b6741478
LP
1877 if (!j)
1878 return -ENOMEM;
1879
89739579 1880 r = add_search_paths(j);
b6741478
LP
1881 if (r < 0)
1882 goto fail;
1883
1884 *ret = j;
1885 return 0;
1886
1887fail:
1888 sd_journal_close(j);
1889 return r;
1890}
1891
1aaa68f5 1892#define OPEN_DIRECTORY_ALLOWED_FLAGS \
10752e82
ZJS
1893 (SD_JOURNAL_OS_ROOT | \
1894 SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER )
1aaa68f5 1895
a963990f
LP
1896_public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1897 sd_journal *j;
1898 int r;
87d2c1ff 1899
1ae464e0
TA
1900 assert_return(ret, -EINVAL);
1901 assert_return(path, -EINVAL);
1aaa68f5 1902 assert_return((flags & ~OPEN_DIRECTORY_ALLOWED_FLAGS) == 0, -EINVAL);
87d2c1ff 1903
7827b1a1 1904 j = journal_new(flags, path);
a963990f
LP
1905 if (!j)
1906 return -ENOMEM;
3fbf9cbb 1907
d077390c
LP
1908 if (flags & SD_JOURNAL_OS_ROOT)
1909 r = add_search_paths(j);
1910 else
1911 r = add_root_directory(j, path, false);
d617408e 1912 if (r < 0)
a963990f 1913 goto fail;
87d2c1ff
LP
1914
1915 *ret = j;
1916 return 0;
1917
1918fail:
1919 sd_journal_close(j);
87d2c1ff 1920 return r;
a963990f 1921}
87d2c1ff 1922
5302ebe1
ZJS
1923_public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
1924 sd_journal *j;
1925 const char **path;
1926 int r;
1927
1ae464e0
TA
1928 assert_return(ret, -EINVAL);
1929 assert_return(flags == 0, -EINVAL);
5302ebe1
ZJS
1930
1931 j = journal_new(flags, NULL);
1932 if (!j)
1933 return -ENOMEM;
1934
1935 STRV_FOREACH(path, paths) {
5d1ce257 1936 r = add_any_file(j, -1, *path);
d617408e 1937 if (r < 0)
5302ebe1 1938 goto fail;
5302ebe1
ZJS
1939 }
1940
1941 j->no_new_files = true;
1942
1943 *ret = j;
1944 return 0;
1945
1946fail:
1947 sd_journal_close(j);
5d1ce257
LP
1948 return r;
1949}
1950
1aaa68f5 1951#define OPEN_DIRECTORY_FD_ALLOWED_FLAGS \
10752e82
ZJS
1952 (SD_JOURNAL_OS_ROOT | \
1953 SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER )
1aaa68f5 1954
5d1ce257
LP
1955_public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) {
1956 sd_journal *j;
1957 struct stat st;
1958 int r;
1959
1960 assert_return(ret, -EINVAL);
1961 assert_return(fd >= 0, -EBADF);
1aaa68f5 1962 assert_return((flags & ~OPEN_DIRECTORY_FD_ALLOWED_FLAGS) == 0, -EINVAL);
5d1ce257
LP
1963
1964 if (fstat(fd, &st) < 0)
1965 return -errno;
1966
1967 if (!S_ISDIR(st.st_mode))
1968 return -EBADFD;
1969
1970 j = journal_new(flags, NULL);
1971 if (!j)
1972 return -ENOMEM;
1973
1974 j->toplevel_fd = fd;
1975
d077390c
LP
1976 if (flags & SD_JOURNAL_OS_ROOT)
1977 r = add_search_paths(j);
1978 else
1979 r = add_root_directory(j, NULL, false);
5d1ce257
LP
1980 if (r < 0)
1981 goto fail;
1982
1983 *ret = j;
1984 return 0;
1985
1986fail:
1987 sd_journal_close(j);
1988 return r;
1989}
1990
1991_public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags) {
1992 Iterator iterator;
1993 JournalFile *f;
1994 sd_journal *j;
1995 unsigned i;
1996 int r;
1997
1998 assert_return(ret, -EINVAL);
1999 assert_return(n_fds > 0, -EBADF);
2000 assert_return(flags == 0, -EINVAL);
2001
2002 j = journal_new(flags, NULL);
2003 if (!j)
2004 return -ENOMEM;
2005
2006 for (i = 0; i < n_fds; i++) {
2007 struct stat st;
2008
2009 if (fds[i] < 0) {
2010 r = -EBADF;
2011 goto fail;
2012 }
2013
2014 if (fstat(fds[i], &st) < 0) {
2015 r = -errno;
2016 goto fail;
2017 }
2018
8d6a4d33
LP
2019 if (S_ISDIR(st.st_mode)) {
2020 r = -EISDIR;
2021 goto fail;
2022 }
5d1ce257
LP
2023 if (!S_ISREG(st.st_mode)) {
2024 r = -EBADFD;
2025 goto fail;
2026 }
2027
2028 r = add_any_file(j, fds[i], NULL);
2029 if (r < 0)
2030 goto fail;
2031 }
2032
2033 j->no_new_files = true;
2034 j->no_inotify = true;
5302ebe1 2035
5d1ce257
LP
2036 *ret = j;
2037 return 0;
2038
2039fail:
f8e2f4d6 2040 /* If we fail, make sure we don't take possession of the files we managed to make use of successfully, and they
5d1ce257
LP
2041 * remain open */
2042 ORDERED_HASHMAP_FOREACH(f, j->files, iterator)
2043 f->close_fd = false;
2044
2045 sd_journal_close(j);
5302ebe1
ZJS
2046 return r;
2047}
2048
a5344d2c 2049_public_ void sd_journal_close(sd_journal *j) {
a963990f 2050 Directory *d;
a963990f 2051
a5344d2c
LP
2052 if (!j)
2053 return;
87d2c1ff 2054
54b1da83
LP
2055 sd_journal_flush_matches(j);
2056
f9168190 2057 ordered_hashmap_free_with_destructor(j->files, journal_file_close);
5d4ba7f2 2058 iterated_cache_free(j->files_cache);
260a2be4 2059
a963990f
LP
2060 while ((d = hashmap_first(j->directories_by_path)))
2061 remove_directory(j, d);
260a2be4 2062
a963990f
LP
2063 while ((d = hashmap_first(j->directories_by_wd)))
2064 remove_directory(j, d);
87d2c1ff 2065
a963990f
LP
2066 hashmap_free(j->directories_by_path);
2067 hashmap_free(j->directories_by_wd);
1cc101f1 2068
03e334a1 2069 safe_close(j->inotify_fd);
50f20cfd 2070
bf807d4d
LP
2071 if (j->mmap) {
2072 log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap));
16e9f408 2073 mmap_cache_unref(j->mmap);
bf807d4d 2074 }
16e9f408 2075
ec1d2909 2076 hashmap_free_free(j->errors);
5768d259 2077
7827b1a1 2078 free(j->path);
89739579 2079 free(j->prefix);
3c1668da 2080 free(j->unique_field);
eb86030e 2081 free(j->fields_buffer);
87d2c1ff
LP
2082 free(j);
2083}
3fbf9cbb 2084
a5344d2c 2085_public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
3fbf9cbb
LP
2086 Object *o;
2087 JournalFile *f;
2088 int r;
2089
1ae464e0
TA
2090 assert_return(j, -EINVAL);
2091 assert_return(!journal_pid_changed(j), -ECHILD);
2092 assert_return(ret, -EINVAL);
3fbf9cbb
LP
2093
2094 f = j->current_file;
2095 if (!f)
de190aef 2096 return -EADDRNOTAVAIL;
3fbf9cbb
LP
2097
2098 if (f->current_offset <= 0)
de190aef 2099 return -EADDRNOTAVAIL;
3fbf9cbb 2100
de190aef 2101 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
2102 if (r < 0)
2103 return r;
2104
2105 *ret = le64toh(o->entry.realtime);
de190aef 2106 return 0;
3fbf9cbb
LP
2107}
2108
a5344d2c 2109_public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
3fbf9cbb
LP
2110 Object *o;
2111 JournalFile *f;
2112 int r;
2113 sd_id128_t id;
2114
1ae464e0
TA
2115 assert_return(j, -EINVAL);
2116 assert_return(!journal_pid_changed(j), -ECHILD);
3fbf9cbb
LP
2117
2118 f = j->current_file;
2119 if (!f)
de190aef 2120 return -EADDRNOTAVAIL;
3fbf9cbb
LP
2121
2122 if (f->current_offset <= 0)
de190aef 2123 return -EADDRNOTAVAIL;
3fbf9cbb 2124
de190aef 2125 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
2126 if (r < 0)
2127 return r;
2128
de190aef
LP
2129 if (ret_boot_id)
2130 *ret_boot_id = o->entry.boot_id;
2131 else {
2132 r = sd_id128_get_boot(&id);
2133 if (r < 0)
2134 return r;
3fbf9cbb 2135
de190aef 2136 if (!sd_id128_equal(id, o->entry.boot_id))
df50185b 2137 return -ESTALE;
de190aef 2138 }
3fbf9cbb 2139
14a65d65
LP
2140 if (ret)
2141 *ret = le64toh(o->entry.monotonic);
2142
de190aef 2143 return 0;
3fbf9cbb
LP
2144}
2145
362a3f81
LP
2146static bool field_is_valid(const char *field) {
2147 const char *p;
2148
2149 assert(field);
2150
2151 if (isempty(field))
2152 return false;
2153
2154 if (startswith(field, "__"))
2155 return false;
2156
2157 for (p = field; *p; p++) {
2158
2159 if (*p == '_')
2160 continue;
2161
2162 if (*p >= 'A' && *p <= 'Z')
2163 continue;
2164
2165 if (*p >= '0' && *p <= '9')
2166 continue;
2167
2168 return false;
2169 }
2170
2171 return true;
2172}
2173
a5344d2c 2174_public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
3fbf9cbb
LP
2175 JournalFile *f;
2176 uint64_t i, n;
2177 size_t field_length;
2178 int r;
2179 Object *o;
2180
1ae464e0
TA
2181 assert_return(j, -EINVAL);
2182 assert_return(!journal_pid_changed(j), -ECHILD);
2183 assert_return(field, -EINVAL);
2184 assert_return(data, -EINVAL);
2185 assert_return(size, -EINVAL);
2186 assert_return(field_is_valid(field), -EINVAL);
3fbf9cbb
LP
2187
2188 f = j->current_file;
2189 if (!f)
de190aef 2190 return -EADDRNOTAVAIL;
3fbf9cbb
LP
2191
2192 if (f->current_offset <= 0)
de190aef 2193 return -EADDRNOTAVAIL;
3fbf9cbb 2194
de190aef 2195 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
2196 if (r < 0)
2197 return r;
2198
2199 field_length = strlen(field);
2200
2201 n = journal_file_entry_n_items(o);
2202 for (i = 0; i < n; i++) {
4fd052ae
FC
2203 uint64_t p, l;
2204 le64_t le_hash;
3fbf9cbb 2205 size_t t;
1ec7120e 2206 int compression;
3fbf9cbb
LP
2207
2208 p = le64toh(o->entry.items[i].object_offset);
807e17f0 2209 le_hash = o->entry.items[i].hash;
de190aef 2210 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
3fbf9cbb
LP
2211 if (r < 0)
2212 return r;
2213
de190aef 2214 if (le_hash != o->data.hash)
de7b95cd
LP
2215 return -EBADMSG;
2216
3fbf9cbb
LP
2217 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2218
1ec7120e 2219 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
3b1a55e1 2220 if (compression) {
349cc4a5 2221#if HAVE_XZ || HAVE_LZ4
2aaec9b4 2222 r = decompress_startswith(compression,
3b1a55e1
ZJS
2223 o->data.payload, l,
2224 &f->compress_buffer, &f->compress_buffer_size,
2aaec9b4
ZJS
2225 field, field_length, '=');
2226 if (r < 0)
82e24b00 2227 log_debug_errno(r, "Cannot decompress %s object of length %"PRIu64" at offset "OFSfmt": %m",
2aaec9b4
ZJS
2228 object_compressed_to_string(compression), l, p);
2229 else if (r > 0) {
3b1a55e1 2230
fa1c4b51 2231 size_t rsize;
3b1a55e1
ZJS
2232
2233 r = decompress_blob(compression,
2234 o->data.payload, l,
2235 &f->compress_buffer, &f->compress_buffer_size, &rsize,
2236 j->data_threshold);
2237 if (r < 0)
2238 return r;
807e17f0 2239
3b1a55e1
ZJS
2240 *data = f->compress_buffer;
2241 *size = (size_t) rsize;
807e17f0 2242
3b1a55e1
ZJS
2243 return 0;
2244 }
2245#else
2246 return -EPROTONOSUPPORT;
2247#endif
807e17f0
LP
2248 } else if (l >= field_length+1 &&
2249 memcmp(o->data.payload, field, field_length) == 0 &&
2250 o->data.payload[field_length] == '=') {
3fbf9cbb 2251
161e54f8 2252 t = (size_t) l;
3fbf9cbb 2253
161e54f8
LP
2254 if ((uint64_t) t != l)
2255 return -E2BIG;
3fbf9cbb 2256
161e54f8
LP
2257 *data = o->data.payload;
2258 *size = t;
3fbf9cbb 2259
99613ec5 2260 return 0;
161e54f8 2261 }
3fbf9cbb 2262
de190aef 2263 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
161e54f8
LP
2264 if (r < 0)
2265 return r;
3fbf9cbb
LP
2266 }
2267
de190aef 2268 return -ENOENT;
3fbf9cbb
LP
2269}
2270
93b73b06 2271static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
3c1668da
LP
2272 size_t t;
2273 uint64_t l;
3b1a55e1 2274 int compression;
3c1668da
LP
2275
2276 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2277 t = (size_t) l;
2278
2279 /* We can't read objects larger than 4G on a 32bit machine */
2280 if ((uint64_t) t != l)
2281 return -E2BIG;
2282
1ec7120e
ZJS
2283 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
2284 if (compression) {
349cc4a5 2285#if HAVE_XZ || HAVE_LZ4
fa1c4b51 2286 size_t rsize;
3b1a55e1 2287 int r;
3c1668da 2288
1ec7120e
ZJS
2289 r = decompress_blob(compression,
2290 o->data.payload, l, &f->compress_buffer,
2291 &f->compress_buffer_size, &rsize, j->data_threshold);
2292 if (r < 0)
2293 return r;
3c1668da
LP
2294
2295 *data = f->compress_buffer;
2296 *size = (size_t) rsize;
3b1a55e1
ZJS
2297#else
2298 return -EPROTONOSUPPORT;
2299#endif
3c1668da
LP
2300 } else {
2301 *data = o->data.payload;
2302 *size = t;
2303 }
2304
2305 return 0;
2306}
2307
a5344d2c 2308_public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
3fbf9cbb 2309 JournalFile *f;
3c1668da 2310 uint64_t p, n;
4fd052ae 2311 le64_t le_hash;
3fbf9cbb
LP
2312 int r;
2313 Object *o;
2314
1ae464e0
TA
2315 assert_return(j, -EINVAL);
2316 assert_return(!journal_pid_changed(j), -ECHILD);
2317 assert_return(data, -EINVAL);
2318 assert_return(size, -EINVAL);
3fbf9cbb
LP
2319
2320 f = j->current_file;
2321 if (!f)
de190aef 2322 return -EADDRNOTAVAIL;
3fbf9cbb
LP
2323
2324 if (f->current_offset <= 0)
de190aef 2325 return -EADDRNOTAVAIL;
3fbf9cbb 2326
de190aef 2327 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
2328 if (r < 0)
2329 return r;
2330
2331 n = journal_file_entry_n_items(o);
7210bfb3 2332 if (j->current_field >= n)
3fbf9cbb
LP
2333 return 0;
2334
7210bfb3 2335 p = le64toh(o->entry.items[j->current_field].object_offset);
de190aef
LP
2336 le_hash = o->entry.items[j->current_field].hash;
2337 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
3fbf9cbb
LP
2338 if (r < 0)
2339 return r;
2340
de190aef 2341 if (le_hash != o->data.hash)
de7b95cd
LP
2342 return -EBADMSG;
2343
93b73b06 2344 r = return_data(j, f, o, data, size);
3c1668da
LP
2345 if (r < 0)
2346 return r;
3fbf9cbb 2347
313cefa1 2348 j->current_field++;
3fbf9cbb
LP
2349
2350 return 1;
2351}
c2373f84 2352
a5344d2c
LP
2353_public_ void sd_journal_restart_data(sd_journal *j) {
2354 if (!j)
2355 return;
8725d60a
LP
2356
2357 j->current_field = 0;
c2373f84 2358}
50f20cfd 2359
858749f7
LP
2360static int reiterate_all_paths(sd_journal *j) {
2361 assert(j);
2362
2363 if (j->no_new_files)
2364 return add_current_paths(j);
2365
2366 if (j->flags & SD_JOURNAL_OS_ROOT)
2367 return add_search_paths(j);
2368
2369 if (j->toplevel_fd >= 0)
2370 return add_root_directory(j, NULL, false);
2371
2372 if (j->path)
2373 return add_root_directory(j, j->path, true);
2374
2375 return add_search_paths(j);
2376}
2377
a5344d2c 2378_public_ int sd_journal_get_fd(sd_journal *j) {
a963990f
LP
2379 int r;
2380
1ae464e0
TA
2381 assert_return(j, -EINVAL);
2382 assert_return(!journal_pid_changed(j), -ECHILD);
50f20cfd 2383
5d1ce257
LP
2384 if (j->no_inotify)
2385 return -EMEDIUMTYPE;
2386
a963990f
LP
2387 if (j->inotify_fd >= 0)
2388 return j->inotify_fd;
2389
2390 r = allocate_inotify(j);
2391 if (r < 0)
2392 return r;
2393
858749f7 2394 log_debug("Reiterating files to get inotify watches established.");
5d1ce257 2395
858749f7
LP
2396 /* Iterate through all dirs again, to add them to the inotify */
2397 r = reiterate_all_paths(j);
a963990f
LP
2398 if (r < 0)
2399 return r;
2400
50f20cfd
LP
2401 return j->inotify_fd;
2402}
2403
ee531d94
LP
2404_public_ int sd_journal_get_events(sd_journal *j) {
2405 int fd;
2406
1ae464e0
TA
2407 assert_return(j, -EINVAL);
2408 assert_return(!journal_pid_changed(j), -ECHILD);
ee531d94
LP
2409
2410 fd = sd_journal_get_fd(j);
2411 if (fd < 0)
2412 return fd;
2413
2414 return POLLIN;
2415}
2416
39c155ea
LP
2417_public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2418 int fd;
2419
1ae464e0
TA
2420 assert_return(j, -EINVAL);
2421 assert_return(!journal_pid_changed(j), -ECHILD);
2422 assert_return(timeout_usec, -EINVAL);
39c155ea
LP
2423
2424 fd = sd_journal_get_fd(j);
2425 if (fd < 0)
2426 return fd;
2427
2428 if (!j->on_network) {
2429 *timeout_usec = (uint64_t) -1;
2430 return 0;
2431 }
2432
2433 /* If we are on the network we need to regularly check for
2434 * changes manually */
2435
2436 *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2437 return 1;
2438}
2439
858749f7
LP
2440static void process_q_overflow(sd_journal *j) {
2441 JournalFile *f;
2442 Directory *m;
2443 Iterator i;
2444
2445 assert(j);
2446
2447 /* When the inotify queue overruns we need to enumerate and re-validate all journal files to bring our list
2448 * back in sync with what's on disk. For this we pick a new generation counter value. It'll be assigned to all
2449 * journal files we encounter. All journal files and all directories that don't carry it after reenumeration
2450 * are subject for unloading. */
2451
2452 log_debug("Inotify queue overrun, reiterating everything.");
2453
2454 j->generation++;
2455 (void) reiterate_all_paths(j);
2456
2457 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2458
2459 if (f->last_seen_generation == j->generation)
2460 continue;
2461
2462 log_debug("File '%s' hasn't been seen in this enumeration, removing.", f->path);
2463 remove_file_real(j, f);
2464 }
2465
2466 HASHMAP_FOREACH(m, j->directories_by_path, i) {
2467
2468 if (m->last_seen_generation == j->generation)
2469 continue;
2470
2471 if (m->is_root) /* Never GC root directories */
2472 continue;
2473
2474 log_debug("Directory '%s' hasn't been seen in this enumeration, removing.", f->path);
2475 remove_directory(j, m);
2476 }
2477
2478 log_debug("Reiteration complete.");
2479}
2480
50f20cfd 2481static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
a963990f 2482 Directory *d;
50f20cfd
LP
2483
2484 assert(j);
2485 assert(e);
2486
858749f7
LP
2487 if (e->mask & IN_Q_OVERFLOW) {
2488 process_q_overflow(j);
2489 return;
2490 }
2491
50f20cfd 2492 /* Is this a subdirectory we watch? */
a963990f
LP
2493 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2494 if (d) {
de2c3907
LP
2495 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2496 (endswith(e->name, ".journal") ||
2497 endswith(e->name, ".journal~"))) {
50f20cfd
LP
2498
2499 /* Event for a journal file */
2500
d617408e
LP
2501 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB))
2502 (void) add_file(j, d->path, e->name);
2503 else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT))
2504 remove_file(j, d->path, e->name);
50f20cfd 2505
a963990f 2506 } else if (!d->is_root && e->len == 0) {
50f20cfd 2507
a963990f 2508 /* Event for a subdirectory */
50f20cfd 2509
b2b46f91
TA
2510 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT))
2511 remove_directory(j, d);
50f20cfd 2512
a9be0692 2513 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && id128_is_valid(e->name)) {
50f20cfd 2514
a963990f 2515 /* Event for root directory */
50f20cfd 2516
d617408e
LP
2517 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB))
2518 (void) add_directory(j, d->path, e->name);
50f20cfd
LP
2519 }
2520
2521 return;
2522 }
2523
2524 if (e->mask & IN_IGNORED)
2525 return;
2526
a9be0692 2527 log_debug("Unexpected inotify event.");
50f20cfd
LP
2528}
2529
a963990f
LP
2530static int determine_change(sd_journal *j) {
2531 bool b;
2532
2533 assert(j);
2534
2535 b = j->current_invalidate_counter != j->last_invalidate_counter;
2536 j->last_invalidate_counter = j->current_invalidate_counter;
2537
2538 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2539}
2540
a5344d2c 2541_public_ int sd_journal_process(sd_journal *j) {
a963990f 2542 bool got_something = false;
50f20cfd 2543
1ae464e0
TA
2544 assert_return(j, -EINVAL);
2545 assert_return(!journal_pid_changed(j), -ECHILD);
50f20cfd 2546
10c4d640
LP
2547 if (j->inotify_fd < 0) /* We have no inotify fd yet? Then there's noting to process. */
2548 return 0;
2549
39c155ea 2550 j->last_process_usec = now(CLOCK_MONOTONIC);
f9346444 2551 j->last_invalidate_counter = j->current_invalidate_counter;
39c155ea 2552
50f20cfd 2553 for (;;) {
0254e944 2554 union inotify_event_buffer buffer;
50f20cfd
LP
2555 struct inotify_event *e;
2556 ssize_t l;
2557
0254e944 2558 l = read(j->inotify_fd, &buffer, sizeof(buffer));
50f20cfd 2559 if (l < 0) {
3742095b 2560 if (IN_SET(errno, EAGAIN, EINTR))
a963990f 2561 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
50f20cfd
LP
2562
2563 return -errno;
2564 }
2565
a963990f
LP
2566 got_something = true;
2567
f7c1ad4f 2568 FOREACH_INOTIFY_EVENT(e, buffer, l)
50f20cfd 2569 process_inotify_event(j, e);
50f20cfd
LP
2570 }
2571}
6ad1d1c3 2572
e02d1cf7 2573_public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
a963990f 2574 int r;
39c155ea 2575 uint64_t t;
e02d1cf7 2576
1ae464e0
TA
2577 assert_return(j, -EINVAL);
2578 assert_return(!journal_pid_changed(j), -ECHILD);
e02d1cf7 2579
a963990f
LP
2580 if (j->inotify_fd < 0) {
2581
2582 /* This is the first invocation, hence create the
2583 * inotify watch */
2584 r = sd_journal_get_fd(j);
2585 if (r < 0)
2586 return r;
2587
2588 /* The journal might have changed since the context
2589 * object was created and we weren't watching before,
2590 * hence don't wait for anything, and return
2591 * immediately. */
2592 return determine_change(j);
2593 }
2594
39c155ea
LP
2595 r = sd_journal_get_timeout(j, &t);
2596 if (r < 0)
2597 return r;
2598
2599 if (t != (uint64_t) -1) {
2600 usec_t n;
2601
2602 n = now(CLOCK_MONOTONIC);
2603 t = t > n ? t - n : 0;
85210bff 2604
39c155ea
LP
2605 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2606 timeout_usec = t;
85210bff
LP
2607 }
2608
a963990f
LP
2609 do {
2610 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2611 } while (r == -EINTR);
e02d1cf7
LP
2612
2613 if (r < 0)
2614 return r;
2615
a963990f 2616 return sd_journal_process(j);
e02d1cf7
LP
2617}
2618
08984293
LP
2619_public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2620 Iterator i;
2621 JournalFile *f;
2622 bool first = true;
581483bf 2623 uint64_t fmin = 0, tmax = 0;
08984293
LP
2624 int r;
2625
1ae464e0
TA
2626 assert_return(j, -EINVAL);
2627 assert_return(!journal_pid_changed(j), -ECHILD);
2628 assert_return(from || to, -EINVAL);
2629 assert_return(from != to, -EINVAL);
08984293 2630
c1f906bd 2631 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
08984293
LP
2632 usec_t fr, t;
2633
2634 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
9f8d2983
LP
2635 if (r == -ENOENT)
2636 continue;
08984293
LP
2637 if (r < 0)
2638 return r;
2639 if (r == 0)
2640 continue;
2641
2642 if (first) {
581483bf
LP
2643 fmin = fr;
2644 tmax = t;
08984293
LP
2645 first = false;
2646 } else {
581483bf
LP
2647 fmin = MIN(fr, fmin);
2648 tmax = MAX(t, tmax);
08984293
LP
2649 }
2650 }
2651
581483bf
LP
2652 if (from)
2653 *from = fmin;
2654 if (to)
2655 *to = tmax;
2656
08984293
LP
2657 return first ? 0 : 1;
2658}
2659
2660_public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2661 Iterator i;
2662 JournalFile *f;
1651e2c6 2663 bool found = false;
08984293
LP
2664 int r;
2665
1ae464e0
TA
2666 assert_return(j, -EINVAL);
2667 assert_return(!journal_pid_changed(j), -ECHILD);
2668 assert_return(from || to, -EINVAL);
2669 assert_return(from != to, -EINVAL);
08984293 2670
c1f906bd 2671 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
08984293
LP
2672 usec_t fr, t;
2673
2674 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
9f8d2983
LP
2675 if (r == -ENOENT)
2676 continue;
08984293
LP
2677 if (r < 0)
2678 return r;
2679 if (r == 0)
2680 continue;
2681
1651e2c6 2682 if (found) {
08984293 2683 if (from)
1651e2c6 2684 *from = MIN(fr, *from);
08984293 2685 if (to)
1651e2c6 2686 *to = MAX(t, *to);
08984293
LP
2687 } else {
2688 if (from)
1651e2c6 2689 *from = fr;
08984293 2690 if (to)
1651e2c6
ZJS
2691 *to = t;
2692 found = true;
08984293
LP
2693 }
2694 }
2695
1651e2c6 2696 return found;
08984293
LP
2697}
2698
dca6219e
LP
2699void journal_print_header(sd_journal *j) {
2700 Iterator i;
2701 JournalFile *f;
2702 bool newline = false;
2703
2704 assert(j);
2705
c1f906bd 2706 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
dca6219e
LP
2707 if (newline)
2708 putchar('\n');
2709 else
2710 newline = true;
2711
2712 journal_file_print_header(f);
2713 }
2714}
08984293 2715
a1a03e30
LP
2716_public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2717 Iterator i;
2718 JournalFile *f;
2719 uint64_t sum = 0;
2720
1ae464e0
TA
2721 assert_return(j, -EINVAL);
2722 assert_return(!journal_pid_changed(j), -ECHILD);
2723 assert_return(bytes, -EINVAL);
a1a03e30 2724
c1f906bd 2725 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
a1a03e30
LP
2726 struct stat st;
2727
2728 if (fstat(f->fd, &st) < 0)
2729 return -errno;
2730
2731 sum += (uint64_t) st.st_blocks * 512ULL;
2732 }
2733
2734 *bytes = sum;
2735 return 0;
2736}
2737
3c1668da
LP
2738_public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2739 char *f;
2740
1ae464e0
TA
2741 assert_return(j, -EINVAL);
2742 assert_return(!journal_pid_changed(j), -ECHILD);
2743 assert_return(!isempty(field), -EINVAL);
2744 assert_return(field_is_valid(field), -EINVAL);
3c1668da
LP
2745
2746 f = strdup(field);
2747 if (!f)
2748 return -ENOMEM;
2749
2750 free(j->unique_field);
2751 j->unique_field = f;
2752 j->unique_file = NULL;
2753 j->unique_offset = 0;
360af4cf 2754 j->unique_file_lost = false;
3c1668da
LP
2755
2756 return 0;
2757}
2758
2759_public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
3c1668da 2760 size_t k;
19a2bd80 2761
1ae464e0
TA
2762 assert_return(j, -EINVAL);
2763 assert_return(!journal_pid_changed(j), -ECHILD);
2764 assert_return(data, -EINVAL);
2765 assert_return(l, -EINVAL);
2766 assert_return(j->unique_field, -EINVAL);
19a2bd80 2767
3c1668da 2768 k = strlen(j->unique_field);
19a2bd80 2769
3c1668da 2770 if (!j->unique_file) {
360af4cf
ZJS
2771 if (j->unique_file_lost)
2772 return 0;
2773
c1f906bd 2774 j->unique_file = ordered_hashmap_first(j->files);
3c1668da
LP
2775 if (!j->unique_file)
2776 return 0;
360af4cf 2777
3c1668da
LP
2778 j->unique_offset = 0;
2779 }
19a2bd80 2780
3c1668da
LP
2781 for (;;) {
2782 JournalFile *of;
2783 Iterator i;
ae97089d 2784 Object *o;
3c1668da
LP
2785 const void *odata;
2786 size_t ol;
2787 bool found;
ae97089d 2788 int r;
3c1668da 2789
bdc02927 2790 /* Proceed to next data object in the field's linked list */
3c1668da
LP
2791 if (j->unique_offset == 0) {
2792 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2793 if (r < 0)
2794 return r;
2795
2796 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2797 } else {
2798 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2799 if (r < 0)
2800 return r;
2801
2802 j->unique_offset = le64toh(o->data.next_field_offset);
2803 }
2804
2805 /* We reached the end of the list? Then start again, with the next file */
2806 if (j->unique_offset == 0) {
c1f906bd 2807 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
360af4cf 2808 if (!j->unique_file)
3c1668da
LP
2809 return 0;
2810
3c1668da
LP
2811 continue;
2812 }
2813
d05089d8
MS
2814 /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED
2815 * instead, so that we can look at this data object at the same
3c1668da 2816 * time as one on another file */
d05089d8 2817 r = journal_file_move_to_object(j->unique_file, OBJECT_UNUSED, j->unique_offset, &o);
3c1668da
LP
2818 if (r < 0)
2819 return r;
2820
2821 /* Let's do the type check by hand, since we used 0 context above. */
ae97089d 2822 if (o->object.type != OBJECT_DATA) {
36202fd2 2823 log_debug("%s:offset " OFSfmt ": object has type %d, expected %d",
ae97089d
ZJS
2824 j->unique_file->path, j->unique_offset,
2825 o->object.type, OBJECT_DATA);
3c1668da 2826 return -EBADMSG;
ae97089d
ZJS
2827 }
2828
93b73b06 2829 r = return_data(j, j->unique_file, o, &odata, &ol);
3c1668da
LP
2830 if (r < 0)
2831 return r;
2832
0f99f74a
ZJS
2833 /* Check if we have at least the field name and "=". */
2834 if (ol <= k) {
2835 log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
2836 j->unique_file->path, j->unique_offset,
2837 ol, k + 1);
2838 return -EBADMSG;
2839 }
2840
2841 if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') {
2842 log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"",
2843 j->unique_file->path, j->unique_offset,
2844 j->unique_field);
2845 return -EBADMSG;
2846 }
2847
3c1668da
LP
2848 /* OK, now let's see if we already returned this data
2849 * object by checking if it exists in the earlier
2850 * traversed files. */
2851 found = false;
c1f906bd 2852 ORDERED_HASHMAP_FOREACH(of, j->files, i) {
3c1668da
LP
2853 if (of == j->unique_file)
2854 break;
2855
ed71f956
LP
2856 /* Skip this file it didn't have any fields indexed */
2857 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) && le64toh(of->header->n_fields) <= 0)
3c1668da
LP
2858 continue;
2859
ed71f956 2860 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), NULL, NULL);
3c1668da
LP
2861 if (r < 0)
2862 return r;
ed71f956 2863 if (r > 0) {
3c1668da 2864 found = true;
ed71f956
LP
2865 break;
2866 }
3c1668da
LP
2867 }
2868
06cc69d4
JJ
2869 if (found)
2870 continue;
2871
93b73b06 2872 r = return_data(j, j->unique_file, o, data, l);
3c1668da
LP
2873 if (r < 0)
2874 return r;
2875
2876 return 1;
2877 }
2878}
2879
115646c7 2880_public_ void sd_journal_restart_unique(sd_journal *j) {
3c1668da
LP
2881 if (!j)
2882 return;
2883
2884 j->unique_file = NULL;
2885 j->unique_offset = 0;
360af4cf 2886 j->unique_file_lost = false;
3c1668da 2887}
85210bff 2888
eb86030e
LP
2889_public_ int sd_journal_enumerate_fields(sd_journal *j, const char **field) {
2890 int r;
2891
2892 assert_return(j, -EINVAL);
2893 assert_return(!journal_pid_changed(j), -ECHILD);
2894 assert_return(field, -EINVAL);
2895
2896 if (!j->fields_file) {
2897 if (j->fields_file_lost)
2898 return 0;
2899
2900 j->fields_file = ordered_hashmap_first(j->files);
2901 if (!j->fields_file)
2902 return 0;
2903
2904 j->fields_hash_table_index = 0;
2905 j->fields_offset = 0;
2906 }
2907
2908 for (;;) {
2909 JournalFile *f, *of;
2910 Iterator i;
2911 uint64_t m;
2912 Object *o;
2913 size_t sz;
2914 bool found;
2915
2916 f = j->fields_file;
2917
2918 if (j->fields_offset == 0) {
2919 bool eof = false;
2920
2921 /* We are not yet positioned at any field. Let's pick the first one */
2922 r = journal_file_map_field_hash_table(f);
2923 if (r < 0)
2924 return r;
2925
2926 m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem);
2927 for (;;) {
2928 if (j->fields_hash_table_index >= m) {
2929 /* Reached the end of the hash table, go to the next file. */
2930 eof = true;
2931 break;
2932 }
2933
2934 j->fields_offset = le64toh(f->field_hash_table[j->fields_hash_table_index].head_hash_offset);
2935
2936 if (j->fields_offset != 0)
2937 break;
2938
2939 /* Empty hash table bucket, go to next one */
2940 j->fields_hash_table_index++;
2941 }
2942
2943 if (eof) {
2944 /* Proceed with next file */
2945 j->fields_file = ordered_hashmap_next(j->files, f->path);
2946 if (!j->fields_file) {
2947 *field = NULL;
2948 return 0;
2949 }
2950
2951 j->fields_offset = 0;
2952 j->fields_hash_table_index = 0;
2953 continue;
2954 }
2955
2956 } else {
2957 /* We are already positioned at a field. If so, let's figure out the next field from it */
2958
2959 r = journal_file_move_to_object(f, OBJECT_FIELD, j->fields_offset, &o);
2960 if (r < 0)
2961 return r;
2962
2963 j->fields_offset = le64toh(o->field.next_hash_offset);
2964 if (j->fields_offset == 0) {
2965 /* Reached the end of the hash table chain */
2966 j->fields_hash_table_index++;
2967 continue;
2968 }
2969 }
2970
1f133e0d 2971 /* We use OBJECT_UNUSED here, so that the iterator below doesn't remove our mmap window */
eb86030e
LP
2972 r = journal_file_move_to_object(f, OBJECT_UNUSED, j->fields_offset, &o);
2973 if (r < 0)
2974 return r;
2975
2976 /* Because we used OBJECT_UNUSED above, we need to do our type check manually */
2977 if (o->object.type != OBJECT_FIELD) {
2978 log_debug("%s:offset " OFSfmt ": object has type %i, expected %i", f->path, j->fields_offset, o->object.type, OBJECT_FIELD);
2979 return -EBADMSG;
2980 }
2981
2982 sz = le64toh(o->object.size) - offsetof(Object, field.payload);
2983
2984 /* Let's see if we already returned this field name before. */
2985 found = false;
2986 ORDERED_HASHMAP_FOREACH(of, j->files, i) {
2987 if (of == f)
2988 break;
2989
2990 /* Skip this file it didn't have any fields indexed */
2991 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) && le64toh(of->header->n_fields) <= 0)
2992 continue;
2993
2994 r = journal_file_find_field_object_with_hash(of, o->field.payload, sz, le64toh(o->field.hash), NULL, NULL);
2995 if (r < 0)
2996 return r;
2997 if (r > 0) {
2998 found = true;
2999 break;
3000 }
3001 }
3002
3003 if (found)
3004 continue;
3005
3006 /* Check if this is really a valid string containing no NUL byte */
3007 if (memchr(o->field.payload, 0, sz))
3008 return -EBADMSG;
3009
3010 if (sz > j->data_threshold)
3011 sz = j->data_threshold;
3012
3013 if (!GREEDY_REALLOC(j->fields_buffer, j->fields_buffer_allocated, sz + 1))
3014 return -ENOMEM;
3015
3016 memcpy(j->fields_buffer, o->field.payload, sz);
3017 j->fields_buffer[sz] = 0;
3018
3019 if (!field_is_valid(j->fields_buffer))
3020 return -EBADMSG;
3021
3022 *field = j->fields_buffer;
3023 return 1;
3024 }
3025}
3026
3027_public_ void sd_journal_restart_fields(sd_journal *j) {
3028 if (!j)
3029 return;
3030
3031 j->fields_file = NULL;
3032 j->fields_hash_table_index = 0;
3033 j->fields_offset = 0;
3034 j->fields_file_lost = false;
3035}
3036
85210bff 3037_public_ int sd_journal_reliable_fd(sd_journal *j) {
1ae464e0
TA
3038 assert_return(j, -EINVAL);
3039 assert_return(!journal_pid_changed(j), -ECHILD);
85210bff
LP
3040
3041 return !j->on_network;
3042}
d4205751
LP
3043
3044static char *lookup_field(const char *field, void *userdata) {
3045 sd_journal *j = userdata;
3046 const void *data;
3047 size_t size, d;
3048 int r;
3049
3050 assert(field);
3051 assert(j);
3052
3053 r = sd_journal_get_data(j, field, &data, &size);
3054 if (r < 0 ||
3055 size > REPLACE_VAR_MAX)
3056 return strdup(field);
3057
3058 d = strlen(field) + 1;
3059
3060 return strndup((const char*) data + d, size - d);
3061}
3062
3063_public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
3064 const void *data;
3065 size_t size;
3066 sd_id128_t id;
3067 _cleanup_free_ char *text = NULL, *cid = NULL;
3068 char *t;
3069 int r;
3070
1ae464e0
TA
3071 assert_return(j, -EINVAL);
3072 assert_return(!journal_pid_changed(j), -ECHILD);
3073 assert_return(ret, -EINVAL);
d4205751
LP
3074
3075 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
3076 if (r < 0)
3077 return r;
3078
3079 cid = strndup((const char*) data + 11, size - 11);
3080 if (!cid)
3081 return -ENOMEM;
3082
3083 r = sd_id128_from_string(cid, &id);
3084 if (r < 0)
3085 return r;
3086
844ec79b 3087 r = catalog_get(CATALOG_DATABASE, id, &text);
d4205751
LP
3088 if (r < 0)
3089 return r;
3090
3091 t = replace_var(text, lookup_field, j);
3092 if (!t)
3093 return -ENOMEM;
3094
3095 *ret = t;
3096 return 0;
3097}
8f1e860f
LP
3098
3099_public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
1ae464e0 3100 assert_return(ret, -EINVAL);
8f1e860f 3101
844ec79b 3102 return catalog_get(CATALOG_DATABASE, id, ret);
8f1e860f 3103}
93b73b06
LP
3104
3105_public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
1ae464e0
TA
3106 assert_return(j, -EINVAL);
3107 assert_return(!journal_pid_changed(j), -ECHILD);
93b73b06
LP
3108
3109 j->data_threshold = sz;
3110 return 0;
3111}
3112
3113_public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
1ae464e0
TA
3114 assert_return(j, -EINVAL);
3115 assert_return(!journal_pid_changed(j), -ECHILD);
3116 assert_return(sz, -EINVAL);
93b73b06
LP
3117
3118 *sz = j->data_threshold;
3119 return 0;
3120}
39fd5b08
JS
3121
3122_public_ int sd_journal_has_runtime_files(sd_journal *j) {
3123 assert_return(j, -EINVAL);
3124
3125 return j->has_runtime_files;
3126}
3127
3128_public_ int sd_journal_has_persistent_files(sd_journal *j) {
3129 assert_return(j, -EINVAL);
3130
3131 return j->has_persistent_files;
3132}