Branch data 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 : 48548 : static bool journal_pid_changed(sd_journal *j) {
55 [ - + ]: 48548 : 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 : 48548 : 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 : 1016 : static void detach_location(sd_journal *j) {
106 : : Iterator i;
107 : : JournalFile *f;
108 : :
109 [ - + ]: 1016 : assert(j);
110 : :
111 : 1016 : j->current_file = NULL;
112 : 1016 : j->current_field = 0;
113 : :
114 [ + + ]: 48912 : ORDERED_HASHMAP_FOREACH(f, j->files, i)
115 : 47896 : journal_file_reset_location(f);
116 : 1016 : }
117 : :
118 : 60 : static void reset_location(sd_journal *j) {
119 [ - + ]: 60 : assert(j);
120 : :
121 : 60 : detach_location(j);
122 [ + - ]: 60 : zero(j->current_location);
123 : 60 : }
124 : :
125 : 41504 : static void init_location(Location *l, LocationType type, JournalFile *f, Object *o) {
126 [ - + ]: 41504 : assert(l);
127 [ + - - + ]: 41504 : assert(IN_SET(type, LOCATION_DISCRETE, LOCATION_SEEK));
128 [ - + ]: 41504 : assert(f);
129 [ - + ]: 41504 : assert(o->object.type == OBJECT_ENTRY);
130 : :
131 : 41504 : l->type = type;
132 : 41504 : l->seqnum = le64toh(o->entry.seqnum);
133 : 41504 : l->seqnum_id = f->header->seqnum_id;
134 : 41504 : l->realtime = le64toh(o->entry.realtime);
135 : 41504 : l->monotonic = le64toh(o->entry.monotonic);
136 : 41504 : l->boot_id = o->entry.boot_id;
137 : 41504 : l->xor_hash = le64toh(o->entry.xor_hash);
138 : :
139 : 41504 : l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
140 : 41504 : }
141 : :
142 : 41504 : static void set_location(sd_journal *j, JournalFile *f, Object *o) {
143 [ - + ]: 41504 : assert(j);
144 [ - + ]: 41504 : assert(f);
145 [ - + ]: 41504 : assert(o);
146 : :
147 : 41504 : init_location(&j->current_location, LOCATION_DISCRETE, f, o);
148 : :
149 : 41504 : j->current_file = f;
150 : 41504 : j->current_field = 0;
151 : :
152 : : /* Let f know its candidate entry was picked. */
153 [ - + ]: 41504 : assert(f->location_type == LOCATION_SEEK);
154 : 41504 : f->location_type = LOCATION_DISCRETE;
155 : 41504 : }
156 : :
157 : 124 : static int match_is_valid(const void *data, size_t size) {
158 : : const char *b, *p;
159 : :
160 [ - + ]: 124 : assert(data);
161 : :
162 [ + + ]: 124 : if (size < 2)
163 : 8 : return false;
164 : :
165 [ - + ]: 116 : if (startswith(data, "__"))
166 : 0 : return false;
167 : :
168 : 116 : b = data;
169 [ + - ]: 548 : for (p = b; p < b + size; p++) {
170 : :
171 [ + + ]: 548 : if (*p == '=')
172 : 108 : return p > b;
173 : :
174 [ + + ]: 440 : if (*p == '_')
175 : 24 : continue;
176 : :
177 [ + + + + ]: 416 : if (*p >= 'A' && *p <= 'Z')
178 : 368 : continue;
179 : :
180 [ + - + + ]: 48 : if (*p >= '0' && *p <= '9')
181 : 40 : continue;
182 : :
183 : 8 : return false;
184 : : }
185 : :
186 : 0 : return false;
187 : : }
188 : :
189 : 120 : static bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
190 : 120 : const uint8_t *a = _a, *b = _b;
191 : : size_t j;
192 : :
193 [ + - + - ]: 300 : for (j = 0; j < s && j < t; j++) {
194 : :
195 [ + + ]: 300 : if (a[j] != b[j])
196 : 84 : return false;
197 : :
198 [ + + ]: 216 : if (a[j] == '=')
199 : 36 : return true;
200 : : }
201 : :
202 : 0 : assert_not_reached("\"=\" not found");
203 : : }
204 : :
205 : 240 : static Match *match_new(Match *p, MatchType t) {
206 : : Match *m;
207 : :
208 : 240 : m = new0(Match, 1);
209 [ - + ]: 240 : if (!m)
210 : 0 : return NULL;
211 : :
212 : 240 : m->type = t;
213 : :
214 [ + + ]: 240 : if (p) {
215 : 220 : m->parent = p;
216 [ - + + + ]: 220 : LIST_PREPEND(matches, p->matches, m);
217 : : }
218 : :
219 : 240 : return m;
220 : : }
221 : :
222 : 240 : static void match_free(Match *m) {
223 [ - + ]: 240 : assert(m);
224 : :
225 [ + + ]: 460 : while (m->matches)
226 : 220 : match_free(m->matches);
227 : :
228 [ + + ]: 240 : if (m->parent)
229 [ - + + + : 220 : LIST_REMOVE(matches, m->parent->matches, m);
- + - + ]
230 : :
231 : 240 : free(m->data);
232 : 240 : free(m);
233 : 240 : }
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 : 124 : _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
243 : 124 : Match *l3, *l4, *add_here = NULL, *m;
244 : : le64_t le_hash;
245 : :
246 [ - + - + ]: 124 : assert_return(j, -EINVAL);
247 [ - + - + ]: 124 : assert_return(!journal_pid_changed(j), -ECHILD);
248 [ - + - + ]: 124 : assert_return(data, -EINVAL);
249 : :
250 [ + + ]: 124 : if (size == 0)
251 : 116 : size = strlen(data);
252 : :
253 [ + + + + ]: 124 : 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 [ + + ]: 104 : if (!j->level0) {
262 : 20 : j->level0 = match_new(NULL, MATCH_AND_TERM);
263 [ - + ]: 20 : if (!j->level0)
264 : 0 : return -ENOMEM;
265 : : }
266 : :
267 [ + + ]: 104 : if (!j->level1) {
268 : 24 : j->level1 = match_new(j->level0, MATCH_OR_TERM);
269 [ - + ]: 24 : if (!j->level1)
270 : 0 : return -ENOMEM;
271 : : }
272 : :
273 [ + + ]: 104 : if (!j->level2) {
274 : 32 : j->level2 = match_new(j->level1, MATCH_AND_TERM);
275 [ - + ]: 32 : if (!j->level2)
276 : 0 : return -ENOMEM;
277 : : }
278 : :
279 [ - + ]: 104 : assert(j->level0->type == MATCH_AND_TERM);
280 [ - + ]: 104 : assert(j->level1->type == MATCH_OR_TERM);
281 [ - + ]: 104 : assert(j->level2->type == MATCH_AND_TERM);
282 : :
283 : 104 : le_hash = htole64(hash64(data, size));
284 : :
285 [ + + ]: 164 : LIST_FOREACH(matches, l3, j->level2->matches) {
286 [ - + ]: 100 : assert(l3->type == MATCH_OR_TERM);
287 : :
288 [ + + ]: 184 : LIST_FOREACH(matches, l4, l3->matches) {
289 [ - + ]: 124 : assert(l4->type == MATCH_DISCRETE);
290 : :
291 : : /* Exactly the same match already? Then ignore
292 : : * this addition */
293 [ + + ]: 124 : if (l4->le_hash == le_hash &&
294 [ + - ]: 4 : l4->size == size &&
295 [ + - ]: 4 : memcmp(l4->data, data, size) == 0)
296 : 4 : return 0;
297 : :
298 : : /* Same field? Then let's add this to this OR term */
299 [ + + ]: 120 : if (same_field(data, size, l4->data, l4->size)) {
300 : 36 : add_here = l3;
301 : 36 : break;
302 : : }
303 : : }
304 : :
305 [ + + ]: 96 : if (add_here)
306 : 36 : break;
307 : : }
308 : :
309 [ + + ]: 100 : if (!add_here) {
310 : 64 : add_here = match_new(j->level2, MATCH_OR_TERM);
311 [ - + ]: 64 : if (!add_here)
312 : 0 : goto fail;
313 : : }
314 : :
315 : 100 : m = match_new(add_here, MATCH_DISCRETE);
316 [ - + ]: 100 : if (!m)
317 : 0 : goto fail;
318 : :
319 : 100 : m->le_hash = le_hash;
320 : 100 : m->size = size;
321 : 100 : m->data = memdup(data, size);
322 [ - + ]: 100 : if (!m->data)
323 : 0 : goto fail;
324 : :
325 : 100 : detach_location(j);
326 : :
327 : 100 : 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 : 4 : _public_ int sd_journal_add_conjunction(sd_journal *j) {
339 [ - + - + ]: 4 : assert_return(j, -EINVAL);
340 [ - + - + ]: 4 : assert_return(!journal_pid_changed(j), -ECHILD);
341 : :
342 [ - + ]: 4 : if (!j->level0)
343 : 0 : return 0;
344 : :
345 [ - + ]: 4 : if (!j->level1)
346 : 0 : return 0;
347 : :
348 [ - + ]: 4 : if (!j->level1->matches)
349 : 0 : return 0;
350 : :
351 : 4 : j->level1 = NULL;
352 : 4 : j->level2 = NULL;
353 : :
354 : 4 : return 0;
355 : : }
356 : :
357 : 8 : _public_ int sd_journal_add_disjunction(sd_journal *j) {
358 [ - + - + ]: 8 : assert_return(j, -EINVAL);
359 [ - + - + ]: 8 : assert_return(!journal_pid_changed(j), -ECHILD);
360 : :
361 [ - + ]: 8 : if (!j->level0)
362 : 0 : return 0;
363 : :
364 [ - + ]: 8 : if (!j->level1)
365 : 0 : return 0;
366 : :
367 [ - + ]: 8 : if (!j->level2)
368 : 0 : return 0;
369 : :
370 [ - + ]: 8 : if (!j->level2->matches)
371 : 0 : return 0;
372 : :
373 : 8 : j->level2 = NULL;
374 : 8 : return 0;
375 : : }
376 : :
377 : 192 : static char *match_make_string(Match *m) {
378 : 192 : char *p = NULL, *r;
379 : : Match *i;
380 : 192 : bool enclose = false;
381 : :
382 [ - + ]: 192 : if (!m)
383 : 0 : return strdup("none");
384 : :
385 [ + + ]: 192 : if (m->type == MATCH_DISCRETE)
386 : 88 : return cescape_length(m->data, m->size);
387 : :
388 [ + + ]: 284 : LIST_FOREACH(matches, i, m->matches) {
389 : : char *t, *k;
390 : :
391 : 180 : t = match_make_string(i);
392 [ - + ]: 180 : if (!t)
393 : 0 : return mfree(p);
394 : :
395 [ + + ]: 180 : if (p) {
396 [ + + ]: 76 : k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t);
397 : 76 : free(p);
398 : 76 : free(t);
399 : :
400 [ - + ]: 76 : if (!k)
401 : 0 : return NULL;
402 : :
403 : 76 : p = k;
404 : :
405 : 76 : enclose = true;
406 : : } else
407 : 104 : p = t;
408 : : }
409 : :
410 [ + + ]: 104 : if (enclose) {
411 : 56 : r = strjoin("(", p, ")");
412 : 56 : free(p);
413 : 56 : return r;
414 : : }
415 : :
416 : 48 : return p;
417 : : }
418 : :
419 : 12 : char *journal_make_match_string(sd_journal *j) {
420 [ - + ]: 12 : assert(j);
421 : :
422 : 12 : return match_make_string(j->level0);
423 : : }
424 : :
425 : 856 : _public_ void sd_journal_flush_matches(sd_journal *j) {
426 [ - + ]: 856 : if (!j)
427 : 0 : return;
428 : :
429 [ + + ]: 856 : if (j->level0)
430 : 20 : match_free(j->level0);
431 : :
432 : 856 : j->level0 = j->level1 = j->level2 = NULL;
433 : :
434 : 856 : detach_location(j);
435 : : }
436 : :
437 : 4005732 : _pure_ static int compare_with_location(JournalFile *f, Location *l) {
438 : : int r;
439 : :
440 [ - + ]: 4005732 : assert(f);
441 [ - + ]: 4005732 : assert(l);
442 [ - + ]: 4005732 : assert(f->location_type == LOCATION_SEEK);
443 [ + - - + ]: 4005732 : assert(IN_SET(l->type, LOCATION_DISCRETE, LOCATION_SEEK));
444 : :
445 [ + - ]: 4005732 : if (l->monotonic_set &&
446 [ + + ]: 4005732 : sd_id128_equal(f->current_boot_id, l->boot_id) &&
447 [ + - ]: 84360 : l->realtime_set &&
448 [ + + ]: 84360 : f->current_realtime == l->realtime &&
449 [ + - ]: 440 : l->xor_hash_set &&
450 [ + + ]: 440 : f->current_xor_hash == l->xor_hash)
451 : 344 : return 0;
452 : :
453 [ + - ]: 4005388 : if (l->seqnum_set &&
454 [ + + ]: 4005388 : sd_id128_equal(f->header->seqnum_id, l->seqnum_id)) {
455 : :
456 [ + + ]: 82784 : r = CMP(f->current_seqnum, l->seqnum);
457 [ + - ]: 82784 : if (r != 0)
458 : 82784 : return r;
459 : : }
460 : :
461 [ + - ]: 3922604 : if (l->monotonic_set &&
462 [ + + ]: 3922604 : sd_id128_equal(f->current_boot_id, l->boot_id)) {
463 : :
464 [ + + ]: 42600 : r = CMP(f->current_monotonic, l->monotonic);
465 [ + - ]: 42600 : if (r != 0)
466 : 42600 : return r;
467 : : }
468 : :
469 [ + - ]: 3880004 : if (l->realtime_set) {
470 : :
471 [ + + ]: 3880004 : r = CMP(f->current_realtime, l->realtime);
472 [ + - ]: 3880004 : if (r != 0)
473 : 3880004 : 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 : 6324 : 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 : 6324 : uint64_t np = 0;
497 : : Object *n;
498 : :
499 [ - + ]: 6324 : assert(j);
500 [ - + ]: 6324 : assert(m);
501 [ - + ]: 6324 : assert(f);
502 : :
503 [ + + ]: 6324 : if (m->type == MATCH_DISCRETE) {
504 : : uint64_t dp;
505 : :
506 : 1776 : r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
507 [ + + ]: 1776 : if (r <= 0)
508 : 40 : return r;
509 : :
510 : 1736 : return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
511 : :
512 [ + + ]: 4548 : } else if (m->type == MATCH_OR_TERM) {
513 : : Match *i;
514 : :
515 : : /* Find the earliest match beyond after_offset */
516 : :
517 [ + + ]: 5216 : LIST_FOREACH(matches, i, m->matches) {
518 : : uint64_t cp;
519 : :
520 : 2636 : r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
521 [ - + ]: 2636 : if (r < 0)
522 : 0 : return r;
523 [ + + ]: 2636 : else if (r > 0) {
524 [ + + + - : 2500 : if (np == 0 || (direction == DIRECTION_DOWN ? cp < np : cp > np))
+ - # # ]
525 : 2500 : np = cp;
526 : : }
527 : : }
528 : :
529 [ + + ]: 2580 : if (np == 0)
530 : 88 : return 0;
531 : :
532 [ + - ]: 1968 : } 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 [ - + ]: 1968 : if (!m->matches)
540 : 0 : return 0;
541 : :
542 : 1968 : r = next_for_match(j, m->matches, f, after_offset, direction, NULL, &np);
543 [ + + ]: 1968 : if (r <= 0)
544 : 88 : return r;
545 : :
546 [ + + - + ]: 1880 : assert(direction == DIRECTION_DOWN ? np >= after_offset : np <= after_offset);
547 : 1880 : last_moved = m->matches;
548 : :
549 [ + + + + : 2492 : LIST_LOOP_BUT_ONE(matches, i, m->matches, last_moved) {
+ + ]
550 : : uint64_t cp;
551 : :
552 : 612 : r = next_for_match(j, i, f, np, direction, NULL, &cp);
553 [ - + ]: 612 : if (r <= 0)
554 : 0 : return r;
555 : :
556 [ + + - + ]: 612 : assert(direction == DIRECTION_DOWN ? cp >= np : cp <= np);
557 [ + + + + ]: 612 : if (direction == DIRECTION_DOWN ? cp > np : cp < np) {
558 : 148 : np = cp;
559 : 148 : last_moved = i;
560 : : }
561 : : }
562 : : }
563 : :
564 [ - + ]: 4372 : assert(np > 0);
565 : :
566 : 4372 : r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
567 [ - + ]: 4372 : if (r < 0)
568 : 0 : return r;
569 : :
570 [ + + ]: 4372 : if (ret)
571 : 816 : *ret = n;
572 [ + - ]: 4372 : if (offset)
573 : 4372 : *offset = np;
574 : :
575 : 4372 : return 1;
576 : : }
577 : :
578 : 2596 : 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 [ - + ]: 2596 : assert(j);
589 [ - + ]: 2596 : assert(m);
590 [ - + ]: 2596 : assert(f);
591 : :
592 [ + + ]: 2596 : if (m->type == MATCH_DISCRETE) {
593 : : uint64_t dp;
594 : :
595 : 668 : r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
596 [ + + ]: 668 : if (r <= 0)
597 : 200 : return r;
598 : :
599 : : /* FIXME: missing: find by monotonic */
600 : :
601 [ + + ]: 468 : if (j->current_location.type == LOCATION_HEAD)
602 : 48 : return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset);
603 [ + - ]: 420 : if (j->current_location.type == LOCATION_TAIL)
604 : 420 : 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 [ + + ]: 1928 : } else if (m->type == MATCH_OR_TERM) {
618 : 1072 : uint64_t np = 0;
619 : : Object *n;
620 : : Match *i;
621 : :
622 : : /* Find the earliest match */
623 : :
624 [ + + ]: 2168 : LIST_FOREACH(matches, i, m->matches) {
625 : : uint64_t cp;
626 : :
627 : 1096 : r = find_location_for_match(j, i, f, direction, NULL, &cp);
628 [ - + ]: 1096 : if (r < 0)
629 : 0 : return r;
630 [ + + ]: 1096 : else if (r > 0) {
631 [ + + + - : 716 : if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
+ - # # ]
632 : 716 : np = cp;
633 : : }
634 : : }
635 : :
636 [ + + ]: 1072 : if (np == 0)
637 : 360 : return 0;
638 : :
639 : 712 : r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
640 [ - + ]: 712 : if (r < 0)
641 : 0 : return r;
642 : :
643 [ - + ]: 712 : if (ret)
644 : 0 : *ret = n;
645 [ + - ]: 712 : if (offset)
646 : 712 : *offset = np;
647 : :
648 : 712 : return 1;
649 : :
650 : : } else {
651 : : Match *i;
652 : 856 : uint64_t np = 0;
653 : :
654 [ - + ]: 856 : 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 [ - + ]: 856 : if (!m->matches)
660 : 0 : return 0;
661 : :
662 [ + + ]: 1568 : LIST_FOREACH(matches, i, m->matches) {
663 : : uint64_t cp;
664 : :
665 : 1072 : r = find_location_for_match(j, i, f, direction, NULL, &cp);
666 [ + + ]: 1072 : if (r <= 0)
667 : 360 : return r;
668 : :
669 [ + + + + : 712 : if (np == 0 || (direction == DIRECTION_DOWN ? cp > np : cp < np))
- + + + ]
670 : 664 : np = cp;
671 : : }
672 : :
673 : 496 : return next_for_match(j, m, f, np, direction, ret, offset);
674 : : }
675 : : }
676 : :
677 : 940 : 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 [ - + ]: 940 : assert(j);
687 [ - + ]: 940 : assert(f);
688 [ - + ]: 940 : assert(ret);
689 [ - + ]: 940 : assert(offset);
690 : :
691 [ + + ]: 940 : if (!j->level0) {
692 : : /* No matches is simple */
693 : :
694 [ + + ]: 512 : if (j->current_location.type == LOCATION_HEAD)
695 : 448 : return journal_file_next_entry(f, 0, DIRECTION_DOWN, ret, offset);
696 [ + + ]: 64 : if (j->current_location.type == LOCATION_TAIL)
697 : 32 : return journal_file_next_entry(f, 0, DIRECTION_UP, ret, offset);
698 [ + - + + ]: 32 : if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
699 : 16 : return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
700 [ + - ]: 16 : if (j->current_location.monotonic_set) {
701 : 16 : r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
702 [ - + ]: 16 : if (r != -ENOENT)
703 : 0 : return r;
704 : : }
705 [ + - ]: 16 : if (j->current_location.realtime_set)
706 : 16 : 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 : 428 : return find_location_for_match(j, j->level0, f, direction, ret, offset);
711 : : }
712 : :
713 : 41824 : 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 [ - + ]: 41824 : assert(j);
721 [ - + ]: 41824 : assert(f);
722 [ - + ]: 41824 : assert(ret);
723 [ - + ]: 41824 : assert(offset);
724 : :
725 : : /* No matches is easy. We simple advance the file
726 : : * pointer by one. */
727 [ + + ]: 41824 : if (!j->level0)
728 : 41212 : 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 [ + + ]: 1224 : return next_for_match(j, j->level0, f,
733 : 388 : direction == DIRECTION_DOWN ? f->current_offset + 1
734 : 224 : : f->current_offset - 1,
735 : : direction, ret, offset);
736 : : }
737 : :
738 : 4048140 : 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 [ - + ]: 4048140 : assert(j);
744 [ - + ]: 4048140 : assert(f);
745 : :
746 : 4048140 : 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 [ + + + + ]: 4048140 : if (f->last_direction == direction && f->location_type == LOCATION_TAIL &&
751 [ + - ]: 41708 : n_entries == f->last_n_entries)
752 : 41708 : return 0;
753 : :
754 : 4006432 : f->last_n_entries = n_entries;
755 : :
756 [ + + + + ]: 4006432 : 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 [ + + ]: 4046856 : if (f->location_type != LOCATION_SEEK) {
761 : 41480 : r = next_with_matches(j, f, direction, &c, &cp);
762 [ + + ]: 41480 : if (r <= 0)
763 : 116 : return r;
764 : :
765 : 41364 : journal_file_save_location(f, c, cp);
766 : : }
767 : : } else {
768 : 940 : f->last_direction = direction;
769 : :
770 : 940 : r = find_location_with_matches(j, f, direction, &c, &cp);
771 [ + + ]: 940 : if (r <= 0)
772 : 184 : return r;
773 : :
774 : 756 : 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 : 324 : for (;;) {
784 : : bool found;
785 : :
786 [ + + ]: 4006456 : if (j->current_location.type == LOCATION_DISCRETE) {
787 : : int k;
788 : :
789 : 4005732 : k = compare_with_location(f, &j->current_location);
790 : :
791 [ + + ]: 4005732 : found = direction == DIRECTION_DOWN ? k > 0 : k < 0;
792 : : } else
793 : 724 : found = true;
794 : :
795 [ + + ]: 4006456 : if (found)
796 : 4006112 : return 1;
797 : :
798 : 344 : r = next_with_matches(j, f, direction, &c, &cp);
799 [ + + ]: 344 : if (r <= 0)
800 : 20 : return r;
801 : :
802 : 324 : journal_file_save_location(f, c, cp);
803 : : }
804 : : }
805 : :
806 : 41556 : static int real_journal_next(sd_journal *j, direction_t direction) {
807 : 41556 : JournalFile *new_file = NULL;
808 : : unsigned i, n_files;
809 : : const void **files;
810 : : Object *o;
811 : : int r;
812 : :
813 [ - + - + ]: 41556 : assert_return(j, -EINVAL);
814 [ - + - + ]: 41556 : assert_return(!journal_pid_changed(j), -ECHILD);
815 : :
816 : 41556 : r = iterated_cache_get(j->files_cache, NULL, &files, &n_files);
817 [ - + ]: 41556 : if (r < 0)
818 : 0 : return r;
819 : :
820 [ + + ]: 4089696 : for (i = 0; i < n_files; i++) {
821 : 4048140 : JournalFile *f = (JournalFile *)files[i];
822 : : bool found;
823 : :
824 : 4048140 : r = next_beyond_location(j, f, direction);
825 [ - + ]: 4048140 : 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 [ + + ]: 4048140 : } else if (r == 0) {
830 : 42028 : f->location_type = LOCATION_TAIL;
831 : 42028 : continue;
832 : : }
833 : :
834 [ + + ]: 4006112 : if (!new_file)
835 : 41504 : found = true;
836 : : else {
837 : : int k;
838 : :
839 : 3964608 : k = journal_file_compare_locations(f, new_file);
840 : :
841 [ + + ]: 3964608 : found = direction == DIRECTION_DOWN ? k < 0 : k > 0;
842 : : }
843 : :
844 [ + + ]: 4006112 : if (found)
845 : 164044 : new_file = f;
846 : : }
847 : :
848 [ + + ]: 41556 : if (!new_file)
849 : 52 : return 0;
850 : :
851 : 41504 : r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_file->current_offset, &o);
852 [ - + ]: 41504 : if (r < 0)
853 : 0 : return r;
854 : :
855 : 41504 : set_location(j, new_file, o);
856 : :
857 : 41504 : return 1;
858 : : }
859 : :
860 : 41216 : _public_ int sd_journal_next(sd_journal *j) {
861 : 41216 : return real_journal_next(j, DIRECTION_DOWN);
862 : : }
863 : :
864 : 276 : _public_ int sd_journal_previous(sd_journal *j) {
865 : 276 : return real_journal_next(j, DIRECTION_UP);
866 : : }
867 : :
868 : 16 : static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
869 : 16 : int c = 0, r;
870 : :
871 [ - + - + ]: 16 : assert_return(j, -EINVAL);
872 [ - + - + ]: 16 : assert_return(!journal_pid_changed(j), -ECHILD);
873 : :
874 [ - + ]: 16 : 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 : 64 : r = real_journal_next(j, direction);
888 [ - + ]: 64 : if (r < 0)
889 : 0 : return r;
890 : :
891 [ - + ]: 64 : if (r == 0)
892 : 0 : return c;
893 : :
894 : 64 : skip--;
895 : 64 : c++;
896 [ + + ]: 64 : } while (skip > 0);
897 : :
898 : 16 : return c;
899 : : }
900 : :
901 : 8 : _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
902 : 8 : return real_journal_next_skip(j, DIRECTION_DOWN, skip);
903 : : }
904 : :
905 : 8 : _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
906 : 8 : return real_journal_next_skip(j, DIRECTION_UP, skip);
907 : : }
908 : :
909 : 2256 : _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 [ - + - + ]: 2256 : assert_return(j, -EINVAL);
915 [ - + - + ]: 2256 : assert_return(!journal_pid_changed(j), -ECHILD);
916 [ - + - + ]: 2256 : assert_return(cursor, -EINVAL);
917 : :
918 [ + - - + ]: 2256 : if (!j->current_file || j->current_file->current_offset <= 0)
919 : 0 : return -EADDRNOTAVAIL;
920 : :
921 : 2256 : r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
922 [ - + ]: 2256 : if (r < 0)
923 : 0 : return r;
924 : :
925 : 2256 : sd_id128_to_string(j->current_file->header->seqnum_id, sid);
926 : 2256 : sd_id128_to_string(o->entry.boot_id, bid);
927 : :
928 [ - + ]: 2256 : if (asprintf(cursor,
929 : : "s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64,
930 : 2256 : sid, le64toh(o->entry.seqnum),
931 : 2256 : bid, le64toh(o->entry.monotonic),
932 : 2256 : le64toh(o->entry.realtime),
933 : 2256 : le64toh(o->entry.xor_hash)) < 0)
934 : 0 : return -ENOMEM;
935 : :
936 : 2256 : 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 : 1288 : _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
1045 : : int r;
1046 : : Object *o;
1047 : :
1048 [ - + - + ]: 1288 : assert_return(j, -EINVAL);
1049 [ - + - + ]: 1288 : assert_return(!journal_pid_changed(j), -ECHILD);
1050 [ - + - + ]: 1288 : assert_return(!isempty(cursor), -EINVAL);
1051 : :
1052 [ + - - + ]: 1288 : if (!j->current_file || j->current_file->current_offset <= 0)
1053 : 0 : return -EADDRNOTAVAIL;
1054 : :
1055 : 1288 : r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
1056 [ - + ]: 1288 : if (r < 0)
1057 : 0 : return r;
1058 : :
1059 : 7728 : for (;;) {
1060 [ + - + ]: 9016 : _cleanup_free_ char *item = NULL;
1061 : : unsigned long long ll;
1062 : : sd_id128_t id;
1063 : 9016 : int k = 0;
1064 : :
1065 : 9016 : r = extract_first_word(&cursor, &item, ";", EXTRACT_DONT_COALESCE_SEPARATORS);
1066 [ - + ]: 9016 : if (r < 0)
1067 : 0 : return r;
1068 : :
1069 [ + + ]: 9016 : if (r == 0)
1070 : 1288 : break;
1071 : :
1072 [ + - - + ]: 7728 : if (strlen(item) < 2 || item[1] != '=')
1073 : 0 : return -EINVAL;
1074 : :
1075 [ + + + + : 7728 : switch (item[0]) {
+ + - ]
1076 : :
1077 : 1288 : case 's':
1078 : 1288 : k = sd_id128_from_string(item+2, &id);
1079 [ - + ]: 1288 : if (k < 0)
1080 : 0 : return k;
1081 [ - + ]: 1288 : if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
1082 : 0 : return 0;
1083 : 1288 : break;
1084 : :
1085 : 1288 : case 'i':
1086 [ - + ]: 1288 : if (sscanf(item+2, "%llx", &ll) != 1)
1087 : 0 : return -EINVAL;
1088 [ - + ]: 1288 : if (ll != le64toh(o->entry.seqnum))
1089 : 0 : return 0;
1090 : 1288 : break;
1091 : :
1092 : 1288 : case 'b':
1093 : 1288 : k = sd_id128_from_string(item+2, &id);
1094 [ - + ]: 1288 : if (k < 0)
1095 : 0 : return k;
1096 [ - + ]: 1288 : if (!sd_id128_equal(id, o->entry.boot_id))
1097 : 0 : return 0;
1098 : 1288 : break;
1099 : :
1100 : 1288 : case 'm':
1101 [ - + ]: 1288 : if (sscanf(item+2, "%llx", &ll) != 1)
1102 : 0 : return -EINVAL;
1103 [ - + ]: 1288 : if (ll != le64toh(o->entry.monotonic))
1104 : 0 : return 0;
1105 : 1288 : break;
1106 : :
1107 : 1288 : case 't':
1108 [ - + ]: 1288 : if (sscanf(item+2, "%llx", &ll) != 1)
1109 : 0 : return -EINVAL;
1110 [ - + ]: 1288 : if (ll != le64toh(o->entry.realtime))
1111 : 0 : return 0;
1112 : 1288 : break;
1113 : :
1114 : 1288 : case 'x':
1115 [ - + ]: 1288 : if (sscanf(item+2, "%llx", &ll) != 1)
1116 : 0 : return -EINVAL;
1117 [ - + ]: 1288 : if (ll != le64toh(o->entry.xor_hash))
1118 : 0 : return 0;
1119 : 1288 : break;
1120 : : }
1121 : 7728 : }
1122 : :
1123 : 1288 : 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 : 36 : _public_ int sd_journal_seek_head(sd_journal *j) {
1152 [ - + - + ]: 36 : assert_return(j, -EINVAL);
1153 [ - + - + ]: 36 : assert_return(!journal_pid_changed(j), -ECHILD);
1154 : :
1155 : 36 : reset_location(j);
1156 : 36 : j->current_location.type = LOCATION_HEAD;
1157 : :
1158 : 36 : return 0;
1159 : : }
1160 : :
1161 : 24 : _public_ int sd_journal_seek_tail(sd_journal *j) {
1162 [ - + - + ]: 24 : assert_return(j, -EINVAL);
1163 [ - + - + ]: 24 : assert_return(!journal_pid_changed(j), -ECHILD);
1164 : :
1165 : 24 : reset_location(j);
1166 : 24 : j->current_location.type = LOCATION_TAIL;
1167 : :
1168 : 24 : return 0;
1169 : : }
1170 : :
1171 : 40620 : static void check_network(sd_journal *j, int fd) {
1172 [ - + ]: 40620 : assert(j);
1173 : :
1174 [ - + ]: 40620 : if (j->on_network)
1175 : 0 : return;
1176 : :
1177 : 40620 : 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 : 39264 : static bool file_type_wanted(int flags, const char *filename) {
1192 [ - + ]: 39264 : assert(filename);
1193 : :
1194 [ + + - + ]: 39264 : if (!endswith(filename, ".journal") && !endswith(filename, ".journal~"))
1195 : 0 : return false;
1196 : :
1197 : : /* no flags set → every type is OK */
1198 [ + - ]: 39264 : if (!(flags & (SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)))
1199 : 39264 : 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 : 44560 : static bool path_has_prefix(sd_journal *j, const char *path, const char *prefix) {
1217 [ - + ]: 44560 : assert(j);
1218 [ - + ]: 44560 : assert(path);
1219 [ - + ]: 44560 : assert(prefix);
1220 : :
1221 [ - + ]: 44560 : if (j->toplevel_fd >= 0)
1222 : 0 : return false;
1223 : :
1224 : 44560 : return path_startswith(path, prefix);
1225 : : }
1226 : :
1227 : 39264 : static void track_file_disposition(sd_journal *j, JournalFile *f) {
1228 [ - + ]: 39264 : assert(j);
1229 [ - + ]: 39264 : assert(f);
1230 : :
1231 [ + - - + ]: 39264 : if (!j->has_runtime_files && path_has_prefix(j, f->path, "/run"))
1232 : 0 : j->has_runtime_files = true;
1233 [ + + + - ]: 39264 : else if (!j->has_persistent_files && path_has_prefix(j, f->path, "/var"))
1234 : 448 : j->has_persistent_files = true;
1235 : 39264 : }
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 : 39264 : static int add_any_file(
1249 : : sd_journal *j,
1250 : : int fd,
1251 : : const char *path) {
1252 : :
1253 : 39264 : bool close_fd = false;
1254 : : JournalFile *f;
1255 : : struct stat st;
1256 : : int r, k;
1257 : :
1258 [ - + ]: 39264 : assert(j);
1259 [ + - - + ]: 39264 : assert(fd >= 0 || path);
1260 : :
1261 [ + - ]: 39264 : if (fd < 0) {
1262 [ - + ]: 39264 : 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 : 39264 : fd = open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
1269 [ - + ]: 39264 : 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 : 39264 : close_fd = true;
1275 : :
1276 : 39264 : r = fd_nonblock(fd, false);
1277 [ - + ]: 39264 : 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 [ - + ]: 39264 : 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 : 39264 : r = stat_verify_regular(&st);
1289 [ - + ]: 39264 : 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 : 39264 : f = ordered_hashmap_get(j->files, path);
1295 [ - + ]: 39264 : 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 [ - + ]: 39264 : 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 : 39264 : r = journal_file_open(fd, path, O_RDONLY, 0, false, 0, false, NULL, j->mmap, NULL, NULL, &f);
1322 [ - + ]: 39264 : 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 : 39264 : r = ordered_hashmap_put(j->files, f->path, f);
1330 [ - + ]: 39264 : 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 : 39264 : close_fd = false; /* the fd is now owned by the JournalFile object */
1337 : :
1338 : 39264 : f->last_seen_generation = j->generation;
1339 : :
1340 : 39264 : track_file_disposition(j, f);
1341 : 39264 : check_network(j, f->fd);
1342 : :
1343 : 39264 : j->current_invalidate_counter++;
1344 : :
1345 [ + + ]: 39264 : log_debug("File %s added.", f->path);
1346 : :
1347 : 39264 : r = 0;
1348 : :
1349 : 39264 : finish:
1350 [ - + ]: 39264 : if (close_fd)
1351 : 0 : safe_close(fd);
1352 : :
1353 [ - + ]: 39264 : if (r < 0) {
1354 : 0 : k = journal_put_error(j, r, path);
1355 [ # # ]: 0 : if (k < 0)
1356 : 0 : return k;
1357 : : }
1358 : :
1359 : 39264 : return r;
1360 : : }
1361 : :
1362 : 39264 : 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 [ - + ]: 39264 : assert(j);
1370 [ - + ]: 39264 : assert(prefix);
1371 [ - + ]: 39264 : assert(filename);
1372 : :
1373 [ - + ]: 39264 : if (j->no_new_files)
1374 : 0 : return 0;
1375 : :
1376 [ - + ]: 39264 : if (!file_type_wanted(j->flags, filename))
1377 : 0 : return 0;
1378 : :
1379 [ - + # # : 39264 : path = prefix_roota(prefix, filename);
- + - + -
+ + - - +
+ - ]
1380 : 39264 : 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 : 5252 : static int dirname_is_machine_id(const char *fn) {
1437 : : sd_id128_t id, machine;
1438 : : int r;
1439 : :
1440 : 5252 : r = sd_id128_get_machine(&machine);
1441 [ - + ]: 5252 : if (r < 0)
1442 : 0 : return r;
1443 : :
1444 : 5252 : r = sd_id128_from_string(fn, &id);
1445 [ - + ]: 5252 : if (r < 0)
1446 : 0 : return r;
1447 : :
1448 : 5252 : return sd_id128_equal(id, machine);
1449 : : }
1450 : :
1451 : 47332 : static bool dirent_is_journal_file(const struct dirent *de) {
1452 [ - + ]: 47332 : assert(de);
1453 : :
1454 [ + + + + ]: 47332 : if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN))
1455 : 8068 : return false;
1456 : :
1457 [ + + ]: 46268 : return endswith(de->d_name, ".journal") ||
1458 [ + - ]: 7004 : endswith(de->d_name, ".journal~");
1459 : : }
1460 : :
1461 : 7128 : static bool dirent_is_id128_subdir(const struct dirent *de) {
1462 [ - + ]: 7128 : assert(de);
1463 : :
1464 [ + + + + ]: 7128 : if (!IN_SET(de->d_type, DT_DIR, DT_LNK, DT_UNKNOWN))
1465 : 76 : return false;
1466 : :
1467 : 7052 : return id128_is_valid(de->d_name);
1468 : : }
1469 : :
1470 : 1776 : static int directory_open(sd_journal *j, const char *path, DIR **ret) {
1471 : : DIR *d;
1472 : :
1473 [ - + ]: 1776 : assert(j);
1474 [ - + ]: 1776 : assert(path);
1475 [ - + ]: 1776 : assert(ret);
1476 : :
1477 [ + - ]: 1776 : if (j->toplevel_fd < 0)
1478 : 1776 : 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 [ + + ]: 1776 : if (!d)
1484 : 420 : return -errno;
1485 : :
1486 : 1356 : *ret = d;
1487 : 1356 : return 0;
1488 : : }
1489 : :
1490 : : static int add_directory(sd_journal *j, const char *prefix, const char *dirname);
1491 : :
1492 : 1356 : static void directory_enumerate(sd_journal *j, Directory *m, DIR *d) {
1493 : : struct dirent *de;
1494 : :
1495 [ - + ]: 1356 : assert(j);
1496 [ - + ]: 1356 : assert(m);
1497 [ - + ]: 1356 : assert(d);
1498 : :
1499 [ + + - + ]: 48688 : FOREACH_DIRENT_ALL(de, d, goto fail) {
1500 : :
1501 [ + + ]: 47332 : if (dirent_is_journal_file(de))
1502 : 39264 : (void) add_file_by_name(j, m->path, de->d_name);
1503 : :
1504 [ + + + + ]: 47332 : if (m->is_root && dirent_is_id128_subdir(de))
1505 : 5356 : (void) add_directory(j, m->path, de->d_name);
1506 : : }
1507 : :
1508 : 1356 : return;
1509 : :
1510 : 0 : fail:
1511 [ # # ]: 0 : log_debug_errno(errno, "Failed to enumerate directory %s, ignoring: %m", m->path);
1512 : : }
1513 : :
1514 : 1356 : static void directory_watch(sd_journal *j, Directory *m, int fd, uint32_t mask) {
1515 : : int r;
1516 : :
1517 [ - + ]: 1356 : assert(j);
1518 [ - + ]: 1356 : assert(m);
1519 [ - + ]: 1356 : assert(fd >= 0);
1520 : :
1521 : : /* Watch this directory if that's enabled and if it not being watched yet. */
1522 : :
1523 [ - + ]: 1356 : if (m->wd > 0) /* Already have a watch? */
1524 : 0 : return;
1525 [ + - ]: 1356 : if (j->inotify_fd < 0) /* Not watching at all? */
1526 : 1356 : 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 : 5356 : static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
1545 : 5356 : _cleanup_free_ char *path = NULL;
1546 : 5356 : _cleanup_closedir_ DIR *d = NULL;
1547 : : Directory *m;
1548 : : int r, k;
1549 : :
1550 [ - + ]: 5356 : assert(j);
1551 [ - + ]: 5356 : 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 : 5356 : path = path_join(prefix, dirname);
1557 [ - + ]: 5356 : if (!path) {
1558 : 0 : r = -ENOMEM;
1559 : 0 : goto fail;
1560 : : }
1561 : :
1562 [ + + ]: 5356 : 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 [ + + + - ]: 5356 : if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
1566 [ + + + - ]: 5252 : !((dirname && dirname_is_machine_id(dirname) > 0) || path_has_prefix(j, path, "/run")))
1567 : 4848 : return 0;
1568 : :
1569 : 508 : r = directory_open(j, path, &d);
1570 [ - + ]: 508 : if (r < 0) {
1571 [ # # ]: 0 : log_debug_errno(r, "Failed to open directory '%s': %m", path);
1572 : 0 : goto fail;
1573 : : }
1574 : :
1575 : 508 : m = hashmap_get(j->directories_by_path, path);
1576 [ + - ]: 508 : if (!m) {
1577 : 508 : m = new0(Directory, 1);
1578 [ - + ]: 508 : if (!m) {
1579 : 0 : r = -ENOMEM;
1580 : 0 : goto fail;
1581 : : }
1582 : :
1583 : 508 : m->is_root = false;
1584 : 508 : m->path = path;
1585 : :
1586 [ - + ]: 508 : 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 : 508 : path = NULL; /* avoid freeing in cleanup */
1593 : 508 : j->current_invalidate_counter++;
1594 : :
1595 [ + + ]: 508 : 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 : 508 : m->last_seen_generation = j->generation;
1601 : :
1602 : 508 : 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 [ + - ]: 508 : if (!j->no_new_files)
1608 : 508 : directory_enumerate(j, m, d);
1609 : :
1610 : 508 : check_network(j, dirfd(d));
1611 : :
1612 : 508 : 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 : 1268 : static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) {
1623 : :
1624 : 1268 : _cleanup_closedir_ DIR *d = NULL;
1625 : : Directory *m;
1626 : : int r, k;
1627 : :
1628 [ - + ]: 1268 : 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 [ + - ]: 1268 : if (p) {
1635 : : /* If there's a path specified, use it. */
1636 : :
1637 [ + + ]: 1268 : log_debug("Considering root directory '%s'.", p);
1638 : :
1639 [ - + ]: 1268 : if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
1640 [ # # ]: 0 : !path_has_prefix(j, p, "/run"))
1641 : 0 : return -EINVAL;
1642 : :
1643 [ - + ]: 1268 : if (j->prefix)
1644 [ # # # # : 0 : p = strjoina(j->prefix, p);
# # # # #
# # # ]
1645 : :
1646 : 1268 : r = directory_open(j, p, &d);
1647 [ + + + - ]: 1268 : if (r == -ENOENT && missing_ok)
1648 : 420 : return 0;
1649 [ - + ]: 848 : 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 : 848 : m = hashmap_get(j->directories_by_path, p);
1678 [ + - ]: 848 : if (!m) {
1679 : 848 : m = new0(Directory, 1);
1680 [ - + ]: 848 : if (!m) {
1681 : 0 : r = -ENOMEM;
1682 : 0 : goto fail;
1683 : : }
1684 : :
1685 : 848 : m->is_root = true;
1686 : :
1687 : 848 : m->path = strdup(p);
1688 [ - + ]: 848 : if (!m->path) {
1689 : 0 : free(m);
1690 : 0 : r = -ENOMEM;
1691 : 0 : goto fail;
1692 : : }
1693 : :
1694 [ - + ]: 848 : 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 : 848 : j->current_invalidate_counter++;
1702 : :
1703 [ + + ]: 848 : log_debug("Root directory %s added.", m->path);
1704 : :
1705 [ # # ]: 0 : } else if (!m->is_root)
1706 : 0 : return 0;
1707 : :
1708 : 848 : directory_watch(j, m, dirfd(d),
1709 : : IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
1710 : : IN_ONLYDIR);
1711 : :
1712 [ + - ]: 848 : if (!j->no_new_files)
1713 : 848 : directory_enumerate(j, m, d);
1714 : :
1715 : 848 : check_network(j, dirfd(d));
1716 : :
1717 : 848 : 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 : 1356 : static void remove_directory(sd_journal *j, Directory *d) {
1728 [ - + ]: 1356 : assert(j);
1729 : :
1730 [ - + ]: 1356 : 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 : 1356 : hashmap_remove(j->directories_by_path, d->path);
1738 : :
1739 [ + + ]: 1356 : if (d->is_root)
1740 [ + + ]: 848 : log_debug("Root directory %s removed.", d->path);
1741 : : else
1742 [ + + ]: 508 : log_debug("Directory %s removed.", d->path);
1743 : :
1744 : 1356 : free(d->path);
1745 : 1356 : free(d);
1746 : 1356 : }
1747 : :
1748 : 412 : 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 [ - + ]: 412 : 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 [ + - + + ]: 1236 : NULSTR_FOREACH(p, search_paths)
1761 : 824 : (void) add_root_directory(j, p, true);
1762 : :
1763 [ + + ]: 412 : if (!(j->flags & SD_JOURNAL_LOCAL_ONLY))
1764 : 8 : (void) add_root_directory(j, "/var/log/journal/remote", true);
1765 : :
1766 : 412 : 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 : 848 : static sd_journal *journal_new(int flags, const char *path) {
1808 : 848 : _cleanup_(sd_journal_closep) sd_journal *j = NULL;
1809 : :
1810 : 848 : j = new0(sd_journal, 1);
1811 [ - + ]: 848 : if (!j)
1812 : 0 : return NULL;
1813 : :
1814 : 848 : j->original_pid = getpid_cached();
1815 : 848 : j->toplevel_fd = -1;
1816 : 848 : j->inotify_fd = -1;
1817 : 848 : j->flags = flags;
1818 : 848 : j->data_threshold = DEFAULT_DATA_THRESHOLD;
1819 : :
1820 [ + + ]: 848 : if (path) {
1821 : : char *t;
1822 : :
1823 : 436 : t = strdup(path);
1824 [ - + ]: 436 : if (!t)
1825 : 0 : return NULL;
1826 : :
1827 [ - + ]: 436 : if (flags & SD_JOURNAL_OS_ROOT)
1828 : 0 : j->prefix = t;
1829 : : else
1830 : 436 : j->path = t;
1831 : : }
1832 : :
1833 : 848 : j->files = ordered_hashmap_new(&path_hash_ops);
1834 [ - + ]: 848 : if (!j->files)
1835 : 0 : return NULL;
1836 : :
1837 : 848 : j->files_cache = ordered_hashmap_iterated_cache_new(j->files);
1838 : 848 : j->directories_by_path = hashmap_new(&path_hash_ops);
1839 : 848 : j->mmap = mmap_cache_new();
1840 [ + - + - : 848 : if (!j->files_cache || !j->directories_by_path || !j->mmap)
- + ]
1841 : 0 : return NULL;
1842 : :
1843 : 848 : 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 : 412 : _public_ int sd_journal_open(sd_journal **ret, int flags) {
1852 : 412 : _cleanup_(sd_journal_closep) sd_journal *j = NULL;
1853 : : int r;
1854 : :
1855 [ - + - + ]: 412 : assert_return(ret, -EINVAL);
1856 [ - + - + ]: 412 : assert_return((flags & ~OPEN_ALLOWED_FLAGS) == 0, -EINVAL);
1857 : :
1858 : 412 : j = journal_new(flags, NULL);
1859 [ - + ]: 412 : if (!j)
1860 : 0 : return -ENOMEM;
1861 : :
1862 : 412 : r = add_search_paths(j);
1863 [ - + ]: 412 : if (r < 0)
1864 : 0 : return r;
1865 : :
1866 : 412 : *ret = TAKE_PTR(j);
1867 : 412 : 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 : 836 : _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
1918 : 836 : _cleanup_(sd_journal_closep) sd_journal *j = NULL;
1919 : : int r;
1920 : :
1921 [ - + - + ]: 836 : assert_return(ret, -EINVAL);
1922 [ - + - + ]: 836 : assert_return(path, -EINVAL);
1923 [ + + + + ]: 836 : assert_return((flags & ~OPEN_DIRECTORY_ALLOWED_FLAGS) == 0, -EINVAL);
1924 : :
1925 : 436 : j = journal_new(flags, path);
1926 [ - + ]: 436 : if (!j)
1927 : 0 : return -ENOMEM;
1928 : :
1929 [ - + ]: 436 : if (flags & SD_JOURNAL_OS_ROOT)
1930 : 0 : r = add_search_paths(j);
1931 : : else
1932 : 436 : r = add_root_directory(j, path, false);
1933 [ - + ]: 436 : if (r < 0)
1934 : 0 : return r;
1935 : :
1936 : 436 : *ret = TAKE_PTR(j);
1937 : 436 : 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 : 848 : _public_ void sd_journal_close(sd_journal *j) {
2053 : : Directory *d;
2054 : :
2055 [ - + ]: 848 : if (!j)
2056 : 0 : return;
2057 : :
2058 : 848 : sd_journal_flush_matches(j);
2059 : :
2060 [ + + ]: 40112 : ordered_hashmap_free_with_destructor(j->files, journal_file_close);
2061 : 848 : iterated_cache_free(j->files_cache);
2062 : :
2063 [ + + ]: 2204 : while ((d = hashmap_first(j->directories_by_path)))
2064 : 1356 : remove_directory(j, d);
2065 : :
2066 [ - + ]: 848 : while ((d = hashmap_first(j->directories_by_wd)))
2067 : 0 : remove_directory(j, d);
2068 : :
2069 : 848 : hashmap_free(j->directories_by_path);
2070 : 848 : hashmap_free(j->directories_by_wd);
2071 : :
2072 : 848 : safe_close(j->inotify_fd);
2073 : :
2074 [ + - ]: 848 : if (j->mmap) {
2075 [ + + ]: 848 : log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap));
2076 : 848 : mmap_cache_unref(j->mmap);
2077 : : }
2078 : :
2079 : 848 : hashmap_free_free(j->errors);
2080 : :
2081 : 848 : free(j->path);
2082 : 848 : free(j->prefix);
2083 : 848 : free(j->unique_field);
2084 : 848 : free(j->fields_buffer);
2085 : 848 : 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 : 2428 : static bool field_is_valid(const char *field) {
2150 : : const char *p;
2151 : :
2152 [ - + ]: 2428 : assert(field);
2153 : :
2154 [ - + ]: 2428 : if (isempty(field))
2155 : 0 : return false;
2156 : :
2157 [ - + ]: 2428 : if (startswith(field, "__"))
2158 : 0 : return false;
2159 : :
2160 [ + + ]: 16068 : for (p = field; *p; p++) {
2161 : :
2162 [ - + ]: 13640 : if (*p == '_')
2163 : 0 : continue;
2164 : :
2165 [ + - + - ]: 13640 : if (*p >= 'A' && *p <= 'Z')
2166 : 13640 : continue;
2167 : :
2168 [ # # # # ]: 0 : if (*p >= '0' && *p <= '9')
2169 : 0 : continue;
2170 : :
2171 : 0 : return false;
2172 : : }
2173 : :
2174 : 2428 : return true;
2175 : : }
2176 : :
2177 : 2424 : _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 [ - + - + ]: 2424 : assert_return(j, -EINVAL);
2185 [ - + - + ]: 2424 : assert_return(!journal_pid_changed(j), -ECHILD);
2186 [ - + - + ]: 2424 : assert_return(field, -EINVAL);
2187 [ - + - + ]: 2424 : assert_return(data, -EINVAL);
2188 [ - + - + ]: 2424 : assert_return(size, -EINVAL);
2189 [ - + - + ]: 2424 : assert_return(field_is_valid(field), -EINVAL);
2190 : :
2191 : 2424 : f = j->current_file;
2192 [ - + ]: 2424 : if (!f)
2193 : 0 : return -EADDRNOTAVAIL;
2194 : :
2195 [ - + ]: 2424 : if (f->current_offset <= 0)
2196 : 0 : return -EADDRNOTAVAIL;
2197 : :
2198 : 2424 : r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2199 [ - + ]: 2424 : if (r < 0)
2200 : 0 : return r;
2201 : :
2202 : 2424 : field_length = strlen(field);
2203 : :
2204 : 2424 : n = journal_file_entry_n_items(o);
2205 [ + - ]: 4282 : 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 : 4282 : p = le64toh(o->entry.items[i].object_offset);
2212 : 4282 : le_hash = o->entry.items[i].hash;
2213 : 4282 : r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2214 [ - + ]: 4282 : if (r < 0)
2215 : 0 : return r;
2216 : :
2217 [ - + ]: 4282 : if (le_hash != o->data.hash)
2218 : 0 : return -EBADMSG;
2219 : :
2220 : 4282 : l = le64toh(o->object.size) - offsetof(Object, data.payload);
2221 : :
2222 : 4282 : compression = o->object.flags & OBJECT_COMPRESSION_MASK;
2223 [ - + ]: 4282 : 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 [ + + ]: 4282 : } else if (l >= field_length+1 &&
2252 [ + + ]: 4227 : memcmp(o->data.payload, field, field_length) == 0 &&
2253 [ + - ]: 2424 : o->data.payload[field_length] == '=') {
2254 : :
2255 : 2424 : t = (size_t) l;
2256 : :
2257 [ - + ]: 2424 : if ((uint64_t) t != l)
2258 : 0 : return -E2BIG;
2259 : :
2260 : 2424 : *data = o->data.payload;
2261 : 2424 : *size = t;
2262 : :
2263 : 2424 : return 0;
2264 : : }
2265 : :
2266 : 1858 : r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
2267 [ - + ]: 1858 : if (r < 0)
2268 : 0 : return r;
2269 : : }
2270 : :
2271 : 0 : return -ENOENT;
2272 : : }
2273 : :
2274 : 1840 : 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 : 1840 : l = le64toh(o->object.size) - offsetof(Object, data.payload);
2280 : 1840 : t = (size_t) l;
2281 : :
2282 : : /* We can't read objects larger than 4G on a 32bit machine */
2283 [ - + ]: 1840 : if ((uint64_t) t != l)
2284 : 0 : return -E2BIG;
2285 : :
2286 : 1840 : compression = o->object.flags & OBJECT_COMPRESSION_MASK;
2287 [ - + ]: 1840 : 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 : 1840 : *data = o->data.payload;
2305 : 1840 : *size = t;
2306 : : }
2307 : :
2308 : 1840 : 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 : 4 : _public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
2742 : : char *f;
2743 : :
2744 [ - + - + ]: 4 : assert_return(j, -EINVAL);
2745 [ - + - + ]: 4 : assert_return(!journal_pid_changed(j), -ECHILD);
2746 [ - + - + ]: 4 : assert_return(!isempty(field), -EINVAL);
2747 [ - + - + ]: 4 : assert_return(field_is_valid(field), -EINVAL);
2748 : :
2749 : 4 : f = strdup(field);
2750 [ - + ]: 4 : if (!f)
2751 : 0 : return -ENOMEM;
2752 : :
2753 : 4 : free(j->unique_field);
2754 : 4 : j->unique_field = f;
2755 : 4 : j->unique_file = NULL;
2756 : 4 : j->unique_offset = 0;
2757 : 4 : j->unique_file_lost = false;
2758 : :
2759 : 4 : return 0;
2760 : : }
2761 : :
2762 : 804 : _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
2763 : : size_t k;
2764 : :
2765 [ - + - + ]: 804 : assert_return(j, -EINVAL);
2766 [ - + - + ]: 804 : assert_return(!journal_pid_changed(j), -ECHILD);
2767 [ - + - + ]: 804 : assert_return(data, -EINVAL);
2768 [ - + - + ]: 804 : assert_return(l, -EINVAL);
2769 [ - + - + ]: 804 : assert_return(j->unique_field, -EINVAL);
2770 : :
2771 : 804 : k = strlen(j->unique_field);
2772 : :
2773 [ + + ]: 804 : if (!j->unique_file) {
2774 [ - + ]: 4 : if (j->unique_file_lost)
2775 : 0 : return 0;
2776 : :
2777 : 4 : j->unique_file = ordered_hashmap_first(j->files);
2778 [ - + ]: 4 : if (!j->unique_file)
2779 : 0 : return 0;
2780 : :
2781 : 4 : j->unique_offset = 0;
2782 : : }
2783 : :
2784 : 248 : 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 [ + + ]: 1052 : if (j->unique_offset == 0) {
2795 : 12 : r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
2796 [ - + ]: 12 : if (r < 0)
2797 : 804 : return r;
2798 : :
2799 [ + - ]: 12 : j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
2800 : : } else {
2801 : 1040 : r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
2802 [ - + ]: 1040 : if (r < 0)
2803 : 0 : return r;
2804 : :
2805 : 1040 : 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 [ + + ]: 1052 : if (j->unique_offset == 0) {
2810 : 12 : j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
2811 [ + + ]: 12 : if (!j->unique_file)
2812 : 4 : return 0;
2813 : :
2814 : 248 : 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 : 1040 : r = journal_file_move_to_object(j->unique_file, OBJECT_UNUSED, j->unique_offset, &o);
2821 [ - + ]: 1040 : if (r < 0)
2822 : 0 : return r;
2823 : :
2824 : : /* Let's do the type check by hand, since we used 0 context above. */
2825 [ - + ]: 1040 : 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 : 1040 : r = return_data(j, j->unique_file, o, &odata, &ol);
2833 [ - + ]: 1040 : if (r < 0)
2834 : 0 : return r;
2835 : :
2836 : : /* Check if we have at least the field name and "=". */
2837 [ - + ]: 1040 : 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 [ + - - + ]: 1040 : 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 : 1040 : found = false;
2854 [ + - ]: 2000 : ORDERED_HASHMAP_FOREACH(of, j->files, i) {
2855 [ + + ]: 2000 : if (of == j->unique_file)
2856 : 800 : break;
2857 : :
2858 : : /* Skip this file it didn't have any fields indexed */
2859 [ + - - + ]: 1200 : if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) && le64toh(of->header->n_fields) <= 0)
2860 : 0 : continue;
2861 : :
2862 : 1200 : r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), NULL, NULL);
2863 [ - + ]: 1200 : if (r < 0)
2864 : 0 : return r;
2865 [ + + ]: 1200 : if (r > 0) {
2866 : 240 : found = true;
2867 : 240 : break;
2868 : : }
2869 : : }
2870 : :
2871 [ + + ]: 1040 : if (found)
2872 : 240 : continue;
2873 : :
2874 : 800 : r = return_data(j, j->unique_file, o, data, l);
2875 [ - + ]: 800 : if (r < 0)
2876 : 0 : return r;
2877 : :
2878 : 800 : return 1;
2879 : : }
2880 : : }
2881 : :
2882 : 4 : _public_ void sd_journal_restart_unique(sd_journal *j) {
2883 [ - + ]: 4 : if (!j)
2884 : 0 : return;
2885 : :
2886 : 4 : j->unique_file = NULL;
2887 : 4 : j->unique_offset = 0;
2888 : 4 : 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 : 4 : _public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
3109 [ - + - + ]: 4 : assert_return(j, -EINVAL);
3110 [ - + - + ]: 4 : assert_return(!journal_pid_changed(j), -ECHILD);
3111 : :
3112 : 4 : j->data_threshold = sz;
3113 : 4 : 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 : : }
|