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