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