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