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