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