]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-journal/sd-journal.c
Merge pull request #31777 from keszybz/unit-retitling-and-comments
[thirdparty/systemd.git] / src / libsystemd / sd-journal / sd-journal.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
87d2c1ff 2
87d2c1ff 3#include <errno.h>
87d2c1ff 4#include <fcntl.h>
82e24b00 5#include <inttypes.h>
07630cea
LP
6#include <linux/magic.h>
7#include <poll.h>
3fbf9cbb 8#include <stddef.h>
50f20cfd 9#include <sys/inotify.h>
85210bff 10#include <sys/vfs.h>
07630cea 11#include <unistd.h>
87d2c1ff
LP
12
13#include "sd-journal.h"
c004493c 14
b5efdb8a 15#include "alloc-util.h"
07630cea
LP
16#include "catalog.h"
17#include "compress.h"
a0956174 18#include "dirent-util.h"
686d13b9 19#include "env-file.h"
9e8b1ec0 20#include "escape.h"
3ffd4af2 21#include "fd-util.h"
07630cea 22#include "fileio.h"
f97b34a6 23#include "format-util.h"
77601719 24#include "fs-util.h"
07630cea
LP
25#include "hashmap.h"
26#include "hostname-util.h"
a9be0692 27#include "id128-util.h"
9e5fd717 28#include "inotify-util.h"
c004493c 29#include "io-util.h"
87d2c1ff 30#include "journal-def.h"
cec736d2 31#include "journal-file.h"
07630cea 32#include "journal-internal.h"
cec736d2 33#include "list.h"
de7b95cd 34#include "lookup3.h"
d8b4d14d 35#include "nulstr-util.h"
e046719b 36#include "origin-id.h"
07630cea 37#include "path-util.h"
34af7494 38#include "prioq.h"
dccca82b 39#include "process-util.h"
d4205751 40#include "replace-var.h"
cb2b0326 41#include "sort-util.h"
15a5e950
LP
42#include "stat-util.h"
43#include "stdio-util.h"
07630cea
LP
44#include "string-util.h"
45#include "strv.h"
456aa879 46#include "syslog-util.h"
8e1ac16b 47#include "uid-classification.h"
87d2c1ff 48
85210bff
LP
49#define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC)
50
8dd7cbce
LP
51/* The maximum size of variable values we'll expand in catalog entries. We bind this to PATH_MAX for now, as
52 * we want to be able to show all officially valid paths at least */
53#define REPLACE_VAR_MAX PATH_MAX
d4205751 54
93b73b06
LP
55#define DEFAULT_DATA_THRESHOLD (64*1024)
56
e046719b
LB
57DEFINE_PRIVATE_ORIGIN_ID_HELPERS(sd_journal, journal);
58
a9a245c1 59static void remove_file_real(sd_journal *j, JournalFile *f);
34af7494 60static int journal_file_read_tail_timestamp(sd_journal *j, JournalFile *f);
2292d377 61static void journal_file_unlink_newest_by_boot_id(sd_journal *j, JournalFile *f);
a9a245c1 62
5768d259 63static int journal_put_error(sd_journal *j, int r, const char *path) {
8231485b 64 _cleanup_free_ char *copy = NULL;
3ac251b8
LP
65 int k;
66
5768d259
LP
67 /* Memorize an error we encountered, and store which
68 * file/directory it was generated from. Note that we store
69 * only *one* path per error code, as the error code is the
70 * key into the hashmap, and the path is the value. This means
71 * we keep track only of all error kinds, but not of all error
72 * locations. This has the benefit that the hashmap cannot
73 * grow beyond bounds.
74 *
75 * We return an error here only if we didn't manage to
76 * memorize the real error. */
77
6fe391c5
ZJS
78 if (r >= 0)
79 return r;
80
5768d259
LP
81 if (path) {
82 copy = strdup(path);
83 if (!copy)
84 return -ENOMEM;
8231485b 85 }
5768d259 86
faa7e5a4 87 k = hashmap_ensure_put(&j->errors, NULL, INT_TO_PTR(r), copy);
5768d259 88 if (k < 0) {
5768d259
LP
89 if (k == -EEXIST)
90 return 0;
91
92 return k;
93 }
94
8231485b 95 TAKE_PTR(copy);
5768d259 96 return 0;
6fe391c5
ZJS
97}
98
de190aef 99static void detach_location(sd_journal *j) {
8f9b6cd9
LP
100 JournalFile *f;
101
102 assert(j);
103
104 j->current_file = NULL;
105 j->current_field = 0;
106
90e74a66 107 ORDERED_HASHMAP_FOREACH(f, j->files)
1fc605b0 108 journal_file_reset_location(f);
8f9b6cd9
LP
109}
110
a87247dd 111static void init_location(Location *l, LocationType type, JournalFile *f, Object *o) {
de190aef 112 assert(l);
3742095b 113 assert(IN_SET(type, LOCATION_DISCRETE, LOCATION_SEEK));
de190aef 114 assert(f);
de190aef 115
bba6e4ae
LP
116 *l = (Location) {
117 .type = type,
118 .seqnum = le64toh(o->entry.seqnum),
119 .seqnum_id = f->header->seqnum_id,
120 .realtime = le64toh(o->entry.realtime),
121 .monotonic = le64toh(o->entry.monotonic),
122 .boot_id = o->entry.boot_id,
123 .xor_hash = le64toh(o->entry.xor_hash),
124 .seqnum_set = true,
125 .realtime_set = true,
126 .monotonic_set = true,
127 .xor_hash_set = true,
128 };
de190aef
LP
129}
130
1eb6332d 131static void set_location(sd_journal *j, JournalFile *f, Object *o) {
de190aef
LP
132 assert(j);
133 assert(f);
134 assert(o);
135
1eb6332d 136 init_location(&j->current_location, LOCATION_DISCRETE, f, o);
de190aef
LP
137
138 j->current_file = f;
139 j->current_field = 0;
140
6573ef05
MS
141 /* Let f know its candidate entry was picked. */
142 assert(f->location_type == LOCATION_SEEK);
143 f->location_type = LOCATION_DISCRETE;
de190aef
LP
144}
145
cbdca852 146static int match_is_valid(const void *data, size_t size) {
99534007 147 const char *b = ASSERT_PTR(data);
cbdca852
LP
148
149 if (size < 2)
150 return false;
151
e0567bc8 152 if (((char*) data)[0] == '_' && ((char*) data)[1] == '_')
cbdca852
LP
153 return false;
154
abcdc02c 155 for (const char *p = b; p < b + size; p++) {
cbdca852
LP
156
157 if (*p == '=')
158 return p > b;
159
160 if (*p == '_')
161 continue;
162
163 if (*p >= 'A' && *p <= 'Z')
164 continue;
165
ff25d338 166 if (ascii_isdigit(*p))
cbdca852
LP
167 continue;
168
169 return false;
170 }
171
172 return false;
173}
174
175static bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
de190aef 176 const uint8_t *a = _a, *b = _b;
de190aef 177
abcdc02c 178 for (size_t j = 0; j < s && j < t; j++) {
de190aef 179
de190aef 180 if (a[j] != b[j])
cbdca852 181 return false;
de190aef 182
cbdca852
LP
183 if (a[j] == '=')
184 return true;
de190aef
LP
185 }
186
04499a70 187 assert_not_reached();
cbdca852
LP
188}
189
190static Match *match_new(Match *p, MatchType t) {
191 Match *m;
192
2f5435a1 193 m = new(Match, 1);
cbdca852
LP
194 if (!m)
195 return NULL;
196
2f5435a1
LP
197 *m = (Match) {
198 .type = t,
199 .parent = p,
200 };
cbdca852 201
2f5435a1 202 if (p)
71fda00f 203 LIST_PREPEND(matches, p->matches, m);
cbdca852
LP
204
205 return m;
206}
207
39dfc0de 208static Match *match_free(Match *m) {
cbdca852
LP
209 assert(m);
210
211 while (m->matches)
212 match_free(m->matches);
213
214 if (m->parent)
71fda00f 215 LIST_REMOVE(matches, m->parent->matches, m);
cbdca852
LP
216
217 free(m->data);
39dfc0de 218 return mfree(m);
cbdca852
LP
219}
220
39dfc0de 221static Match *match_free_if_empty(Match *m) {
c5a10d9c 222 if (!m || m->matches)
39dfc0de 223 return m;
cbdca852 224
39dfc0de 225 return match_free(m);
de190aef
LP
226}
227
a5344d2c 228_public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
03677889 229 Match *add_here = NULL, *m = NULL;
cde8c5f7 230 uint64_t hash;
87d2c1ff 231
1ae464e0 232 assert_return(j, -EINVAL);
e046719b 233 assert_return(!journal_origin_changed(j), -ECHILD);
1ae464e0 234 assert_return(data, -EINVAL);
cbdca852
LP
235
236 if (size == 0)
237 size = strlen(data);
238
17122c3d
FS
239 if (!match_is_valid(data, size))
240 return -EINVAL;
1cc101f1 241
cd34b3c6
HH
242 /* level 0: AND term
243 * level 1: OR terms
244 * level 2: AND terms
245 * level 3: OR terms
246 * level 4: concrete matches */
cbdca852
LP
247
248 if (!j->level0) {
cd34b3c6 249 j->level0 = match_new(NULL, MATCH_AND_TERM);
cbdca852
LP
250 if (!j->level0)
251 return -ENOMEM;
252 }
253
254 if (!j->level1) {
cd34b3c6 255 j->level1 = match_new(j->level0, MATCH_OR_TERM);
cbdca852
LP
256 if (!j->level1)
257 return -ENOMEM;
258 }
259
cd34b3c6
HH
260 if (!j->level2) {
261 j->level2 = match_new(j->level1, MATCH_AND_TERM);
262 if (!j->level2)
263 return -ENOMEM;
264 }
265
266 assert(j->level0->type == MATCH_AND_TERM);
267 assert(j->level1->type == MATCH_OR_TERM);
268 assert(j->level2->type == MATCH_AND_TERM);
ab4979d2 269
4ce534f4
LP
270 /* Old-style Jenkins (unkeyed) hashing only here. We do not cover new-style siphash (keyed) hashing
271 * here, since it's different for each file, and thus can't be pre-calculated in the Match object. */
20b0acfa 272 hash = jenkins_hash64(data, size);
de190aef 273
cd34b3c6
HH
274 LIST_FOREACH(matches, l3, j->level2->matches) {
275 assert(l3->type == MATCH_OR_TERM);
de190aef 276
cd34b3c6
HH
277 LIST_FOREACH(matches, l4, l3->matches) {
278 assert(l4->type == MATCH_DISCRETE);
de190aef 279
cbdca852
LP
280 /* Exactly the same match already? Then ignore
281 * this addition */
cde8c5f7 282 if (l4->hash == hash &&
cd34b3c6
HH
283 l4->size == size &&
284 memcmp(l4->data, data, size) == 0)
cbdca852
LP
285 return 0;
286
287 /* Same field? Then let's add this to this OR term */
cd34b3c6
HH
288 if (same_field(data, size, l4->data, l4->size)) {
289 add_here = l3;
cbdca852
LP
290 break;
291 }
292 }
293
294 if (add_here)
295 break;
de190aef
LP
296 }
297
cbdca852 298 if (!add_here) {
cd34b3c6 299 add_here = match_new(j->level2, MATCH_OR_TERM);
cbdca852
LP
300 if (!add_here)
301 goto fail;
302 }
303
304 m = match_new(add_here, MATCH_DISCRETE);
cec736d2 305 if (!m)
cbdca852 306 goto fail;
87d2c1ff 307
cde8c5f7 308 m->hash = hash;
1cc101f1 309 m->size = size;
cbdca852
LP
310 m->data = memdup(data, size);
311 if (!m->data)
312 goto fail;
313
314 detach_location(j);
315
316 return 0;
317
318fail:
418cce62 319 match_free(m);
c5a10d9c 320 match_free_if_empty(add_here);
39dfc0de
YW
321 j->level2 = match_free_if_empty(j->level2);
322 j->level1 = match_free_if_empty(j->level1);
323 j->level0 = match_free_if_empty(j->level0);
cbdca852
LP
324
325 return -ENOMEM;
326}
327
cd34b3c6 328_public_ int sd_journal_add_conjunction(sd_journal *j) {
1ae464e0 329 assert_return(j, -EINVAL);
e046719b 330 assert_return(!journal_origin_changed(j), -ECHILD);
1cc101f1 331
cbdca852
LP
332 if (!j->level0)
333 return 0;
334
335 if (!j->level1)
336 return 0;
337
338 if (!j->level1->matches)
339 return 0;
340
cd34b3c6
HH
341 j->level1 = NULL;
342 j->level2 = NULL;
343
344 return 0;
345}
346
347_public_ int sd_journal_add_disjunction(sd_journal *j) {
1ae464e0 348 assert_return(j, -EINVAL);
e046719b 349 assert_return(!journal_origin_changed(j), -ECHILD);
cd34b3c6
HH
350
351 if (!j->level0)
352 return 0;
353
354 if (!j->level1)
355 return 0;
356
357 if (!j->level2)
358 return 0;
359
360 if (!j->level2->matches)
361 return 0;
cbdca852 362
cd34b3c6 363 j->level2 = NULL;
cbdca852
LP
364 return 0;
365}
366
367static char *match_make_string(Match *m) {
6320409c 368 _cleanup_free_ char *p = NULL;
cbdca852
LP
369 bool enclose = false;
370
371 if (!m)
4ad16808 372 return strdup("none");
cbdca852
LP
373
374 if (m->type == MATCH_DISCRETE)
9e8b1ec0 375 return cescape_length(m->data, m->size);
cbdca852 376
cbdca852 377 LIST_FOREACH(matches, i, m->matches) {
6320409c 378 _cleanup_free_ char *t = NULL;
cbdca852
LP
379
380 t = match_make_string(i);
6b430fdb 381 if (!t)
6320409c 382 return NULL;
cbdca852
LP
383
384 if (p) {
6320409c 385 if (!strextend(&p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t))
cbdca852
LP
386 return NULL;
387
cbdca852 388 enclose = true;
bc302926 389 } else
6320409c 390 p = TAKE_PTR(t);
87d2c1ff
LP
391 }
392
6320409c
LP
393 if (enclose)
394 return strjoin("(", p, ")");
87d2c1ff 395
6320409c 396 return TAKE_PTR(p);
cbdca852 397}
de7b95cd 398
cbdca852
LP
399char *journal_make_match_string(sd_journal *j) {
400 assert(j);
8f9b6cd9 401
cbdca852 402 return match_make_string(j->level0);
87d2c1ff
LP
403}
404
a5344d2c 405_public_ void sd_journal_flush_matches(sd_journal *j) {
e046719b 406 if (!j || journal_origin_changed(j))
a5344d2c 407 return;
87d2c1ff 408
cbdca852
LP
409 if (j->level0)
410 match_free(j->level0);
de7b95cd 411
cd34b3c6 412 j->level0 = j->level1 = j->level2 = NULL;
8f9b6cd9 413
de190aef 414 detach_location(j);
87d2c1ff
LP
415}
416
cb2b0326
YW
417static int newest_by_boot_id_compare(const NewestByBootId *a, const NewestByBootId *b) {
418 return id128_compare_func(&a->boot_id, &b->boot_id);
419}
420
421static void journal_file_unlink_newest_by_boot_id(sd_journal *j, JournalFile *f) {
422 NewestByBootId *found;
423
424 assert(j);
425 assert(f);
426
427 if (f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL) /* not linked currently, hence this is a NOP */
428 return;
429
430 found = typesafe_bsearch(&(NewestByBootId) { .boot_id = f->newest_boot_id },
431 j->newest_by_boot_id, j->n_newest_by_boot_id, newest_by_boot_id_compare);
432 assert(found);
433
434 assert_se(prioq_remove(found->prioq, f, &f->newest_boot_id_prioq_idx) > 0);
435 f->newest_boot_id_prioq_idx = PRIOQ_IDX_NULL;
436
437 /* The prioq may be empty, but that should not cause any issue. Let's keep it. */
438}
439
440static void journal_clear_newest_by_boot_id(sd_journal *j) {
441 FOREACH_ARRAY(i, j->newest_by_boot_id, j->n_newest_by_boot_id) {
442 JournalFile *f;
443
444 while ((f = prioq_peek(i->prioq)))
445 journal_file_unlink_newest_by_boot_id(j, f);
446
447 prioq_free(i->prioq);
448 }
449
450 j->newest_by_boot_id = mfree(j->newest_by_boot_id);
451 j->n_newest_by_boot_id = 0;
452}
453
454static int journal_file_newest_monotonic_compare(const void *a, const void *b) {
455 const JournalFile *x = a, *y = b;
456
457 return -CMP(x->newest_monotonic_usec, y->newest_monotonic_usec); /* Invert order, we want newest first! */
458}
459
460static int journal_file_reshuffle_newest_by_boot_id(sd_journal *j, JournalFile *f) {
461 NewestByBootId *found;
462 int r;
463
464 assert(j);
465 assert(f);
466
467 found = typesafe_bsearch(&(NewestByBootId) { .boot_id = f->newest_boot_id },
468 j->newest_by_boot_id, j->n_newest_by_boot_id, newest_by_boot_id_compare);
469 if (found) {
470 /* There's already a priority queue for this boot ID */
471
472 if (f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL) {
473 r = prioq_put(found->prioq, f, &f->newest_boot_id_prioq_idx); /* Insert if we aren't in there yet */
474 if (r < 0)
475 return r;
476 } else
477 prioq_reshuffle(found->prioq, f, &f->newest_boot_id_prioq_idx); /* Reshuffle otherwise */
478
479 } else {
480 _cleanup_(prioq_freep) Prioq *q = NULL;
481
482 /* No priority queue yet, then allocate one */
483
484 assert(f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL); /* we can't be a member either */
485
486 q = prioq_new(journal_file_newest_monotonic_compare);
487 if (!q)
488 return -ENOMEM;
489
490 r = prioq_put(q, f, &f->newest_boot_id_prioq_idx);
491 if (r < 0)
492 return r;
493
494 if (!GREEDY_REALLOC(j->newest_by_boot_id, j->n_newest_by_boot_id + 1)) {
495 f->newest_boot_id_prioq_idx = PRIOQ_IDX_NULL;
496 return -ENOMEM;
497 }
498
499 j->newest_by_boot_id[j->n_newest_by_boot_id++] = (NewestByBootId) {
500 .boot_id = f->newest_boot_id,
501 .prioq = TAKE_PTR(q),
502 };
503
504 typesafe_qsort(j->newest_by_boot_id, j->n_newest_by_boot_id, newest_by_boot_id_compare);
505 }
506
507 return 0;
508}
509
262299dc
LP
510static int journal_file_find_newest_for_boot_id(
511 sd_journal *j,
512 sd_id128_t id,
513 JournalFile **ret) {
514
515 JournalFile *prev = NULL;
516 int r;
517
518 assert(j);
519 assert(ret);
520
521 /* Before we use it, let's refresh the timestamp from the header, and reshuffle our prioq
522 * accordingly. We do this only a bunch of times, to not be caught in some update loop. */
523 for (unsigned n_tries = 0;; n_tries++) {
cb2b0326 524 NewestByBootId *found;
262299dc 525 JournalFile *f;
262299dc 526
cb2b0326
YW
527 found = typesafe_bsearch(&(NewestByBootId) { .boot_id = id },
528 j->newest_by_boot_id, j->n_newest_by_boot_id, newest_by_boot_id_compare);
529
530 f = found ? prioq_peek(found->prioq) : NULL;
531 if (!f)
262299dc
LP
532 return log_debug_errno(SYNTHETIC_ERRNO(ENODATA),
533 "Requested delta for boot ID %s, but we have no information about that boot ID.", SD_ID128_TO_STRING(id));
534
262299dc
LP
535 if (f == prev || n_tries >= 5) {
536 /* This was already the best answer in the previous run, or we tried too often, use it */
537 *ret = f;
538 return 0;
539 }
540
541 prev = f;
542
543 /* Let's read the journal file's current timestamp once, before we return it, maybe it has changed. */
544 r = journal_file_read_tail_timestamp(j, f);
545 if (r < 0)
546 return log_debug_errno(r, "Failed to read tail timestamp while trying to find newest journal file for boot ID %s.", SD_ID128_TO_STRING(id));
51b2bcf8
YW
547 if (r == 0) {
548 /* No new entry found. */
549 *ret = f;
550 return 0;
551 }
262299dc
LP
552
553 /* Refreshing the timestamp we read might have reshuffled the prioq, hence let's check the
4cbabc19 554 * prioq again and only use the information once we reached an equilibrium or hit a limit */
262299dc
LP
555 }
556}
557
558static int compare_boot_ids(sd_journal *j, sd_id128_t a, sd_id128_t b) {
559 JournalFile *x, *y;
560
561 assert(j);
562
563 /* Try to find the newest open journal file for the two boot ids */
564 if (journal_file_find_newest_for_boot_id(j, a, &x) < 0 ||
565 journal_file_find_newest_for_boot_id(j, b, &y) < 0)
566 return 0;
567
568 /* Only compare the boot id timestamps if they originate from the same machine. If they are from
569 * different machines, then we timestamps of the boot ids might be as off as the timestamps on the
570 * entries and hence not useful for comparing. */
571 if (!sd_id128_equal(x->newest_machine_id, y->newest_machine_id))
572 return 0;
573
574 return CMP(x->newest_realtime_usec, y->newest_realtime_usec);
575}
576
577static int compare_with_location(
578 sd_journal *j,
579 const JournalFile *f,
580 const Location *l,
581 const JournalFile *current_file) {
90c88092
YW
582 int r;
583
262299dc 584 assert(j);
487d3720 585 assert(f);
de190aef 586 assert(l);
487d3720 587 assert(f->location_type == LOCATION_SEEK);
4c701096 588 assert(IN_SET(l->type, LOCATION_DISCRETE, LOCATION_SEEK));
de190aef
LP
589
590 if (l->monotonic_set &&
487d3720 591 sd_id128_equal(f->current_boot_id, l->boot_id) &&
de190aef 592 l->realtime_set &&
487d3720 593 f->current_realtime == l->realtime &&
de190aef 594 l->xor_hash_set &&
b6849042 595 f->current_xor_hash == l->xor_hash &&
b17f651a 596 l->seqnum_set &&
597 sd_id128_equal(f->header->seqnum_id, l->seqnum_id) &&
598 f->current_seqnum == l->seqnum &&
b6849042 599 f != current_file)
de190aef
LP
600 return 0;
601
602 if (l->seqnum_set &&
487d3720 603 sd_id128_equal(f->header->seqnum_id, l->seqnum_id)) {
90c88092
YW
604 r = CMP(f->current_seqnum, l->seqnum);
605 if (r != 0)
606 return r;
de190aef
LP
607 }
608
262299dc
LP
609 if (l->monotonic_set) {
610 /* If both arguments have the same boot ID, then we can compare the monotonic timestamps. If
611 * they are distinct, then we might able to lookup the timestamps of those boot IDs (if they
612 * are from the same machine) and order by that. */
613 if (sd_id128_equal(f->current_boot_id, l->boot_id))
614 r = CMP(f->current_monotonic, l->monotonic);
615 else
616 r = compare_boot_ids(j, f->current_boot_id, l->boot_id);
90c88092
YW
617 if (r != 0)
618 return r;
de190aef
LP
619 }
620
621 if (l->realtime_set) {
90c88092
YW
622 r = CMP(f->current_realtime, l->realtime);
623 if (r != 0)
624 return r;
de190aef
LP
625 }
626
627 if (l->xor_hash_set) {
90c88092
YW
628 r = CMP(f->current_xor_hash, l->xor_hash);
629 if (r != 0)
630 return r;
de190aef
LP
631 }
632
633 return 0;
634}
635
cbdca852
LP
636static int next_for_match(
637 sd_journal *j,
638 Match *m,
639 JournalFile *f,
640 uint64_t after_offset,
641 direction_t direction,
642 Object **ret,
643 uint64_t *offset) {
644
de7b95cd 645 int r;
cbdca852 646 uint64_t np = 0;
de7b95cd
LP
647
648 assert(j);
cbdca852
LP
649 assert(m);
650 assert(f);
de7b95cd 651
cbdca852 652 if (m->type == MATCH_DISCRETE) {
ec50313d
DDM
653 Object *d;
654 uint64_t hash;
de190aef 655
4ce534f4
LP
656 /* If the keyed hash logic is used, we need to calculate the hash fresh per file. Otherwise
657 * we can use what we pre-calculated. */
658 if (JOURNAL_HEADER_KEYED_HASH(f->header))
659 hash = journal_file_hash_data(f, m->data, m->size);
660 else
661 hash = m->hash;
662
ec50313d 663 r = journal_file_find_data_object_with_hash(f, m->data, m->size, hash, &d, NULL);
de190aef
LP
664 if (r <= 0)
665 return r;
666
ec50313d 667 return journal_file_move_to_entry_by_offset_for_data(f, d, after_offset, direction, ret, offset);
de190aef 668
cbdca852 669 } else if (m->type == MATCH_OR_TERM) {
de7b95cd 670
cbdca852 671 /* Find the earliest match beyond after_offset */
de190aef 672
cbdca852
LP
673 LIST_FOREACH(matches, i, m->matches) {
674 uint64_t cp;
de190aef 675
cbdca852 676 r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
b4e5f920
LP
677 if (r < 0)
678 return r;
cbdca852 679 else if (r > 0) {
bc302926 680 if (np == 0 || (direction == DIRECTION_DOWN ? cp < np : cp > np))
cbdca852
LP
681 np = cp;
682 }
683 }
b4e5f920 684
bc302926
ZJS
685 if (np == 0)
686 return 0;
687
cbdca852 688 } else if (m->type == MATCH_AND_TERM) {
03677889 689 Match *last_moved;
de190aef 690
cbdca852 691 /* Always jump to the next matching entry and repeat
2bc8ca0c 692 * this until we find an offset that matches for all
cbdca852 693 * matches. */
de190aef 694
cbdca852
LP
695 if (!m->matches)
696 return 0;
de7b95cd 697
2bc8ca0c
ZJS
698 r = next_for_match(j, m->matches, f, after_offset, direction, NULL, &np);
699 if (r <= 0)
700 return r;
de190aef 701
2bc8ca0c
ZJS
702 assert(direction == DIRECTION_DOWN ? np >= after_offset : np <= after_offset);
703 last_moved = m->matches;
de190aef 704
2bc8ca0c
ZJS
705 LIST_LOOP_BUT_ONE(matches, i, m->matches, last_moved) {
706 uint64_t cp;
de190aef 707
2bc8ca0c
ZJS
708 r = next_for_match(j, i, f, np, direction, NULL, &cp);
709 if (r <= 0)
710 return r;
de190aef 711
2bc8ca0c
ZJS
712 assert(direction == DIRECTION_DOWN ? cp >= np : cp <= np);
713 if (direction == DIRECTION_DOWN ? cp > np : cp < np) {
714 np = cp;
715 last_moved = i;
de190aef 716 }
2bc8ca0c 717 }
cbdca852 718 }
de190aef 719
bc302926 720 assert(np > 0);
de190aef 721
4d8b09e4
DDM
722 if (ret) {
723 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, ret);
724 if (r < 0)
725 return r;
726 }
de7b95cd 727
de190aef 728 if (offset)
cbdca852 729 *offset = np;
de190aef
LP
730
731 return 1;
732}
733
cbdca852
LP
734static int find_location_for_match(
735 sd_journal *j,
736 Match *m,
737 JournalFile *f,
738 direction_t direction,
739 Object **ret,
740 uint64_t *offset) {
741
de190aef 742 int r;
de190aef
LP
743
744 assert(j);
cbdca852 745 assert(m);
de190aef 746 assert(f);
de190aef 747
cbdca852 748 if (m->type == MATCH_DISCRETE) {
ec50313d 749 Object *d;
4ce534f4
LP
750 uint64_t dp, hash;
751
752 if (JOURNAL_HEADER_KEYED_HASH(f->header))
753 hash = journal_file_hash_data(f, m->data, m->size);
754 else
755 hash = m->hash;
de190aef 756
ec50313d 757 r = journal_file_find_data_object_with_hash(f, m->data, m->size, hash, &d, &dp);
de7b95cd
LP
758 if (r <= 0)
759 return r;
760
cbdca852 761 /* FIXME: missing: find by monotonic */
de7b95cd 762
cbdca852 763 if (j->current_location.type == LOCATION_HEAD)
d37eeabc 764 return direction == DIRECTION_DOWN ? journal_file_move_to_entry_for_data(f, d, DIRECTION_DOWN, ret, offset) : 0;
cbdca852 765 if (j->current_location.type == LOCATION_TAIL)
d37eeabc 766 return direction == DIRECTION_UP ? journal_file_move_to_entry_for_data(f, d, DIRECTION_UP, ret, offset) : 0;
cbdca852 767 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
ec50313d 768 return journal_file_move_to_entry_by_seqnum_for_data(f, d, j->current_location.seqnum, direction, ret, offset);
cbdca852 769 if (j->current_location.monotonic_set) {
ec50313d 770 r = journal_file_move_to_entry_by_monotonic_for_data(f, d, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
304cb08f 771 if (r != 0)
cbdca852 772 return r;
ec50313d
DDM
773
774 /* The data object might have been invalidated. */
775 r = journal_file_move_to_object(f, OBJECT_DATA, dp, &d);
776 if (r < 0)
777 return r;
cbdca852
LP
778 }
779 if (j->current_location.realtime_set)
ec50313d 780 return journal_file_move_to_entry_by_realtime_for_data(f, d, j->current_location.realtime, direction, ret, offset);
de190aef 781
d37eeabc 782 return journal_file_move_to_entry_for_data(f, d, direction, ret, offset);
de7b95cd 783
cbdca852
LP
784 } else if (m->type == MATCH_OR_TERM) {
785 uint64_t np = 0;
de7b95cd 786
cbdca852 787 /* Find the earliest match */
de7b95cd 788
cbdca852
LP
789 LIST_FOREACH(matches, i, m->matches) {
790 uint64_t cp;
791
792 r = find_location_for_match(j, i, f, direction, NULL, &cp);
793 if (r < 0)
794 return r;
795 else if (r > 0) {
796 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
797 np = cp;
de190aef 798 }
cbdca852 799 }
de190aef 800
cbdca852
LP
801 if (np == 0)
802 return 0;
de7b95cd 803
4d8b09e4
DDM
804 if (ret) {
805 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, ret);
806 if (r < 0)
807 return r;
808 }
de7b95cd 809
cbdca852
LP
810 if (offset)
811 *offset = np;
de190aef 812
cbdca852 813 return 1;
e892bd17 814
cbdca852 815 } else {
cbdca852
LP
816 uint64_t np = 0;
817
818 assert(m->type == MATCH_AND_TERM);
819
820 /* First jump to the last match, and then find the
821 * next one where all matches match */
822
823 if (!m->matches)
824 return 0;
825
826 LIST_FOREACH(matches, i, m->matches) {
827 uint64_t cp;
828
829 r = find_location_for_match(j, i, f, direction, NULL, &cp);
830 if (r <= 0)
4b067dc9
LP
831 return r;
832
bc302926 833 if (np == 0 || (direction == DIRECTION_DOWN ? cp > np : cp < np))
cbdca852 834 np = cp;
de7b95cd
LP
835 }
836
cbdca852
LP
837 return next_for_match(j, m, f, np, direction, ret, offset);
838 }
839}
de190aef 840
cbdca852
LP
841static int find_location_with_matches(
842 sd_journal *j,
843 JournalFile *f,
844 direction_t direction,
845 Object **ret,
846 uint64_t *offset) {
847
848 int r;
849
850 assert(j);
851 assert(f);
852 assert(ret);
853 assert(offset);
854
855 if (!j->level0) {
856 /* No matches is simple */
857
858 if (j->current_location.type == LOCATION_HEAD)
7a4ee861 859 return direction == DIRECTION_DOWN ? journal_file_next_entry(f, 0, DIRECTION_DOWN, ret, offset) : 0;
cbdca852 860 if (j->current_location.type == LOCATION_TAIL)
7a4ee861 861 return direction == DIRECTION_UP ? journal_file_next_entry(f, 0, DIRECTION_UP, ret, offset) : 0;
cbdca852
LP
862 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
863 return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
864 if (j->current_location.monotonic_set) {
865 r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
304cb08f 866 if (r != 0)
cbdca852 867 return r;
de7b95cd 868 }
cbdca852
LP
869 if (j->current_location.realtime_set)
870 return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
de7b95cd 871
f534928a 872 return journal_file_next_entry(f, 0, direction, ret, offset);
cbdca852
LP
873 } else
874 return find_location_for_match(j, j->level0, f, direction, ret, offset);
875}
de7b95cd 876
cbdca852
LP
877static int next_with_matches(
878 sd_journal *j,
879 JournalFile *f,
880 direction_t direction,
881 Object **ret,
882 uint64_t *offset) {
883
cbdca852
LP
884 assert(j);
885 assert(f);
886 assert(ret);
887 assert(offset);
888
cbdca852
LP
889 /* No matches is easy. We simple advance the file
890 * pointer by one. */
891 if (!j->level0)
b29ddfcb 892 return journal_file_next_entry(f, f->current_offset, direction, ret, offset);
cbdca852
LP
893
894 /* If we have a match then we look for the next matching entry
49f43d5f 895 * with an offset at least one step larger */
b29ddfcb
MS
896 return next_for_match(j, j->level0, f,
897 direction == DIRECTION_DOWN ? f->current_offset + 1
898 : f->current_offset - 1,
899 direction, ret, offset);
de7b95cd
LP
900}
901
58439db4 902static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction) {
de190aef 903 Object *c;
6e693b42 904 uint64_t cp, n_entries;
cbdca852 905 int r;
de190aef
LP
906
907 assert(j);
908 assert(f);
909
34af7494
LP
910 (void) journal_file_read_tail_timestamp(j, f);
911
950c07d4
MS
912 n_entries = le64toh(f->header->n_entries);
913
914 /* If we hit EOF before, we don't need to look into this file again
915 * unless direction changed or new entries appeared. */
a9414960
YW
916 if (f->last_direction == direction &&
917 f->location_type == (direction == DIRECTION_DOWN ? LOCATION_TAIL : LOCATION_HEAD) &&
950c07d4
MS
918 n_entries == f->last_n_entries)
919 return 0;
668c965a 920
950c07d4
MS
921 f->last_n_entries = n_entries;
922
923 if (f->last_direction == direction && f->current_offset > 0) {
7943f422
MS
924 /* LOCATION_SEEK here means we did the work in a previous
925 * iteration and the current location already points to a
926 * candidate entry. */
487d3720
MS
927 if (f->location_type != LOCATION_SEEK) {
928 r = next_with_matches(j, f, direction, &c, &cp);
929 if (r <= 0)
930 return r;
466ccd92 931
950c07d4 932 journal_file_save_location(f, c, cp);
487d3720 933 }
de190aef 934 } else {
950c07d4
MS
935 f->last_direction = direction;
936
cbdca852 937 r = find_location_with_matches(j, f, direction, &c, &cp);
de190aef
LP
938 if (r <= 0)
939 return r;
487d3720 940
950c07d4 941 journal_file_save_location(f, c, cp);
de190aef
LP
942 }
943
bc302926 944 /* OK, we found the spot, now let's advance until an entry
cbdca852
LP
945 * that is actually different from what we were previously
946 * looking at. This is necessary to handle entries which exist
947 * in two (or more) journal files, and which shall all be
948 * suppressed but one. */
949
de190aef
LP
950 for (;;) {
951 bool found;
952
953 if (j->current_location.type == LOCATION_DISCRETE) {
954 int k;
955
262299dc 956 k = compare_with_location(j, f, &j->current_location, j->current_file);
1cdf7175
CH
957
958 found = direction == DIRECTION_DOWN ? k > 0 : k < 0;
de190aef
LP
959 } else
960 found = true;
961
487d3720 962 if (found)
de190aef 963 return 1;
de190aef
LP
964
965 r = next_with_matches(j, f, direction, &c, &cp);
966 if (r <= 0)
967 return r;
487d3720 968
950c07d4 969 journal_file_save_location(f, c, cp);
de190aef
LP
970 }
971}
972
262299dc 973static int compare_locations(sd_journal *j, JournalFile *af, JournalFile *bf) {
01e4f03f
LP
974 int r;
975
262299dc 976 assert(j);
01e4f03f
LP
977 assert(af);
978 assert(af->header);
979 assert(bf);
980 assert(bf->header);
981 assert(af->location_type == LOCATION_SEEK);
982 assert(bf->location_type == LOCATION_SEEK);
983
984 /* If contents, timestamps and seqnum match, these entries are identical. */
985 if (sd_id128_equal(af->current_boot_id, bf->current_boot_id) &&
986 af->current_monotonic == bf->current_monotonic &&
987 af->current_realtime == bf->current_realtime &&
988 af->current_xor_hash == bf->current_xor_hash &&
989 sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id) &&
990 af->current_seqnum == bf->current_seqnum)
991 return 0;
992
993 if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
994 /* If this is from the same seqnum source, compare seqnums */
995 r = CMP(af->current_seqnum, bf->current_seqnum);
996 if (r != 0)
997 return r;
998
999 /* Wow! This is weird, different data but the same seqnums? Something is borked, but let's
1000 * make the best of it and compare by time. */
1001 }
1002
262299dc 1003 if (sd_id128_equal(af->current_boot_id, bf->current_boot_id))
01e4f03f
LP
1004 /* If the boot id matches, compare monotonic time */
1005 r = CMP(af->current_monotonic, bf->current_monotonic);
262299dc
LP
1006 else
1007 /* If they don't match try to compare boot IDs */
1008 r = compare_boot_ids(j, af->current_boot_id, bf->current_boot_id);
1009 if (r != 0)
1010 return r;
01e4f03f
LP
1011
1012 /* Otherwise, compare UTC time */
1013 r = CMP(af->current_realtime, bf->current_realtime);
1014 if (r != 0)
1015 return r;
1016
1017 /* Finally, compare by contents */
1018 return CMP(af->current_xor_hash, bf->current_xor_hash);
1019}
1020
e892bd17 1021static int real_journal_next(sd_journal *j, direction_t direction) {
5d4ba7f2 1022 JournalFile *new_file = NULL;
abcdc02c 1023 unsigned n_files;
5d4ba7f2 1024 const void **files;
a002d44b 1025 Object *o;
87d2c1ff
LP
1026 int r;
1027
1ae464e0 1028 assert_return(j, -EINVAL);
e046719b 1029 assert_return(!journal_origin_changed(j), -ECHILD);
87d2c1ff 1030
5d4ba7f2
VC
1031 r = iterated_cache_get(j->files_cache, NULL, &files, &n_files);
1032 if (r < 0)
1033 return r;
1034
474536c9
YW
1035 FOREACH_ARRAY(_f, files, n_files) {
1036 JournalFile *f = (JournalFile*) *_f;
de190aef 1037 bool found;
87d2c1ff 1038
58439db4 1039 r = next_beyond_location(j, f, direction);
e590af26 1040 if (r < 0) {
da927ba9 1041 log_debug_errno(r, "Can't iterate through %s, ignoring: %m", f->path);
a9a245c1 1042 remove_file_real(j, f);
e590af26 1043 continue;
6573ef05 1044 } else if (r == 0) {
a9414960 1045 f->location_type = direction == DIRECTION_DOWN ? LOCATION_TAIL : LOCATION_HEAD;
cec736d2 1046 continue;
6573ef05 1047 }
87d2c1ff 1048
468b21de 1049 if (!new_file)
de190aef
LP
1050 found = true;
1051 else {
1052 int k;
1053
262299dc 1054 k = compare_locations(j, f, new_file);
de190aef 1055
bc302926 1056 found = direction == DIRECTION_DOWN ? k < 0 : k > 0;
de190aef
LP
1057 }
1058
e499c999 1059 if (found)
468b21de 1060 new_file = f;
87d2c1ff
LP
1061 }
1062
468b21de 1063 if (!new_file)
de190aef 1064 return 0;
ae2cc8ef 1065
e499c999 1066 r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_file->current_offset, &o);
468b21de
LP
1067 if (r < 0)
1068 return r;
1069
1eb6332d 1070 set_location(j, new_file, o);
ae2cc8ef 1071
de190aef
LP
1072 return 1;
1073}
ae2cc8ef 1074
a5344d2c 1075_public_ int sd_journal_next(sd_journal *j) {
de190aef
LP
1076 return real_journal_next(j, DIRECTION_DOWN);
1077}
ae2cc8ef 1078
a5344d2c 1079_public_ int sd_journal_previous(sd_journal *j) {
de190aef
LP
1080 return real_journal_next(j, DIRECTION_UP);
1081}
ae2cc8ef 1082
b78f9481
YW
1083_public_ int sd_journal_step_one(sd_journal *j, int advanced) {
1084 assert_return(j, -EINVAL);
1085
1086 if (j->current_location.type == LOCATION_HEAD)
1087 return sd_journal_next(j);
1088 if (j->current_location.type == LOCATION_TAIL)
1089 return sd_journal_previous(j);
1090 return real_journal_next(j, advanced ? DIRECTION_DOWN : DIRECTION_UP);
1091}
1092
6f003b43 1093static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
de190aef 1094 int c = 0, r;
ae2cc8ef 1095
1ae464e0 1096 assert_return(j, -EINVAL);
e046719b 1097 assert_return(!journal_origin_changed(j), -ECHILD);
41d544a1 1098 assert_return(skip <= INT_MAX, -ERANGE);
de190aef 1099
6f003b43
LP
1100 if (skip == 0) {
1101 /* If this is not a discrete skip, then at least
1102 * resolve the current location */
04884354
LK
1103 if (j->current_location.type != LOCATION_DISCRETE) {
1104 r = real_journal_next(j, direction);
1105 if (r < 0)
1106 return r;
1107 }
6f003b43
LP
1108
1109 return 0;
1110 }
1111
1112 do {
1113 r = real_journal_next(j, direction);
de190aef
LP
1114 if (r < 0)
1115 return r;
1116
1117 if (r == 0)
1118 return c;
1119
1120 skip--;
1121 c++;
6f003b43 1122 } while (skip > 0);
87d2c1ff 1123
de190aef 1124 return c;
87d2c1ff
LP
1125}
1126
6f003b43
LP
1127_public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
1128 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
1129}
de190aef 1130
6f003b43
LP
1131_public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
1132 return real_journal_next_skip(j, DIRECTION_UP, skip);
87d2c1ff
LP
1133}
1134
a5344d2c 1135_public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
cec736d2 1136 Object *o;
87d2c1ff
LP
1137 int r;
1138
1ae464e0 1139 assert_return(j, -EINVAL);
e046719b 1140 assert_return(!journal_origin_changed(j), -ECHILD);
1ae464e0 1141 assert_return(cursor, -EINVAL);
87d2c1ff 1142
3fbf9cbb
LP
1143 if (!j->current_file || j->current_file->current_offset <= 0)
1144 return -EADDRNOTAVAIL;
87d2c1ff 1145
de190aef 1146 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
87d2c1ff
LP
1147 if (r < 0)
1148 return r;
1149
3fbf9cbb 1150 if (asprintf(cursor,
507f22bd 1151 "s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64,
85b55869
LP
1152 SD_ID128_TO_STRING(j->current_file->header->seqnum_id), le64toh(o->entry.seqnum),
1153 SD_ID128_TO_STRING(o->entry.boot_id), le64toh(o->entry.monotonic),
507f22bd
ZJS
1154 le64toh(o->entry.realtime),
1155 le64toh(o->entry.xor_hash)) < 0)
3fbf9cbb 1156 return -ENOMEM;
87d2c1ff 1157
6f47ad30 1158 return 0;
87d2c1ff
LP
1159}
1160
a5344d2c 1161_public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
de190aef 1162 unsigned long long seqnum, monotonic, realtime, xor_hash;
da277e90
ZJS
1163 bool seqnum_id_set = false,
1164 seqnum_set = false,
1165 boot_id_set = false,
1166 monotonic_set = false,
1167 realtime_set = false,
1168 xor_hash_set = false;
de190aef 1169 sd_id128_t seqnum_id, boot_id;
da277e90 1170 int r;
de190aef 1171
1ae464e0 1172 assert_return(j, -EINVAL);
e046719b 1173 assert_return(!journal_origin_changed(j), -ECHILD);
1ae464e0 1174 assert_return(!isempty(cursor), -EINVAL);
de190aef 1175
da277e90
ZJS
1176 for (const char *p = cursor;;) {
1177 _cleanup_free_ char *word = NULL;
de190aef 1178
da277e90
ZJS
1179 r = extract_first_word(&p, &word, ";", EXTRACT_DONT_COALESCE_SEPARATORS);
1180 if (r < 0)
1181 return r;
1182 if (r == 0)
1183 break;
de190aef 1184
da277e90
ZJS
1185 if (word[0] == '\0' || word[1] != '=')
1186 return -EINVAL;
de190aef 1187
a2a5291b 1188 switch (word[0]) {
de190aef
LP
1189 case 's':
1190 seqnum_id_set = true;
da277e90
ZJS
1191 r = sd_id128_from_string(word + 2, &seqnum_id);
1192 if (r < 0)
1193 return r;
de190aef
LP
1194 break;
1195
1196 case 'i':
1197 seqnum_set = true;
da277e90
ZJS
1198 if (sscanf(word + 2, "%llx", &seqnum) != 1)
1199 return -EINVAL;
de190aef
LP
1200 break;
1201
1202 case 'b':
1203 boot_id_set = true;
da277e90 1204 r = sd_id128_from_string(word + 2, &boot_id);
c9f5ac09
SS
1205 if (r < 0)
1206 return r;
de190aef
LP
1207 break;
1208
1209 case 'm':
1210 monotonic_set = true;
da277e90
ZJS
1211 if (sscanf(word + 2, "%llx", &monotonic) != 1)
1212 return -EINVAL;
de190aef
LP
1213 break;
1214
1215 case 't':
1216 realtime_set = true;
da277e90
ZJS
1217 if (sscanf(word + 2, "%llx", &realtime) != 1)
1218 return -EINVAL;
de190aef
LP
1219 break;
1220
1221 case 'x':
1222 xor_hash_set = true;
da277e90
ZJS
1223 if (sscanf(word + 2, "%llx", &xor_hash) != 1)
1224 return -EINVAL;
de190aef
LP
1225 break;
1226 }
de190aef
LP
1227 }
1228
1229 if ((!seqnum_set || !seqnum_id_set) &&
1230 (!monotonic_set || !boot_id_set) &&
1231 !realtime_set)
1232 return -EINVAL;
1233
bba6e4ae
LP
1234 detach_location(j);
1235 j->current_location = (Location) {
1236 .type = LOCATION_SEEK,
1237 };
de190aef
LP
1238
1239 if (realtime_set) {
1240 j->current_location.realtime = (uint64_t) realtime;
1241 j->current_location.realtime_set = true;
1242 }
1243
1244 if (seqnum_set && seqnum_id_set) {
1245 j->current_location.seqnum = (uint64_t) seqnum;
1246 j->current_location.seqnum_id = seqnum_id;
1247 j->current_location.seqnum_set = true;
1248 }
1249
1250 if (monotonic_set && boot_id_set) {
1251 j->current_location.monotonic = (uint64_t) monotonic;
1252 j->current_location.boot_id = boot_id;
1253 j->current_location.monotonic_set = true;
1254 }
1255
1256 if (xor_hash_set) {
1257 j->current_location.xor_hash = (uint64_t) xor_hash;
1258 j->current_location.xor_hash_set = true;
1259 }
1260
1261 return 0;
1262}
1263
c6511e85
LP
1264_public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
1265 int r;
c6511e85
LP
1266 Object *o;
1267
1ae464e0 1268 assert_return(j, -EINVAL);
e046719b 1269 assert_return(!journal_origin_changed(j), -ECHILD);
1ae464e0 1270 assert_return(!isempty(cursor), -EINVAL);
c6511e85
LP
1271
1272 if (!j->current_file || j->current_file->current_offset <= 0)
1273 return -EADDRNOTAVAIL;
1274
1275 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1276 if (r < 0)
1277 return r;
1278
9ed794a3 1279 for (;;) {
c6511e85 1280 _cleanup_free_ char *item = NULL;
c6511e85 1281 unsigned long long ll;
7294bb5b 1282 sd_id128_t id;
c6511e85
LP
1283 int k = 0;
1284
7294bb5b
SS
1285 r = extract_first_word(&cursor, &item, ";", EXTRACT_DONT_COALESCE_SEPARATORS);
1286 if (r < 0)
1287 return r;
c6511e85 1288
7294bb5b
SS
1289 if (r == 0)
1290 break;
c6511e85 1291
7294bb5b
SS
1292 if (strlen(item) < 2 || item[1] != '=')
1293 return -EINVAL;
1294
1295 switch (item[0]) {
c6511e85
LP
1296
1297 case 's':
1298 k = sd_id128_from_string(item+2, &id);
1299 if (k < 0)
1300 return k;
1301 if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
1302 return 0;
1303 break;
1304
1305 case 'i':
1306 if (sscanf(item+2, "%llx", &ll) != 1)
1307 return -EINVAL;
1308 if (ll != le64toh(o->entry.seqnum))
1309 return 0;
1310 break;
1311
1312 case 'b':
1313 k = sd_id128_from_string(item+2, &id);
1314 if (k < 0)
1315 return k;
1316 if (!sd_id128_equal(id, o->entry.boot_id))
1317 return 0;
1318 break;
1319
1320 case 'm':
1321 if (sscanf(item+2, "%llx", &ll) != 1)
1322 return -EINVAL;
1323 if (ll != le64toh(o->entry.monotonic))
1324 return 0;
1325 break;
1326
1327 case 't':
1328 if (sscanf(item+2, "%llx", &ll) != 1)
1329 return -EINVAL;
1330 if (ll != le64toh(o->entry.realtime))
1331 return 0;
1332 break;
1333
1334 case 'x':
1335 if (sscanf(item+2, "%llx", &ll) != 1)
1336 return -EINVAL;
1337 if (ll != le64toh(o->entry.xor_hash))
1338 return 0;
1339 break;
1340 }
1341 }
1342
1343 return 1;
1344}
1345
a5344d2c 1346_public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1ae464e0 1347 assert_return(j, -EINVAL);
e046719b 1348 assert_return(!journal_origin_changed(j), -ECHILD);
de190aef 1349
bba6e4ae
LP
1350 detach_location(j);
1351
1352 j->current_location = (Location) {
1353 .type = LOCATION_SEEK,
1354 .boot_id = boot_id,
1355 .monotonic = usec,
1356 .monotonic_set = true,
1357 };
de190aef
LP
1358
1359 return 0;
1360}
1361
a5344d2c 1362_public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1ae464e0 1363 assert_return(j, -EINVAL);
e046719b 1364 assert_return(!journal_origin_changed(j), -ECHILD);
de190aef 1365
bba6e4ae
LP
1366 detach_location(j);
1367
1368 j->current_location = (Location) {
1369 .type = LOCATION_SEEK,
1370 .realtime = usec,
1371 .realtime_set = true,
1372 };
de190aef
LP
1373
1374 return 0;
1375}
1376
a5344d2c 1377_public_ int sd_journal_seek_head(sd_journal *j) {
1ae464e0 1378 assert_return(j, -EINVAL);
e046719b 1379 assert_return(!journal_origin_changed(j), -ECHILD);
de190aef 1380
bba6e4ae
LP
1381 detach_location(j);
1382
1383 j->current_location = (Location) {
1384 .type = LOCATION_HEAD,
1385 };
de190aef
LP
1386
1387 return 0;
1388}
1389
a5344d2c 1390_public_ int sd_journal_seek_tail(sd_journal *j) {
1ae464e0 1391 assert_return(j, -EINVAL);
e046719b 1392 assert_return(!journal_origin_changed(j), -ECHILD);
de190aef 1393
bba6e4ae
LP
1394 detach_location(j);
1395
1396 j->current_location = (Location) {
1397 .type = LOCATION_TAIL,
1398 };
de190aef
LP
1399
1400 return 0;
87d2c1ff
LP
1401}
1402
85210bff 1403static void check_network(sd_journal *j, int fd) {
85210bff
LP
1404 assert(j);
1405
1406 if (j->on_network)
1407 return;
1408
77f9fa3b 1409 j->on_network = fd_is_network_fs(fd);
85210bff
LP
1410}
1411
a688baa8
ZJS
1412static bool file_has_type_prefix(const char *prefix, const char *filename) {
1413 const char *full, *tilded, *atted;
1414
63c372cb
LP
1415 full = strjoina(prefix, ".journal");
1416 tilded = strjoina(full, "~");
1417 atted = strjoina(prefix, "@");
a688baa8 1418
0cbd293e 1419 return STR_IN_SET(filename, full, tilded) ||
a688baa8
ZJS
1420 startswith(filename, atted);
1421}
1422
1423static bool file_type_wanted(int flags, const char *filename) {
d617408e
LP
1424 assert(filename);
1425
97c621b7 1426 if (!ENDSWITH_SET(filename, ".journal", ".journal~"))
a688baa8
ZJS
1427 return false;
1428
1429 /* no flags set → every type is OK */
1430 if (!(flags & (SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)))
1431 return true;
1432
97c621b7 1433 if (FLAGS_SET(flags, SD_JOURNAL_CURRENT_USER)) {
a688baa8
ZJS
1434 char prefix[5 + DECIMAL_STR_MAX(uid_t) + 1];
1435
97c621b7 1436 xsprintf(prefix, "user-" UID_FMT, getuid());
a688baa8
ZJS
1437
1438 if (file_has_type_prefix(prefix, filename))
1439 return true;
97c621b7
LP
1440
1441 /* If SD_JOURNAL_CURRENT_USER is specified and we are invoked under a system UID, then
1442 * automatically enable SD_JOURNAL_SYSTEM too, because journald will actually put system user
1443 * data into the system journal. */
1444
1445 if (uid_for_system_journal(getuid()))
1446 flags |= SD_JOURNAL_SYSTEM;
a688baa8
ZJS
1447 }
1448
97c621b7
LP
1449 if (FLAGS_SET(flags, SD_JOURNAL_SYSTEM) && file_has_type_prefix("system", filename))
1450 return true;
1451
a688baa8
ZJS
1452 return false;
1453}
1454
5d1ce257
LP
1455static bool path_has_prefix(sd_journal *j, const char *path, const char *prefix) {
1456 assert(j);
1457 assert(path);
1458 assert(prefix);
1459
1460 if (j->toplevel_fd >= 0)
1461 return false;
1462
1463 return path_startswith(path, prefix);
1464}
1465
9c66f528
LP
1466static void track_file_disposition(sd_journal *j, JournalFile *f) {
1467 assert(j);
1468 assert(f);
1469
1470 if (!j->has_runtime_files && path_has_prefix(j, f->path, "/run"))
1471 j->has_runtime_files = true;
1472 else if (!j->has_persistent_files && path_has_prefix(j, f->path, "/var"))
1473 j->has_persistent_files = true;
1474}
1475
9c66f528
LP
1476static int add_any_file(
1477 sd_journal *j,
1478 int fd,
1479 const char *path) {
1480
254d1313 1481 _cleanup_close_ int our_fd = -EBADF;
9c66f528
LP
1482 JournalFile *f;
1483 struct stat st;
70f50a47 1484 int r;
3fbf9cbb
LP
1485
1486 assert(j);
5d1ce257 1487 assert(fd >= 0 || path);
3fbf9cbb 1488
9c66f528 1489 if (fd < 0) {
85b0ff8a 1490 assert(path); /* For gcc. */
9c66f528
LP
1491 if (j->toplevel_fd >= 0)
1492 /* If there's a top-level fd defined make the path relative, explicitly, since otherwise
1493 * openat() ignores the first argument. */
1494
baaca3db 1495 fd = our_fd = openat(j->toplevel_fd, skip_leading_slash(path), O_RDONLY|O_CLOEXEC|O_NONBLOCK);
9c66f528 1496 else
70f50a47 1497 fd = our_fd = open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
9c66f528
LP
1498 if (fd < 0) {
1499 r = log_debug_errno(errno, "Failed to open journal file %s: %m", path);
70f50a47 1500 goto error;
9c66f528
LP
1501 }
1502
9c66f528
LP
1503 r = fd_nonblock(fd, false);
1504 if (r < 0) {
1505 r = log_debug_errno(errno, "Failed to turn off O_NONBLOCK for %s: %m", path);
70f50a47 1506 goto error;
858749f7
LP
1507 }
1508 }
50f20cfd 1509
9c66f528 1510 if (fstat(fd, &st) < 0) {
85b0ff8a 1511 r = log_debug_errno(errno, "Failed to fstat %s: %m", path ?: "fd");
70f50a47 1512 goto error;
9c66f528 1513 }
3cc44114
LP
1514
1515 r = stat_verify_regular(&st);
1516 if (r < 0) {
85b0ff8a 1517 log_debug_errno(r, "Refusing to open %s: %m", path ?: "fd");
70f50a47 1518 goto error;
9e8abdf0
LP
1519 }
1520
85b0ff8a
ZJS
1521 if (path) {
1522 f = ordered_hashmap_get(j->files, path);
1523 if (f) {
1524 if (stat_inode_same(&f->last_stat, &st)) {
1525 /* We already track this file, under the same path and with the same
1526 * device/inode numbers, it's hence really the same. Mark this file as seen
1527 * in this generation. This is used to GC old files in process_q_overflow()
1528 * to detect journal files that are still there and discern them from those
1529 * which are gone. */
1530
1531 f->last_seen_generation = j->generation;
34af7494 1532 (void) journal_file_read_tail_timestamp(j, f);
70f50a47 1533 return 0;
85b0ff8a 1534 }
5d1ce257 1535
85b0ff8a
ZJS
1536 /* So we tracked a file under this name, but it has a different inode/device. In that
1537 * case, it got replaced (probably due to rotation?), let's drop it hence from our
1538 * list. */
1539 remove_file_real(j, f);
1540 f = NULL;
5d1ce257 1541 }
9c66f528
LP
1542 }
1543
1544 if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
70f50a47
ZJS
1545 r = log_debug_errno(SYNTHETIC_ERRNO(ETOOMANYREFS),
1546 "Too many open journal files, not adding %s.", path ?: "fd");
1547 goto error;
5d1ce257
LP
1548 }
1549
49615dbd 1550 r = journal_file_open(fd, path, O_RDONLY, 0, 0, 0, NULL, j->mmap, NULL, &f);
d617408e 1551 if (r < 0) {
85b0ff8a 1552 log_debug_errno(r, "Failed to open journal file %s: %m", path ?: "from fd");
70f50a47 1553 goto error;
d617408e 1554 }
3fbf9cbb 1555
72f59706 1556 /* journal_file_dump(f); */
de190aef 1557
85b0ff8a 1558 /* journal_file_open() generates an replacement fname if necessary, so we can use f->path. */
c1f906bd 1559 r = ordered_hashmap_put(j->files, f->path, f);
3fbf9cbb 1560 if (r < 0) {
85b0ff8a
ZJS
1561 f->close_fd = false; /* Make sure journal_file_close() doesn't close the caller's fd
1562 * (or our own). The caller or we will do that ourselves. */
69a3a6fd 1563 (void) journal_file_close(f);
70f50a47 1564 goto error;
3fbf9cbb
LP
1565 }
1566
70f50a47 1567 TAKE_FD(our_fd); /* the fd is now owned by the JournalFile object */
5d1ce257 1568
9c66f528 1569 f->last_seen_generation = j->generation;
a50d7d43 1570
9c66f528 1571 track_file_disposition(j, f);
85210bff 1572 check_network(j, f->fd);
34af7494 1573 (void) journal_file_read_tail_timestamp(j, f);
85210bff 1574
313cefa1 1575 j->current_invalidate_counter++;
a963990f 1576
9c66f528 1577 log_debug("File %s added.", f->path);
d617408e 1578
70f50a47 1579 return 0;
d617408e 1580
70f50a47
ZJS
1581error:
1582 (void) journal_put_error(j, r, path); /* path==NULL is OK. */
d617408e 1583 return r;
50f20cfd
LP
1584}
1585
1b7aa998
YW
1586int journal_get_directories(sd_journal *j, char ***ret) {
1587 _cleanup_strv_free_ char **paths = NULL;
1588 JournalFile *f;
1589 const char *p;
1590 size_t n = SIZE_MAX;
1591 int r;
1592
1593 assert(j);
1594 assert(ret);
1595
1596 /* This returns parent directories of opened journal files. */
1597
1598 ORDERED_HASHMAP_FOREACH_KEY(f, p, j->files) {
1599 _cleanup_free_ char *d = NULL;
1600
1601 /* Ignore paths generated from fd. */
1602 if (path_startswith(p, "/proc/"))
1603 continue;
1604
1605 r = path_extract_directory(p, &d);
1606 if (r < 0)
1607 return r;
1608
1609 if (path_strv_contains(paths, d))
1610 continue;
1611
1612 r = strv_extend_with_size(&paths, &n, d);
1613 if (r < 0)
1614 return r;
1615 }
1616
1617 *ret = TAKE_PTR(paths);
1618 return 0;
1619}
1620
fc1813c0
LP
1621static int add_file_by_name(
1622 sd_journal *j,
1623 const char *prefix,
1624 const char *filename) {
1625
8e7e4a73 1626 _cleanup_free_ char *path = NULL;
5302ebe1
ZJS
1627
1628 assert(j);
1629 assert(prefix);
1630 assert(filename);
1631
5d1ce257 1632 if (j->no_new_files)
5302ebe1
ZJS
1633 return 0;
1634
5d1ce257
LP
1635 if (!file_type_wanted(j->flags, filename))
1636 return 0;
39fd5b08 1637
8e7e4a73
LP
1638 path = path_join(prefix, filename);
1639 if (!path)
1640 return -ENOMEM;
1641
5d1ce257 1642 return add_any_file(j, -1, path);
5302ebe1
ZJS
1643}
1644
8e7e4a73 1645static int remove_file_by_name(
fc1813c0
LP
1646 sd_journal *j,
1647 const char *prefix,
1648 const char *filename) {
1649
8e7e4a73 1650 _cleanup_free_ char *path = NULL;
50f20cfd
LP
1651 JournalFile *f;
1652
1653 assert(j);
1654 assert(prefix);
1655 assert(filename);
1656
8e7e4a73
LP
1657 path = path_join(prefix, filename);
1658 if (!path)
1659 return -ENOMEM;
1660
c1f906bd 1661 f = ordered_hashmap_get(j->files, path);
50f20cfd 1662 if (!f)
8e7e4a73 1663 return 0;
50f20cfd 1664
a9a245c1 1665 remove_file_real(j, f);
8e7e4a73 1666 return 1;
a9a245c1
ZJS
1667}
1668
1669static void remove_file_real(sd_journal *j, JournalFile *f) {
1670 assert(j);
1671 assert(f);
1672
fc1813c0 1673 (void) ordered_hashmap_remove(j->files, f->path);
44a5fa34 1674
5ec76417 1675 log_debug("File %s removed.", f->path);
44a5fa34 1676
3c1668da
LP
1677 if (j->current_file == f) {
1678 j->current_file = NULL;
1679 j->current_field = 0;
1680 }
1681
1682 if (j->unique_file == f) {
360af4cf 1683 /* Jump to the next unique_file or NULL if that one was last */
c1f906bd 1684 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
3c1668da 1685 j->unique_offset = 0;
360af4cf
ZJS
1686 if (!j->unique_file)
1687 j->unique_file_lost = true;
3c1668da
LP
1688 }
1689
eb86030e
LP
1690 if (j->fields_file == f) {
1691 j->fields_file = ordered_hashmap_next(j->files, j->fields_file->path);
1692 j->fields_offset = 0;
1693 if (!j->fields_file)
1694 j->fields_file_lost = true;
1695 }
1696
2292d377 1697 journal_file_unlink_newest_by_boot_id(j, f);
69a3a6fd 1698 (void) journal_file_close(f);
50f20cfd 1699
313cefa1 1700 j->current_invalidate_counter++;
3fbf9cbb
LP
1701}
1702
d617408e
LP
1703static int dirname_is_machine_id(const char *fn) {
1704 sd_id128_t id, machine;
456aa879 1705 const char *e;
d617408e
LP
1706 int r;
1707
456aa879
LP
1708 /* Returns true if the specified directory name matches the local machine ID */
1709
d617408e
LP
1710 r = sd_id128_get_machine(&machine);
1711 if (r < 0)
1712 return r;
1713
456aa879
LP
1714 e = strchr(fn, '.');
1715 if (e) {
1716 const char *k;
1717
1718 /* Looks like it has a namespace suffix. Verify that. */
1719 if (!log_namespace_name_valid(e + 1))
1720 return false;
1721
2f82562b 1722 k = strndupa_safe(fn, e - fn);
456aa879
LP
1723 r = sd_id128_from_string(k, &id);
1724 } else
1725 r = sd_id128_from_string(fn, &id);
d617408e
LP
1726 if (r < 0)
1727 return r;
1728
1729 return sd_id128_equal(id, machine);
1730}
1731
456aa879
LP
1732static int dirname_has_namespace(const char *fn, const char *namespace) {
1733 const char *e;
1734
1735 /* Returns true if the specified directory name matches the specified namespace */
1736
1737 e = strchr(fn, '.');
1738 if (e) {
1739 const char *k;
1740
1741 if (!namespace)
1742 return false;
1743
1744 if (!streq(e + 1, namespace))
1745 return false;
1746
2f82562b 1747 k = strndupa_safe(fn, e - fn);
456aa879
LP
1748 return id128_is_valid(k);
1749 }
1750
1751 if (namespace)
1752 return false;
1753
1754 return id128_is_valid(fn);
1755}
1756
858749f7
LP
1757static bool dirent_is_journal_file(const struct dirent *de) {
1758 assert(de);
1759
456aa879
LP
1760 /* Returns true if the specified directory entry looks like a journal file we might be interested in */
1761
858749f7
LP
1762 if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN))
1763 return false;
1764
1765 return endswith(de->d_name, ".journal") ||
1766 endswith(de->d_name, ".journal~");
1767}
1768
456aa879
LP
1769static bool dirent_is_journal_subdir(const struct dirent *de) {
1770 const char *e, *n;
858749f7
LP
1771 assert(de);
1772
456aa879 1773 /* returns true if the specified directory entry looks like a directory that might contain journal
da890466 1774 * files we might be interested in, i.e. is either a 128-bit ID or a 128-bit ID suffixed by a
456aa879
LP
1775 * namespace. */
1776
858749f7
LP
1777 if (!IN_SET(de->d_type, DT_DIR, DT_LNK, DT_UNKNOWN))
1778 return false;
1779
456aa879
LP
1780 e = strchr(de->d_name, '.');
1781 if (!e)
1782 return id128_is_valid(de->d_name); /* No namespace */
1783
2f82562b 1784 n = strndupa_safe(de->d_name, e - de->d_name);
456aa879
LP
1785 if (!id128_is_valid(n))
1786 return false;
1787
1788 return log_namespace_name_valid(e + 1);
858749f7
LP
1789}
1790
1791static int directory_open(sd_journal *j, const char *path, DIR **ret) {
1792 DIR *d;
1793
1794 assert(j);
1795 assert(path);
1796 assert(ret);
1797
1798 if (j->toplevel_fd < 0)
1799 d = opendir(path);
1800 else
1801 /* Open the specified directory relative to the toplevel fd. Enforce that the path specified is
1802 * relative, by dropping the initial slash */
baaca3db 1803 d = xopendirat(j->toplevel_fd, skip_leading_slash(path), 0);
858749f7
LP
1804 if (!d)
1805 return -errno;
1806
1807 *ret = d;
1808 return 0;
1809}
1810
83a04175
YW
1811static Directory* directory_free(Directory *d) {
1812 if (!d)
1813 return NULL;
1814
1815 if (d->journal) {
1816 if (d->wd > 0 &&
1817 hashmap_remove_value(d->journal->directories_by_wd, INT_TO_PTR(d->wd), d) &&
1818 d->journal->inotify_fd >= 0)
1819 (void) inotify_rm_watch(d->journal->inotify_fd, d->wd);
1820
1821 if (d->path)
1822 hashmap_remove_value(d->journal->directories_by_path, d->path, d);
1823 }
1824
1825 if (d->path) {
1826 if (d->is_root)
1827 log_debug("Root directory %s removed.", d->path);
1828 else
1829 log_debug("Directory %s removed.", d->path);
1830
1831 free(d->path);
1832 }
1833
1834 return mfree(d);
1835}
1836
1837DEFINE_TRIVIAL_CLEANUP_FUNC(Directory*, directory_free);
1838
1839DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
1840 directories_by_path_hash_ops,
1841 char,
1842 path_hash_func,
1843 path_compare,
1844 Directory,
1845 directory_free);
1846
1847DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
1848 directories_by_wd_hash_ops,
1849 void,
1850 trivial_hash_func,
1851 trivial_compare_func,
1852 Directory,
1853 directory_free);
1854
1855static int add_directory_impl(sd_journal *j, const char *path, bool is_root, Directory **ret) {
1856 _cleanup_(directory_freep) Directory *m = NULL;
1857 Directory *existing;
1858 int r;
1859
1860 assert(j);
1861 assert(path);
1862 assert(ret);
1863
1864 existing = hashmap_get(j->directories_by_path, path);
1865 if (existing) {
1866 if (existing->is_root != is_root) {
1867 /* Don't 'downgrade' from root directory */
1868 *ret = NULL;
1869 return 0;
1870 }
1871
1872 *ret = existing;
1873 return 1;
1874 }
1875
1876 m = new(Directory, 1);
1877 if (!m)
1878 return -ENOMEM;
1879
1880 *m = (Directory) {
1881 .journal = j,
1882 .is_root = is_root,
1883 .path = strdup(path),
1884 .wd = -1,
1885 };
1886
1887 if (!m->path)
1888 return -ENOMEM;
1889
1890 r = hashmap_ensure_put(&j->directories_by_path, &directories_by_path_hash_ops, m->path, m);
1891 if (r < 0)
1892 return r;
1893
1894 j->current_invalidate_counter++;
1895
1896 if (is_root)
1897 log_debug("Root directory %s added.", m->path);
1898 else
1899 log_debug("Directory %s added.", m->path);
1900
1901 *ret = TAKE_PTR(m);
1902 return 1;
1903}
1904
858749f7
LP
1905static int add_directory(sd_journal *j, const char *prefix, const char *dirname);
1906
1907static void directory_enumerate(sd_journal *j, Directory *m, DIR *d) {
858749f7
LP
1908 assert(j);
1909 assert(m);
1910 assert(d);
1911
1912 FOREACH_DIRENT_ALL(de, d, goto fail) {
1913 if (dirent_is_journal_file(de))
fc1813c0 1914 (void) add_file_by_name(j, m->path, de->d_name);
858749f7 1915
456aa879 1916 if (m->is_root && dirent_is_journal_subdir(de))
858749f7
LP
1917 (void) add_directory(j, m->path, de->d_name);
1918 }
1919
1920 return;
858749f7
LP
1921fail:
1922 log_debug_errno(errno, "Failed to enumerate directory %s, ignoring: %m", m->path);
1923}
1924
1925static void directory_watch(sd_journal *j, Directory *m, int fd, uint32_t mask) {
1926 int r;
1927
1928 assert(j);
1929 assert(m);
1930 assert(fd >= 0);
1931
1932 /* Watch this directory if that's enabled and if it not being watched yet. */
1933
1934 if (m->wd > 0) /* Already have a watch? */
1935 return;
1936 if (j->inotify_fd < 0) /* Not watching at all? */
1937 return;
1938
1939 m->wd = inotify_add_watch_fd(j->inotify_fd, fd, mask);
1940 if (m->wd < 0) {
1941 log_debug_errno(errno, "Failed to watch journal directory '%s', ignoring: %m", m->path);
1942 return;
1943 }
1944
83a04175 1945 r = hashmap_ensure_put(&j->directories_by_wd, &directories_by_wd_hash_ops, INT_TO_PTR(m->wd), m);
858749f7 1946 if (r < 0) {
83a04175
YW
1947 if (r == -EEXIST)
1948 log_debug_errno(r, "Directory '%s' already being watched under a different path, ignoring: %m", m->path);
1949 else {
1950 log_debug_errno(r, "Failed to add watch for journal directory '%s' to hashmap, ignoring: %m", m->path);
1951 (void) inotify_rm_watch(j->inotify_fd, m->wd);
1952 }
858749f7
LP
1953 m->wd = -1;
1954 }
1955}
1956
456aa879
LP
1957static int add_directory(
1958 sd_journal *j,
1959 const char *prefix,
1960 const char *dirname) {
1961
7fd1b19b 1962 _cleanup_free_ char *path = NULL;
7fd1b19b 1963 _cleanup_closedir_ DIR *d = NULL;
a963990f 1964 Directory *m;
d617408e 1965 int r, k;
3fbf9cbb
LP
1966
1967 assert(j);
1968 assert(prefix);
3fbf9cbb 1969
5d1ce257
LP
1970 /* Adds a journal file directory to watch. If the directory is already tracked this updates the inotify watch
1971 * and reenumerates directory contents */
d95b1fb3 1972
657ee2d8 1973 path = path_join(prefix, dirname);
d617408e
LP
1974 if (!path) {
1975 r = -ENOMEM;
1976 goto fail;
1977 }
3fbf9cbb 1978
858749f7 1979 log_debug("Considering directory '%s'.", path);
5d1ce257
LP
1980
1981 /* We consider everything local that is in a directory for the local machine ID, or that is stored in /run */
1982 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1983 !((dirname && dirname_is_machine_id(dirname) > 0) || path_has_prefix(j, path, "/run")))
858749f7 1984 return 0;
5d1ce257 1985
2b6df46d
LP
1986 if (dirname &&
1987 (!(FLAGS_SET(j->flags, SD_JOURNAL_ALL_NAMESPACES) ||
1988 dirname_has_namespace(dirname, j->namespace) > 0 ||
1989 (FLAGS_SET(j->flags, SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE) && dirname_has_namespace(dirname, NULL) > 0))))
456aa879
LP
1990 return 0;
1991
858749f7
LP
1992 r = directory_open(j, path, &d);
1993 if (r < 0) {
1994 log_debug_errno(r, "Failed to open directory '%s': %m", path);
d617408e 1995 goto fail;
3fbf9cbb
LP
1996 }
1997
83a04175
YW
1998 r = add_directory_impl(j, path, /* is_root = */ false, &m);
1999 if (r < 0)
2000 goto fail;
2001 if (r == 0)
2002 return 0;
a963990f 2003
858749f7 2004 m->last_seen_generation = j->generation;
a963990f 2005
858749f7
LP
2006 directory_watch(j, m, dirfd(d),
2007 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
2008 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
2009 IN_ONLYDIR);
a963990f 2010
858749f7
LP
2011 if (!j->no_new_files)
2012 directory_enumerate(j, m, d);
a963990f 2013
85210bff
LP
2014 check_network(j, dirfd(d));
2015
a963990f 2016 return 0;
d617408e
LP
2017
2018fail:
5d1ce257 2019 k = journal_put_error(j, r, path ?: prefix);
d617408e
LP
2020 if (k < 0)
2021 return k;
2022
2023 return r;
a963990f
LP
2024}
2025
d617408e 2026static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) {
5d1ce257 2027
7fd1b19b 2028 _cleanup_closedir_ DIR *d = NULL;
a963990f 2029 Directory *m;
d617408e 2030 int r, k;
a963990f
LP
2031
2032 assert(j);
a963990f 2033
5d1ce257
LP
2034 /* Adds a root directory to our set of directories to use. If the root directory is already in the set, we
2035 * update the inotify logic, and renumerate the directory entries. This call may hence be called to initially
2036 * populate the set, as well as to update it later. */
a963990f 2037
5d1ce257
LP
2038 if (p) {
2039 /* If there's a path specified, use it. */
b6741478 2040
858749f7
LP
2041 log_debug("Considering root directory '%s'.", p);
2042
5d1ce257
LP
2043 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
2044 !path_has_prefix(j, p, "/run"))
2045 return -EINVAL;
d617408e 2046
5d1ce257
LP
2047 if (j->prefix)
2048 p = strjoina(j->prefix, p);
2049
858749f7
LP
2050 r = directory_open(j, p, &d);
2051 if (r == -ENOENT && missing_ok)
2052 return 0;
2053 if (r < 0) {
2054 log_debug_errno(r, "Failed to open root directory %s: %m", p);
5d1ce257
LP
2055 goto fail;
2056 }
2057 } else {
254d1313 2058 _cleanup_close_ int dfd = -EBADF;
5d1ce257
LP
2059
2060 /* If there's no path specified, then we use the top-level fd itself. We duplicate the fd here, since
2061 * opendir() will take possession of the fd, and close it, which we don't want. */
2062
2063 p = "."; /* store this as "." in the directories hashmap */
2064
2065 dfd = fcntl(j->toplevel_fd, F_DUPFD_CLOEXEC, 3);
2066 if (dfd < 0) {
2067 r = -errno;
2068 goto fail;
2069 }
2070
8e06af80 2071 d = take_fdopendir(&dfd);
5d1ce257
LP
2072 if (!d) {
2073 r = -errno;
5d1ce257
LP
2074 goto fail;
2075 }
2076
2077 rewinddir(d);
d617408e 2078 }
a963990f 2079
83a04175
YW
2080 r = add_directory_impl(j, p, /* is_root = */ true, &m);
2081 if (r < 0)
2082 goto fail;
2083 if (r == 0)
a963990f 2084 return 0;
50f20cfd 2085
858749f7
LP
2086 directory_watch(j, m, dirfd(d),
2087 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
2088 IN_ONLYDIR);
a963990f 2089
858749f7
LP
2090 if (!j->no_new_files)
2091 directory_enumerate(j, m, d);
3fbf9cbb 2092
85210bff
LP
2093 check_network(j, dirfd(d));
2094
a963990f 2095 return 0;
d617408e
LP
2096
2097fail:
5768d259 2098 k = journal_put_error(j, r, p);
d617408e
LP
2099 if (k < 0)
2100 return k;
2101
2102 return r;
a963990f
LP
2103}
2104
89739579 2105static int add_search_paths(sd_journal *j) {
d617408e
LP
2106
2107 static const char search_paths[] =
a963990f
LP
2108 "/run/log/journal\0"
2109 "/var/log/journal\0";
50f20cfd
LP
2110
2111 assert(j);
50f20cfd 2112
a963990f
LP
2113 /* We ignore most errors here, since the idea is to only open
2114 * what's actually accessible, and ignore the rest. */
50f20cfd 2115
d617408e
LP
2116 NULSTR_FOREACH(p, search_paths)
2117 (void) add_root_directory(j, p, true);
50f20cfd 2118
574b77ef
MB
2119 if (!(j->flags & SD_JOURNAL_LOCAL_ONLY))
2120 (void) add_root_directory(j, "/var/log/journal/remote", true);
2121
a963990f 2122 return 0;
50f20cfd
LP
2123}
2124
5302ebe1 2125static int add_current_paths(sd_journal *j) {
5302ebe1
ZJS
2126 JournalFile *f;
2127
2128 assert(j);
2129 assert(j->no_new_files);
2130
5d1ce257 2131 /* Simply adds all directories for files we have open as directories. We don't expect errors here, so we
5302ebe1
ZJS
2132 * treat them as fatal. */
2133
90e74a66 2134 ORDERED_HASHMAP_FOREACH(f, j->files) {
c2b2df60 2135 _cleanup_free_ char *dir = NULL;
e9174f29 2136 int r;
5302ebe1 2137
45519d13
LP
2138 r = path_extract_directory(f->path, &dir);
2139 if (r < 0)
2140 return r;
5302ebe1 2141
5d1ce257 2142 r = add_directory(j, dir, NULL);
d617408e 2143 if (r < 0)
5302ebe1 2144 return r;
5302ebe1
ZJS
2145 }
2146
2147 return 0;
2148}
2149
a963990f 2150static int allocate_inotify(sd_journal *j) {
50f20cfd 2151 assert(j);
50f20cfd 2152
a963990f
LP
2153 if (j->inotify_fd < 0) {
2154 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
2155 if (j->inotify_fd < 0)
2156 return -errno;
2157 }
50f20cfd 2158
83a04175 2159 return 0;
50f20cfd
LP
2160}
2161
456aa879 2162static sd_journal *journal_new(int flags, const char *path, const char *namespace) {
17c9aff8 2163 _cleanup_(sd_journal_closep) sd_journal *j = NULL;
50f20cfd 2164
6f30a67a 2165 j = new(sd_journal, 1);
a963990f
LP
2166 if (!j)
2167 return NULL;
50f20cfd 2168
6f30a67a 2169 *j = (sd_journal) {
e046719b 2170 .origin_id = origin_id_query(),
6f30a67a
LP
2171 .toplevel_fd = -EBADF,
2172 .inotify_fd = -EBADF,
2173 .flags = flags,
2174 .data_threshold = DEFAULT_DATA_THRESHOLD,
2175 };
50f20cfd 2176
7827b1a1 2177 if (path) {
16fefe90
ZJS
2178 char *t;
2179
2180 t = strdup(path);
2181 if (!t)
17c9aff8 2182 return NULL;
16fefe90
ZJS
2183
2184 if (flags & SD_JOURNAL_OS_ROOT)
2185 j->prefix = t;
2186 else
2187 j->path = t;
7827b1a1
LP
2188 }
2189
456aa879
LP
2190 if (namespace) {
2191 j->namespace = strdup(namespace);
2192 if (!j->namespace)
2193 return NULL;
2194 }
2195
548f6937 2196 j->files = ordered_hashmap_new(&path_hash_ops);
5d4ba7f2 2197 if (!j->files)
17c9aff8 2198 return NULL;
5d4ba7f2
VC
2199
2200 j->files_cache = ordered_hashmap_iterated_cache_new(j->files);
84168d80 2201 j->mmap = mmap_cache_new();
83a04175 2202 if (!j->files_cache || !j->mmap)
17c9aff8 2203 return NULL;
6180fc61 2204
17c9aff8 2205 return TAKE_PTR(j);
50f20cfd
LP
2206}
2207
1aaa68f5
ZJS
2208#define OPEN_ALLOWED_FLAGS \
2209 (SD_JOURNAL_LOCAL_ONLY | \
2210 SD_JOURNAL_RUNTIME_ONLY | \
456aa879
LP
2211 SD_JOURNAL_SYSTEM | \
2212 SD_JOURNAL_CURRENT_USER | \
2213 SD_JOURNAL_ALL_NAMESPACES | \
bd1af1d5
YW
2214 SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE | \
2215 SD_JOURNAL_ASSUME_IMMUTABLE)
1aaa68f5 2216
456aa879 2217_public_ int sd_journal_open_namespace(sd_journal **ret, const char *namespace, int flags) {
17c9aff8 2218 _cleanup_(sd_journal_closep) sd_journal *j = NULL;
3fbf9cbb 2219 int r;
87d2c1ff 2220
1ae464e0 2221 assert_return(ret, -EINVAL);
1aaa68f5 2222 assert_return((flags & ~OPEN_ALLOWED_FLAGS) == 0, -EINVAL);
87d2c1ff 2223
456aa879 2224 j = journal_new(flags, NULL, namespace);
87d2c1ff
LP
2225 if (!j)
2226 return -ENOMEM;
2227
89739579 2228 r = add_search_paths(j);
a963990f 2229 if (r < 0)
17c9aff8 2230 return r;
50f20cfd 2231
17c9aff8 2232 *ret = TAKE_PTR(j);
a963990f 2233 return 0;
a963990f 2234}
50f20cfd 2235
456aa879
LP
2236_public_ int sd_journal_open(sd_journal **ret, int flags) {
2237 return sd_journal_open_namespace(ret, NULL, flags);
2238}
2239
1aaa68f5 2240#define OPEN_CONTAINER_ALLOWED_FLAGS \
bd1af1d5
YW
2241 (SD_JOURNAL_LOCAL_ONLY | \
2242 SD_JOURNAL_SYSTEM | \
2243 SD_JOURNAL_ASSUME_IMMUTABLE)
1aaa68f5 2244
b6741478
LP
2245_public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
2246 _cleanup_free_ char *root = NULL, *class = NULL;
17c9aff8 2247 _cleanup_(sd_journal_closep) sd_journal *j = NULL;
b6741478
LP
2248 char *p;
2249 int r;
2250
68312977 2251 /* This is deprecated, people should use machined's OpenMachineRootDirectory() call instead in
2daa9cbd
LP
2252 * combination with sd_journal_open_directory_fd(). */
2253
b6741478
LP
2254 assert_return(machine, -EINVAL);
2255 assert_return(ret, -EINVAL);
1aaa68f5 2256 assert_return((flags & ~OPEN_CONTAINER_ALLOWED_FLAGS) == 0, -EINVAL);
52ef5dd7 2257 assert_return(hostname_is_valid(machine, 0), -EINVAL);
b6741478 2258
63c372cb 2259 p = strjoina("/run/systemd/machines/", machine);
13df9c39
LP
2260 r = parse_env_file(NULL, p,
2261 "ROOT", &root,
2262 "CLASS", &class);
b6741478
LP
2263 if (r == -ENOENT)
2264 return -EHOSTDOWN;
2265 if (r < 0)
2266 return r;
2267 if (!root)
2268 return -ENODATA;
2269
2270 if (!streq_ptr(class, "container"))
2271 return -EIO;
2272
456aa879 2273 j = journal_new(flags, root, NULL);
b6741478
LP
2274 if (!j)
2275 return -ENOMEM;
2276
89739579 2277 r = add_search_paths(j);
b6741478 2278 if (r < 0)
17c9aff8 2279 return r;
b6741478 2280
17c9aff8 2281 *ret = TAKE_PTR(j);
b6741478 2282 return 0;
b6741478
LP
2283}
2284
1aaa68f5 2285#define OPEN_DIRECTORY_ALLOWED_FLAGS \
10752e82 2286 (SD_JOURNAL_OS_ROOT | \
bd1af1d5
YW
2287 SD_JOURNAL_SYSTEM | \
2288 SD_JOURNAL_CURRENT_USER | \
2289 SD_JOURNAL_ASSUME_IMMUTABLE)
1aaa68f5 2290
a963990f 2291_public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
17c9aff8 2292 _cleanup_(sd_journal_closep) sd_journal *j = NULL;
a963990f 2293 int r;
87d2c1ff 2294
1ae464e0
TA
2295 assert_return(ret, -EINVAL);
2296 assert_return(path, -EINVAL);
1aaa68f5 2297 assert_return((flags & ~OPEN_DIRECTORY_ALLOWED_FLAGS) == 0, -EINVAL);
87d2c1ff 2298
456aa879 2299 j = journal_new(flags, path, NULL);
a963990f
LP
2300 if (!j)
2301 return -ENOMEM;
3fbf9cbb 2302
d077390c
LP
2303 if (flags & SD_JOURNAL_OS_ROOT)
2304 r = add_search_paths(j);
2305 else
2306 r = add_root_directory(j, path, false);
d617408e 2307 if (r < 0)
17c9aff8 2308 return r;
87d2c1ff 2309
17c9aff8 2310 *ret = TAKE_PTR(j);
87d2c1ff 2311 return 0;
a963990f 2312}
87d2c1ff 2313
bd1af1d5
YW
2314#define OPEN_FILES_ALLOWED_FLAGS \
2315 (SD_JOURNAL_ASSUME_IMMUTABLE)
2316
5302ebe1 2317_public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
17c9aff8 2318 _cleanup_(sd_journal_closep) sd_journal *j = NULL;
5302ebe1
ZJS
2319 int r;
2320
1ae464e0 2321 assert_return(ret, -EINVAL);
bd1af1d5 2322 assert_return((flags & ~OPEN_FILES_ALLOWED_FLAGS) == 0, -EINVAL);
5302ebe1 2323
456aa879 2324 j = journal_new(flags, NULL, NULL);
5302ebe1
ZJS
2325 if (!j)
2326 return -ENOMEM;
2327
2328 STRV_FOREACH(path, paths) {
5d1ce257 2329 r = add_any_file(j, -1, *path);
d617408e 2330 if (r < 0)
17c9aff8 2331 return r;
5302ebe1
ZJS
2332 }
2333
2334 j->no_new_files = true;
2335
17c9aff8 2336 *ret = TAKE_PTR(j);
5302ebe1 2337 return 0;
5d1ce257
LP
2338}
2339
4a45a2e0 2340#define OPEN_DIRECTORY_FD_ALLOWED_FLAGS \
10752e82 2341 (SD_JOURNAL_OS_ROOT | \
4a45a2e0
YW
2342 SD_JOURNAL_SYSTEM | \
2343 SD_JOURNAL_CURRENT_USER | \
bd1af1d5
YW
2344 SD_JOURNAL_TAKE_DIRECTORY_FD | \
2345 SD_JOURNAL_ASSUME_IMMUTABLE)
1aaa68f5 2346
5d1ce257 2347_public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) {
17c9aff8 2348 _cleanup_(sd_journal_closep) sd_journal *j = NULL;
5d1ce257 2349 struct stat st;
4a45a2e0 2350 bool take_fd;
5d1ce257
LP
2351 int r;
2352
2353 assert_return(ret, -EINVAL);
2354 assert_return(fd >= 0, -EBADF);
1aaa68f5 2355 assert_return((flags & ~OPEN_DIRECTORY_FD_ALLOWED_FLAGS) == 0, -EINVAL);
5d1ce257
LP
2356
2357 if (fstat(fd, &st) < 0)
2358 return -errno;
2359
2360 if (!S_ISDIR(st.st_mode))
2361 return -EBADFD;
2362
4a45a2e0
YW
2363 take_fd = FLAGS_SET(flags, SD_JOURNAL_TAKE_DIRECTORY_FD);
2364 j = journal_new(flags & ~SD_JOURNAL_TAKE_DIRECTORY_FD, NULL, NULL);
5d1ce257
LP
2365 if (!j)
2366 return -ENOMEM;
2367
2368 j->toplevel_fd = fd;
2369
d077390c
LP
2370 if (flags & SD_JOURNAL_OS_ROOT)
2371 r = add_search_paths(j);
2372 else
2373 r = add_root_directory(j, NULL, false);
5d1ce257 2374 if (r < 0)
17c9aff8 2375 return r;
5d1ce257 2376
4a45a2e0
YW
2377 SET_FLAG(j->flags, SD_JOURNAL_TAKE_DIRECTORY_FD, take_fd);
2378
17c9aff8 2379 *ret = TAKE_PTR(j);
5d1ce257 2380 return 0;
5d1ce257
LP
2381}
2382
bd1af1d5
YW
2383#define OPEN_FILES_FD_ALLOWED_FLAGS \
2384 (SD_JOURNAL_ASSUME_IMMUTABLE)
2385
5d1ce257 2386_public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags) {
5d1ce257 2387 JournalFile *f;
17c9aff8 2388 _cleanup_(sd_journal_closep) sd_journal *j = NULL;
5d1ce257
LP
2389 int r;
2390
2391 assert_return(ret, -EINVAL);
2392 assert_return(n_fds > 0, -EBADF);
bd1af1d5 2393 assert_return((flags & ~OPEN_FILES_FD_ALLOWED_FLAGS) == 0, -EINVAL);
5d1ce257 2394
456aa879 2395 j = journal_new(flags, NULL, NULL);
5d1ce257
LP
2396 if (!j)
2397 return -ENOMEM;
2398
abcdc02c 2399 for (unsigned i = 0; i < n_fds; i++) {
5d1ce257
LP
2400 struct stat st;
2401
2402 if (fds[i] < 0) {
2403 r = -EBADF;
2404 goto fail;
2405 }
2406
2407 if (fstat(fds[i], &st) < 0) {
2408 r = -errno;
2409 goto fail;
2410 }
2411
3cc44114
LP
2412 r = stat_verify_regular(&st);
2413 if (r < 0)
5d1ce257 2414 goto fail;
5d1ce257
LP
2415
2416 r = add_any_file(j, fds[i], NULL);
2417 if (r < 0)
2418 goto fail;
2419 }
2420
2421 j->no_new_files = true;
2422 j->no_inotify = true;
5302ebe1 2423
17c9aff8 2424 *ret = TAKE_PTR(j);
5d1ce257
LP
2425 return 0;
2426
2427fail:
f8e2f4d6 2428 /* If we fail, make sure we don't take possession of the files we managed to make use of successfully, and they
5d1ce257 2429 * remain open */
90e74a66 2430 ORDERED_HASHMAP_FOREACH(f, j->files)
5d1ce257
LP
2431 f->close_fd = false;
2432
5302ebe1
ZJS
2433 return r;
2434}
2435
a5344d2c 2436_public_ void sd_journal_close(sd_journal *j) {
e046719b 2437 if (!j || journal_origin_changed(j))
a5344d2c 2438 return;
87d2c1ff 2439
cb2b0326 2440 journal_clear_newest_by_boot_id(j);
34af7494 2441
54b1da83
LP
2442 sd_journal_flush_matches(j);
2443
f9168190 2444 ordered_hashmap_free_with_destructor(j->files, journal_file_close);
5d4ba7f2 2445 iterated_cache_free(j->files_cache);
260a2be4 2446
a963990f
LP
2447 hashmap_free(j->directories_by_path);
2448 hashmap_free(j->directories_by_wd);
1cc101f1 2449
4a45a2e0
YW
2450 if (FLAGS_SET(j->flags, SD_JOURNAL_TAKE_DIRECTORY_FD))
2451 safe_close(j->toplevel_fd);
2452
03e334a1 2453 safe_close(j->inotify_fd);
50f20cfd 2454
bf807d4d 2455 if (j->mmap) {
3a595c59 2456 mmap_cache_stats_log_debug(j->mmap);
16e9f408 2457 mmap_cache_unref(j->mmap);
bf807d4d 2458 }
16e9f408 2459
ec1d2909 2460 hashmap_free_free(j->errors);
5768d259 2461
25aa35d4
SZ
2462 set_free(j->exclude_syslog_identifiers);
2463
7827b1a1 2464 free(j->path);
89739579 2465 free(j->prefix);
456aa879 2466 free(j->namespace);
3c1668da 2467 free(j->unique_field);
eb86030e 2468 free(j->fields_buffer);
87d2c1ff
LP
2469 free(j);
2470}
3fbf9cbb 2471
34af7494
LP
2472static int journal_file_read_tail_timestamp(sd_journal *j, JournalFile *f) {
2473 uint64_t offset, mo, rt;
2474 sd_id128_t id;
2475 ObjectType type;
2476 Object *o;
2477 int r;
2478
2479 assert(j);
2480 assert(f);
2481 assert(f->header);
2482
2483 /* Tries to read the timestamp of the most recently written entry. */
2484
bd1af1d5
YW
2485 if (FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE) && f->newest_entry_offset != 0)
2486 return 0; /* We have already read the file, and we assume that the file is immutable. */
2487
99e6f682
YW
2488 if (f->header->state == f->newest_state &&
2489 f->header->state == STATE_ARCHIVED &&
2490 f->newest_entry_offset != 0)
2491 return 0; /* We have already read archived file. */
34af7494
LP
2492
2493 if (JOURNAL_HEADER_CONTAINS(f->header, tail_entry_offset)) {
2494 offset = le64toh(READ_NOW(f->header->tail_entry_offset));
2495 type = OBJECT_ENTRY;
2496 } else {
2497 offset = le64toh(READ_NOW(f->header->tail_object_offset));
2498 type = OBJECT_UNUSED;
2499 }
2500 if (offset == 0)
2501 return -ENODATA; /* not a single object/entry, hence no tail timestamp */
99e6f682
YW
2502 if (offset == f->newest_entry_offset)
2503 return 0; /* No new entry is added after we read last time. */
34af7494
LP
2504
2505 /* Move to the last object in the journal file, in the hope it is an entry (which it usually will
2506 * be). If we lack the "tail_entry_offset" field in the header, we specify the type as OBJECT_UNUSED
2507 * here, since we cannot be sure what the last object will be, and want no noisy logging if it isn't
2508 * an entry. We instead check after figuring out the pointer. */
2509 r = journal_file_move_to_object(f, type, offset, &o);
2510 if (r < 0) {
2511 log_debug_errno(r, "Failed to move to last object in journal file, ignoring: %m");
2512 o = NULL;
99e6f682 2513 offset = 0;
34af7494
LP
2514 }
2515 if (o && o->object.type == OBJECT_ENTRY) {
2516 /* Yay, last object is an entry, let's use the data. */
2517 id = o->entry.boot_id;
2518 mo = le64toh(o->entry.monotonic);
2519 rt = le64toh(o->entry.realtime);
2520 } else {
2521 /* So the object is not an entry or we couldn't access it? In that case, let's read the most
2522 * recent entry timestamps from the header. It's equally good. Unfortunately though, in old
2523 * versions of the journal the boot ID in the header doesn't have to match the monotonic
2524 * timestamp of the header. Let's check the header flag that indicates whether this strictly
2525 * matches first hence, before using the data. */
2526
2527 if (JOURNAL_HEADER_TAIL_ENTRY_BOOT_ID(f->header) && f->header->state == STATE_ARCHIVED) {
2528 mo = le64toh(f->header->tail_entry_monotonic);
2529 rt = le64toh(f->header->tail_entry_realtime);
2530 id = f->header->tail_entry_boot_id;
99e6f682 2531 offset = UINT64_MAX;
34af7494
LP
2532 } else {
2533 /* Otherwise let's find the last entry manually (this possibly means traversing the
2534 * chain of entry arrays, till the end */
99e6f682 2535 r = journal_file_next_entry(f, 0, DIRECTION_UP, &o, offset == 0 ? &offset : NULL);
34af7494
LP
2536 if (r < 0)
2537 return r;
3b1b0f1a
YW
2538 if (r == 0)
2539 return -ENODATA;
34af7494
LP
2540
2541 id = o->entry.boot_id;
2542 mo = le64toh(o->entry.monotonic);
2543 rt = le64toh(o->entry.realtime);
2544 }
2545 }
2546
2547 if (mo > rt) /* monotonic clock is further ahead than realtime? that's weird, refuse to use the data */
2548 return -ENODATA;
2549
51b2bcf8
YW
2550 if (offset == f->newest_entry_offset) {
2551 /* Cached data and the current one should be equivalent. */
2552 if (!sd_id128_equal(f->newest_machine_id, f->header->machine_id) ||
2553 !sd_id128_equal(f->newest_boot_id, id) ||
2554 f->newest_monotonic_usec != mo ||
2555 f->newest_realtime_usec != rt)
2556 return -EBADMSG;
2557
2558 return 0; /* No new entry is added after we read last time. */
2559 }
2560
34af7494 2561 if (!sd_id128_equal(f->newest_boot_id, id))
2292d377 2562 journal_file_unlink_newest_by_boot_id(j, f);
34af7494
LP
2563
2564 f->newest_boot_id = id;
2565 f->newest_monotonic_usec = mo;
2566 f->newest_realtime_usec = rt;
2567 f->newest_machine_id = f->header->machine_id;
99e6f682
YW
2568 f->newest_entry_offset = offset;
2569 f->newest_state = f->header->state;
34af7494
LP
2570
2571 r = journal_file_reshuffle_newest_by_boot_id(j, f);
2572 if (r < 0)
2573 return r;
2574
51b2bcf8 2575 return 1; /* Updated. */
34af7494
LP
2576}
2577
a5344d2c 2578_public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
3fbf9cbb 2579 JournalFile *f;
834f759c 2580 Object *o;
3fbf9cbb
LP
2581 int r;
2582
1ae464e0 2583 assert_return(j, -EINVAL);
e046719b 2584 assert_return(!journal_origin_changed(j), -ECHILD);
3fbf9cbb
LP
2585
2586 f = j->current_file;
2587 if (!f)
de190aef 2588 return -EADDRNOTAVAIL;
3fbf9cbb 2589 if (f->current_offset <= 0)
de190aef 2590 return -EADDRNOTAVAIL;
3fbf9cbb 2591
de190aef 2592 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
2593 if (r < 0)
2594 return r;
2595
834f759c
LP
2596 uint64_t t = le64toh(o->entry.realtime);
2597 if (!VALID_REALTIME(t))
2598 return -EBADMSG;
2599
2600 if (ret)
2601 *ret = t;
2602
de190aef 2603 return 0;
3fbf9cbb
LP
2604}
2605
a5344d2c 2606_public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
3fbf9cbb 2607 JournalFile *f;
404803e6 2608 Object *o;
3fbf9cbb 2609 int r;
3fbf9cbb 2610
1ae464e0 2611 assert_return(j, -EINVAL);
e046719b 2612 assert_return(!journal_origin_changed(j), -ECHILD);
3fbf9cbb
LP
2613
2614 f = j->current_file;
2615 if (!f)
de190aef 2616 return -EADDRNOTAVAIL;
3fbf9cbb 2617 if (f->current_offset <= 0)
de190aef 2618 return -EADDRNOTAVAIL;
3fbf9cbb 2619
de190aef 2620 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
2621 if (r < 0)
2622 return r;
2623
de190aef
LP
2624 if (ret_boot_id)
2625 *ret_boot_id = o->entry.boot_id;
2626 else {
d4739bc4
VC
2627 sd_id128_t id;
2628
de190aef
LP
2629 r = sd_id128_get_boot(&id);
2630 if (r < 0)
2631 return r;
3fbf9cbb 2632
de190aef 2633 if (!sd_id128_equal(id, o->entry.boot_id))
df50185b 2634 return -ESTALE;
de190aef 2635 }
3fbf9cbb 2636
404803e6
LP
2637 uint64_t t = le64toh(o->entry.monotonic);
2638 if (!VALID_MONOTONIC(t))
2639 return -EBADMSG;
2640
14a65d65 2641 if (ret)
404803e6 2642 *ret = t;
14a65d65 2643
de190aef 2644 return 0;
3fbf9cbb
LP
2645}
2646
b1712fab
LP
2647_public_ int sd_journal_get_seqnum(
2648 sd_journal *j,
2649 uint64_t *ret_seqnum,
2650 sd_id128_t *ret_seqnum_id) {
2651
2652 JournalFile *f;
2653 Object *o;
2654 int r;
2655
2656 assert_return(j, -EINVAL);
e046719b 2657 assert_return(!journal_origin_changed(j), -ECHILD);
b1712fab
LP
2658
2659 f = j->current_file;
2660 if (!f)
2661 return -EADDRNOTAVAIL;
2662
2663 if (f->current_offset <= 0)
2664 return -EADDRNOTAVAIL;
2665
2666 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2667 if (r < 0)
2668 return r;
2669
2670 if (ret_seqnum_id)
2671 *ret_seqnum_id = f->header->seqnum_id;
2672 if (ret_seqnum)
2673 *ret_seqnum = le64toh(o->entry.seqnum);
2674
2675 return 0;
2676}
2677
362a3f81 2678static bool field_is_valid(const char *field) {
362a3f81
LP
2679 assert(field);
2680
2681 if (isempty(field))
2682 return false;
2683
2684 if (startswith(field, "__"))
2685 return false;
2686
abcdc02c 2687 for (const char *p = field; *p; p++) {
362a3f81
LP
2688
2689 if (*p == '_')
2690 continue;
2691
2692 if (*p >= 'A' && *p <= 'Z')
2693 continue;
2694
ff25d338 2695 if (ascii_isdigit(*p))
362a3f81
LP
2696 continue;
2697
2698 return false;
2699 }
2700
2701 return true;
2702}
2703
a5344d2c 2704_public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
3fbf9cbb 2705 JournalFile *f;
3fbf9cbb 2706 size_t field_length;
3fbf9cbb 2707 Object *o;
a1640191 2708 int r;
3fbf9cbb 2709
1ae464e0 2710 assert_return(j, -EINVAL);
e046719b 2711 assert_return(!journal_origin_changed(j), -ECHILD);
1ae464e0
TA
2712 assert_return(field, -EINVAL);
2713 assert_return(data, -EINVAL);
2714 assert_return(size, -EINVAL);
2715 assert_return(field_is_valid(field), -EINVAL);
3fbf9cbb
LP
2716
2717 f = j->current_file;
2718 if (!f)
de190aef 2719 return -EADDRNOTAVAIL;
3fbf9cbb
LP
2720
2721 if (f->current_offset <= 0)
de190aef 2722 return -EADDRNOTAVAIL;
3fbf9cbb 2723
de190aef 2724 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
2725 if (r < 0)
2726 return r;
2727
2728 field_length = strlen(field);
2729
a9089a66 2730 uint64_t n = journal_file_entry_n_items(f, o);
abcdc02c 2731 for (uint64_t i = 0; i < n; i++) {
0e35afff
DDM
2732 uint64_t p;
2733 void *d;
2734 size_t l;
3fbf9cbb 2735
a9089a66 2736 p = journal_file_entry_item_object_offset(f, o, i);
0e35afff
DDM
2737 r = journal_file_data_payload(f, NULL, p, field, field_length, j->data_threshold, &d, &l);
2738 if (r == 0)
31438511 2739 continue;
df207ccb
DDM
2740 if (IN_SET(r, -EADDRNOTAVAIL, -EBADMSG)) {
2741 log_debug_errno(r, "Entry item %"PRIu64" data object is bad, skipping over it: %m", i);
31438511 2742 continue;
8a799bed 2743 }
3fbf9cbb
LP
2744 if (r < 0)
2745 return r;
2746
0e35afff
DDM
2747 *data = d;
2748 *size = l;
3fbf9cbb 2749
0e35afff 2750 return 0;
3fbf9cbb
LP
2751 }
2752
de190aef 2753 return -ENOENT;
3fbf9cbb
LP
2754}
2755
a5344d2c 2756_public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
3fbf9cbb 2757 JournalFile *f;
3fbf9cbb 2758 Object *o;
5a94a2bf 2759 int r;
3fbf9cbb 2760
1ae464e0 2761 assert_return(j, -EINVAL);
e046719b 2762 assert_return(!journal_origin_changed(j), -ECHILD);
1ae464e0
TA
2763 assert_return(data, -EINVAL);
2764 assert_return(size, -EINVAL);
3fbf9cbb
LP
2765
2766 f = j->current_file;
2767 if (!f)
de190aef 2768 return -EADDRNOTAVAIL;
3fbf9cbb
LP
2769
2770 if (f->current_offset <= 0)
de190aef 2771 return -EADDRNOTAVAIL;
3fbf9cbb 2772
de190aef 2773 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
2774 if (r < 0)
2775 return r;
2776
a9089a66 2777 for (uint64_t n = journal_file_entry_n_items(f, o); j->current_field < n; j->current_field++) {
5a94a2bf 2778 uint64_t p;
0e35afff
DDM
2779 void *d;
2780 size_t l;
3fbf9cbb 2781
a9089a66 2782 p = journal_file_entry_item_object_offset(f, o, j->current_field);
0e35afff 2783 r = journal_file_data_payload(f, NULL, p, NULL, 0, j->data_threshold, &d, &l);
df207ccb
DDM
2784 if (IN_SET(r, -EADDRNOTAVAIL, -EBADMSG)) {
2785 log_debug_errno(r, "Entry item %"PRIu64" data object is bad, skipping over it: %m", j->current_field);
31438511 2786 continue;
5a94a2bf
DDM
2787 }
2788 if (r < 0)
2789 return r;
0e35afff 2790 assert(r > 0);
3fbf9cbb 2791
0e35afff
DDM
2792 *data = d;
2793 *size = l;
3fbf9cbb 2794
5a94a2bf 2795 j->current_field++;
3fbf9cbb 2796
5a94a2bf
DDM
2797 return 1;
2798 }
2799
2800 return 0;
3fbf9cbb 2801}
c2373f84 2802
76cbafcd
ZJS
2803_public_ int sd_journal_enumerate_available_data(sd_journal *j, const void **data, size_t *size) {
2804 for (;;) {
2805 int r;
2806
2807 r = sd_journal_enumerate_data(j, data, size);
2808 if (r >= 0)
2809 return r;
2810 if (!JOURNAL_ERRNO_IS_UNAVAILABLE_FIELD(r))
2811 return r;
2812 j->current_field++; /* Try with the next field */
2813 }
2814}
2815
a5344d2c 2816_public_ void sd_journal_restart_data(sd_journal *j) {
e046719b 2817 if (!j || journal_origin_changed(j))
a5344d2c 2818 return;
8725d60a
LP
2819
2820 j->current_field = 0;
c2373f84 2821}
50f20cfd 2822
858749f7
LP
2823static int reiterate_all_paths(sd_journal *j) {
2824 assert(j);
2825
2826 if (j->no_new_files)
2827 return add_current_paths(j);
2828
2829 if (j->flags & SD_JOURNAL_OS_ROOT)
2830 return add_search_paths(j);
2831
2832 if (j->toplevel_fd >= 0)
2833 return add_root_directory(j, NULL, false);
2834
2835 if (j->path)
2836 return add_root_directory(j, j->path, true);
2837
2838 return add_search_paths(j);
2839}
2840
a5344d2c 2841_public_ int sd_journal_get_fd(sd_journal *j) {
a963990f
LP
2842 int r;
2843
1ae464e0 2844 assert_return(j, -EINVAL);
e046719b 2845 assert_return(!journal_origin_changed(j), -ECHILD);
bd1af1d5 2846 assert_return(!FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE), -EUNATCH);
50f20cfd 2847
5d1ce257
LP
2848 if (j->no_inotify)
2849 return -EMEDIUMTYPE;
2850
a963990f
LP
2851 if (j->inotify_fd >= 0)
2852 return j->inotify_fd;
2853
2854 r = allocate_inotify(j);
2855 if (r < 0)
2856 return r;
2857
858749f7 2858 log_debug("Reiterating files to get inotify watches established.");
5d1ce257 2859
858749f7
LP
2860 /* Iterate through all dirs again, to add them to the inotify */
2861 r = reiterate_all_paths(j);
a963990f
LP
2862 if (r < 0)
2863 return r;
2864
50f20cfd
LP
2865 return j->inotify_fd;
2866}
2867
ee531d94
LP
2868_public_ int sd_journal_get_events(sd_journal *j) {
2869 int fd;
2870
1ae464e0 2871 assert_return(j, -EINVAL);
e046719b 2872 assert_return(!journal_origin_changed(j), -ECHILD);
bd1af1d5 2873 assert_return(!FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE), -EUNATCH);
ee531d94
LP
2874
2875 fd = sd_journal_get_fd(j);
2876 if (fd < 0)
2877 return fd;
2878
2879 return POLLIN;
2880}
2881
39c155ea
LP
2882_public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2883 int fd;
2884
1ae464e0 2885 assert_return(j, -EINVAL);
e046719b 2886 assert_return(!journal_origin_changed(j), -ECHILD);
bd1af1d5 2887 assert_return(!FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE), -EUNATCH);
1ae464e0 2888 assert_return(timeout_usec, -EINVAL);
39c155ea
LP
2889
2890 fd = sd_journal_get_fd(j);
2891 if (fd < 0)
2892 return fd;
2893
2894 if (!j->on_network) {
f5fbe71d 2895 *timeout_usec = UINT64_MAX;
39c155ea
LP
2896 return 0;
2897 }
2898
2899 /* If we are on the network we need to regularly check for
2900 * changes manually */
2901
2902 *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2903 return 1;
2904}
2905
858749f7
LP
2906static void process_q_overflow(sd_journal *j) {
2907 JournalFile *f;
2908 Directory *m;
858749f7
LP
2909
2910 assert(j);
2911
2912 /* When the inotify queue overruns we need to enumerate and re-validate all journal files to bring our list
2913 * back in sync with what's on disk. For this we pick a new generation counter value. It'll be assigned to all
2914 * journal files we encounter. All journal files and all directories that don't carry it after reenumeration
2915 * are subject for unloading. */
2916
2917 log_debug("Inotify queue overrun, reiterating everything.");
2918
2919 j->generation++;
2920 (void) reiterate_all_paths(j);
2921
90e74a66 2922 ORDERED_HASHMAP_FOREACH(f, j->files) {
858749f7
LP
2923
2924 if (f->last_seen_generation == j->generation)
2925 continue;
2926
2927 log_debug("File '%s' hasn't been seen in this enumeration, removing.", f->path);
2928 remove_file_real(j, f);
2929 }
2930
90e74a66 2931 HASHMAP_FOREACH(m, j->directories_by_path) {
858749f7
LP
2932
2933 if (m->last_seen_generation == j->generation)
2934 continue;
2935
2936 if (m->is_root) /* Never GC root directories */
2937 continue;
2938
2939 log_debug("Directory '%s' hasn't been seen in this enumeration, removing.", f->path);
83a04175 2940 directory_free(m);
858749f7
LP
2941 }
2942
2943 log_debug("Reiteration complete.");
2944}
2945
31e99dd2 2946static void process_inotify_event(sd_journal *j, const struct inotify_event *e) {
a963990f 2947 Directory *d;
50f20cfd
LP
2948
2949 assert(j);
2950 assert(e);
2951
858749f7
LP
2952 if (e->mask & IN_Q_OVERFLOW) {
2953 process_q_overflow(j);
2954 return;
2955 }
2956
50f20cfd 2957 /* Is this a subdirectory we watch? */
a963990f
LP
2958 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2959 if (d) {
de2c3907
LP
2960 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2961 (endswith(e->name, ".journal") ||
2962 endswith(e->name, ".journal~"))) {
50f20cfd
LP
2963
2964 /* Event for a journal file */
2965
d617408e 2966 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB))
fc1813c0 2967 (void) add_file_by_name(j, d->path, e->name);
d617408e 2968 else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT))
8e7e4a73 2969 (void) remove_file_by_name(j, d->path, e->name);
50f20cfd 2970
a963990f 2971 } else if (!d->is_root && e->len == 0) {
50f20cfd 2972
a963990f 2973 /* Event for a subdirectory */
50f20cfd 2974
b2b46f91 2975 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT))
83a04175 2976 directory_free(d);
50f20cfd 2977
a9be0692 2978 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && id128_is_valid(e->name)) {
50f20cfd 2979
a963990f 2980 /* Event for root directory */
50f20cfd 2981
d617408e
LP
2982 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB))
2983 (void) add_directory(j, d->path, e->name);
50f20cfd
LP
2984 }
2985
2986 return;
2987 }
2988
2989 if (e->mask & IN_IGNORED)
2990 return;
2991
a9be0692 2992 log_debug("Unexpected inotify event.");
50f20cfd
LP
2993}
2994
a963990f
LP
2995static int determine_change(sd_journal *j) {
2996 bool b;
2997
2998 assert(j);
2999
3000 b = j->current_invalidate_counter != j->last_invalidate_counter;
3001 j->last_invalidate_counter = j->current_invalidate_counter;
3002
3003 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
3004}
3005
a5344d2c 3006_public_ int sd_journal_process(sd_journal *j) {
a963990f 3007 bool got_something = false;
50f20cfd 3008
1ae464e0 3009 assert_return(j, -EINVAL);
e046719b 3010 assert_return(!journal_origin_changed(j), -ECHILD);
50f20cfd 3011
10c4d640
LP
3012 if (j->inotify_fd < 0) /* We have no inotify fd yet? Then there's noting to process. */
3013 return 0;
3014
bd1af1d5
YW
3015 assert_return(!FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE), -EUNATCH);
3016
39c155ea 3017 j->last_process_usec = now(CLOCK_MONOTONIC);
f9346444 3018 j->last_invalidate_counter = j->current_invalidate_counter;
39c155ea 3019
50f20cfd 3020 for (;;) {
0254e944 3021 union inotify_event_buffer buffer;
50f20cfd
LP
3022 ssize_t l;
3023
0254e944 3024 l = read(j->inotify_fd, &buffer, sizeof(buffer));
50f20cfd 3025 if (l < 0) {
8add30a0 3026 if (ERRNO_IS_TRANSIENT(errno))
a963990f 3027 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
50f20cfd
LP
3028
3029 return -errno;
3030 }
3031
a963990f
LP
3032 got_something = true;
3033
f7c1ad4f 3034 FOREACH_INOTIFY_EVENT(e, buffer, l)
50f20cfd 3035 process_inotify_event(j, e);
50f20cfd
LP
3036 }
3037}
6ad1d1c3 3038
e02d1cf7 3039_public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
a963990f 3040 int r;
39c155ea 3041 uint64_t t;
e02d1cf7 3042
1ae464e0 3043 assert_return(j, -EINVAL);
e046719b 3044 assert_return(!journal_origin_changed(j), -ECHILD);
bd1af1d5 3045 assert_return(!FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE), -EUNATCH);
e02d1cf7 3046
a963990f 3047 if (j->inotify_fd < 0) {
28ca867a 3048 JournalFile *f;
a963990f 3049
9eba03c7 3050 /* This is the first invocation, hence create the inotify watch */
a963990f
LP
3051 r = sd_journal_get_fd(j);
3052 if (r < 0)
3053 return r;
3054
9eba03c7
LP
3055 /* Server might have done some vacuuming while we weren't watching. Get rid of the deleted
3056 * files now so they don't stay around indefinitely. */
90e74a66 3057 ORDERED_HASHMAP_FOREACH(f, j->files) {
28ca867a 3058 r = journal_file_fstat(f);
8581b9f9
MS
3059 if (r == -EIDRM)
3060 remove_file_real(j, f);
9eba03c7
LP
3061 else if (r < 0)
3062 log_debug_errno(r, "Failed to fstat() journal file '%s', ignoring: %m", f->path);
28ca867a
MS
3063 }
3064
9eba03c7
LP
3065 /* The journal might have changed since the context object was created and we weren't
3066 * watching before, hence don't wait for anything, and return immediately. */
a963990f
LP
3067 return determine_change(j);
3068 }
3069
39c155ea
LP
3070 r = sd_journal_get_timeout(j, &t);
3071 if (r < 0)
3072 return r;
3073
f5fbe71d 3074 if (t != UINT64_MAX) {
496db330 3075 t = usec_sub_unsigned(t, now(CLOCK_MONOTONIC));
85210bff 3076
f5fbe71d 3077 if (timeout_usec == UINT64_MAX || timeout_usec > t)
39c155ea 3078 timeout_usec = t;
85210bff
LP
3079 }
3080
a963990f
LP
3081 do {
3082 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
3083 } while (r == -EINTR);
e02d1cf7
LP
3084
3085 if (r < 0)
3086 return r;
3087
a963990f 3088 return sd_journal_process(j);
e02d1cf7
LP
3089}
3090
08984293 3091_public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
08984293
LP
3092 JournalFile *f;
3093 bool first = true;
581483bf 3094 uint64_t fmin = 0, tmax = 0;
08984293
LP
3095 int r;
3096
1ae464e0 3097 assert_return(j, -EINVAL);
e046719b 3098 assert_return(!journal_origin_changed(j), -ECHILD);
1ae464e0
TA
3099 assert_return(from || to, -EINVAL);
3100 assert_return(from != to, -EINVAL);
08984293 3101
90e74a66 3102 ORDERED_HASHMAP_FOREACH(f, j->files) {
08984293
LP
3103 usec_t fr, t;
3104
3105 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
9f8d2983
LP
3106 if (r == -ENOENT)
3107 continue;
08984293
LP
3108 if (r < 0)
3109 return r;
3110 if (r == 0)
3111 continue;
3112
3113 if (first) {
581483bf
LP
3114 fmin = fr;
3115 tmax = t;
08984293
LP
3116 first = false;
3117 } else {
581483bf
LP
3118 fmin = MIN(fr, fmin);
3119 tmax = MAX(t, tmax);
08984293
LP
3120 }
3121 }
3122
581483bf
LP
3123 if (from)
3124 *from = fmin;
3125 if (to)
3126 *to = tmax;
3127
08984293
LP
3128 return first ? 0 : 1;
3129}
3130
f4cb1bfd
LP
3131_public_ int sd_journal_get_cutoff_monotonic_usec(
3132 sd_journal *j,
3133 sd_id128_t boot_id,
3134 uint64_t *ret_from,
3135 uint64_t *ret_to) {
3136
3137 uint64_t from = UINT64_MAX, to = UINT64_MAX;
1651e2c6 3138 bool found = false;
f4cb1bfd 3139 JournalFile *f;
08984293
LP
3140 int r;
3141
1ae464e0 3142 assert_return(j, -EINVAL);
e046719b 3143 assert_return(!journal_origin_changed(j), -ECHILD);
f4cb1bfd 3144 assert_return(ret_from != ret_to, -EINVAL);
08984293 3145
90e74a66 3146 ORDERED_HASHMAP_FOREACH(f, j->files) {
f4cb1bfd 3147 usec_t ff, tt;
08984293 3148
f4cb1bfd 3149 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &ff, &tt);
9f8d2983
LP
3150 if (r == -ENOENT)
3151 continue;
08984293
LP
3152 if (r < 0)
3153 return r;
3154 if (r == 0)
3155 continue;
3156
1651e2c6 3157 if (found) {
f4cb1bfd
LP
3158 from = MIN(ff, from);
3159 to = MAX(tt, to);
08984293 3160 } else {
f4cb1bfd
LP
3161 from = ff;
3162 to = tt;
1651e2c6 3163 found = true;
08984293
LP
3164 }
3165 }
3166
f4cb1bfd
LP
3167 if (ret_from)
3168 *ret_from = from;
3169 if (ret_to)
3170 *ret_to = to;
3171
1651e2c6 3172 return found;
08984293
LP
3173}
3174
dca6219e 3175void journal_print_header(sd_journal *j) {
dca6219e
LP
3176 JournalFile *f;
3177 bool newline = false;
3178
3179 assert(j);
3180
90e74a66 3181 ORDERED_HASHMAP_FOREACH(f, j->files) {
dca6219e
LP
3182 if (newline)
3183 putchar('\n');
3184 else
3185 newline = true;
3186
3187 journal_file_print_header(f);
3188 }
3189}
08984293 3190
d8671b1c 3191_public_ int sd_journal_get_usage(sd_journal *j, uint64_t *ret) {
a1a03e30
LP
3192 JournalFile *f;
3193 uint64_t sum = 0;
3194
1ae464e0 3195 assert_return(j, -EINVAL);
e046719b 3196 assert_return(!journal_origin_changed(j), -ECHILD);
d8671b1c 3197 assert_return(ret, -EINVAL);
a1a03e30 3198
90e74a66 3199 ORDERED_HASHMAP_FOREACH(f, j->files) {
a1a03e30 3200 struct stat st;
d8671b1c 3201 uint64_t b;
a1a03e30
LP
3202
3203 if (fstat(f->fd, &st) < 0)
3204 return -errno;
3205
d8671b1c
LP
3206 b = (uint64_t) st.st_blocks;
3207 if (b > UINT64_MAX / 512)
3208 return -EOVERFLOW;
3209 b *= 512;
3210
3211 if (sum > UINT64_MAX - b)
3212 return -EOVERFLOW;
3213 sum += b;
a1a03e30
LP
3214 }
3215
d8671b1c 3216 *ret = sum;
a1a03e30
LP
3217 return 0;
3218}
3219
3c1668da 3220_public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
900952ec 3221 int r;
3c1668da 3222
1ae464e0 3223 assert_return(j, -EINVAL);
e046719b 3224 assert_return(!journal_origin_changed(j), -ECHILD);
9988043b
FS
3225
3226 if (!field_is_valid(field))
3227 return -EINVAL;
3c1668da 3228
900952ec
LP
3229 r = free_and_strdup(&j->unique_field, field);
3230 if (r < 0)
3231 return r;
3c1668da 3232
3c1668da
LP
3233 j->unique_file = NULL;
3234 j->unique_offset = 0;
360af4cf 3235 j->unique_file_lost = false;
3c1668da
LP
3236
3237 return 0;
3238}
3239
0e0b0529
LP
3240_public_ int sd_journal_enumerate_unique(
3241 sd_journal *j,
3242 const void **ret_data,
3243 size_t *ret_size) {
3244
3c1668da 3245 size_t k;
19a2bd80 3246
1ae464e0 3247 assert_return(j, -EINVAL);
e046719b 3248 assert_return(!journal_origin_changed(j), -ECHILD);
1ae464e0 3249 assert_return(j->unique_field, -EINVAL);
19a2bd80 3250
3c1668da 3251 k = strlen(j->unique_field);
19a2bd80 3252
3c1668da 3253 if (!j->unique_file) {
360af4cf
ZJS
3254 if (j->unique_file_lost)
3255 return 0;
3256
c1f906bd 3257 j->unique_file = ordered_hashmap_first(j->files);
3c1668da
LP
3258 if (!j->unique_file)
3259 return 0;
360af4cf 3260
3c1668da
LP
3261 j->unique_offset = 0;
3262 }
19a2bd80 3263
3c1668da
LP
3264 for (;;) {
3265 JournalFile *of;
ae97089d 3266 Object *o;
0e35afff 3267 void *odata;
3c1668da
LP
3268 size_t ol;
3269 bool found;
ae97089d 3270 int r;
3c1668da 3271
bdc02927 3272 /* Proceed to next data object in the field's linked list */
3c1668da
LP
3273 if (j->unique_offset == 0) {
3274 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
3275 if (r < 0)
3276 return r;
3277
3278 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
3279 } else {
3280 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
3281 if (r < 0)
3282 return r;
3283
3284 j->unique_offset = le64toh(o->data.next_field_offset);
3285 }
3286
3287 /* We reached the end of the list? Then start again, with the next file */
3288 if (j->unique_offset == 0) {
c1f906bd 3289 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
360af4cf 3290 if (!j->unique_file)
3c1668da
LP
3291 return 0;
3292
3c1668da
LP
3293 continue;
3294 }
3295
a1b8d21f 3296 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
3c1668da
LP
3297 if (r < 0)
3298 return r;
3299
a1b8d21f
YW
3300 /* Let's pin the data object, so we can look at it at the same time as one on another file. */
3301 r = journal_file_pin_object(j->unique_file, o);
3302 if (r < 0)
3303 return r;
ae97089d 3304
0e35afff
DDM
3305 r = journal_file_data_payload(j->unique_file, o, j->unique_offset, NULL, 0,
3306 j->data_threshold, &odata, &ol);
3c1668da
LP
3307 if (r < 0)
3308 return r;
3309
0f99f74a 3310 /* Check if we have at least the field name and "=". */
baaa35ad
ZJS
3311 if (ol <= k)
3312 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
3313 "%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
3314 j->unique_file->path,
3315 j->unique_offset, ol, k + 1);
3316
0e0b0529 3317 if (memcmp(odata, j->unique_field, k) != 0 || ((const char*) odata)[k] != '=')
baaa35ad
ZJS
3318 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
3319 "%s:offset " OFSfmt ": object does not start with \"%s=\"",
3320 j->unique_file->path,
3321 j->unique_offset,
3322 j->unique_field);
0f99f74a 3323
0e0b0529
LP
3324 /* OK, now let's see if we already returned this data object by checking if it exists in the
3325 * earlier traversed files. */
3c1668da 3326 found = false;
90e74a66 3327 ORDERED_HASHMAP_FOREACH(of, j->files) {
3c1668da
LP
3328 if (of == j->unique_file)
3329 break;
3330
ed71f956
LP
3331 /* Skip this file it didn't have any fields indexed */
3332 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) && le64toh(of->header->n_fields) <= 0)
3c1668da
LP
3333 continue;
3334
2e1a8a5d
LP
3335 /* We can reuse the hash from our current file only on old-style journal files
3336 * without keyed hashes. On new-style files we have to calculate the hash anew, to
3337 * take the per-file hash seed into consideration. */
3338 if (!JOURNAL_HEADER_KEYED_HASH(j->unique_file->header) && !JOURNAL_HEADER_KEYED_HASH(of->header))
3339 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), NULL, NULL);
3340 else
3341 r = journal_file_find_data_object(of, odata, ol, NULL, NULL);
3c1668da
LP
3342 if (r < 0)
3343 return r;
ed71f956 3344 if (r > 0) {
3c1668da 3345 found = true;
ed71f956
LP
3346 break;
3347 }
3c1668da
LP
3348 }
3349
06cc69d4
JJ
3350 if (found)
3351 continue;
3352
0e35afff
DDM
3353 *ret_data = odata;
3354 *ret_size = ol;
3c1668da
LP
3355
3356 return 1;
3357 }
3358}
3359
76cbafcd
ZJS
3360_public_ int sd_journal_enumerate_available_unique(sd_journal *j, const void **data, size_t *size) {
3361 for (;;) {
3362 int r;
3363
3364 r = sd_journal_enumerate_unique(j, data, size);
3365 if (r >= 0)
3366 return r;
3367 if (!JOURNAL_ERRNO_IS_UNAVAILABLE_FIELD(r))
3368 return r;
3369 /* Try with the next field. sd_journal_enumerate_unique() modifies state, so on the next try
3370 * we will access the next field. */
3371 }
3372}
3373
115646c7 3374_public_ void sd_journal_restart_unique(sd_journal *j) {
e046719b 3375 if (!j || journal_origin_changed(j))
3c1668da
LP
3376 return;
3377
3378 j->unique_file = NULL;
3379 j->unique_offset = 0;
360af4cf 3380 j->unique_file_lost = false;
3c1668da 3381}
85210bff 3382
eb86030e
LP
3383_public_ int sd_journal_enumerate_fields(sd_journal *j, const char **field) {
3384 int r;
3385
3386 assert_return(j, -EINVAL);
e046719b 3387 assert_return(!journal_origin_changed(j), -ECHILD);
eb86030e
LP
3388 assert_return(field, -EINVAL);
3389
3390 if (!j->fields_file) {
3391 if (j->fields_file_lost)
3392 return 0;
3393
3394 j->fields_file = ordered_hashmap_first(j->files);
3395 if (!j->fields_file)
3396 return 0;
3397
3398 j->fields_hash_table_index = 0;
3399 j->fields_offset = 0;
3400 }
3401
3402 for (;;) {
3403 JournalFile *f, *of;
eb86030e
LP
3404 uint64_t m;
3405 Object *o;
3406 size_t sz;
3407 bool found;
3408
3409 f = j->fields_file;
3410
3411 if (j->fields_offset == 0) {
3412 bool eof = false;
3413
3414 /* We are not yet positioned at any field. Let's pick the first one */
3415 r = journal_file_map_field_hash_table(f);
3416 if (r < 0)
3417 return r;
3418
3419 m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem);
3420 for (;;) {
3421 if (j->fields_hash_table_index >= m) {
3422 /* Reached the end of the hash table, go to the next file. */
3423 eof = true;
3424 break;
3425 }
3426
3427 j->fields_offset = le64toh(f->field_hash_table[j->fields_hash_table_index].head_hash_offset);
3428
3429 if (j->fields_offset != 0)
3430 break;
3431
3432 /* Empty hash table bucket, go to next one */
3433 j->fields_hash_table_index++;
3434 }
3435
3436 if (eof) {
3437 /* Proceed with next file */
3438 j->fields_file = ordered_hashmap_next(j->files, f->path);
3439 if (!j->fields_file) {
3440 *field = NULL;
3441 return 0;
3442 }
3443
3444 j->fields_offset = 0;
3445 j->fields_hash_table_index = 0;
3446 continue;
3447 }
3448
3449 } else {
3450 /* We are already positioned at a field. If so, let's figure out the next field from it */
3451
3452 r = journal_file_move_to_object(f, OBJECT_FIELD, j->fields_offset, &o);
3453 if (r < 0)
3454 return r;
3455
3456 j->fields_offset = le64toh(o->field.next_hash_offset);
3457 if (j->fields_offset == 0) {
3458 /* Reached the end of the hash table chain */
3459 j->fields_hash_table_index++;
3460 continue;
3461 }
3462 }
3463
1f133e0d 3464 /* We use OBJECT_UNUSED here, so that the iterator below doesn't remove our mmap window */
eb86030e
LP
3465 r = journal_file_move_to_object(f, OBJECT_UNUSED, j->fields_offset, &o);
3466 if (r < 0)
3467 return r;
3468
3469 /* Because we used OBJECT_UNUSED above, we need to do our type check manually */
baaa35ad
ZJS
3470 if (o->object.type != OBJECT_FIELD)
3471 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
3472 "%s:offset " OFSfmt ": object has type %i, expected %i",
3473 f->path, j->fields_offset,
3474 o->object.type, OBJECT_FIELD);
eb86030e
LP
3475
3476 sz = le64toh(o->object.size) - offsetof(Object, field.payload);
3477
3478 /* Let's see if we already returned this field name before. */
3479 found = false;
90e74a66 3480 ORDERED_HASHMAP_FOREACH(of, j->files) {
eb86030e
LP
3481 if (of == f)
3482 break;
3483
3484 /* Skip this file it didn't have any fields indexed */
3485 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) && le64toh(of->header->n_fields) <= 0)
3486 continue;
3487
27bf0ab7
DDM
3488 if (!JOURNAL_HEADER_KEYED_HASH(f->header) && !JOURNAL_HEADER_KEYED_HASH(of->header))
3489 r = journal_file_find_field_object_with_hash(of, o->field.payload, sz,
3490 le64toh(o->field.hash), NULL, NULL);
3491 else
3492 r = journal_file_find_field_object(of, o->field.payload, sz, NULL, NULL);
eb86030e
LP
3493 if (r < 0)
3494 return r;
3495 if (r > 0) {
3496 found = true;
3497 break;
3498 }
3499 }
3500
3501 if (found)
3502 continue;
3503
3504 /* Check if this is really a valid string containing no NUL byte */
3505 if (memchr(o->field.payload, 0, sz))
3506 return -EBADMSG;
3507
adbd80f5 3508 if (j->data_threshold > 0 && sz > j->data_threshold)
eb86030e
LP
3509 sz = j->data_threshold;
3510
319a4f4b 3511 if (!GREEDY_REALLOC(j->fields_buffer, sz + 1))
eb86030e
LP
3512 return -ENOMEM;
3513
3514 memcpy(j->fields_buffer, o->field.payload, sz);
3515 j->fields_buffer[sz] = 0;
3516
3517 if (!field_is_valid(j->fields_buffer))
3518 return -EBADMSG;
3519
3520 *field = j->fields_buffer;
3521 return 1;
3522 }
3523}
3524
3525_public_ void sd_journal_restart_fields(sd_journal *j) {
e046719b 3526 if (!j || journal_origin_changed(j))
eb86030e
LP
3527 return;
3528
3529 j->fields_file = NULL;
3530 j->fields_hash_table_index = 0;
3531 j->fields_offset = 0;
3532 j->fields_file_lost = false;
3533}
3534
85210bff 3535_public_ int sd_journal_reliable_fd(sd_journal *j) {
1ae464e0 3536 assert_return(j, -EINVAL);
e046719b 3537 assert_return(!journal_origin_changed(j), -ECHILD);
85210bff
LP
3538
3539 return !j->on_network;
3540}
d4205751
LP
3541
3542static char *lookup_field(const char *field, void *userdata) {
99534007 3543 sd_journal *j = ASSERT_PTR(userdata);
d4205751
LP
3544 const void *data;
3545 size_t size, d;
3546 int r;
3547
3548 assert(field);
d4205751
LP
3549
3550 r = sd_journal_get_data(j, field, &data, &size);
3551 if (r < 0 ||
3552 size > REPLACE_VAR_MAX)
3553 return strdup(field);
3554
3555 d = strlen(field) + 1;
3556
3557 return strndup((const char*) data + d, size - d);
3558}
3559
3560_public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
3561 const void *data;
3562 size_t size;
3563 sd_id128_t id;
3564 _cleanup_free_ char *text = NULL, *cid = NULL;
3565 char *t;
3566 int r;
3567
1ae464e0 3568 assert_return(j, -EINVAL);
e046719b 3569 assert_return(!journal_origin_changed(j), -ECHILD);
1ae464e0 3570 assert_return(ret, -EINVAL);
d4205751
LP
3571
3572 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
3573 if (r < 0)
3574 return r;
3575
3576 cid = strndup((const char*) data + 11, size - 11);
3577 if (!cid)
3578 return -ENOMEM;
3579
3580 r = sd_id128_from_string(cid, &id);
3581 if (r < 0)
3582 return r;
3583
fba84e12 3584 r = catalog_get(secure_getenv("SYSTEMD_CATALOG") ?: CATALOG_DATABASE, id, &text);
d4205751
LP
3585 if (r < 0)
3586 return r;
3587
3588 t = replace_var(text, lookup_field, j);
3589 if (!t)
3590 return -ENOMEM;
3591
3592 *ret = t;
3593 return 0;
3594}
8f1e860f
LP
3595
3596_public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
1ae464e0 3597 assert_return(ret, -EINVAL);
8f1e860f 3598
844ec79b 3599 return catalog_get(CATALOG_DATABASE, id, ret);
8f1e860f 3600}
93b73b06
LP
3601
3602_public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
1ae464e0 3603 assert_return(j, -EINVAL);
e046719b 3604 assert_return(!journal_origin_changed(j), -ECHILD);
93b73b06
LP
3605
3606 j->data_threshold = sz;
3607 return 0;
3608}
3609
3610_public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
1ae464e0 3611 assert_return(j, -EINVAL);
e046719b 3612 assert_return(!journal_origin_changed(j), -ECHILD);
1ae464e0 3613 assert_return(sz, -EINVAL);
93b73b06
LP
3614
3615 *sz = j->data_threshold;
3616 return 0;
3617}
39fd5b08
JS
3618
3619_public_ int sd_journal_has_runtime_files(sd_journal *j) {
3620 assert_return(j, -EINVAL);
3621
3622 return j->has_runtime_files;
3623}
3624
3625_public_ int sd_journal_has_persistent_files(sd_journal *j) {
3626 assert_return(j, -EINVAL);
3627
3628 return j->has_persistent_files;
3629}