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