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