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