]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/sd-journal.c
sd-journal: rename add_file() → add_file_by_name()
[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
fc1813c0
LP
1336static int add_file_by_name(
1337 sd_journal *j,
1338 const char *prefix,
1339 const char *filename) {
1340
d617408e 1341 const char *path;
5302ebe1
ZJS
1342
1343 assert(j);
1344 assert(prefix);
1345 assert(filename);
1346
5d1ce257 1347 if (j->no_new_files)
5302ebe1
ZJS
1348 return 0;
1349
5d1ce257
LP
1350 if (!file_type_wanted(j->flags, filename))
1351 return 0;
39fd5b08 1352
5d1ce257
LP
1353 path = strjoina(prefix, "/", filename);
1354 return add_any_file(j, -1, path);
5302ebe1
ZJS
1355}
1356
fc1813c0
LP
1357static void remove_file_by_name(
1358 sd_journal *j,
1359 const char *prefix,
1360 const char *filename) {
1361
d617408e 1362 const char *path;
50f20cfd
LP
1363 JournalFile *f;
1364
1365 assert(j);
1366 assert(prefix);
1367 assert(filename);
1368
d617408e 1369 path = strjoina(prefix, "/", filename);
c1f906bd 1370 f = ordered_hashmap_get(j->files, path);
50f20cfd 1371 if (!f)
d617408e 1372 return;
50f20cfd 1373
a9a245c1 1374 remove_file_real(j, f);
a9a245c1
ZJS
1375}
1376
1377static void remove_file_real(sd_journal *j, JournalFile *f) {
1378 assert(j);
1379 assert(f);
1380
fc1813c0 1381 (void) ordered_hashmap_remove(j->files, f->path);
44a5fa34 1382
5ec76417 1383 log_debug("File %s removed.", f->path);
44a5fa34 1384
3c1668da
LP
1385 if (j->current_file == f) {
1386 j->current_file = NULL;
1387 j->current_field = 0;
1388 }
1389
1390 if (j->unique_file == f) {
360af4cf 1391 /* Jump to the next unique_file or NULL if that one was last */
c1f906bd 1392 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
3c1668da 1393 j->unique_offset = 0;
360af4cf
ZJS
1394 if (!j->unique_file)
1395 j->unique_file_lost = true;
3c1668da
LP
1396 }
1397
eb86030e
LP
1398 if (j->fields_file == f) {
1399 j->fields_file = ordered_hashmap_next(j->files, j->fields_file->path);
1400 j->fields_offset = 0;
1401 if (!j->fields_file)
1402 j->fields_file_lost = true;
1403 }
1404
69a3a6fd 1405 (void) journal_file_close(f);
50f20cfd 1406
313cefa1 1407 j->current_invalidate_counter++;
3fbf9cbb
LP
1408}
1409
d617408e
LP
1410static int dirname_is_machine_id(const char *fn) {
1411 sd_id128_t id, machine;
1412 int r;
1413
1414 r = sd_id128_get_machine(&machine);
1415 if (r < 0)
1416 return r;
1417
1418 r = sd_id128_from_string(fn, &id);
1419 if (r < 0)
1420 return r;
1421
1422 return sd_id128_equal(id, machine);
1423}
1424
858749f7
LP
1425static bool dirent_is_journal_file(const struct dirent *de) {
1426 assert(de);
1427
1428 if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN))
1429 return false;
1430
1431 return endswith(de->d_name, ".journal") ||
1432 endswith(de->d_name, ".journal~");
1433}
1434
1435static bool dirent_is_id128_subdir(const struct dirent *de) {
1436 assert(de);
1437
1438 if (!IN_SET(de->d_type, DT_DIR, DT_LNK, DT_UNKNOWN))
1439 return false;
1440
1441 return id128_is_valid(de->d_name);
1442}
1443
1444static int directory_open(sd_journal *j, const char *path, DIR **ret) {
1445 DIR *d;
1446
1447 assert(j);
1448 assert(path);
1449 assert(ret);
1450
1451 if (j->toplevel_fd < 0)
1452 d = opendir(path);
1453 else
1454 /* Open the specified directory relative to the toplevel fd. Enforce that the path specified is
1455 * relative, by dropping the initial slash */
1456 d = xopendirat(j->toplevel_fd, skip_slash(path), 0);
1457 if (!d)
1458 return -errno;
1459
1460 *ret = d;
1461 return 0;
1462}
1463
1464static int add_directory(sd_journal *j, const char *prefix, const char *dirname);
1465
1466static void directory_enumerate(sd_journal *j, Directory *m, DIR *d) {
1467 struct dirent *de;
1468
1469 assert(j);
1470 assert(m);
1471 assert(d);
1472
1473 FOREACH_DIRENT_ALL(de, d, goto fail) {
fc1813c0 1474
858749f7 1475 if (dirent_is_journal_file(de))
fc1813c0 1476 (void) add_file_by_name(j, m->path, de->d_name);
858749f7
LP
1477
1478 if (m->is_root && dirent_is_id128_subdir(de))
1479 (void) add_directory(j, m->path, de->d_name);
1480 }
1481
1482 return;
1483
1484fail:
1485 log_debug_errno(errno, "Failed to enumerate directory %s, ignoring: %m", m->path);
1486}
1487
1488static void directory_watch(sd_journal *j, Directory *m, int fd, uint32_t mask) {
1489 int r;
1490
1491 assert(j);
1492 assert(m);
1493 assert(fd >= 0);
1494
1495 /* Watch this directory if that's enabled and if it not being watched yet. */
1496
1497 if (m->wd > 0) /* Already have a watch? */
1498 return;
1499 if (j->inotify_fd < 0) /* Not watching at all? */
1500 return;
1501
1502 m->wd = inotify_add_watch_fd(j->inotify_fd, fd, mask);
1503 if (m->wd < 0) {
1504 log_debug_errno(errno, "Failed to watch journal directory '%s', ignoring: %m", m->path);
1505 return;
1506 }
1507
1508 r = hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m);
1509 if (r == -EEXIST)
1510 log_debug_errno(r, "Directory '%s' already being watched under a different path, ignoring: %m", m->path);
1511 if (r < 0) {
1512 log_debug_errno(r, "Failed to add watch for journal directory '%s' to hashmap, ignoring: %m", m->path);
1513 (void) inotify_rm_watch(j->inotify_fd, m->wd);
1514 m->wd = -1;
1515 }
1516}
1517
a963990f 1518static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
7fd1b19b 1519 _cleanup_free_ char *path = NULL;
7fd1b19b 1520 _cleanup_closedir_ DIR *d = NULL;
a963990f 1521 Directory *m;
d617408e 1522 int r, k;
3fbf9cbb
LP
1523
1524 assert(j);
1525 assert(prefix);
3fbf9cbb 1526
5d1ce257
LP
1527 /* Adds a journal file directory to watch. If the directory is already tracked this updates the inotify watch
1528 * and reenumerates directory contents */
d95b1fb3 1529
5d1ce257 1530 if (dirname)
605405c6 1531 path = strjoin(prefix, "/", dirname);
5d1ce257
LP
1532 else
1533 path = strdup(prefix);
d617408e
LP
1534 if (!path) {
1535 r = -ENOMEM;
1536 goto fail;
1537 }
3fbf9cbb 1538
858749f7 1539 log_debug("Considering directory '%s'.", path);
5d1ce257
LP
1540
1541 /* We consider everything local that is in a directory for the local machine ID, or that is stored in /run */
1542 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1543 !((dirname && dirname_is_machine_id(dirname) > 0) || path_has_prefix(j, path, "/run")))
858749f7 1544 return 0;
5d1ce257 1545
858749f7
LP
1546 r = directory_open(j, path, &d);
1547 if (r < 0) {
1548 log_debug_errno(r, "Failed to open directory '%s': %m", path);
d617408e 1549 goto fail;
3fbf9cbb
LP
1550 }
1551
a963990f
LP
1552 m = hashmap_get(j->directories_by_path, path);
1553 if (!m) {
1554 m = new0(Directory, 1);
d617408e
LP
1555 if (!m) {
1556 r = -ENOMEM;
1557 goto fail;
1558 }
a963990f
LP
1559
1560 m->is_root = false;
1561 m->path = path;
1562
1563 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
a963990f 1564 free(m);
d617408e
LP
1565 r = -ENOMEM;
1566 goto fail;
a963990f
LP
1567 }
1568
a50d7d43 1569 path = NULL; /* avoid freeing in cleanup */
313cefa1 1570 j->current_invalidate_counter++;
a963990f 1571
5ec76417 1572 log_debug("Directory %s added.", m->path);
a963990f 1573
a50d7d43 1574 } else if (m->is_root)
858749f7 1575 return 0; /* Don't 'downgrade' from root directory */
a963990f 1576
858749f7 1577 m->last_seen_generation = j->generation;
a963990f 1578
858749f7
LP
1579 directory_watch(j, m, dirfd(d),
1580 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1581 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
1582 IN_ONLYDIR);
a963990f 1583
858749f7
LP
1584 if (!j->no_new_files)
1585 directory_enumerate(j, m, d);
a963990f 1586
85210bff
LP
1587 check_network(j, dirfd(d));
1588
a963990f 1589 return 0;
d617408e
LP
1590
1591fail:
5d1ce257 1592 k = journal_put_error(j, r, path ?: prefix);
d617408e
LP
1593 if (k < 0)
1594 return k;
1595
1596 return r;
a963990f
LP
1597}
1598
d617408e 1599static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) {
5d1ce257 1600
7fd1b19b 1601 _cleanup_closedir_ DIR *d = NULL;
a963990f 1602 Directory *m;
d617408e 1603 int r, k;
a963990f
LP
1604
1605 assert(j);
a963990f 1606
5d1ce257
LP
1607 /* Adds a root directory to our set of directories to use. If the root directory is already in the set, we
1608 * update the inotify logic, and renumerate the directory entries. This call may hence be called to initially
1609 * populate the set, as well as to update it later. */
a963990f 1610
5d1ce257
LP
1611 if (p) {
1612 /* If there's a path specified, use it. */
b6741478 1613
858749f7
LP
1614 log_debug("Considering root directory '%s'.", p);
1615
5d1ce257
LP
1616 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1617 !path_has_prefix(j, p, "/run"))
1618 return -EINVAL;
d617408e 1619
5d1ce257
LP
1620 if (j->prefix)
1621 p = strjoina(j->prefix, p);
1622
858749f7
LP
1623 r = directory_open(j, p, &d);
1624 if (r == -ENOENT && missing_ok)
1625 return 0;
1626 if (r < 0) {
1627 log_debug_errno(r, "Failed to open root directory %s: %m", p);
5d1ce257
LP
1628 goto fail;
1629 }
1630 } else {
1631 int dfd;
1632
1633 /* If there's no path specified, then we use the top-level fd itself. We duplicate the fd here, since
1634 * opendir() will take possession of the fd, and close it, which we don't want. */
1635
1636 p = "."; /* store this as "." in the directories hashmap */
1637
1638 dfd = fcntl(j->toplevel_fd, F_DUPFD_CLOEXEC, 3);
1639 if (dfd < 0) {
1640 r = -errno;
1641 goto fail;
1642 }
1643
1644 d = fdopendir(dfd);
1645 if (!d) {
1646 r = -errno;
1647 safe_close(dfd);
1648 goto fail;
1649 }
1650
1651 rewinddir(d);
d617408e 1652 }
a963990f
LP
1653
1654 m = hashmap_get(j->directories_by_path, p);
1655 if (!m) {
1656 m = new0(Directory, 1);
d617408e
LP
1657 if (!m) {
1658 r = -ENOMEM;
1659 goto fail;
1660 }
a963990f
LP
1661
1662 m->is_root = true;
5d1ce257 1663
a963990f
LP
1664 m->path = strdup(p);
1665 if (!m->path) {
a963990f 1666 free(m);
d617408e
LP
1667 r = -ENOMEM;
1668 goto fail;
a963990f
LP
1669 }
1670
1671 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
a963990f
LP
1672 free(m->path);
1673 free(m);
d617408e
LP
1674 r = -ENOMEM;
1675 goto fail;
a963990f
LP
1676 }
1677
313cefa1 1678 j->current_invalidate_counter++;
a963990f 1679
5ec76417 1680 log_debug("Root directory %s added.", m->path);
a963990f 1681
a50d7d43 1682 } else if (!m->is_root)
a963990f 1683 return 0;
50f20cfd 1684
858749f7
LP
1685 directory_watch(j, m, dirfd(d),
1686 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1687 IN_ONLYDIR);
a963990f 1688
858749f7
LP
1689 if (!j->no_new_files)
1690 directory_enumerate(j, m, d);
3fbf9cbb 1691
85210bff
LP
1692 check_network(j, dirfd(d));
1693
a963990f 1694 return 0;
d617408e
LP
1695
1696fail:
5768d259 1697 k = journal_put_error(j, r, p);
d617408e
LP
1698 if (k < 0)
1699 return k;
1700
1701 return r;
a963990f
LP
1702}
1703
b2b46f91 1704static void remove_directory(sd_journal *j, Directory *d) {
a963990f
LP
1705 assert(j);
1706
1707 if (d->wd > 0) {
1708 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1709
1710 if (j->inotify_fd >= 0)
1711 inotify_rm_watch(j->inotify_fd, d->wd);
1712 }
1713
1714 hashmap_remove(j->directories_by_path, d->path);
1715
1716 if (d->is_root)
5ec76417 1717 log_debug("Root directory %s removed.", d->path);
a963990f 1718 else
5ec76417 1719 log_debug("Directory %s removed.", d->path);
a963990f
LP
1720
1721 free(d->path);
1722 free(d);
3fbf9cbb
LP
1723}
1724
89739579 1725static int add_search_paths(sd_journal *j) {
d617408e
LP
1726
1727 static const char search_paths[] =
a963990f
LP
1728 "/run/log/journal\0"
1729 "/var/log/journal\0";
1730 const char *p;
50f20cfd
LP
1731
1732 assert(j);
50f20cfd 1733
a963990f
LP
1734 /* We ignore most errors here, since the idea is to only open
1735 * what's actually accessible, and ignore the rest. */
50f20cfd 1736
d617408e
LP
1737 NULSTR_FOREACH(p, search_paths)
1738 (void) add_root_directory(j, p, true);
50f20cfd 1739
574b77ef
MB
1740 if (!(j->flags & SD_JOURNAL_LOCAL_ONLY))
1741 (void) add_root_directory(j, "/var/log/journal/remote", true);
1742
a963990f 1743 return 0;
50f20cfd
LP
1744}
1745
5302ebe1
ZJS
1746static int add_current_paths(sd_journal *j) {
1747 Iterator i;
1748 JournalFile *f;
1749
1750 assert(j);
1751 assert(j->no_new_files);
1752
5d1ce257 1753 /* Simply adds all directories for files we have open as directories. We don't expect errors here, so we
5302ebe1
ZJS
1754 * treat them as fatal. */
1755
c1f906bd 1756 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
5302ebe1 1757 _cleanup_free_ char *dir;
e9174f29 1758 int r;
5302ebe1
ZJS
1759
1760 dir = dirname_malloc(f->path);
1761 if (!dir)
1762 return -ENOMEM;
1763
5d1ce257 1764 r = add_directory(j, dir, NULL);
d617408e 1765 if (r < 0)
5302ebe1 1766 return r;
5302ebe1
ZJS
1767 }
1768
1769 return 0;
1770}
1771
a963990f 1772static int allocate_inotify(sd_journal *j) {
50f20cfd 1773 assert(j);
50f20cfd 1774
a963990f
LP
1775 if (j->inotify_fd < 0) {
1776 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1777 if (j->inotify_fd < 0)
1778 return -errno;
1779 }
50f20cfd 1780
cb306f5d 1781 return hashmap_ensure_allocated(&j->directories_by_wd, NULL);
50f20cfd
LP
1782}
1783
7827b1a1 1784static sd_journal *journal_new(int flags, const char *path) {
a963990f 1785 sd_journal *j;
50f20cfd 1786
a963990f
LP
1787 j = new0(sd_journal, 1);
1788 if (!j)
1789 return NULL;
50f20cfd 1790
df0ff127 1791 j->original_pid = getpid_cached();
5d1ce257 1792 j->toplevel_fd = -1;
a963990f
LP
1793 j->inotify_fd = -1;
1794 j->flags = flags;
93b73b06 1795 j->data_threshold = DEFAULT_DATA_THRESHOLD;
50f20cfd 1796
7827b1a1 1797 if (path) {
16fefe90
ZJS
1798 char *t;
1799
1800 t = strdup(path);
1801 if (!t)
6180fc61 1802 goto fail;
16fefe90
ZJS
1803
1804 if (flags & SD_JOURNAL_OS_ROOT)
1805 j->prefix = t;
1806 else
1807 j->path = t;
7827b1a1
LP
1808 }
1809
548f6937 1810 j->files = ordered_hashmap_new(&path_hash_ops);
5d4ba7f2
VC
1811 if (!j->files)
1812 goto fail;
1813
1814 j->files_cache = ordered_hashmap_iterated_cache_new(j->files);
548f6937 1815 j->directories_by_path = hashmap_new(&path_hash_ops);
84168d80 1816 j->mmap = mmap_cache_new();
5d4ba7f2 1817 if (!j->files_cache || !j->directories_by_path || !j->mmap)
6180fc61 1818 goto fail;
16e9f408 1819
a963990f 1820 return j;
6180fc61
ZJS
1821
1822fail:
1823 sd_journal_close(j);
1824 return NULL;
50f20cfd
LP
1825}
1826
1aaa68f5
ZJS
1827#define OPEN_ALLOWED_FLAGS \
1828 (SD_JOURNAL_LOCAL_ONLY | \
1829 SD_JOURNAL_RUNTIME_ONLY | \
1830 SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)
1831
a5344d2c 1832_public_ int sd_journal_open(sd_journal **ret, int flags) {
87d2c1ff 1833 sd_journal *j;
3fbf9cbb 1834 int r;
87d2c1ff 1835
1ae464e0 1836 assert_return(ret, -EINVAL);
1aaa68f5 1837 assert_return((flags & ~OPEN_ALLOWED_FLAGS) == 0, -EINVAL);
87d2c1ff 1838
7827b1a1 1839 j = journal_new(flags, NULL);
87d2c1ff
LP
1840 if (!j)
1841 return -ENOMEM;
1842
89739579 1843 r = add_search_paths(j);
a963990f 1844 if (r < 0)
50f20cfd 1845 goto fail;
50f20cfd 1846
a963990f
LP
1847 *ret = j;
1848 return 0;
cf244689 1849
a963990f
LP
1850fail:
1851 sd_journal_close(j);
87d2c1ff 1852
a963990f
LP
1853 return r;
1854}
50f20cfd 1855
1aaa68f5
ZJS
1856#define OPEN_CONTAINER_ALLOWED_FLAGS \
1857 (SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_SYSTEM)
1858
b6741478
LP
1859_public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
1860 _cleanup_free_ char *root = NULL, *class = NULL;
1861 sd_journal *j;
1862 char *p;
1863 int r;
1864
2daa9cbd
LP
1865 /* This is pretty much deprecated, people should use machined's OpenMachineRootDirectory() call instead in
1866 * combination with sd_journal_open_directory_fd(). */
1867
b6741478
LP
1868 assert_return(machine, -EINVAL);
1869 assert_return(ret, -EINVAL);
1aaa68f5 1870 assert_return((flags & ~OPEN_CONTAINER_ALLOWED_FLAGS) == 0, -EINVAL);
affcf189 1871 assert_return(machine_name_is_valid(machine), -EINVAL);
b6741478 1872
63c372cb 1873 p = strjoina("/run/systemd/machines/", machine);
b6741478
LP
1874 r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL);
1875 if (r == -ENOENT)
1876 return -EHOSTDOWN;
1877 if (r < 0)
1878 return r;
1879 if (!root)
1880 return -ENODATA;
1881
1882 if (!streq_ptr(class, "container"))
1883 return -EIO;
1884
16fefe90 1885 j = journal_new(flags, root);
b6741478
LP
1886 if (!j)
1887 return -ENOMEM;
1888
89739579 1889 r = add_search_paths(j);
b6741478
LP
1890 if (r < 0)
1891 goto fail;
1892
1893 *ret = j;
1894 return 0;
1895
1896fail:
1897 sd_journal_close(j);
1898 return r;
1899}
1900
1aaa68f5 1901#define OPEN_DIRECTORY_ALLOWED_FLAGS \
10752e82
ZJS
1902 (SD_JOURNAL_OS_ROOT | \
1903 SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER )
1aaa68f5 1904
a963990f
LP
1905_public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1906 sd_journal *j;
1907 int r;
87d2c1ff 1908
1ae464e0
TA
1909 assert_return(ret, -EINVAL);
1910 assert_return(path, -EINVAL);
1aaa68f5 1911 assert_return((flags & ~OPEN_DIRECTORY_ALLOWED_FLAGS) == 0, -EINVAL);
87d2c1ff 1912
7827b1a1 1913 j = journal_new(flags, path);
a963990f
LP
1914 if (!j)
1915 return -ENOMEM;
3fbf9cbb 1916
d077390c
LP
1917 if (flags & SD_JOURNAL_OS_ROOT)
1918 r = add_search_paths(j);
1919 else
1920 r = add_root_directory(j, path, false);
d617408e 1921 if (r < 0)
a963990f 1922 goto fail;
87d2c1ff
LP
1923
1924 *ret = j;
1925 return 0;
1926
1927fail:
1928 sd_journal_close(j);
87d2c1ff 1929 return r;
a963990f 1930}
87d2c1ff 1931
5302ebe1
ZJS
1932_public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
1933 sd_journal *j;
1934 const char **path;
1935 int r;
1936
1ae464e0
TA
1937 assert_return(ret, -EINVAL);
1938 assert_return(flags == 0, -EINVAL);
5302ebe1
ZJS
1939
1940 j = journal_new(flags, NULL);
1941 if (!j)
1942 return -ENOMEM;
1943
1944 STRV_FOREACH(path, paths) {
5d1ce257 1945 r = add_any_file(j, -1, *path);
d617408e 1946 if (r < 0)
5302ebe1 1947 goto fail;
5302ebe1
ZJS
1948 }
1949
1950 j->no_new_files = true;
1951
1952 *ret = j;
1953 return 0;
1954
1955fail:
1956 sd_journal_close(j);
5d1ce257
LP
1957 return r;
1958}
1959
1aaa68f5 1960#define OPEN_DIRECTORY_FD_ALLOWED_FLAGS \
10752e82
ZJS
1961 (SD_JOURNAL_OS_ROOT | \
1962 SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER )
1aaa68f5 1963
5d1ce257
LP
1964_public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) {
1965 sd_journal *j;
1966 struct stat st;
1967 int r;
1968
1969 assert_return(ret, -EINVAL);
1970 assert_return(fd >= 0, -EBADF);
1aaa68f5 1971 assert_return((flags & ~OPEN_DIRECTORY_FD_ALLOWED_FLAGS) == 0, -EINVAL);
5d1ce257
LP
1972
1973 if (fstat(fd, &st) < 0)
1974 return -errno;
1975
1976 if (!S_ISDIR(st.st_mode))
1977 return -EBADFD;
1978
1979 j = journal_new(flags, NULL);
1980 if (!j)
1981 return -ENOMEM;
1982
1983 j->toplevel_fd = fd;
1984
d077390c
LP
1985 if (flags & SD_JOURNAL_OS_ROOT)
1986 r = add_search_paths(j);
1987 else
1988 r = add_root_directory(j, NULL, false);
5d1ce257
LP
1989 if (r < 0)
1990 goto fail;
1991
1992 *ret = j;
1993 return 0;
1994
1995fail:
1996 sd_journal_close(j);
1997 return r;
1998}
1999
2000_public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags) {
2001 Iterator iterator;
2002 JournalFile *f;
2003 sd_journal *j;
2004 unsigned i;
2005 int r;
2006
2007 assert_return(ret, -EINVAL);
2008 assert_return(n_fds > 0, -EBADF);
2009 assert_return(flags == 0, -EINVAL);
2010
2011 j = journal_new(flags, NULL);
2012 if (!j)
2013 return -ENOMEM;
2014
2015 for (i = 0; i < n_fds; i++) {
2016 struct stat st;
2017
2018 if (fds[i] < 0) {
2019 r = -EBADF;
2020 goto fail;
2021 }
2022
2023 if (fstat(fds[i], &st) < 0) {
2024 r = -errno;
2025 goto fail;
2026 }
2027
8d6a4d33
LP
2028 if (S_ISDIR(st.st_mode)) {
2029 r = -EISDIR;
2030 goto fail;
2031 }
5d1ce257
LP
2032 if (!S_ISREG(st.st_mode)) {
2033 r = -EBADFD;
2034 goto fail;
2035 }
2036
2037 r = add_any_file(j, fds[i], NULL);
2038 if (r < 0)
2039 goto fail;
2040 }
2041
2042 j->no_new_files = true;
2043 j->no_inotify = true;
5302ebe1 2044
5d1ce257
LP
2045 *ret = j;
2046 return 0;
2047
2048fail:
f8e2f4d6 2049 /* If we fail, make sure we don't take possession of the files we managed to make use of successfully, and they
5d1ce257
LP
2050 * remain open */
2051 ORDERED_HASHMAP_FOREACH(f, j->files, iterator)
2052 f->close_fd = false;
2053
2054 sd_journal_close(j);
5302ebe1
ZJS
2055 return r;
2056}
2057
a5344d2c 2058_public_ void sd_journal_close(sd_journal *j) {
a963990f 2059 Directory *d;
a963990f 2060
a5344d2c
LP
2061 if (!j)
2062 return;
87d2c1ff 2063
54b1da83
LP
2064 sd_journal_flush_matches(j);
2065
f9168190 2066 ordered_hashmap_free_with_destructor(j->files, journal_file_close);
5d4ba7f2 2067 iterated_cache_free(j->files_cache);
260a2be4 2068
a963990f
LP
2069 while ((d = hashmap_first(j->directories_by_path)))
2070 remove_directory(j, d);
260a2be4 2071
a963990f
LP
2072 while ((d = hashmap_first(j->directories_by_wd)))
2073 remove_directory(j, d);
87d2c1ff 2074
a963990f
LP
2075 hashmap_free(j->directories_by_path);
2076 hashmap_free(j->directories_by_wd);
1cc101f1 2077
03e334a1 2078 safe_close(j->inotify_fd);
50f20cfd 2079
bf807d4d
LP
2080 if (j->mmap) {
2081 log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap));
16e9f408 2082 mmap_cache_unref(j->mmap);
bf807d4d 2083 }
16e9f408 2084
ec1d2909 2085 hashmap_free_free(j->errors);
5768d259 2086
7827b1a1 2087 free(j->path);
89739579 2088 free(j->prefix);
3c1668da 2089 free(j->unique_field);
eb86030e 2090 free(j->fields_buffer);
87d2c1ff
LP
2091 free(j);
2092}
3fbf9cbb 2093
a5344d2c 2094_public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
3fbf9cbb
LP
2095 Object *o;
2096 JournalFile *f;
2097 int r;
2098
1ae464e0
TA
2099 assert_return(j, -EINVAL);
2100 assert_return(!journal_pid_changed(j), -ECHILD);
2101 assert_return(ret, -EINVAL);
3fbf9cbb
LP
2102
2103 f = j->current_file;
2104 if (!f)
de190aef 2105 return -EADDRNOTAVAIL;
3fbf9cbb
LP
2106
2107 if (f->current_offset <= 0)
de190aef 2108 return -EADDRNOTAVAIL;
3fbf9cbb 2109
de190aef 2110 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
2111 if (r < 0)
2112 return r;
2113
2114 *ret = le64toh(o->entry.realtime);
de190aef 2115 return 0;
3fbf9cbb
LP
2116}
2117
a5344d2c 2118_public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
3fbf9cbb
LP
2119 Object *o;
2120 JournalFile *f;
2121 int r;
2122 sd_id128_t id;
2123
1ae464e0
TA
2124 assert_return(j, -EINVAL);
2125 assert_return(!journal_pid_changed(j), -ECHILD);
3fbf9cbb
LP
2126
2127 f = j->current_file;
2128 if (!f)
de190aef 2129 return -EADDRNOTAVAIL;
3fbf9cbb
LP
2130
2131 if (f->current_offset <= 0)
de190aef 2132 return -EADDRNOTAVAIL;
3fbf9cbb 2133
de190aef 2134 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
2135 if (r < 0)
2136 return r;
2137
de190aef
LP
2138 if (ret_boot_id)
2139 *ret_boot_id = o->entry.boot_id;
2140 else {
2141 r = sd_id128_get_boot(&id);
2142 if (r < 0)
2143 return r;
3fbf9cbb 2144
de190aef 2145 if (!sd_id128_equal(id, o->entry.boot_id))
df50185b 2146 return -ESTALE;
de190aef 2147 }
3fbf9cbb 2148
14a65d65
LP
2149 if (ret)
2150 *ret = le64toh(o->entry.monotonic);
2151
de190aef 2152 return 0;
3fbf9cbb
LP
2153}
2154
362a3f81
LP
2155static bool field_is_valid(const char *field) {
2156 const char *p;
2157
2158 assert(field);
2159
2160 if (isempty(field))
2161 return false;
2162
2163 if (startswith(field, "__"))
2164 return false;
2165
2166 for (p = field; *p; p++) {
2167
2168 if (*p == '_')
2169 continue;
2170
2171 if (*p >= 'A' && *p <= 'Z')
2172 continue;
2173
2174 if (*p >= '0' && *p <= '9')
2175 continue;
2176
2177 return false;
2178 }
2179
2180 return true;
2181}
2182
a5344d2c 2183_public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
3fbf9cbb
LP
2184 JournalFile *f;
2185 uint64_t i, n;
2186 size_t field_length;
2187 int r;
2188 Object *o;
2189
1ae464e0
TA
2190 assert_return(j, -EINVAL);
2191 assert_return(!journal_pid_changed(j), -ECHILD);
2192 assert_return(field, -EINVAL);
2193 assert_return(data, -EINVAL);
2194 assert_return(size, -EINVAL);
2195 assert_return(field_is_valid(field), -EINVAL);
3fbf9cbb
LP
2196
2197 f = j->current_file;
2198 if (!f)
de190aef 2199 return -EADDRNOTAVAIL;
3fbf9cbb
LP
2200
2201 if (f->current_offset <= 0)
de190aef 2202 return -EADDRNOTAVAIL;
3fbf9cbb 2203
de190aef 2204 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
2205 if (r < 0)
2206 return r;
2207
2208 field_length = strlen(field);
2209
2210 n = journal_file_entry_n_items(o);
2211 for (i = 0; i < n; i++) {
4fd052ae
FC
2212 uint64_t p, l;
2213 le64_t le_hash;
3fbf9cbb 2214 size_t t;
1ec7120e 2215 int compression;
3fbf9cbb
LP
2216
2217 p = le64toh(o->entry.items[i].object_offset);
807e17f0 2218 le_hash = o->entry.items[i].hash;
de190aef 2219 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
3fbf9cbb
LP
2220 if (r < 0)
2221 return r;
2222
de190aef 2223 if (le_hash != o->data.hash)
de7b95cd
LP
2224 return -EBADMSG;
2225
3fbf9cbb
LP
2226 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2227
1ec7120e 2228 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
3b1a55e1 2229 if (compression) {
349cc4a5 2230#if HAVE_XZ || HAVE_LZ4
2aaec9b4 2231 r = decompress_startswith(compression,
3b1a55e1
ZJS
2232 o->data.payload, l,
2233 &f->compress_buffer, &f->compress_buffer_size,
2aaec9b4
ZJS
2234 field, field_length, '=');
2235 if (r < 0)
82e24b00 2236 log_debug_errno(r, "Cannot decompress %s object of length %"PRIu64" at offset "OFSfmt": %m",
2aaec9b4
ZJS
2237 object_compressed_to_string(compression), l, p);
2238 else if (r > 0) {
3b1a55e1 2239
fa1c4b51 2240 size_t rsize;
3b1a55e1
ZJS
2241
2242 r = decompress_blob(compression,
2243 o->data.payload, l,
2244 &f->compress_buffer, &f->compress_buffer_size, &rsize,
2245 j->data_threshold);
2246 if (r < 0)
2247 return r;
807e17f0 2248
3b1a55e1
ZJS
2249 *data = f->compress_buffer;
2250 *size = (size_t) rsize;
807e17f0 2251
3b1a55e1
ZJS
2252 return 0;
2253 }
2254#else
2255 return -EPROTONOSUPPORT;
2256#endif
807e17f0
LP
2257 } else if (l >= field_length+1 &&
2258 memcmp(o->data.payload, field, field_length) == 0 &&
2259 o->data.payload[field_length] == '=') {
3fbf9cbb 2260
161e54f8 2261 t = (size_t) l;
3fbf9cbb 2262
161e54f8
LP
2263 if ((uint64_t) t != l)
2264 return -E2BIG;
3fbf9cbb 2265
161e54f8
LP
2266 *data = o->data.payload;
2267 *size = t;
3fbf9cbb 2268
99613ec5 2269 return 0;
161e54f8 2270 }
3fbf9cbb 2271
de190aef 2272 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
161e54f8
LP
2273 if (r < 0)
2274 return r;
3fbf9cbb
LP
2275 }
2276
de190aef 2277 return -ENOENT;
3fbf9cbb
LP
2278}
2279
93b73b06 2280static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
3c1668da
LP
2281 size_t t;
2282 uint64_t l;
3b1a55e1 2283 int compression;
3c1668da
LP
2284
2285 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2286 t = (size_t) l;
2287
2288 /* We can't read objects larger than 4G on a 32bit machine */
2289 if ((uint64_t) t != l)
2290 return -E2BIG;
2291
1ec7120e
ZJS
2292 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
2293 if (compression) {
349cc4a5 2294#if HAVE_XZ || HAVE_LZ4
fa1c4b51 2295 size_t rsize;
3b1a55e1 2296 int r;
3c1668da 2297
1ec7120e
ZJS
2298 r = decompress_blob(compression,
2299 o->data.payload, l, &f->compress_buffer,
2300 &f->compress_buffer_size, &rsize, j->data_threshold);
2301 if (r < 0)
2302 return r;
3c1668da
LP
2303
2304 *data = f->compress_buffer;
2305 *size = (size_t) rsize;
3b1a55e1
ZJS
2306#else
2307 return -EPROTONOSUPPORT;
2308#endif
3c1668da
LP
2309 } else {
2310 *data = o->data.payload;
2311 *size = t;
2312 }
2313
2314 return 0;
2315}
2316
a5344d2c 2317_public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
3fbf9cbb 2318 JournalFile *f;
3c1668da 2319 uint64_t p, n;
4fd052ae 2320 le64_t le_hash;
3fbf9cbb
LP
2321 int r;
2322 Object *o;
2323
1ae464e0
TA
2324 assert_return(j, -EINVAL);
2325 assert_return(!journal_pid_changed(j), -ECHILD);
2326 assert_return(data, -EINVAL);
2327 assert_return(size, -EINVAL);
3fbf9cbb
LP
2328
2329 f = j->current_file;
2330 if (!f)
de190aef 2331 return -EADDRNOTAVAIL;
3fbf9cbb
LP
2332
2333 if (f->current_offset <= 0)
de190aef 2334 return -EADDRNOTAVAIL;
3fbf9cbb 2335
de190aef 2336 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
2337 if (r < 0)
2338 return r;
2339
2340 n = journal_file_entry_n_items(o);
7210bfb3 2341 if (j->current_field >= n)
3fbf9cbb
LP
2342 return 0;
2343
7210bfb3 2344 p = le64toh(o->entry.items[j->current_field].object_offset);
de190aef
LP
2345 le_hash = o->entry.items[j->current_field].hash;
2346 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
3fbf9cbb
LP
2347 if (r < 0)
2348 return r;
2349
de190aef 2350 if (le_hash != o->data.hash)
de7b95cd
LP
2351 return -EBADMSG;
2352
93b73b06 2353 r = return_data(j, f, o, data, size);
3c1668da
LP
2354 if (r < 0)
2355 return r;
3fbf9cbb 2356
313cefa1 2357 j->current_field++;
3fbf9cbb
LP
2358
2359 return 1;
2360}
c2373f84 2361
a5344d2c
LP
2362_public_ void sd_journal_restart_data(sd_journal *j) {
2363 if (!j)
2364 return;
8725d60a
LP
2365
2366 j->current_field = 0;
c2373f84 2367}
50f20cfd 2368
858749f7
LP
2369static int reiterate_all_paths(sd_journal *j) {
2370 assert(j);
2371
2372 if (j->no_new_files)
2373 return add_current_paths(j);
2374
2375 if (j->flags & SD_JOURNAL_OS_ROOT)
2376 return add_search_paths(j);
2377
2378 if (j->toplevel_fd >= 0)
2379 return add_root_directory(j, NULL, false);
2380
2381 if (j->path)
2382 return add_root_directory(j, j->path, true);
2383
2384 return add_search_paths(j);
2385}
2386
a5344d2c 2387_public_ int sd_journal_get_fd(sd_journal *j) {
a963990f
LP
2388 int r;
2389
1ae464e0
TA
2390 assert_return(j, -EINVAL);
2391 assert_return(!journal_pid_changed(j), -ECHILD);
50f20cfd 2392
5d1ce257
LP
2393 if (j->no_inotify)
2394 return -EMEDIUMTYPE;
2395
a963990f
LP
2396 if (j->inotify_fd >= 0)
2397 return j->inotify_fd;
2398
2399 r = allocate_inotify(j);
2400 if (r < 0)
2401 return r;
2402
858749f7 2403 log_debug("Reiterating files to get inotify watches established.");
5d1ce257 2404
858749f7
LP
2405 /* Iterate through all dirs again, to add them to the inotify */
2406 r = reiterate_all_paths(j);
a963990f
LP
2407 if (r < 0)
2408 return r;
2409
50f20cfd
LP
2410 return j->inotify_fd;
2411}
2412
ee531d94
LP
2413_public_ int sd_journal_get_events(sd_journal *j) {
2414 int fd;
2415
1ae464e0
TA
2416 assert_return(j, -EINVAL);
2417 assert_return(!journal_pid_changed(j), -ECHILD);
ee531d94
LP
2418
2419 fd = sd_journal_get_fd(j);
2420 if (fd < 0)
2421 return fd;
2422
2423 return POLLIN;
2424}
2425
39c155ea
LP
2426_public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2427 int fd;
2428
1ae464e0
TA
2429 assert_return(j, -EINVAL);
2430 assert_return(!journal_pid_changed(j), -ECHILD);
2431 assert_return(timeout_usec, -EINVAL);
39c155ea
LP
2432
2433 fd = sd_journal_get_fd(j);
2434 if (fd < 0)
2435 return fd;
2436
2437 if (!j->on_network) {
2438 *timeout_usec = (uint64_t) -1;
2439 return 0;
2440 }
2441
2442 /* If we are on the network we need to regularly check for
2443 * changes manually */
2444
2445 *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2446 return 1;
2447}
2448
858749f7
LP
2449static void process_q_overflow(sd_journal *j) {
2450 JournalFile *f;
2451 Directory *m;
2452 Iterator i;
2453
2454 assert(j);
2455
2456 /* When the inotify queue overruns we need to enumerate and re-validate all journal files to bring our list
2457 * back in sync with what's on disk. For this we pick a new generation counter value. It'll be assigned to all
2458 * journal files we encounter. All journal files and all directories that don't carry it after reenumeration
2459 * are subject for unloading. */
2460
2461 log_debug("Inotify queue overrun, reiterating everything.");
2462
2463 j->generation++;
2464 (void) reiterate_all_paths(j);
2465
2466 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
2467
2468 if (f->last_seen_generation == j->generation)
2469 continue;
2470
2471 log_debug("File '%s' hasn't been seen in this enumeration, removing.", f->path);
2472 remove_file_real(j, f);
2473 }
2474
2475 HASHMAP_FOREACH(m, j->directories_by_path, i) {
2476
2477 if (m->last_seen_generation == j->generation)
2478 continue;
2479
2480 if (m->is_root) /* Never GC root directories */
2481 continue;
2482
2483 log_debug("Directory '%s' hasn't been seen in this enumeration, removing.", f->path);
2484 remove_directory(j, m);
2485 }
2486
2487 log_debug("Reiteration complete.");
2488}
2489
50f20cfd 2490static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
a963990f 2491 Directory *d;
50f20cfd
LP
2492
2493 assert(j);
2494 assert(e);
2495
858749f7
LP
2496 if (e->mask & IN_Q_OVERFLOW) {
2497 process_q_overflow(j);
2498 return;
2499 }
2500
50f20cfd 2501 /* Is this a subdirectory we watch? */
a963990f
LP
2502 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2503 if (d) {
de2c3907
LP
2504 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2505 (endswith(e->name, ".journal") ||
2506 endswith(e->name, ".journal~"))) {
50f20cfd
LP
2507
2508 /* Event for a journal file */
2509
d617408e 2510 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB))
fc1813c0 2511 (void) add_file_by_name(j, d->path, e->name);
d617408e 2512 else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT))
fc1813c0 2513 remove_file_by_name(j, d->path, e->name);
50f20cfd 2514
a963990f 2515 } else if (!d->is_root && e->len == 0) {
50f20cfd 2516
a963990f 2517 /* Event for a subdirectory */
50f20cfd 2518
b2b46f91
TA
2519 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT))
2520 remove_directory(j, d);
50f20cfd 2521
a9be0692 2522 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && id128_is_valid(e->name)) {
50f20cfd 2523
a963990f 2524 /* Event for root directory */
50f20cfd 2525
d617408e
LP
2526 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB))
2527 (void) add_directory(j, d->path, e->name);
50f20cfd
LP
2528 }
2529
2530 return;
2531 }
2532
2533 if (e->mask & IN_IGNORED)
2534 return;
2535
a9be0692 2536 log_debug("Unexpected inotify event.");
50f20cfd
LP
2537}
2538
a963990f
LP
2539static int determine_change(sd_journal *j) {
2540 bool b;
2541
2542 assert(j);
2543
2544 b = j->current_invalidate_counter != j->last_invalidate_counter;
2545 j->last_invalidate_counter = j->current_invalidate_counter;
2546
2547 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2548}
2549
a5344d2c 2550_public_ int sd_journal_process(sd_journal *j) {
a963990f 2551 bool got_something = false;
50f20cfd 2552
1ae464e0
TA
2553 assert_return(j, -EINVAL);
2554 assert_return(!journal_pid_changed(j), -ECHILD);
50f20cfd 2555
10c4d640
LP
2556 if (j->inotify_fd < 0) /* We have no inotify fd yet? Then there's noting to process. */
2557 return 0;
2558
39c155ea 2559 j->last_process_usec = now(CLOCK_MONOTONIC);
f9346444 2560 j->last_invalidate_counter = j->current_invalidate_counter;
39c155ea 2561
50f20cfd 2562 for (;;) {
0254e944 2563 union inotify_event_buffer buffer;
50f20cfd
LP
2564 struct inotify_event *e;
2565 ssize_t l;
2566
0254e944 2567 l = read(j->inotify_fd, &buffer, sizeof(buffer));
50f20cfd 2568 if (l < 0) {
3742095b 2569 if (IN_SET(errno, EAGAIN, EINTR))
a963990f 2570 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
50f20cfd
LP
2571
2572 return -errno;
2573 }
2574
a963990f
LP
2575 got_something = true;
2576
f7c1ad4f 2577 FOREACH_INOTIFY_EVENT(e, buffer, l)
50f20cfd 2578 process_inotify_event(j, e);
50f20cfd
LP
2579 }
2580}
6ad1d1c3 2581
e02d1cf7 2582_public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
a963990f 2583 int r;
39c155ea 2584 uint64_t t;
e02d1cf7 2585
1ae464e0
TA
2586 assert_return(j, -EINVAL);
2587 assert_return(!journal_pid_changed(j), -ECHILD);
e02d1cf7 2588
a963990f
LP
2589 if (j->inotify_fd < 0) {
2590
2591 /* This is the first invocation, hence create the
2592 * inotify watch */
2593 r = sd_journal_get_fd(j);
2594 if (r < 0)
2595 return r;
2596
2597 /* The journal might have changed since the context
2598 * object was created and we weren't watching before,
2599 * hence don't wait for anything, and return
2600 * immediately. */
2601 return determine_change(j);
2602 }
2603
39c155ea
LP
2604 r = sd_journal_get_timeout(j, &t);
2605 if (r < 0)
2606 return r;
2607
2608 if (t != (uint64_t) -1) {
2609 usec_t n;
2610
2611 n = now(CLOCK_MONOTONIC);
2612 t = t > n ? t - n : 0;
85210bff 2613
39c155ea
LP
2614 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2615 timeout_usec = t;
85210bff
LP
2616 }
2617
a963990f
LP
2618 do {
2619 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2620 } while (r == -EINTR);
e02d1cf7
LP
2621
2622 if (r < 0)
2623 return r;
2624
a963990f 2625 return sd_journal_process(j);
e02d1cf7
LP
2626}
2627
08984293
LP
2628_public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2629 Iterator i;
2630 JournalFile *f;
2631 bool first = true;
581483bf 2632 uint64_t fmin = 0, tmax = 0;
08984293
LP
2633 int r;
2634
1ae464e0
TA
2635 assert_return(j, -EINVAL);
2636 assert_return(!journal_pid_changed(j), -ECHILD);
2637 assert_return(from || to, -EINVAL);
2638 assert_return(from != to, -EINVAL);
08984293 2639
c1f906bd 2640 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
08984293
LP
2641 usec_t fr, t;
2642
2643 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
9f8d2983
LP
2644 if (r == -ENOENT)
2645 continue;
08984293
LP
2646 if (r < 0)
2647 return r;
2648 if (r == 0)
2649 continue;
2650
2651 if (first) {
581483bf
LP
2652 fmin = fr;
2653 tmax = t;
08984293
LP
2654 first = false;
2655 } else {
581483bf
LP
2656 fmin = MIN(fr, fmin);
2657 tmax = MAX(t, tmax);
08984293
LP
2658 }
2659 }
2660
581483bf
LP
2661 if (from)
2662 *from = fmin;
2663 if (to)
2664 *to = tmax;
2665
08984293
LP
2666 return first ? 0 : 1;
2667}
2668
2669_public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2670 Iterator i;
2671 JournalFile *f;
1651e2c6 2672 bool found = false;
08984293
LP
2673 int r;
2674
1ae464e0
TA
2675 assert_return(j, -EINVAL);
2676 assert_return(!journal_pid_changed(j), -ECHILD);
2677 assert_return(from || to, -EINVAL);
2678 assert_return(from != to, -EINVAL);
08984293 2679
c1f906bd 2680 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
08984293
LP
2681 usec_t fr, t;
2682
2683 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
9f8d2983
LP
2684 if (r == -ENOENT)
2685 continue;
08984293
LP
2686 if (r < 0)
2687 return r;
2688 if (r == 0)
2689 continue;
2690
1651e2c6 2691 if (found) {
08984293 2692 if (from)
1651e2c6 2693 *from = MIN(fr, *from);
08984293 2694 if (to)
1651e2c6 2695 *to = MAX(t, *to);
08984293
LP
2696 } else {
2697 if (from)
1651e2c6 2698 *from = fr;
08984293 2699 if (to)
1651e2c6
ZJS
2700 *to = t;
2701 found = true;
08984293
LP
2702 }
2703 }
2704
1651e2c6 2705 return found;
08984293
LP
2706}
2707
dca6219e
LP
2708void journal_print_header(sd_journal *j) {
2709 Iterator i;
2710 JournalFile *f;
2711 bool newline = false;
2712
2713 assert(j);
2714
c1f906bd 2715 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
dca6219e
LP
2716 if (newline)
2717 putchar('\n');
2718 else
2719 newline = true;
2720
2721 journal_file_print_header(f);
2722 }
2723}
08984293 2724
a1a03e30
LP
2725_public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2726 Iterator i;
2727 JournalFile *f;
2728 uint64_t sum = 0;
2729
1ae464e0
TA
2730 assert_return(j, -EINVAL);
2731 assert_return(!journal_pid_changed(j), -ECHILD);
2732 assert_return(bytes, -EINVAL);
a1a03e30 2733
c1f906bd 2734 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
a1a03e30
LP
2735 struct stat st;
2736
2737 if (fstat(f->fd, &st) < 0)
2738 return -errno;
2739
2740 sum += (uint64_t) st.st_blocks * 512ULL;
2741 }
2742
2743 *bytes = sum;
2744 return 0;
2745}
2746
3c1668da
LP
2747_public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2748 char *f;
2749
1ae464e0
TA
2750 assert_return(j, -EINVAL);
2751 assert_return(!journal_pid_changed(j), -ECHILD);
2752 assert_return(!isempty(field), -EINVAL);
2753 assert_return(field_is_valid(field), -EINVAL);
3c1668da
LP
2754
2755 f = strdup(field);
2756 if (!f)
2757 return -ENOMEM;
2758
2759 free(j->unique_field);
2760 j->unique_field = f;
2761 j->unique_file = NULL;
2762 j->unique_offset = 0;
360af4cf 2763 j->unique_file_lost = false;
3c1668da
LP
2764
2765 return 0;
2766}
2767
2768_public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
3c1668da 2769 size_t k;
19a2bd80 2770
1ae464e0
TA
2771 assert_return(j, -EINVAL);
2772 assert_return(!journal_pid_changed(j), -ECHILD);
2773 assert_return(data, -EINVAL);
2774 assert_return(l, -EINVAL);
2775 assert_return(j->unique_field, -EINVAL);
19a2bd80 2776
3c1668da 2777 k = strlen(j->unique_field);
19a2bd80 2778
3c1668da 2779 if (!j->unique_file) {
360af4cf
ZJS
2780 if (j->unique_file_lost)
2781 return 0;
2782
c1f906bd 2783 j->unique_file = ordered_hashmap_first(j->files);
3c1668da
LP
2784 if (!j->unique_file)
2785 return 0;
360af4cf 2786
3c1668da
LP
2787 j->unique_offset = 0;
2788 }
19a2bd80 2789
3c1668da
LP
2790 for (;;) {
2791 JournalFile *of;
2792 Iterator i;
ae97089d 2793 Object *o;
3c1668da
LP
2794 const void *odata;
2795 size_t ol;
2796 bool found;
ae97089d 2797 int r;
3c1668da 2798
bdc02927 2799 /* Proceed to next data object in the field's linked list */
3c1668da
LP
2800 if (j->unique_offset == 0) {
2801 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2802 if (r < 0)
2803 return r;
2804
2805 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2806 } else {
2807 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2808 if (r < 0)
2809 return r;
2810
2811 j->unique_offset = le64toh(o->data.next_field_offset);
2812 }
2813
2814 /* We reached the end of the list? Then start again, with the next file */
2815 if (j->unique_offset == 0) {
c1f906bd 2816 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
360af4cf 2817 if (!j->unique_file)
3c1668da
LP
2818 return 0;
2819
3c1668da
LP
2820 continue;
2821 }
2822
d05089d8
MS
2823 /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED
2824 * instead, so that we can look at this data object at the same
3c1668da 2825 * time as one on another file */
d05089d8 2826 r = journal_file_move_to_object(j->unique_file, OBJECT_UNUSED, j->unique_offset, &o);
3c1668da
LP
2827 if (r < 0)
2828 return r;
2829
2830 /* Let's do the type check by hand, since we used 0 context above. */
ae97089d 2831 if (o->object.type != OBJECT_DATA) {
36202fd2 2832 log_debug("%s:offset " OFSfmt ": object has type %d, expected %d",
ae97089d
ZJS
2833 j->unique_file->path, j->unique_offset,
2834 o->object.type, OBJECT_DATA);
3c1668da 2835 return -EBADMSG;
ae97089d
ZJS
2836 }
2837
93b73b06 2838 r = return_data(j, j->unique_file, o, &odata, &ol);
3c1668da
LP
2839 if (r < 0)
2840 return r;
2841
0f99f74a
ZJS
2842 /* Check if we have at least the field name and "=". */
2843 if (ol <= k) {
2844 log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
2845 j->unique_file->path, j->unique_offset,
2846 ol, k + 1);
2847 return -EBADMSG;
2848 }
2849
2850 if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') {
2851 log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"",
2852 j->unique_file->path, j->unique_offset,
2853 j->unique_field);
2854 return -EBADMSG;
2855 }
2856
3c1668da
LP
2857 /* OK, now let's see if we already returned this data
2858 * object by checking if it exists in the earlier
2859 * traversed files. */
2860 found = false;
c1f906bd 2861 ORDERED_HASHMAP_FOREACH(of, j->files, i) {
3c1668da
LP
2862 if (of == j->unique_file)
2863 break;
2864
ed71f956
LP
2865 /* Skip this file it didn't have any fields indexed */
2866 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) && le64toh(of->header->n_fields) <= 0)
3c1668da
LP
2867 continue;
2868
ed71f956 2869 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), NULL, NULL);
3c1668da
LP
2870 if (r < 0)
2871 return r;
ed71f956 2872 if (r > 0) {
3c1668da 2873 found = true;
ed71f956
LP
2874 break;
2875 }
3c1668da
LP
2876 }
2877
06cc69d4
JJ
2878 if (found)
2879 continue;
2880
93b73b06 2881 r = return_data(j, j->unique_file, o, data, l);
3c1668da
LP
2882 if (r < 0)
2883 return r;
2884
2885 return 1;
2886 }
2887}
2888
115646c7 2889_public_ void sd_journal_restart_unique(sd_journal *j) {
3c1668da
LP
2890 if (!j)
2891 return;
2892
2893 j->unique_file = NULL;
2894 j->unique_offset = 0;
360af4cf 2895 j->unique_file_lost = false;
3c1668da 2896}
85210bff 2897
eb86030e
LP
2898_public_ int sd_journal_enumerate_fields(sd_journal *j, const char **field) {
2899 int r;
2900
2901 assert_return(j, -EINVAL);
2902 assert_return(!journal_pid_changed(j), -ECHILD);
2903 assert_return(field, -EINVAL);
2904
2905 if (!j->fields_file) {
2906 if (j->fields_file_lost)
2907 return 0;
2908
2909 j->fields_file = ordered_hashmap_first(j->files);
2910 if (!j->fields_file)
2911 return 0;
2912
2913 j->fields_hash_table_index = 0;
2914 j->fields_offset = 0;
2915 }
2916
2917 for (;;) {
2918 JournalFile *f, *of;
2919 Iterator i;
2920 uint64_t m;
2921 Object *o;
2922 size_t sz;
2923 bool found;
2924
2925 f = j->fields_file;
2926
2927 if (j->fields_offset == 0) {
2928 bool eof = false;
2929
2930 /* We are not yet positioned at any field. Let's pick the first one */
2931 r = journal_file_map_field_hash_table(f);
2932 if (r < 0)
2933 return r;
2934
2935 m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem);
2936 for (;;) {
2937 if (j->fields_hash_table_index >= m) {
2938 /* Reached the end of the hash table, go to the next file. */
2939 eof = true;
2940 break;
2941 }
2942
2943 j->fields_offset = le64toh(f->field_hash_table[j->fields_hash_table_index].head_hash_offset);
2944
2945 if (j->fields_offset != 0)
2946 break;
2947
2948 /* Empty hash table bucket, go to next one */
2949 j->fields_hash_table_index++;
2950 }
2951
2952 if (eof) {
2953 /* Proceed with next file */
2954 j->fields_file = ordered_hashmap_next(j->files, f->path);
2955 if (!j->fields_file) {
2956 *field = NULL;
2957 return 0;
2958 }
2959
2960 j->fields_offset = 0;
2961 j->fields_hash_table_index = 0;
2962 continue;
2963 }
2964
2965 } else {
2966 /* We are already positioned at a field. If so, let's figure out the next field from it */
2967
2968 r = journal_file_move_to_object(f, OBJECT_FIELD, j->fields_offset, &o);
2969 if (r < 0)
2970 return r;
2971
2972 j->fields_offset = le64toh(o->field.next_hash_offset);
2973 if (j->fields_offset == 0) {
2974 /* Reached the end of the hash table chain */
2975 j->fields_hash_table_index++;
2976 continue;
2977 }
2978 }
2979
1f133e0d 2980 /* We use OBJECT_UNUSED here, so that the iterator below doesn't remove our mmap window */
eb86030e
LP
2981 r = journal_file_move_to_object(f, OBJECT_UNUSED, j->fields_offset, &o);
2982 if (r < 0)
2983 return r;
2984
2985 /* Because we used OBJECT_UNUSED above, we need to do our type check manually */
2986 if (o->object.type != OBJECT_FIELD) {
2987 log_debug("%s:offset " OFSfmt ": object has type %i, expected %i", f->path, j->fields_offset, o->object.type, OBJECT_FIELD);
2988 return -EBADMSG;
2989 }
2990
2991 sz = le64toh(o->object.size) - offsetof(Object, field.payload);
2992
2993 /* Let's see if we already returned this field name before. */
2994 found = false;
2995 ORDERED_HASHMAP_FOREACH(of, j->files, i) {
2996 if (of == f)
2997 break;
2998
2999 /* Skip this file it didn't have any fields indexed */
3000 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) && le64toh(of->header->n_fields) <= 0)
3001 continue;
3002
3003 r = journal_file_find_field_object_with_hash(of, o->field.payload, sz, le64toh(o->field.hash), NULL, NULL);
3004 if (r < 0)
3005 return r;
3006 if (r > 0) {
3007 found = true;
3008 break;
3009 }
3010 }
3011
3012 if (found)
3013 continue;
3014
3015 /* Check if this is really a valid string containing no NUL byte */
3016 if (memchr(o->field.payload, 0, sz))
3017 return -EBADMSG;
3018
3019 if (sz > j->data_threshold)
3020 sz = j->data_threshold;
3021
3022 if (!GREEDY_REALLOC(j->fields_buffer, j->fields_buffer_allocated, sz + 1))
3023 return -ENOMEM;
3024
3025 memcpy(j->fields_buffer, o->field.payload, sz);
3026 j->fields_buffer[sz] = 0;
3027
3028 if (!field_is_valid(j->fields_buffer))
3029 return -EBADMSG;
3030
3031 *field = j->fields_buffer;
3032 return 1;
3033 }
3034}
3035
3036_public_ void sd_journal_restart_fields(sd_journal *j) {
3037 if (!j)
3038 return;
3039
3040 j->fields_file = NULL;
3041 j->fields_hash_table_index = 0;
3042 j->fields_offset = 0;
3043 j->fields_file_lost = false;
3044}
3045
85210bff 3046_public_ int sd_journal_reliable_fd(sd_journal *j) {
1ae464e0
TA
3047 assert_return(j, -EINVAL);
3048 assert_return(!journal_pid_changed(j), -ECHILD);
85210bff
LP
3049
3050 return !j->on_network;
3051}
d4205751
LP
3052
3053static char *lookup_field(const char *field, void *userdata) {
3054 sd_journal *j = userdata;
3055 const void *data;
3056 size_t size, d;
3057 int r;
3058
3059 assert(field);
3060 assert(j);
3061
3062 r = sd_journal_get_data(j, field, &data, &size);
3063 if (r < 0 ||
3064 size > REPLACE_VAR_MAX)
3065 return strdup(field);
3066
3067 d = strlen(field) + 1;
3068
3069 return strndup((const char*) data + d, size - d);
3070}
3071
3072_public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
3073 const void *data;
3074 size_t size;
3075 sd_id128_t id;
3076 _cleanup_free_ char *text = NULL, *cid = NULL;
3077 char *t;
3078 int r;
3079
1ae464e0
TA
3080 assert_return(j, -EINVAL);
3081 assert_return(!journal_pid_changed(j), -ECHILD);
3082 assert_return(ret, -EINVAL);
d4205751
LP
3083
3084 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
3085 if (r < 0)
3086 return r;
3087
3088 cid = strndup((const char*) data + 11, size - 11);
3089 if (!cid)
3090 return -ENOMEM;
3091
3092 r = sd_id128_from_string(cid, &id);
3093 if (r < 0)
3094 return r;
3095
844ec79b 3096 r = catalog_get(CATALOG_DATABASE, id, &text);
d4205751
LP
3097 if (r < 0)
3098 return r;
3099
3100 t = replace_var(text, lookup_field, j);
3101 if (!t)
3102 return -ENOMEM;
3103
3104 *ret = t;
3105 return 0;
3106}
8f1e860f
LP
3107
3108_public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
1ae464e0 3109 assert_return(ret, -EINVAL);
8f1e860f 3110
844ec79b 3111 return catalog_get(CATALOG_DATABASE, id, ret);
8f1e860f 3112}
93b73b06
LP
3113
3114_public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
1ae464e0
TA
3115 assert_return(j, -EINVAL);
3116 assert_return(!journal_pid_changed(j), -ECHILD);
93b73b06
LP
3117
3118 j->data_threshold = sz;
3119 return 0;
3120}
3121
3122_public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
1ae464e0
TA
3123 assert_return(j, -EINVAL);
3124 assert_return(!journal_pid_changed(j), -ECHILD);
3125 assert_return(sz, -EINVAL);
93b73b06
LP
3126
3127 *sz = j->data_threshold;
3128 return 0;
3129}
39fd5b08
JS
3130
3131_public_ int sd_journal_has_runtime_files(sd_journal *j) {
3132 assert_return(j, -EINVAL);
3133
3134 return j->has_runtime_files;
3135}
3136
3137_public_ int sd_journal_has_persistent_files(sd_journal *j) {
3138 assert_return(j, -EINVAL);
3139
3140 return j->has_persistent_files;
3141}