]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/sd-journal.c
Add SPDX license identifiers to source files under the LGPL
[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) {
468b21de 822 JournalFile *f, *new_file = NULL;
cec736d2 823 Iterator i;
a002d44b 824 Object *o;
87d2c1ff
LP
825 int r;
826
1ae464e0
TA
827 assert_return(j, -EINVAL);
828 assert_return(!journal_pid_changed(j), -ECHILD);
87d2c1ff 829
c1f906bd 830 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
de190aef 831 bool found;
87d2c1ff 832
58439db4 833 r = next_beyond_location(j, f, direction);
e590af26 834 if (r < 0) {
da927ba9 835 log_debug_errno(r, "Can't iterate through %s, ignoring: %m", f->path);
a9a245c1 836 remove_file_real(j, f);
e590af26 837 continue;
6573ef05
MS
838 } else if (r == 0) {
839 f->location_type = LOCATION_TAIL;
cec736d2 840 continue;
6573ef05 841 }
87d2c1ff 842
468b21de 843 if (!new_file)
de190aef
LP
844 found = true;
845 else {
846 int k;
847
d8ae66d7 848 k = journal_file_compare_locations(f, new_file);
de190aef 849
bc302926 850 found = direction == DIRECTION_DOWN ? k < 0 : k > 0;
de190aef
LP
851 }
852
e499c999 853 if (found)
468b21de 854 new_file = f;
87d2c1ff
LP
855 }
856
468b21de 857 if (!new_file)
de190aef 858 return 0;
ae2cc8ef 859
e499c999 860 r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_file->current_offset, &o);
468b21de
LP
861 if (r < 0)
862 return r;
863
1eb6332d 864 set_location(j, new_file, o);
ae2cc8ef 865
de190aef
LP
866 return 1;
867}
ae2cc8ef 868
a5344d2c 869_public_ int sd_journal_next(sd_journal *j) {
de190aef
LP
870 return real_journal_next(j, DIRECTION_DOWN);
871}
ae2cc8ef 872
a5344d2c 873_public_ int sd_journal_previous(sd_journal *j) {
de190aef
LP
874 return real_journal_next(j, DIRECTION_UP);
875}
ae2cc8ef 876
6f003b43 877static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
de190aef 878 int c = 0, r;
ae2cc8ef 879
1ae464e0
TA
880 assert_return(j, -EINVAL);
881 assert_return(!journal_pid_changed(j), -ECHILD);
de190aef 882
6f003b43
LP
883 if (skip == 0) {
884 /* If this is not a discrete skip, then at least
885 * resolve the current location */
04884354
LK
886 if (j->current_location.type != LOCATION_DISCRETE) {
887 r = real_journal_next(j, direction);
888 if (r < 0)
889 return r;
890 }
6f003b43
LP
891
892 return 0;
893 }
894
895 do {
896 r = real_journal_next(j, direction);
de190aef
LP
897 if (r < 0)
898 return r;
899
900 if (r == 0)
901 return c;
902
903 skip--;
904 c++;
6f003b43 905 } while (skip > 0);
87d2c1ff 906
de190aef 907 return c;
87d2c1ff
LP
908}
909
6f003b43
LP
910_public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
911 return real_journal_next_skip(j, DIRECTION_DOWN, skip);
912}
de190aef 913
6f003b43
LP
914_public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
915 return real_journal_next_skip(j, DIRECTION_UP, skip);
87d2c1ff
LP
916}
917
a5344d2c 918_public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
cec736d2 919 Object *o;
87d2c1ff 920 int r;
3fbf9cbb 921 char bid[33], sid[33];
87d2c1ff 922
1ae464e0
TA
923 assert_return(j, -EINVAL);
924 assert_return(!journal_pid_changed(j), -ECHILD);
925 assert_return(cursor, -EINVAL);
87d2c1ff 926
3fbf9cbb
LP
927 if (!j->current_file || j->current_file->current_offset <= 0)
928 return -EADDRNOTAVAIL;
87d2c1ff 929
de190aef 930 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
87d2c1ff
LP
931 if (r < 0)
932 return r;
933
3fbf9cbb
LP
934 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
935 sd_id128_to_string(o->entry.boot_id, bid);
87d2c1ff 936
3fbf9cbb 937 if (asprintf(cursor,
507f22bd
ZJS
938 "s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64,
939 sid, le64toh(o->entry.seqnum),
940 bid, le64toh(o->entry.monotonic),
941 le64toh(o->entry.realtime),
942 le64toh(o->entry.xor_hash)) < 0)
3fbf9cbb 943 return -ENOMEM;
87d2c1ff 944
6f47ad30 945 return 0;
87d2c1ff
LP
946}
947
a5344d2c 948_public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
a2a5291b 949 const char *word, *state;
de190aef 950 size_t l;
de190aef
LP
951 unsigned long long seqnum, monotonic, realtime, xor_hash;
952 bool
953 seqnum_id_set = false,
954 seqnum_set = false,
955 boot_id_set = false,
956 monotonic_set = false,
957 realtime_set = false,
958 xor_hash_set = false;
959 sd_id128_t seqnum_id, boot_id;
960
1ae464e0
TA
961 assert_return(j, -EINVAL);
962 assert_return(!journal_pid_changed(j), -ECHILD);
963 assert_return(!isempty(cursor), -EINVAL);
de190aef 964
a2a5291b 965 FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
de190aef
LP
966 char *item;
967 int k = 0;
968
a2a5291b 969 if (l < 2 || word[1] != '=')
de190aef
LP
970 return -EINVAL;
971
a2a5291b 972 item = strndup(word, l);
de190aef
LP
973 if (!item)
974 return -ENOMEM;
975
a2a5291b 976 switch (word[0]) {
de190aef
LP
977
978 case 's':
979 seqnum_id_set = true;
be3ea5ea 980 k = sd_id128_from_string(item+2, &seqnum_id);
de190aef
LP
981 break;
982
983 case 'i':
984 seqnum_set = true;
be3ea5ea 985 if (sscanf(item+2, "%llx", &seqnum) != 1)
de190aef
LP
986 k = -EINVAL;
987 break;
988
989 case 'b':
990 boot_id_set = true;
be3ea5ea 991 k = sd_id128_from_string(item+2, &boot_id);
de190aef
LP
992 break;
993
994 case 'm':
995 monotonic_set = true;
be3ea5ea 996 if (sscanf(item+2, "%llx", &monotonic) != 1)
de190aef
LP
997 k = -EINVAL;
998 break;
999
1000 case 't':
1001 realtime_set = true;
be3ea5ea 1002 if (sscanf(item+2, "%llx", &realtime) != 1)
de190aef
LP
1003 k = -EINVAL;
1004 break;
1005
1006 case 'x':
1007 xor_hash_set = true;
be3ea5ea 1008 if (sscanf(item+2, "%llx", &xor_hash) != 1)
de190aef
LP
1009 k = -EINVAL;
1010 break;
1011 }
1012
1013 free(item);
1014
1015 if (k < 0)
1016 return k;
1017 }
1018
1019 if ((!seqnum_set || !seqnum_id_set) &&
1020 (!monotonic_set || !boot_id_set) &&
1021 !realtime_set)
1022 return -EINVAL;
1023
1024 reset_location(j);
1025
a87247dd 1026 j->current_location.type = LOCATION_SEEK;
de190aef
LP
1027
1028 if (realtime_set) {
1029 j->current_location.realtime = (uint64_t) realtime;
1030 j->current_location.realtime_set = true;
1031 }
1032
1033 if (seqnum_set && seqnum_id_set) {
1034 j->current_location.seqnum = (uint64_t) seqnum;
1035 j->current_location.seqnum_id = seqnum_id;
1036 j->current_location.seqnum_set = true;
1037 }
1038
1039 if (monotonic_set && boot_id_set) {
1040 j->current_location.monotonic = (uint64_t) monotonic;
1041 j->current_location.boot_id = boot_id;
1042 j->current_location.monotonic_set = true;
1043 }
1044
1045 if (xor_hash_set) {
1046 j->current_location.xor_hash = (uint64_t) xor_hash;
1047 j->current_location.xor_hash_set = true;
1048 }
1049
1050 return 0;
1051}
1052
c6511e85
LP
1053_public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
1054 int r;
c6511e85
LP
1055 Object *o;
1056
1ae464e0
TA
1057 assert_return(j, -EINVAL);
1058 assert_return(!journal_pid_changed(j), -ECHILD);
1059 assert_return(!isempty(cursor), -EINVAL);
c6511e85
LP
1060
1061 if (!j->current_file || j->current_file->current_offset <= 0)
1062 return -EADDRNOTAVAIL;
1063
1064 r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1065 if (r < 0)
1066 return r;
1067
9ed794a3 1068 for (;;) {
c6511e85 1069 _cleanup_free_ char *item = NULL;
c6511e85 1070 unsigned long long ll;
7294bb5b 1071 sd_id128_t id;
c6511e85
LP
1072 int k = 0;
1073
7294bb5b
SS
1074 r = extract_first_word(&cursor, &item, ";", EXTRACT_DONT_COALESCE_SEPARATORS);
1075 if (r < 0)
1076 return r;
c6511e85 1077
7294bb5b
SS
1078 if (r == 0)
1079 break;
c6511e85 1080
7294bb5b
SS
1081 if (strlen(item) < 2 || item[1] != '=')
1082 return -EINVAL;
1083
1084 switch (item[0]) {
c6511e85
LP
1085
1086 case 's':
1087 k = sd_id128_from_string(item+2, &id);
1088 if (k < 0)
1089 return k;
1090 if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
1091 return 0;
1092 break;
1093
1094 case 'i':
1095 if (sscanf(item+2, "%llx", &ll) != 1)
1096 return -EINVAL;
1097 if (ll != le64toh(o->entry.seqnum))
1098 return 0;
1099 break;
1100
1101 case 'b':
1102 k = sd_id128_from_string(item+2, &id);
1103 if (k < 0)
1104 return k;
1105 if (!sd_id128_equal(id, o->entry.boot_id))
1106 return 0;
1107 break;
1108
1109 case 'm':
1110 if (sscanf(item+2, "%llx", &ll) != 1)
1111 return -EINVAL;
1112 if (ll != le64toh(o->entry.monotonic))
1113 return 0;
1114 break;
1115
1116 case 't':
1117 if (sscanf(item+2, "%llx", &ll) != 1)
1118 return -EINVAL;
1119 if (ll != le64toh(o->entry.realtime))
1120 return 0;
1121 break;
1122
1123 case 'x':
1124 if (sscanf(item+2, "%llx", &ll) != 1)
1125 return -EINVAL;
1126 if (ll != le64toh(o->entry.xor_hash))
1127 return 0;
1128 break;
1129 }
1130 }
1131
1132 return 1;
1133}
1134
1135
a5344d2c 1136_public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
1ae464e0
TA
1137 assert_return(j, -EINVAL);
1138 assert_return(!journal_pid_changed(j), -ECHILD);
de190aef
LP
1139
1140 reset_location(j);
a87247dd 1141 j->current_location.type = LOCATION_SEEK;
de190aef
LP
1142 j->current_location.boot_id = boot_id;
1143 j->current_location.monotonic = usec;
1144 j->current_location.monotonic_set = true;
1145
1146 return 0;
1147}
1148
a5344d2c 1149_public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
1ae464e0
TA
1150 assert_return(j, -EINVAL);
1151 assert_return(!journal_pid_changed(j), -ECHILD);
de190aef
LP
1152
1153 reset_location(j);
a87247dd 1154 j->current_location.type = LOCATION_SEEK;
de190aef
LP
1155 j->current_location.realtime = usec;
1156 j->current_location.realtime_set = true;
1157
1158 return 0;
1159}
1160
a5344d2c 1161_public_ int sd_journal_seek_head(sd_journal *j) {
1ae464e0
TA
1162 assert_return(j, -EINVAL);
1163 assert_return(!journal_pid_changed(j), -ECHILD);
de190aef
LP
1164
1165 reset_location(j);
1166 j->current_location.type = LOCATION_HEAD;
1167
1168 return 0;
1169}
1170
a5344d2c 1171_public_ int sd_journal_seek_tail(sd_journal *j) {
1ae464e0
TA
1172 assert_return(j, -EINVAL);
1173 assert_return(!journal_pid_changed(j), -ECHILD);
de190aef
LP
1174
1175 reset_location(j);
1176 j->current_location.type = LOCATION_TAIL;
1177
1178 return 0;
87d2c1ff
LP
1179}
1180
85210bff
LP
1181static void check_network(sd_journal *j, int fd) {
1182 struct statfs sfs;
1183
1184 assert(j);
1185
1186 if (j->on_network)
1187 return;
1188
1189 if (fstatfs(fd, &sfs) < 0)
1190 return;
1191
1192 j->on_network =
c51cf056
ZJS
1193 F_TYPE_EQUAL(sfs.f_type, CIFS_MAGIC_NUMBER) ||
1194 F_TYPE_EQUAL(sfs.f_type, CODA_SUPER_MAGIC) ||
1195 F_TYPE_EQUAL(sfs.f_type, NCP_SUPER_MAGIC) ||
1196 F_TYPE_EQUAL(sfs.f_type, NFS_SUPER_MAGIC) ||
1197 F_TYPE_EQUAL(sfs.f_type, SMB_SUPER_MAGIC);
85210bff
LP
1198}
1199
a688baa8
ZJS
1200static bool file_has_type_prefix(const char *prefix, const char *filename) {
1201 const char *full, *tilded, *atted;
1202
63c372cb
LP
1203 full = strjoina(prefix, ".journal");
1204 tilded = strjoina(full, "~");
1205 atted = strjoina(prefix, "@");
a688baa8
ZJS
1206
1207 return streq(filename, full) ||
1208 streq(filename, tilded) ||
1209 startswith(filename, atted);
1210}
1211
1212static bool file_type_wanted(int flags, const char *filename) {
d617408e
LP
1213 assert(filename);
1214
a688baa8
ZJS
1215 if (!endswith(filename, ".journal") && !endswith(filename, ".journal~"))
1216 return false;
1217
1218 /* no flags set → every type is OK */
1219 if (!(flags & (SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)))
1220 return true;
1221
1222 if (flags & SD_JOURNAL_SYSTEM && file_has_type_prefix("system", filename))
1223 return true;
1224
1225 if (flags & SD_JOURNAL_CURRENT_USER) {
1226 char prefix[5 + DECIMAL_STR_MAX(uid_t) + 1];
1227
5ffa8c81 1228 xsprintf(prefix, "user-"UID_FMT, getuid());
a688baa8
ZJS
1229
1230 if (file_has_type_prefix(prefix, filename))
1231 return true;
1232 }
1233
1234 return false;
1235}
1236
5d1ce257
LP
1237static bool path_has_prefix(sd_journal *j, const char *path, const char *prefix) {
1238 assert(j);
1239 assert(path);
1240 assert(prefix);
1241
1242 if (j->toplevel_fd >= 0)
1243 return false;
1244
1245 return path_startswith(path, prefix);
1246}
1247
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
1259static int add_any_file(sd_journal *j, int fd, const char *path) {
39883f62 1260 JournalFile *f = NULL;
5d1ce257 1261 bool close_fd = false;
d617408e 1262 int r, k;
3fbf9cbb
LP
1263
1264 assert(j);
5d1ce257 1265 assert(fd >= 0 || path);
3fbf9cbb 1266
5d1ce257 1267 if (path && ordered_hashmap_get(j->files, path))
50f20cfd 1268 return 0;
50f20cfd 1269
9e8abdf0 1270 if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
d617408e
LP
1271 log_debug("Too many open journal files, not adding %s.", path);
1272 r = -ETOOMANYREFS;
1273 goto fail;
9e8abdf0
LP
1274 }
1275
5d1ce257
LP
1276 if (fd < 0 && j->toplevel_fd >= 0) {
1277
1278 /* If there's a top-level fd defined, open the file relative to this now. (Make the path relative,
1279 * explicitly, since otherwise openat() ignores the first argument.) */
1280
1281 fd = openat(j->toplevel_fd, skip_slash(path), O_RDONLY|O_CLOEXEC);
1282 if (fd < 0) {
1283 r = log_debug_errno(errno, "Failed to open journal file %s: %m", path);
1284 goto fail;
1285 }
1286
1287 close_fd = true;
1288 }
1289
1290 r = journal_file_open(fd, path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, NULL, &f);
d617408e 1291 if (r < 0) {
5d1ce257
LP
1292 if (close_fd)
1293 safe_close(fd);
d617408e
LP
1294 log_debug_errno(r, "Failed to open journal file %s: %m", path);
1295 goto fail;
1296 }
3fbf9cbb 1297
72f59706 1298 /* journal_file_dump(f); */
de190aef 1299
c1f906bd 1300 r = ordered_hashmap_put(j->files, f->path, f);
3fbf9cbb 1301 if (r < 0) {
5d1ce257 1302 f->close_fd = close_fd;
69a3a6fd 1303 (void) journal_file_close(f);
d617408e 1304 goto fail;
3fbf9cbb
LP
1305 }
1306
5d1ce257
LP
1307 if (!j->has_runtime_files && path_has_prefix(j, f->path, "/run"))
1308 j->has_runtime_files = true;
1309 else if (!j->has_persistent_files && path_has_prefix(j, f->path, "/var"))
1310 j->has_persistent_files = true;
1311
5ec76417 1312 log_debug("File %s added.", f->path);
a50d7d43 1313
85210bff
LP
1314 check_network(j, f->fd);
1315
313cefa1 1316 j->current_invalidate_counter++;
a963990f 1317
50f20cfd 1318 return 0;
d617408e
LP
1319
1320fail:
5768d259 1321 k = journal_put_error(j, r, path);
d617408e
LP
1322 if (k < 0)
1323 return k;
1324
1325 return r;
50f20cfd
LP
1326}
1327
5302ebe1 1328static int add_file(sd_journal *j, const char *prefix, const char *filename) {
d617408e 1329 const char *path;
5302ebe1
ZJS
1330
1331 assert(j);
1332 assert(prefix);
1333 assert(filename);
1334
5d1ce257 1335 if (j->no_new_files)
5302ebe1
ZJS
1336 return 0;
1337
5d1ce257
LP
1338 if (!file_type_wanted(j->flags, filename))
1339 return 0;
39fd5b08 1340
5d1ce257
LP
1341 path = strjoina(prefix, "/", filename);
1342 return add_any_file(j, -1, path);
5302ebe1
ZJS
1343}
1344
d617408e
LP
1345static void remove_file(sd_journal *j, const char *prefix, const char *filename) {
1346 const char *path;
50f20cfd
LP
1347 JournalFile *f;
1348
1349 assert(j);
1350 assert(prefix);
1351 assert(filename);
1352
d617408e 1353 path = strjoina(prefix, "/", filename);
c1f906bd 1354 f = ordered_hashmap_get(j->files, path);
50f20cfd 1355 if (!f)
d617408e 1356 return;
50f20cfd 1357
a9a245c1 1358 remove_file_real(j, f);
a9a245c1
ZJS
1359}
1360
1361static void remove_file_real(sd_journal *j, JournalFile *f) {
1362 assert(j);
1363 assert(f);
1364
c1f906bd 1365 ordered_hashmap_remove(j->files, f->path);
44a5fa34 1366
5ec76417 1367 log_debug("File %s removed.", f->path);
44a5fa34 1368
3c1668da
LP
1369 if (j->current_file == f) {
1370 j->current_file = NULL;
1371 j->current_field = 0;
1372 }
1373
1374 if (j->unique_file == f) {
360af4cf 1375 /* Jump to the next unique_file or NULL if that one was last */
c1f906bd 1376 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
3c1668da 1377 j->unique_offset = 0;
360af4cf
ZJS
1378 if (!j->unique_file)
1379 j->unique_file_lost = true;
3c1668da
LP
1380 }
1381
eb86030e
LP
1382 if (j->fields_file == f) {
1383 j->fields_file = ordered_hashmap_next(j->files, j->fields_file->path);
1384 j->fields_offset = 0;
1385 if (!j->fields_file)
1386 j->fields_file_lost = true;
1387 }
1388
69a3a6fd 1389 (void) journal_file_close(f);
50f20cfd 1390
313cefa1 1391 j->current_invalidate_counter++;
3fbf9cbb
LP
1392}
1393
d617408e
LP
1394static int dirname_is_machine_id(const char *fn) {
1395 sd_id128_t id, machine;
1396 int r;
1397
1398 r = sd_id128_get_machine(&machine);
1399 if (r < 0)
1400 return r;
1401
1402 r = sd_id128_from_string(fn, &id);
1403 if (r < 0)
1404 return r;
1405
1406 return sd_id128_equal(id, machine);
1407}
1408
a963990f 1409static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
7fd1b19b 1410 _cleanup_free_ char *path = NULL;
7fd1b19b 1411 _cleanup_closedir_ DIR *d = NULL;
d617408e 1412 struct dirent *de = NULL;
a963990f 1413 Directory *m;
d617408e 1414 int r, k;
3fbf9cbb
LP
1415
1416 assert(j);
1417 assert(prefix);
3fbf9cbb 1418
5d1ce257
LP
1419 /* Adds a journal file directory to watch. If the directory is already tracked this updates the inotify watch
1420 * and reenumerates directory contents */
d95b1fb3 1421
5d1ce257 1422 if (dirname)
605405c6 1423 path = strjoin(prefix, "/", dirname);
5d1ce257
LP
1424 else
1425 path = strdup(prefix);
d617408e
LP
1426 if (!path) {
1427 r = -ENOMEM;
1428 goto fail;
1429 }
3fbf9cbb 1430
5d1ce257
LP
1431 log_debug("Considering directory %s.", path);
1432
1433 /* We consider everything local that is in a directory for the local machine ID, or that is stored in /run */
1434 if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1435 !((dirname && dirname_is_machine_id(dirname) > 0) || path_has_prefix(j, path, "/run")))
1436 return 0;
1437
1438
1439 if (j->toplevel_fd < 0)
1440 d = opendir(path);
1441 else
61233823 1442 /* Open the specified directory relative to the toplevel fd. Enforce that the path specified is
5d1ce257
LP
1443 * relative, by dropping the initial slash */
1444 d = xopendirat(j->toplevel_fd, skip_slash(path), 0);
3fbf9cbb 1445 if (!d) {
d617408e
LP
1446 r = log_debug_errno(errno, "Failed to open directory %s: %m", path);
1447 goto fail;
3fbf9cbb
LP
1448 }
1449
a963990f
LP
1450 m = hashmap_get(j->directories_by_path, path);
1451 if (!m) {
1452 m = new0(Directory, 1);
d617408e
LP
1453 if (!m) {
1454 r = -ENOMEM;
1455 goto fail;
1456 }
a963990f
LP
1457
1458 m->is_root = false;
1459 m->path = path;
1460
1461 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
a963990f 1462 free(m);
d617408e
LP
1463 r = -ENOMEM;
1464 goto fail;
a963990f
LP
1465 }
1466
a50d7d43 1467 path = NULL; /* avoid freeing in cleanup */
313cefa1 1468 j->current_invalidate_counter++;
a963990f 1469
5ec76417 1470 log_debug("Directory %s added.", m->path);
a963990f 1471
a50d7d43 1472 } else if (m->is_root)
a963990f 1473 return 0;
a963990f
LP
1474
1475 if (m->wd <= 0 && j->inotify_fd >= 0) {
5d1ce257 1476 /* Watch this directory, if it not being watched yet. */
a963990f 1477
430fbf8e
LP
1478 m->wd = inotify_add_watch_fd(j->inotify_fd, dirfd(d),
1479 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1480 IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
1481 IN_ONLYDIR);
a963990f
LP
1482
1483 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1484 inotify_rm_watch(j->inotify_fd, m->wd);
1485 }
1486
f6377261 1487 FOREACH_DIRENT_ALL(de, d, r = log_debug_errno(errno, "Failed to read directory %s: %m", m->path); goto fail) {
a963990f 1488
de2c3907 1489 if (dirent_is_file_with_suffix(de, ".journal") ||
d617408e
LP
1490 dirent_is_file_with_suffix(de, ".journal~"))
1491 (void) add_file(j, m->path, de->d_name);
a963990f
LP
1492 }
1493
85210bff
LP
1494 check_network(j, dirfd(d));
1495
a963990f 1496 return 0;
d617408e
LP
1497
1498fail:
5d1ce257 1499 k = journal_put_error(j, r, path ?: prefix);
d617408e
LP
1500 if (k < 0)
1501 return k;
1502
1503 return r;
a963990f
LP
1504}
1505
d617408e 1506static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) {
5d1ce257 1507
7fd1b19b 1508 _cleanup_closedir_ DIR *d = NULL;
d617408e 1509 struct dirent *de;
a963990f 1510 Directory *m;
d617408e 1511 int r, k;
a963990f
LP
1512
1513 assert(j);
a963990f 1514
5d1ce257
LP
1515 /* Adds a root directory to our set of directories to use. If the root directory is already in the set, we
1516 * update the inotify logic, and renumerate the directory entries. This call may hence be called to initially
1517 * populate the set, as well as to update it later. */
a963990f 1518
5d1ce257
LP
1519 if (p) {
1520 /* If there's a path specified, use it. */
b6741478 1521
5d1ce257
LP
1522 if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1523 !path_has_prefix(j, p, "/run"))
1524 return -EINVAL;
d617408e 1525
5d1ce257
LP
1526 if (j->prefix)
1527 p = strjoina(j->prefix, p);
1528
1529 if (j->toplevel_fd < 0)
1530 d = opendir(p);
1531 else
1532 d = xopendirat(j->toplevel_fd, skip_slash(p), 0);
1533
1534 if (!d) {
1535 if (errno == ENOENT && missing_ok)
1536 return 0;
1537
1538 r = log_debug_errno(errno, "Failed to open root directory %s: %m", p);
1539 goto fail;
1540 }
1541 } else {
1542 int dfd;
1543
1544 /* If there's no path specified, then we use the top-level fd itself. We duplicate the fd here, since
1545 * opendir() will take possession of the fd, and close it, which we don't want. */
1546
1547 p = "."; /* store this as "." in the directories hashmap */
1548
1549 dfd = fcntl(j->toplevel_fd, F_DUPFD_CLOEXEC, 3);
1550 if (dfd < 0) {
1551 r = -errno;
1552 goto fail;
1553 }
1554
1555 d = fdopendir(dfd);
1556 if (!d) {
1557 r = -errno;
1558 safe_close(dfd);
1559 goto fail;
1560 }
1561
1562 rewinddir(d);
d617408e 1563 }
a963990f
LP
1564
1565 m = hashmap_get(j->directories_by_path, p);
1566 if (!m) {
1567 m = new0(Directory, 1);
d617408e
LP
1568 if (!m) {
1569 r = -ENOMEM;
1570 goto fail;
1571 }
a963990f
LP
1572
1573 m->is_root = true;
5d1ce257 1574
a963990f
LP
1575 m->path = strdup(p);
1576 if (!m->path) {
a963990f 1577 free(m);
d617408e
LP
1578 r = -ENOMEM;
1579 goto fail;
a963990f
LP
1580 }
1581
1582 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
a963990f
LP
1583 free(m->path);
1584 free(m);
d617408e
LP
1585 r = -ENOMEM;
1586 goto fail;
a963990f
LP
1587 }
1588
313cefa1 1589 j->current_invalidate_counter++;
a963990f 1590
5ec76417 1591 log_debug("Root directory %s added.", m->path);
a963990f 1592
a50d7d43 1593 } else if (!m->is_root)
a963990f 1594 return 0;
50f20cfd 1595
a963990f
LP
1596 if (m->wd <= 0 && j->inotify_fd >= 0) {
1597
430fbf8e 1598 m->wd = inotify_add_watch_fd(j->inotify_fd, dirfd(d),
a963990f 1599 IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
4a842cad 1600 IN_ONLYDIR);
a963990f
LP
1601
1602 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
1603 inotify_rm_watch(j->inotify_fd, m->wd);
1604 }
50f20cfd 1605
5302ebe1
ZJS
1606 if (j->no_new_files)
1607 return 0;
1608
f6377261 1609 FOREACH_DIRENT_ALL(de, d, r = log_debug_errno(errno, "Failed to read directory %s: %m", m->path); goto fail) {
a963990f 1610 sd_id128_t id;
3fbf9cbb 1611
de2c3907 1612 if (dirent_is_file_with_suffix(de, ".journal") ||
d617408e
LP
1613 dirent_is_file_with_suffix(de, ".journal~"))
1614 (void) add_file(j, m->path, de->d_name);
1615 else if (IN_SET(de->d_type, DT_DIR, DT_LNK, DT_UNKNOWN) &&
1616 sd_id128_from_string(de->d_name, &id) >= 0)
1617 (void) add_directory(j, m->path, de->d_name);
3fbf9cbb
LP
1618 }
1619
85210bff
LP
1620 check_network(j, dirfd(d));
1621
a963990f 1622 return 0;
d617408e
LP
1623
1624fail:
5768d259 1625 k = journal_put_error(j, r, p);
d617408e
LP
1626 if (k < 0)
1627 return k;
1628
1629 return r;
a963990f
LP
1630}
1631
b2b46f91 1632static void remove_directory(sd_journal *j, Directory *d) {
a963990f
LP
1633 assert(j);
1634
1635 if (d->wd > 0) {
1636 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
1637
1638 if (j->inotify_fd >= 0)
1639 inotify_rm_watch(j->inotify_fd, d->wd);
1640 }
1641
1642 hashmap_remove(j->directories_by_path, d->path);
1643
1644 if (d->is_root)
5ec76417 1645 log_debug("Root directory %s removed.", d->path);
a963990f 1646 else
5ec76417 1647 log_debug("Directory %s removed.", d->path);
a963990f
LP
1648
1649 free(d->path);
1650 free(d);
3fbf9cbb
LP
1651}
1652
89739579 1653static int add_search_paths(sd_journal *j) {
d617408e
LP
1654
1655 static const char search_paths[] =
a963990f
LP
1656 "/run/log/journal\0"
1657 "/var/log/journal\0";
1658 const char *p;
50f20cfd
LP
1659
1660 assert(j);
50f20cfd 1661
a963990f
LP
1662 /* We ignore most errors here, since the idea is to only open
1663 * what's actually accessible, and ignore the rest. */
50f20cfd 1664
d617408e
LP
1665 NULSTR_FOREACH(p, search_paths)
1666 (void) add_root_directory(j, p, true);
50f20cfd 1667
574b77ef
MB
1668 if (!(j->flags & SD_JOURNAL_LOCAL_ONLY))
1669 (void) add_root_directory(j, "/var/log/journal/remote", true);
1670
a963990f 1671 return 0;
50f20cfd
LP
1672}
1673
5302ebe1
ZJS
1674static int add_current_paths(sd_journal *j) {
1675 Iterator i;
1676 JournalFile *f;
1677
1678 assert(j);
1679 assert(j->no_new_files);
1680
5d1ce257 1681 /* Simply adds all directories for files we have open as directories. We don't expect errors here, so we
5302ebe1
ZJS
1682 * treat them as fatal. */
1683
c1f906bd 1684 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
5302ebe1 1685 _cleanup_free_ char *dir;
e9174f29 1686 int r;
5302ebe1
ZJS
1687
1688 dir = dirname_malloc(f->path);
1689 if (!dir)
1690 return -ENOMEM;
1691
5d1ce257 1692 r = add_directory(j, dir, NULL);
d617408e 1693 if (r < 0)
5302ebe1 1694 return r;
5302ebe1
ZJS
1695 }
1696
1697 return 0;
1698}
1699
a963990f 1700static int allocate_inotify(sd_journal *j) {
50f20cfd 1701 assert(j);
50f20cfd 1702
a963990f
LP
1703 if (j->inotify_fd < 0) {
1704 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
1705 if (j->inotify_fd < 0)
1706 return -errno;
1707 }
50f20cfd 1708
cb306f5d 1709 return hashmap_ensure_allocated(&j->directories_by_wd, NULL);
50f20cfd
LP
1710}
1711
7827b1a1 1712static sd_journal *journal_new(int flags, const char *path) {
a963990f 1713 sd_journal *j;
50f20cfd 1714
a963990f
LP
1715 j = new0(sd_journal, 1);
1716 if (!j)
1717 return NULL;
50f20cfd 1718
df0ff127 1719 j->original_pid = getpid_cached();
5d1ce257 1720 j->toplevel_fd = -1;
a963990f
LP
1721 j->inotify_fd = -1;
1722 j->flags = flags;
93b73b06 1723 j->data_threshold = DEFAULT_DATA_THRESHOLD;
50f20cfd 1724
7827b1a1 1725 if (path) {
16fefe90
ZJS
1726 char *t;
1727
1728 t = strdup(path);
1729 if (!t)
6180fc61 1730 goto fail;
16fefe90
ZJS
1731
1732 if (flags & SD_JOURNAL_OS_ROOT)
1733 j->prefix = t;
1734 else
1735 j->path = t;
7827b1a1
LP
1736 }
1737
c1f906bd 1738 j->files = ordered_hashmap_new(&string_hash_ops);
d5099efc 1739 j->directories_by_path = hashmap_new(&string_hash_ops);
84168d80 1740 j->mmap = mmap_cache_new();
3ac251b8 1741 if (!j->files || !j->directories_by_path || !j->mmap)
6180fc61 1742 goto fail;
16e9f408 1743
a963990f 1744 return j;
6180fc61
ZJS
1745
1746fail:
1747 sd_journal_close(j);
1748 return NULL;
50f20cfd
LP
1749}
1750
1aaa68f5
ZJS
1751#define OPEN_ALLOWED_FLAGS \
1752 (SD_JOURNAL_LOCAL_ONLY | \
1753 SD_JOURNAL_RUNTIME_ONLY | \
1754 SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)
1755
a5344d2c 1756_public_ int sd_journal_open(sd_journal **ret, int flags) {
87d2c1ff 1757 sd_journal *j;
3fbf9cbb 1758 int r;
87d2c1ff 1759
1ae464e0 1760 assert_return(ret, -EINVAL);
1aaa68f5 1761 assert_return((flags & ~OPEN_ALLOWED_FLAGS) == 0, -EINVAL);
87d2c1ff 1762
7827b1a1 1763 j = journal_new(flags, NULL);
87d2c1ff
LP
1764 if (!j)
1765 return -ENOMEM;
1766
89739579 1767 r = add_search_paths(j);
a963990f 1768 if (r < 0)
50f20cfd 1769 goto fail;
50f20cfd 1770
a963990f
LP
1771 *ret = j;
1772 return 0;
cf244689 1773
a963990f
LP
1774fail:
1775 sd_journal_close(j);
87d2c1ff 1776
a963990f
LP
1777 return r;
1778}
50f20cfd 1779
1aaa68f5
ZJS
1780#define OPEN_CONTAINER_ALLOWED_FLAGS \
1781 (SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_SYSTEM)
1782
b6741478
LP
1783_public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
1784 _cleanup_free_ char *root = NULL, *class = NULL;
1785 sd_journal *j;
1786 char *p;
1787 int r;
1788
2daa9cbd
LP
1789 /* This is pretty much deprecated, people should use machined's OpenMachineRootDirectory() call instead in
1790 * combination with sd_journal_open_directory_fd(). */
1791
b6741478
LP
1792 assert_return(machine, -EINVAL);
1793 assert_return(ret, -EINVAL);
1aaa68f5 1794 assert_return((flags & ~OPEN_CONTAINER_ALLOWED_FLAGS) == 0, -EINVAL);
affcf189 1795 assert_return(machine_name_is_valid(machine), -EINVAL);
b6741478 1796
63c372cb 1797 p = strjoina("/run/systemd/machines/", machine);
b6741478
LP
1798 r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL);
1799 if (r == -ENOENT)
1800 return -EHOSTDOWN;
1801 if (r < 0)
1802 return r;
1803 if (!root)
1804 return -ENODATA;
1805
1806 if (!streq_ptr(class, "container"))
1807 return -EIO;
1808
16fefe90 1809 j = journal_new(flags, root);
b6741478
LP
1810 if (!j)
1811 return -ENOMEM;
1812
89739579 1813 r = add_search_paths(j);
b6741478
LP
1814 if (r < 0)
1815 goto fail;
1816
1817 *ret = j;
1818 return 0;
1819
1820fail:
1821 sd_journal_close(j);
1822 return r;
1823}
1824
1aaa68f5 1825#define OPEN_DIRECTORY_ALLOWED_FLAGS \
10752e82
ZJS
1826 (SD_JOURNAL_OS_ROOT | \
1827 SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER )
1aaa68f5 1828
a963990f
LP
1829_public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1830 sd_journal *j;
1831 int r;
87d2c1ff 1832
1ae464e0
TA
1833 assert_return(ret, -EINVAL);
1834 assert_return(path, -EINVAL);
1aaa68f5 1835 assert_return((flags & ~OPEN_DIRECTORY_ALLOWED_FLAGS) == 0, -EINVAL);
87d2c1ff 1836
7827b1a1 1837 j = journal_new(flags, path);
a963990f
LP
1838 if (!j)
1839 return -ENOMEM;
3fbf9cbb 1840
d077390c
LP
1841 if (flags & SD_JOURNAL_OS_ROOT)
1842 r = add_search_paths(j);
1843 else
1844 r = add_root_directory(j, path, false);
d617408e 1845 if (r < 0)
a963990f 1846 goto fail;
87d2c1ff
LP
1847
1848 *ret = j;
1849 return 0;
1850
1851fail:
1852 sd_journal_close(j);
87d2c1ff 1853 return r;
a963990f 1854}
87d2c1ff 1855
5302ebe1
ZJS
1856_public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
1857 sd_journal *j;
1858 const char **path;
1859 int r;
1860
1ae464e0
TA
1861 assert_return(ret, -EINVAL);
1862 assert_return(flags == 0, -EINVAL);
5302ebe1
ZJS
1863
1864 j = journal_new(flags, NULL);
1865 if (!j)
1866 return -ENOMEM;
1867
1868 STRV_FOREACH(path, paths) {
5d1ce257 1869 r = add_any_file(j, -1, *path);
d617408e 1870 if (r < 0)
5302ebe1 1871 goto fail;
5302ebe1
ZJS
1872 }
1873
1874 j->no_new_files = true;
1875
1876 *ret = j;
1877 return 0;
1878
1879fail:
1880 sd_journal_close(j);
5d1ce257
LP
1881 return r;
1882}
1883
1aaa68f5 1884#define OPEN_DIRECTORY_FD_ALLOWED_FLAGS \
10752e82
ZJS
1885 (SD_JOURNAL_OS_ROOT | \
1886 SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER )
1aaa68f5 1887
5d1ce257
LP
1888_public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) {
1889 sd_journal *j;
1890 struct stat st;
1891 int r;
1892
1893 assert_return(ret, -EINVAL);
1894 assert_return(fd >= 0, -EBADF);
1aaa68f5 1895 assert_return((flags & ~OPEN_DIRECTORY_FD_ALLOWED_FLAGS) == 0, -EINVAL);
5d1ce257
LP
1896
1897 if (fstat(fd, &st) < 0)
1898 return -errno;
1899
1900 if (!S_ISDIR(st.st_mode))
1901 return -EBADFD;
1902
1903 j = journal_new(flags, NULL);
1904 if (!j)
1905 return -ENOMEM;
1906
1907 j->toplevel_fd = fd;
1908
d077390c
LP
1909 if (flags & SD_JOURNAL_OS_ROOT)
1910 r = add_search_paths(j);
1911 else
1912 r = add_root_directory(j, NULL, false);
5d1ce257
LP
1913 if (r < 0)
1914 goto fail;
1915
1916 *ret = j;
1917 return 0;
1918
1919fail:
1920 sd_journal_close(j);
1921 return r;
1922}
1923
1924_public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags) {
1925 Iterator iterator;
1926 JournalFile *f;
1927 sd_journal *j;
1928 unsigned i;
1929 int r;
1930
1931 assert_return(ret, -EINVAL);
1932 assert_return(n_fds > 0, -EBADF);
1933 assert_return(flags == 0, -EINVAL);
1934
1935 j = journal_new(flags, NULL);
1936 if (!j)
1937 return -ENOMEM;
1938
1939 for (i = 0; i < n_fds; i++) {
1940 struct stat st;
1941
1942 if (fds[i] < 0) {
1943 r = -EBADF;
1944 goto fail;
1945 }
1946
1947 if (fstat(fds[i], &st) < 0) {
1948 r = -errno;
1949 goto fail;
1950 }
1951
1952 if (!S_ISREG(st.st_mode)) {
1953 r = -EBADFD;
1954 goto fail;
1955 }
1956
1957 r = add_any_file(j, fds[i], NULL);
1958 if (r < 0)
1959 goto fail;
1960 }
1961
1962 j->no_new_files = true;
1963 j->no_inotify = true;
5302ebe1 1964
5d1ce257
LP
1965 *ret = j;
1966 return 0;
1967
1968fail:
f8e2f4d6 1969 /* If we fail, make sure we don't take possession of the files we managed to make use of successfully, and they
5d1ce257
LP
1970 * remain open */
1971 ORDERED_HASHMAP_FOREACH(f, j->files, iterator)
1972 f->close_fd = false;
1973
1974 sd_journal_close(j);
5302ebe1
ZJS
1975 return r;
1976}
1977
a5344d2c 1978_public_ void sd_journal_close(sd_journal *j) {
a963990f
LP
1979 Directory *d;
1980 JournalFile *f;
5768d259 1981 char *p;
a963990f 1982
a5344d2c
LP
1983 if (!j)
1984 return;
87d2c1ff 1985
54b1da83
LP
1986 sd_journal_flush_matches(j);
1987
c1f906bd 1988 while ((f = ordered_hashmap_steal_first(j->files)))
69a3a6fd 1989 (void) journal_file_close(f);
50f20cfd 1990
c1f906bd 1991 ordered_hashmap_free(j->files);
260a2be4 1992
a963990f
LP
1993 while ((d = hashmap_first(j->directories_by_path)))
1994 remove_directory(j, d);
260a2be4 1995
a963990f
LP
1996 while ((d = hashmap_first(j->directories_by_wd)))
1997 remove_directory(j, d);
87d2c1ff 1998
a963990f
LP
1999 hashmap_free(j->directories_by_path);
2000 hashmap_free(j->directories_by_wd);
1cc101f1 2001
03e334a1 2002 safe_close(j->inotify_fd);
50f20cfd 2003
bf807d4d
LP
2004 if (j->mmap) {
2005 log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap));
16e9f408 2006 mmap_cache_unref(j->mmap);
bf807d4d 2007 }
16e9f408 2008
5768d259
LP
2009 while ((p = hashmap_steal_first(j->errors)))
2010 free(p);
2011 hashmap_free(j->errors);
2012
7827b1a1 2013 free(j->path);
89739579 2014 free(j->prefix);
3c1668da 2015 free(j->unique_field);
eb86030e 2016 free(j->fields_buffer);
87d2c1ff
LP
2017 free(j);
2018}
3fbf9cbb 2019
a5344d2c 2020_public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
3fbf9cbb
LP
2021 Object *o;
2022 JournalFile *f;
2023 int r;
2024
1ae464e0
TA
2025 assert_return(j, -EINVAL);
2026 assert_return(!journal_pid_changed(j), -ECHILD);
2027 assert_return(ret, -EINVAL);
3fbf9cbb
LP
2028
2029 f = j->current_file;
2030 if (!f)
de190aef 2031 return -EADDRNOTAVAIL;
3fbf9cbb
LP
2032
2033 if (f->current_offset <= 0)
de190aef 2034 return -EADDRNOTAVAIL;
3fbf9cbb 2035
de190aef 2036 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
2037 if (r < 0)
2038 return r;
2039
2040 *ret = le64toh(o->entry.realtime);
de190aef 2041 return 0;
3fbf9cbb
LP
2042}
2043
a5344d2c 2044_public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
3fbf9cbb
LP
2045 Object *o;
2046 JournalFile *f;
2047 int r;
2048 sd_id128_t id;
2049
1ae464e0
TA
2050 assert_return(j, -EINVAL);
2051 assert_return(!journal_pid_changed(j), -ECHILD);
3fbf9cbb
LP
2052
2053 f = j->current_file;
2054 if (!f)
de190aef 2055 return -EADDRNOTAVAIL;
3fbf9cbb
LP
2056
2057 if (f->current_offset <= 0)
de190aef 2058 return -EADDRNOTAVAIL;
3fbf9cbb 2059
de190aef 2060 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
2061 if (r < 0)
2062 return r;
2063
de190aef
LP
2064 if (ret_boot_id)
2065 *ret_boot_id = o->entry.boot_id;
2066 else {
2067 r = sd_id128_get_boot(&id);
2068 if (r < 0)
2069 return r;
3fbf9cbb 2070
de190aef 2071 if (!sd_id128_equal(id, o->entry.boot_id))
df50185b 2072 return -ESTALE;
de190aef 2073 }
3fbf9cbb 2074
14a65d65
LP
2075 if (ret)
2076 *ret = le64toh(o->entry.monotonic);
2077
de190aef 2078 return 0;
3fbf9cbb
LP
2079}
2080
362a3f81
LP
2081static bool field_is_valid(const char *field) {
2082 const char *p;
2083
2084 assert(field);
2085
2086 if (isempty(field))
2087 return false;
2088
2089 if (startswith(field, "__"))
2090 return false;
2091
2092 for (p = field; *p; p++) {
2093
2094 if (*p == '_')
2095 continue;
2096
2097 if (*p >= 'A' && *p <= 'Z')
2098 continue;
2099
2100 if (*p >= '0' && *p <= '9')
2101 continue;
2102
2103 return false;
2104 }
2105
2106 return true;
2107}
2108
a5344d2c 2109_public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
3fbf9cbb
LP
2110 JournalFile *f;
2111 uint64_t i, n;
2112 size_t field_length;
2113 int r;
2114 Object *o;
2115
1ae464e0
TA
2116 assert_return(j, -EINVAL);
2117 assert_return(!journal_pid_changed(j), -ECHILD);
2118 assert_return(field, -EINVAL);
2119 assert_return(data, -EINVAL);
2120 assert_return(size, -EINVAL);
2121 assert_return(field_is_valid(field), -EINVAL);
3fbf9cbb
LP
2122
2123 f = j->current_file;
2124 if (!f)
de190aef 2125 return -EADDRNOTAVAIL;
3fbf9cbb
LP
2126
2127 if (f->current_offset <= 0)
de190aef 2128 return -EADDRNOTAVAIL;
3fbf9cbb 2129
de190aef 2130 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
2131 if (r < 0)
2132 return r;
2133
2134 field_length = strlen(field);
2135
2136 n = journal_file_entry_n_items(o);
2137 for (i = 0; i < n; i++) {
4fd052ae
FC
2138 uint64_t p, l;
2139 le64_t le_hash;
3fbf9cbb 2140 size_t t;
1ec7120e 2141 int compression;
3fbf9cbb
LP
2142
2143 p = le64toh(o->entry.items[i].object_offset);
807e17f0 2144 le_hash = o->entry.items[i].hash;
de190aef 2145 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
3fbf9cbb
LP
2146 if (r < 0)
2147 return r;
2148
de190aef 2149 if (le_hash != o->data.hash)
de7b95cd
LP
2150 return -EBADMSG;
2151
3fbf9cbb
LP
2152 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2153
1ec7120e 2154 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
3b1a55e1 2155 if (compression) {
349cc4a5 2156#if HAVE_XZ || HAVE_LZ4
2aaec9b4 2157 r = decompress_startswith(compression,
3b1a55e1
ZJS
2158 o->data.payload, l,
2159 &f->compress_buffer, &f->compress_buffer_size,
2aaec9b4
ZJS
2160 field, field_length, '=');
2161 if (r < 0)
82e24b00 2162 log_debug_errno(r, "Cannot decompress %s object of length %"PRIu64" at offset "OFSfmt": %m",
2aaec9b4
ZJS
2163 object_compressed_to_string(compression), l, p);
2164 else if (r > 0) {
3b1a55e1 2165
fa1c4b51 2166 size_t rsize;
3b1a55e1
ZJS
2167
2168 r = decompress_blob(compression,
2169 o->data.payload, l,
2170 &f->compress_buffer, &f->compress_buffer_size, &rsize,
2171 j->data_threshold);
2172 if (r < 0)
2173 return r;
807e17f0 2174
3b1a55e1
ZJS
2175 *data = f->compress_buffer;
2176 *size = (size_t) rsize;
807e17f0 2177
3b1a55e1
ZJS
2178 return 0;
2179 }
2180#else
2181 return -EPROTONOSUPPORT;
2182#endif
807e17f0
LP
2183 } else if (l >= field_length+1 &&
2184 memcmp(o->data.payload, field, field_length) == 0 &&
2185 o->data.payload[field_length] == '=') {
3fbf9cbb 2186
161e54f8 2187 t = (size_t) l;
3fbf9cbb 2188
161e54f8
LP
2189 if ((uint64_t) t != l)
2190 return -E2BIG;
3fbf9cbb 2191
161e54f8
LP
2192 *data = o->data.payload;
2193 *size = t;
3fbf9cbb 2194
99613ec5 2195 return 0;
161e54f8 2196 }
3fbf9cbb 2197
de190aef 2198 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
161e54f8
LP
2199 if (r < 0)
2200 return r;
3fbf9cbb
LP
2201 }
2202
de190aef 2203 return -ENOENT;
3fbf9cbb
LP
2204}
2205
93b73b06 2206static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
3c1668da
LP
2207 size_t t;
2208 uint64_t l;
3b1a55e1 2209 int compression;
3c1668da
LP
2210
2211 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2212 t = (size_t) l;
2213
2214 /* We can't read objects larger than 4G on a 32bit machine */
2215 if ((uint64_t) t != l)
2216 return -E2BIG;
2217
1ec7120e
ZJS
2218 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
2219 if (compression) {
349cc4a5 2220#if HAVE_XZ || HAVE_LZ4
fa1c4b51 2221 size_t rsize;
3b1a55e1 2222 int r;
3c1668da 2223
1ec7120e
ZJS
2224 r = decompress_blob(compression,
2225 o->data.payload, l, &f->compress_buffer,
2226 &f->compress_buffer_size, &rsize, j->data_threshold);
2227 if (r < 0)
2228 return r;
3c1668da
LP
2229
2230 *data = f->compress_buffer;
2231 *size = (size_t) rsize;
3b1a55e1
ZJS
2232#else
2233 return -EPROTONOSUPPORT;
2234#endif
3c1668da
LP
2235 } else {
2236 *data = o->data.payload;
2237 *size = t;
2238 }
2239
2240 return 0;
2241}
2242
a5344d2c 2243_public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
3fbf9cbb 2244 JournalFile *f;
3c1668da 2245 uint64_t p, n;
4fd052ae 2246 le64_t le_hash;
3fbf9cbb
LP
2247 int r;
2248 Object *o;
2249
1ae464e0
TA
2250 assert_return(j, -EINVAL);
2251 assert_return(!journal_pid_changed(j), -ECHILD);
2252 assert_return(data, -EINVAL);
2253 assert_return(size, -EINVAL);
3fbf9cbb
LP
2254
2255 f = j->current_file;
2256 if (!f)
de190aef 2257 return -EADDRNOTAVAIL;
3fbf9cbb
LP
2258
2259 if (f->current_offset <= 0)
de190aef 2260 return -EADDRNOTAVAIL;
3fbf9cbb 2261
de190aef 2262 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
3fbf9cbb
LP
2263 if (r < 0)
2264 return r;
2265
2266 n = journal_file_entry_n_items(o);
7210bfb3 2267 if (j->current_field >= n)
3fbf9cbb
LP
2268 return 0;
2269
7210bfb3 2270 p = le64toh(o->entry.items[j->current_field].object_offset);
de190aef
LP
2271 le_hash = o->entry.items[j->current_field].hash;
2272 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
3fbf9cbb
LP
2273 if (r < 0)
2274 return r;
2275
de190aef 2276 if (le_hash != o->data.hash)
de7b95cd
LP
2277 return -EBADMSG;
2278
93b73b06 2279 r = return_data(j, f, o, data, size);
3c1668da
LP
2280 if (r < 0)
2281 return r;
3fbf9cbb 2282
313cefa1 2283 j->current_field++;
3fbf9cbb
LP
2284
2285 return 1;
2286}
c2373f84 2287
a5344d2c
LP
2288_public_ void sd_journal_restart_data(sd_journal *j) {
2289 if (!j)
2290 return;
8725d60a
LP
2291
2292 j->current_field = 0;
c2373f84 2293}
50f20cfd 2294
a5344d2c 2295_public_ int sd_journal_get_fd(sd_journal *j) {
a963990f
LP
2296 int r;
2297
1ae464e0
TA
2298 assert_return(j, -EINVAL);
2299 assert_return(!journal_pid_changed(j), -ECHILD);
50f20cfd 2300
5d1ce257
LP
2301 if (j->no_inotify)
2302 return -EMEDIUMTYPE;
2303
a963990f
LP
2304 if (j->inotify_fd >= 0)
2305 return j->inotify_fd;
2306
2307 r = allocate_inotify(j);
2308 if (r < 0)
2309 return r;
2310
5d1ce257
LP
2311 log_debug("Reiterating files to get inotify watches established");
2312
a963990f
LP
2313 /* Iterate through all dirs again, to add them to the
2314 * inotify */
5302ebe1
ZJS
2315 if (j->no_new_files)
2316 r = add_current_paths(j);
e73529f9
EV
2317 else if (j->flags & SD_JOURNAL_OS_ROOT)
2318 r = add_search_paths(j);
5d1ce257
LP
2319 else if (j->toplevel_fd >= 0)
2320 r = add_root_directory(j, NULL, false);
5302ebe1 2321 else if (j->path)
d617408e 2322 r = add_root_directory(j, j->path, true);
7827b1a1 2323 else
89739579 2324 r = add_search_paths(j);
a963990f
LP
2325 if (r < 0)
2326 return r;
2327
50f20cfd
LP
2328 return j->inotify_fd;
2329}
2330
ee531d94
LP
2331_public_ int sd_journal_get_events(sd_journal *j) {
2332 int fd;
2333
1ae464e0
TA
2334 assert_return(j, -EINVAL);
2335 assert_return(!journal_pid_changed(j), -ECHILD);
ee531d94
LP
2336
2337 fd = sd_journal_get_fd(j);
2338 if (fd < 0)
2339 return fd;
2340
2341 return POLLIN;
2342}
2343
39c155ea
LP
2344_public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
2345 int fd;
2346
1ae464e0
TA
2347 assert_return(j, -EINVAL);
2348 assert_return(!journal_pid_changed(j), -ECHILD);
2349 assert_return(timeout_usec, -EINVAL);
39c155ea
LP
2350
2351 fd = sd_journal_get_fd(j);
2352 if (fd < 0)
2353 return fd;
2354
2355 if (!j->on_network) {
2356 *timeout_usec = (uint64_t) -1;
2357 return 0;
2358 }
2359
2360 /* If we are on the network we need to regularly check for
2361 * changes manually */
2362
2363 *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
2364 return 1;
2365}
2366
50f20cfd 2367static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
a963990f 2368 Directory *d;
50f20cfd
LP
2369
2370 assert(j);
2371 assert(e);
2372
2373 /* Is this a subdirectory we watch? */
a963990f
LP
2374 d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
2375 if (d) {
2376 sd_id128_t id;
50f20cfd 2377
de2c3907
LP
2378 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
2379 (endswith(e->name, ".journal") ||
2380 endswith(e->name, ".journal~"))) {
50f20cfd
LP
2381
2382 /* Event for a journal file */
2383
d617408e
LP
2384 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB))
2385 (void) add_file(j, d->path, e->name);
2386 else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT))
2387 remove_file(j, d->path, e->name);
50f20cfd 2388
a963990f 2389 } else if (!d->is_root && e->len == 0) {
50f20cfd 2390
a963990f 2391 /* Event for a subdirectory */
50f20cfd 2392
b2b46f91
TA
2393 if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT))
2394 remove_directory(j, d);
50f20cfd 2395
a963990f 2396 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
50f20cfd 2397
a963990f 2398 /* Event for root directory */
50f20cfd 2399
d617408e
LP
2400 if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB))
2401 (void) add_directory(j, d->path, e->name);
50f20cfd
LP
2402 }
2403
2404 return;
2405 }
2406
2407 if (e->mask & IN_IGNORED)
2408 return;
2409
d617408e 2410 log_debug("Unknown inotify event.");
50f20cfd
LP
2411}
2412
a963990f
LP
2413static int determine_change(sd_journal *j) {
2414 bool b;
2415
2416 assert(j);
2417
2418 b = j->current_invalidate_counter != j->last_invalidate_counter;
2419 j->last_invalidate_counter = j->current_invalidate_counter;
2420
2421 return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
2422}
2423
a5344d2c 2424_public_ int sd_journal_process(sd_journal *j) {
a963990f 2425 bool got_something = false;
50f20cfd 2426
1ae464e0
TA
2427 assert_return(j, -EINVAL);
2428 assert_return(!journal_pid_changed(j), -ECHILD);
50f20cfd 2429
39c155ea 2430 j->last_process_usec = now(CLOCK_MONOTONIC);
f9346444 2431 j->last_invalidate_counter = j->current_invalidate_counter;
39c155ea 2432
50f20cfd 2433 for (;;) {
0254e944 2434 union inotify_event_buffer buffer;
50f20cfd
LP
2435 struct inotify_event *e;
2436 ssize_t l;
2437
0254e944 2438 l = read(j->inotify_fd, &buffer, sizeof(buffer));
50f20cfd 2439 if (l < 0) {
3742095b 2440 if (IN_SET(errno, EAGAIN, EINTR))
a963990f 2441 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
50f20cfd
LP
2442
2443 return -errno;
2444 }
2445
a963990f
LP
2446 got_something = true;
2447
f7c1ad4f 2448 FOREACH_INOTIFY_EVENT(e, buffer, l)
50f20cfd 2449 process_inotify_event(j, e);
50f20cfd
LP
2450 }
2451}
6ad1d1c3 2452
e02d1cf7 2453_public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
a963990f 2454 int r;
39c155ea 2455 uint64_t t;
e02d1cf7 2456
1ae464e0
TA
2457 assert_return(j, -EINVAL);
2458 assert_return(!journal_pid_changed(j), -ECHILD);
e02d1cf7 2459
a963990f
LP
2460 if (j->inotify_fd < 0) {
2461
2462 /* This is the first invocation, hence create the
2463 * inotify watch */
2464 r = sd_journal_get_fd(j);
2465 if (r < 0)
2466 return r;
2467
2468 /* The journal might have changed since the context
2469 * object was created and we weren't watching before,
2470 * hence don't wait for anything, and return
2471 * immediately. */
2472 return determine_change(j);
2473 }
2474
39c155ea
LP
2475 r = sd_journal_get_timeout(j, &t);
2476 if (r < 0)
2477 return r;
2478
2479 if (t != (uint64_t) -1) {
2480 usec_t n;
2481
2482 n = now(CLOCK_MONOTONIC);
2483 t = t > n ? t - n : 0;
85210bff 2484
39c155ea
LP
2485 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
2486 timeout_usec = t;
85210bff
LP
2487 }
2488
a963990f
LP
2489 do {
2490 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
2491 } while (r == -EINTR);
e02d1cf7
LP
2492
2493 if (r < 0)
2494 return r;
2495
a963990f 2496 return sd_journal_process(j);
e02d1cf7
LP
2497}
2498
08984293
LP
2499_public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
2500 Iterator i;
2501 JournalFile *f;
2502 bool first = true;
581483bf 2503 uint64_t fmin = 0, tmax = 0;
08984293
LP
2504 int r;
2505
1ae464e0
TA
2506 assert_return(j, -EINVAL);
2507 assert_return(!journal_pid_changed(j), -ECHILD);
2508 assert_return(from || to, -EINVAL);
2509 assert_return(from != to, -EINVAL);
08984293 2510
c1f906bd 2511 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
08984293
LP
2512 usec_t fr, t;
2513
2514 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
9f8d2983
LP
2515 if (r == -ENOENT)
2516 continue;
08984293
LP
2517 if (r < 0)
2518 return r;
2519 if (r == 0)
2520 continue;
2521
2522 if (first) {
581483bf
LP
2523 fmin = fr;
2524 tmax = t;
08984293
LP
2525 first = false;
2526 } else {
581483bf
LP
2527 fmin = MIN(fr, fmin);
2528 tmax = MAX(t, tmax);
08984293
LP
2529 }
2530 }
2531
581483bf
LP
2532 if (from)
2533 *from = fmin;
2534 if (to)
2535 *to = tmax;
2536
08984293
LP
2537 return first ? 0 : 1;
2538}
2539
2540_public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
2541 Iterator i;
2542 JournalFile *f;
1651e2c6 2543 bool found = false;
08984293
LP
2544 int r;
2545
1ae464e0
TA
2546 assert_return(j, -EINVAL);
2547 assert_return(!journal_pid_changed(j), -ECHILD);
2548 assert_return(from || to, -EINVAL);
2549 assert_return(from != to, -EINVAL);
08984293 2550
c1f906bd 2551 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
08984293
LP
2552 usec_t fr, t;
2553
2554 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
9f8d2983
LP
2555 if (r == -ENOENT)
2556 continue;
08984293
LP
2557 if (r < 0)
2558 return r;
2559 if (r == 0)
2560 continue;
2561
1651e2c6 2562 if (found) {
08984293 2563 if (from)
1651e2c6 2564 *from = MIN(fr, *from);
08984293 2565 if (to)
1651e2c6 2566 *to = MAX(t, *to);
08984293
LP
2567 } else {
2568 if (from)
1651e2c6 2569 *from = fr;
08984293 2570 if (to)
1651e2c6
ZJS
2571 *to = t;
2572 found = true;
08984293
LP
2573 }
2574 }
2575
1651e2c6 2576 return found;
08984293
LP
2577}
2578
dca6219e
LP
2579void journal_print_header(sd_journal *j) {
2580 Iterator i;
2581 JournalFile *f;
2582 bool newline = false;
2583
2584 assert(j);
2585
c1f906bd 2586 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
dca6219e
LP
2587 if (newline)
2588 putchar('\n');
2589 else
2590 newline = true;
2591
2592 journal_file_print_header(f);
2593 }
2594}
08984293 2595
a1a03e30
LP
2596_public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
2597 Iterator i;
2598 JournalFile *f;
2599 uint64_t sum = 0;
2600
1ae464e0
TA
2601 assert_return(j, -EINVAL);
2602 assert_return(!journal_pid_changed(j), -ECHILD);
2603 assert_return(bytes, -EINVAL);
a1a03e30 2604
c1f906bd 2605 ORDERED_HASHMAP_FOREACH(f, j->files, i) {
a1a03e30
LP
2606 struct stat st;
2607
2608 if (fstat(f->fd, &st) < 0)
2609 return -errno;
2610
2611 sum += (uint64_t) st.st_blocks * 512ULL;
2612 }
2613
2614 *bytes = sum;
2615 return 0;
2616}
2617
3c1668da
LP
2618_public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2619 char *f;
2620
1ae464e0
TA
2621 assert_return(j, -EINVAL);
2622 assert_return(!journal_pid_changed(j), -ECHILD);
2623 assert_return(!isempty(field), -EINVAL);
2624 assert_return(field_is_valid(field), -EINVAL);
3c1668da
LP
2625
2626 f = strdup(field);
2627 if (!f)
2628 return -ENOMEM;
2629
2630 free(j->unique_field);
2631 j->unique_field = f;
2632 j->unique_file = NULL;
2633 j->unique_offset = 0;
360af4cf 2634 j->unique_file_lost = false;
3c1668da
LP
2635
2636 return 0;
2637}
2638
2639_public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
3c1668da 2640 size_t k;
19a2bd80 2641
1ae464e0
TA
2642 assert_return(j, -EINVAL);
2643 assert_return(!journal_pid_changed(j), -ECHILD);
2644 assert_return(data, -EINVAL);
2645 assert_return(l, -EINVAL);
2646 assert_return(j->unique_field, -EINVAL);
19a2bd80 2647
3c1668da 2648 k = strlen(j->unique_field);
19a2bd80 2649
3c1668da 2650 if (!j->unique_file) {
360af4cf
ZJS
2651 if (j->unique_file_lost)
2652 return 0;
2653
c1f906bd 2654 j->unique_file = ordered_hashmap_first(j->files);
3c1668da
LP
2655 if (!j->unique_file)
2656 return 0;
360af4cf 2657
3c1668da
LP
2658 j->unique_offset = 0;
2659 }
19a2bd80 2660
3c1668da
LP
2661 for (;;) {
2662 JournalFile *of;
2663 Iterator i;
ae97089d 2664 Object *o;
3c1668da
LP
2665 const void *odata;
2666 size_t ol;
2667 bool found;
ae97089d 2668 int r;
3c1668da 2669
bdc02927 2670 /* Proceed to next data object in the field's linked list */
3c1668da
LP
2671 if (j->unique_offset == 0) {
2672 r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2673 if (r < 0)
2674 return r;
2675
2676 j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2677 } else {
2678 r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2679 if (r < 0)
2680 return r;
2681
2682 j->unique_offset = le64toh(o->data.next_field_offset);
2683 }
2684
2685 /* We reached the end of the list? Then start again, with the next file */
2686 if (j->unique_offset == 0) {
c1f906bd 2687 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
360af4cf 2688 if (!j->unique_file)
3c1668da
LP
2689 return 0;
2690
3c1668da
LP
2691 continue;
2692 }
2693
d05089d8
MS
2694 /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED
2695 * instead, so that we can look at this data object at the same
3c1668da 2696 * time as one on another file */
d05089d8 2697 r = journal_file_move_to_object(j->unique_file, OBJECT_UNUSED, j->unique_offset, &o);
3c1668da
LP
2698 if (r < 0)
2699 return r;
2700
2701 /* Let's do the type check by hand, since we used 0 context above. */
ae97089d 2702 if (o->object.type != OBJECT_DATA) {
36202fd2 2703 log_debug("%s:offset " OFSfmt ": object has type %d, expected %d",
ae97089d
ZJS
2704 j->unique_file->path, j->unique_offset,
2705 o->object.type, OBJECT_DATA);
3c1668da 2706 return -EBADMSG;
ae97089d
ZJS
2707 }
2708
93b73b06 2709 r = return_data(j, j->unique_file, o, &odata, &ol);
3c1668da
LP
2710 if (r < 0)
2711 return r;
2712
0f99f74a
ZJS
2713 /* Check if we have at least the field name and "=". */
2714 if (ol <= k) {
2715 log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
2716 j->unique_file->path, j->unique_offset,
2717 ol, k + 1);
2718 return -EBADMSG;
2719 }
2720
2721 if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') {
2722 log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"",
2723 j->unique_file->path, j->unique_offset,
2724 j->unique_field);
2725 return -EBADMSG;
2726 }
2727
3c1668da
LP
2728 /* OK, now let's see if we already returned this data
2729 * object by checking if it exists in the earlier
2730 * traversed files. */
2731 found = false;
c1f906bd 2732 ORDERED_HASHMAP_FOREACH(of, j->files, i) {
3c1668da
LP
2733 if (of == j->unique_file)
2734 break;
2735
ed71f956
LP
2736 /* Skip this file it didn't have any fields indexed */
2737 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) && le64toh(of->header->n_fields) <= 0)
3c1668da
LP
2738 continue;
2739
ed71f956 2740 r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), NULL, NULL);
3c1668da
LP
2741 if (r < 0)
2742 return r;
ed71f956 2743 if (r > 0) {
3c1668da 2744 found = true;
ed71f956
LP
2745 break;
2746 }
3c1668da
LP
2747 }
2748
06cc69d4
JJ
2749 if (found)
2750 continue;
2751
93b73b06 2752 r = return_data(j, j->unique_file, o, data, l);
3c1668da
LP
2753 if (r < 0)
2754 return r;
2755
2756 return 1;
2757 }
2758}
2759
115646c7 2760_public_ void sd_journal_restart_unique(sd_journal *j) {
3c1668da
LP
2761 if (!j)
2762 return;
2763
2764 j->unique_file = NULL;
2765 j->unique_offset = 0;
360af4cf 2766 j->unique_file_lost = false;
3c1668da 2767}
85210bff 2768
eb86030e
LP
2769_public_ int sd_journal_enumerate_fields(sd_journal *j, const char **field) {
2770 int r;
2771
2772 assert_return(j, -EINVAL);
2773 assert_return(!journal_pid_changed(j), -ECHILD);
2774 assert_return(field, -EINVAL);
2775
2776 if (!j->fields_file) {
2777 if (j->fields_file_lost)
2778 return 0;
2779
2780 j->fields_file = ordered_hashmap_first(j->files);
2781 if (!j->fields_file)
2782 return 0;
2783
2784 j->fields_hash_table_index = 0;
2785 j->fields_offset = 0;
2786 }
2787
2788 for (;;) {
2789 JournalFile *f, *of;
2790 Iterator i;
2791 uint64_t m;
2792 Object *o;
2793 size_t sz;
2794 bool found;
2795
2796 f = j->fields_file;
2797
2798 if (j->fields_offset == 0) {
2799 bool eof = false;
2800
2801 /* We are not yet positioned at any field. Let's pick the first one */
2802 r = journal_file_map_field_hash_table(f);
2803 if (r < 0)
2804 return r;
2805
2806 m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem);
2807 for (;;) {
2808 if (j->fields_hash_table_index >= m) {
2809 /* Reached the end of the hash table, go to the next file. */
2810 eof = true;
2811 break;
2812 }
2813
2814 j->fields_offset = le64toh(f->field_hash_table[j->fields_hash_table_index].head_hash_offset);
2815
2816 if (j->fields_offset != 0)
2817 break;
2818
2819 /* Empty hash table bucket, go to next one */
2820 j->fields_hash_table_index++;
2821 }
2822
2823 if (eof) {
2824 /* Proceed with next file */
2825 j->fields_file = ordered_hashmap_next(j->files, f->path);
2826 if (!j->fields_file) {
2827 *field = NULL;
2828 return 0;
2829 }
2830
2831 j->fields_offset = 0;
2832 j->fields_hash_table_index = 0;
2833 continue;
2834 }
2835
2836 } else {
2837 /* We are already positioned at a field. If so, let's figure out the next field from it */
2838
2839 r = journal_file_move_to_object(f, OBJECT_FIELD, j->fields_offset, &o);
2840 if (r < 0)
2841 return r;
2842
2843 j->fields_offset = le64toh(o->field.next_hash_offset);
2844 if (j->fields_offset == 0) {
2845 /* Reached the end of the hash table chain */
2846 j->fields_hash_table_index++;
2847 continue;
2848 }
2849 }
2850
1f133e0d 2851 /* We use OBJECT_UNUSED here, so that the iterator below doesn't remove our mmap window */
eb86030e
LP
2852 r = journal_file_move_to_object(f, OBJECT_UNUSED, j->fields_offset, &o);
2853 if (r < 0)
2854 return r;
2855
2856 /* Because we used OBJECT_UNUSED above, we need to do our type check manually */
2857 if (o->object.type != OBJECT_FIELD) {
2858 log_debug("%s:offset " OFSfmt ": object has type %i, expected %i", f->path, j->fields_offset, o->object.type, OBJECT_FIELD);
2859 return -EBADMSG;
2860 }
2861
2862 sz = le64toh(o->object.size) - offsetof(Object, field.payload);
2863
2864 /* Let's see if we already returned this field name before. */
2865 found = false;
2866 ORDERED_HASHMAP_FOREACH(of, j->files, i) {
2867 if (of == f)
2868 break;
2869
2870 /* Skip this file it didn't have any fields indexed */
2871 if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) && le64toh(of->header->n_fields) <= 0)
2872 continue;
2873
2874 r = journal_file_find_field_object_with_hash(of, o->field.payload, sz, le64toh(o->field.hash), NULL, NULL);
2875 if (r < 0)
2876 return r;
2877 if (r > 0) {
2878 found = true;
2879 break;
2880 }
2881 }
2882
2883 if (found)
2884 continue;
2885
2886 /* Check if this is really a valid string containing no NUL byte */
2887 if (memchr(o->field.payload, 0, sz))
2888 return -EBADMSG;
2889
2890 if (sz > j->data_threshold)
2891 sz = j->data_threshold;
2892
2893 if (!GREEDY_REALLOC(j->fields_buffer, j->fields_buffer_allocated, sz + 1))
2894 return -ENOMEM;
2895
2896 memcpy(j->fields_buffer, o->field.payload, sz);
2897 j->fields_buffer[sz] = 0;
2898
2899 if (!field_is_valid(j->fields_buffer))
2900 return -EBADMSG;
2901
2902 *field = j->fields_buffer;
2903 return 1;
2904 }
2905}
2906
2907_public_ void sd_journal_restart_fields(sd_journal *j) {
2908 if (!j)
2909 return;
2910
2911 j->fields_file = NULL;
2912 j->fields_hash_table_index = 0;
2913 j->fields_offset = 0;
2914 j->fields_file_lost = false;
2915}
2916
85210bff 2917_public_ int sd_journal_reliable_fd(sd_journal *j) {
1ae464e0
TA
2918 assert_return(j, -EINVAL);
2919 assert_return(!journal_pid_changed(j), -ECHILD);
85210bff
LP
2920
2921 return !j->on_network;
2922}
d4205751
LP
2923
2924static char *lookup_field(const char *field, void *userdata) {
2925 sd_journal *j = userdata;
2926 const void *data;
2927 size_t size, d;
2928 int r;
2929
2930 assert(field);
2931 assert(j);
2932
2933 r = sd_journal_get_data(j, field, &data, &size);
2934 if (r < 0 ||
2935 size > REPLACE_VAR_MAX)
2936 return strdup(field);
2937
2938 d = strlen(field) + 1;
2939
2940 return strndup((const char*) data + d, size - d);
2941}
2942
2943_public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
2944 const void *data;
2945 size_t size;
2946 sd_id128_t id;
2947 _cleanup_free_ char *text = NULL, *cid = NULL;
2948 char *t;
2949 int r;
2950
1ae464e0
TA
2951 assert_return(j, -EINVAL);
2952 assert_return(!journal_pid_changed(j), -ECHILD);
2953 assert_return(ret, -EINVAL);
d4205751
LP
2954
2955 r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
2956 if (r < 0)
2957 return r;
2958
2959 cid = strndup((const char*) data + 11, size - 11);
2960 if (!cid)
2961 return -ENOMEM;
2962
2963 r = sd_id128_from_string(cid, &id);
2964 if (r < 0)
2965 return r;
2966
844ec79b 2967 r = catalog_get(CATALOG_DATABASE, id, &text);
d4205751
LP
2968 if (r < 0)
2969 return r;
2970
2971 t = replace_var(text, lookup_field, j);
2972 if (!t)
2973 return -ENOMEM;
2974
2975 *ret = t;
2976 return 0;
2977}
8f1e860f
LP
2978
2979_public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
1ae464e0 2980 assert_return(ret, -EINVAL);
8f1e860f 2981
844ec79b 2982 return catalog_get(CATALOG_DATABASE, id, ret);
8f1e860f 2983}
93b73b06
LP
2984
2985_public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
1ae464e0
TA
2986 assert_return(j, -EINVAL);
2987 assert_return(!journal_pid_changed(j), -ECHILD);
93b73b06
LP
2988
2989 j->data_threshold = sz;
2990 return 0;
2991}
2992
2993_public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
1ae464e0
TA
2994 assert_return(j, -EINVAL);
2995 assert_return(!journal_pid_changed(j), -ECHILD);
2996 assert_return(sz, -EINVAL);
93b73b06
LP
2997
2998 *sz = j->data_threshold;
2999 return 0;
3000}
39fd5b08
JS
3001
3002_public_ int sd_journal_has_runtime_files(sd_journal *j) {
3003 assert_return(j, -EINVAL);
3004
3005 return j->has_runtime_files;
3006}
3007
3008_public_ int sd_journal_has_persistent_files(sd_journal *j) {
3009 assert_return(j, -EINVAL);
3010
3011 return j->has_persistent_files;
3012}