Coverage Report

Created: 2019-06-19 13:33

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