~ubuntu-branches/debian/jessie/systemd/jessie

« back to all changes in this revision

Viewing changes to src/journal/sd-journal.c

  • Committer: Package Import Robot
  • Author(s): Tollef Fog Heen, Tollef Fog Heen, Michael Biebl
  • Date: 2012-04-03 19:59:17 UTC
  • mfrom: (1.1.10) (6.1.3 experimental)
  • Revision ID: package-import@ubuntu.com-20120403195917-l532urrbg4pkreas
Tags: 44-1
[ Tollef Fog Heen ]
* New upstream version.
  - Backport 3492207: journal: PAGE_SIZE is not known on ppc and other
    archs
  - Backport 5a2a2a1: journal: react with immediate rotation to a couple
    of more errors
  - Backport 693ce21: util: never follow symlinks in rm_rf_children()
    Fixes CVE-2012-1174, closes: #664364
* Drop output message from init-functions hook, it's pointless.
* Only rmdir /lib/init/rw if it exists.
* Explicitly order debian-fixup before sysinit.target to prevent a
  possible race condition with the creation of sockets.  Thanks to
  Michael Biebl for debugging this.
* Always restart the initctl socket on upgrades, to mask sysvinit
  removing it.

[ Michael Biebl ]
* Remove workaround for non-interactive sessions from pam config again.
* Create compat /dev/initctl symlink in case we are upgrading from a system
  running a newer version of sysvinit (using /run/initctl) and sysvinit is
  replaced with systemd-sysv during the upgrade. Closes: #663219
* Install new man pages.
* Build-Depend on valac (>= 0.12) instead of valac-0.12. Closes: #663323

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
 
2
 
 
3
/***
 
4
  This file is part of systemd.
 
5
 
 
6
  Copyright 2011 Lennart Poettering
 
7
 
 
8
  systemd is free software; you can redistribute it and/or modify it
 
9
  under the terms of the GNU General Public License as published by
 
10
  the Free Software Foundation; either version 2 of the License, or
 
11
  (at your option) any later version.
 
12
 
 
13
  systemd is distributed in the hope that it will be useful, but
 
14
  WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 
16
  General Public License for more details.
 
17
 
 
18
  You should have received a copy of the GNU General Public License
 
19
  along with systemd; If not, see <http://www.gnu.org/licenses/>.
 
20
***/
 
21
 
 
22
#include <errno.h>
 
23
#include <fcntl.h>
 
24
#include <stddef.h>
 
25
#include <unistd.h>
 
26
#include <sys/inotify.h>
 
27
 
 
28
#include "sd-journal.h"
 
29
#include "journal-def.h"
 
30
#include "journal-file.h"
 
31
#include "hashmap.h"
 
32
#include "list.h"
 
33
#include "lookup3.h"
 
34
#include "compress.h"
 
35
#include "journal-internal.h"
 
36
 
 
37
#define JOURNAL_FILES_MAX 1024
 
38
 
 
39
static void detach_location(sd_journal *j) {
 
40
        Iterator i;
 
41
        JournalFile *f;
 
42
 
 
43
        assert(j);
 
44
 
 
45
        j->current_file = NULL;
 
46
        j->current_field = 0;
 
47
 
 
48
        HASHMAP_FOREACH(f, j->files, i)
 
49
                f->current_offset = 0;
 
50
}
 
51
 
 
52
static void reset_location(sd_journal *j) {
 
53
        assert(j);
 
54
 
 
55
        detach_location(j);
 
56
        zero(j->current_location);
 
57
}
 
58
 
 
59
static void init_location(Location *l, JournalFile *f, Object *o) {
 
60
        assert(l);
 
61
        assert(f);
 
62
        assert(o->object.type == OBJECT_ENTRY);
 
63
 
 
64
        l->type = LOCATION_DISCRETE;
 
65
        l->seqnum = le64toh(o->entry.seqnum);
 
66
        l->seqnum_id = f->header->seqnum_id;
 
67
        l->realtime = le64toh(o->entry.realtime);
 
68
        l->monotonic = le64toh(o->entry.monotonic);
 
69
        l->boot_id = o->entry.boot_id;
 
70
        l->xor_hash = le64toh(o->entry.xor_hash);
 
71
 
 
72
        l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
 
73
}
 
74
 
 
75
static void set_location(sd_journal *j, JournalFile *f, Object *o, uint64_t offset) {
 
76
        assert(j);
 
77
        assert(f);
 
78
        assert(o);
 
79
 
 
80
        init_location(&j->current_location, f, o);
 
81
 
 
82
        j->current_file = f;
 
83
        j->current_field = 0;
 
84
 
 
85
        f->current_offset = offset;
 
86
}
 
87
 
 
88
static int same_field(const void *_a, size_t s, const void *_b, size_t t) {
 
89
        const uint8_t *a = _a, *b = _b;
 
90
        size_t j;
 
91
        bool a_good = false, b_good = false, different = false;
 
92
 
 
93
        for (j = 0; j < s && j < t; j++) {
 
94
 
 
95
                if (a[j] == '=')
 
96
                        a_good = true;
 
97
                if (b[j] == '=')
 
98
                        b_good = true;
 
99
                if (a[j] != b[j])
 
100
                        different = true;
 
101
 
 
102
                if (a_good && b_good)
 
103
                        return different ? 0 : 1;
 
104
        }
 
105
 
 
106
        return -EINVAL;
 
107
}
 
108
 
 
109
_public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
 
110
        Match *m, *after = NULL;
 
111
        uint64_t le_hash;
 
112
 
 
113
        if (!j)
 
114
                return -EINVAL;
 
115
        if (!data)
 
116
                return -EINVAL;
 
117
        if (size <= 0)
 
118
                return -EINVAL;
 
119
 
 
120
        le_hash = htole64(hash64(data, size));
 
121
 
 
122
        LIST_FOREACH(matches, m, j->matches) {
 
123
                int r;
 
124
 
 
125
                if (m->le_hash == le_hash &&
 
126
                    m->size == size &&
 
127
                    memcmp(m->data, data, size) == 0)
 
128
                        return 0;
 
129
 
 
130
                r = same_field(data, size, m->data, m->size);
 
131
                if (r < 0)
 
132
                        return r;
 
133
                else if (r > 0)
 
134
                        after = m;
 
135
        }
 
136
 
 
137
        m = new0(Match, 1);
 
138
        if (!m)
 
139
                return -ENOMEM;
 
140
 
 
141
        m->size = size;
 
142
 
 
143
        m->data = malloc(m->size);
 
144
        if (!m->data) {
 
145
                free(m);
 
146
                return -ENOMEM;
 
147
        }
 
148
 
 
149
        memcpy(m->data, data, size);
 
150
        m->le_hash = le_hash;
 
151
 
 
152
        /* Matches for the same fields we order adjacent to each
 
153
         * other */
 
154
        LIST_INSERT_AFTER(Match, matches, j->matches, after, m);
 
155
        j->n_matches ++;
 
156
 
 
157
        detach_location(j);
 
158
 
 
159
        return 0;
 
160
}
 
161
 
 
162
_public_ void sd_journal_flush_matches(sd_journal *j) {
 
163
        if (!j)
 
164
                return;
 
165
 
 
166
        while (j->matches) {
 
167
                Match *m = j->matches;
 
168
 
 
169
                LIST_REMOVE(Match, matches, j->matches, m);
 
170
                free(m->data);
 
171
                free(m);
 
172
        }
 
173
 
 
174
        j->n_matches = 0;
 
175
 
 
176
        detach_location(j);
 
177
}
 
178
 
 
179
static int compare_order(JournalFile *af, Object *ao,
 
180
                         JournalFile *bf, Object *bo) {
 
181
 
 
182
        uint64_t a, b;
 
183
 
 
184
        assert(af);
 
185
        assert(ao);
 
186
        assert(bf);
 
187
        assert(bo);
 
188
 
 
189
        /* We operate on two different files here, hence we can access
 
190
         * two objects at the same time, which we normally can't.
 
191
         *
 
192
         * If contents and timestamps match, these entries are
 
193
         * identical, even if the seqnum does not match */
 
194
 
 
195
        if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
 
196
            ao->entry.monotonic == bo->entry.monotonic &&
 
197
            ao->entry.realtime == bo->entry.realtime &&
 
198
            ao->entry.xor_hash == bo->entry.xor_hash)
 
199
                return 0;
 
200
 
 
201
        if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
 
202
 
 
203
                /* If this is from the same seqnum source, compare
 
204
                 * seqnums */
 
205
                a = le64toh(ao->entry.seqnum);
 
206
                b = le64toh(bo->entry.seqnum);
 
207
 
 
208
                if (a < b)
 
209
                        return -1;
 
210
                if (a > b)
 
211
                        return 1;
 
212
 
 
213
                /* Wow! This is weird, different data but the same
 
214
                 * seqnums? Something is borked, but let's make the
 
215
                 * best of it and compare by time. */
 
216
        }
 
217
 
 
218
        if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
 
219
 
 
220
                /* If the boot id matches compare monotonic time */
 
221
                a = le64toh(ao->entry.monotonic);
 
222
                b = le64toh(bo->entry.monotonic);
 
223
 
 
224
                if (a < b)
 
225
                        return -1;
 
226
                if (a > b)
 
227
                        return 1;
 
228
        }
 
229
 
 
230
        /* Otherwise compare UTC time */
 
231
        a = le64toh(ao->entry.realtime);
 
232
        b = le64toh(ao->entry.realtime);
 
233
 
 
234
        if (a < b)
 
235
                return -1;
 
236
        if (a > b)
 
237
                return 1;
 
238
 
 
239
        /* Finally, compare by contents */
 
240
        a = le64toh(ao->entry.xor_hash);
 
241
        b = le64toh(ao->entry.xor_hash);
 
242
 
 
243
        if (a < b)
 
244
                return -1;
 
245
        if (a > b)
 
246
                return 1;
 
247
 
 
248
        return 0;
 
249
}
 
250
 
 
251
static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
 
252
        uint64_t a;
 
253
 
 
254
        assert(af);
 
255
        assert(ao);
 
256
        assert(l);
 
257
        assert(l->type == LOCATION_DISCRETE);
 
258
 
 
259
        if (l->monotonic_set &&
 
260
            sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
 
261
            l->realtime_set &&
 
262
            le64toh(ao->entry.realtime) == l->realtime &&
 
263
            l->xor_hash_set &&
 
264
            le64toh(ao->entry.xor_hash) == l->xor_hash)
 
265
                return 0;
 
266
 
 
267
        if (l->seqnum_set &&
 
268
            sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
 
269
 
 
270
                a = le64toh(ao->entry.seqnum);
 
271
 
 
272
                if (a < l->seqnum)
 
273
                        return -1;
 
274
                if (a > l->seqnum)
 
275
                        return 1;
 
276
        }
 
277
 
 
278
        if (l->monotonic_set &&
 
279
            sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
 
280
 
 
281
                a = le64toh(ao->entry.monotonic);
 
282
 
 
283
                if (a < l->monotonic)
 
284
                        return -1;
 
285
                if (a > l->monotonic)
 
286
                        return 1;
 
287
        }
 
288
 
 
289
        if (l->realtime_set) {
 
290
 
 
291
                a = le64toh(ao->entry.realtime);
 
292
 
 
293
                if (a < l->realtime)
 
294
                        return -1;
 
295
                if (a > l->realtime)
 
296
                        return 1;
 
297
        }
 
298
 
 
299
        if (l->xor_hash_set) {
 
300
                a = le64toh(ao->entry.xor_hash);
 
301
 
 
302
                if (a < l->xor_hash)
 
303
                        return -1;
 
304
                if (a > l->xor_hash)
 
305
                        return 1;
 
306
        }
 
307
 
 
308
        return 0;
 
309
}
 
310
 
 
311
static int find_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
 
312
        Object *o = NULL;
 
313
        uint64_t p = 0;
 
314
        int r;
 
315
 
 
316
        assert(j);
 
317
 
 
318
        if (!j->matches) {
 
319
                /* No matches is simple */
 
320
 
 
321
                if (j->current_location.type == LOCATION_HEAD)
 
322
                        r = journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, &o, &p);
 
323
                else if (j->current_location.type == LOCATION_TAIL)
 
324
                        r = journal_file_next_entry(f, NULL, 0, DIRECTION_UP, &o, &p);
 
325
                else if (j->current_location.seqnum_set &&
 
326
                         sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
 
327
                        r = journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, &o, &p);
 
328
                else if (j->current_location.monotonic_set)
 
329
                        r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, &o, &p);
 
330
                else if (j->current_location.realtime_set)
 
331
                        r = journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, &o, &p);
 
332
                else
 
333
                        r = journal_file_next_entry(f, NULL, 0, direction, &o, &p);
 
334
 
 
335
                if (r <= 0)
 
336
                        return r;
 
337
 
 
338
        } else  {
 
339
                Match *m, *term_match = NULL;
 
340
                Object *to = NULL;
 
341
                uint64_t tp = 0;
 
342
 
 
343
                /* We have matches, first, let's jump to the monotonic
 
344
                 * position if we have any, since it implies a
 
345
                 * match. */
 
346
 
 
347
                if (j->current_location.type == LOCATION_DISCRETE &&
 
348
                    j->current_location.monotonic_set) {
 
349
 
 
350
                        r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, &o, &p);
 
351
                        if (r <= 0)
 
352
                                return r == -ENOENT ? 0 : r;
 
353
                }
 
354
 
 
355
                LIST_FOREACH(matches, m, j->matches) {
 
356
                        Object *c, *d;
 
357
                        uint64_t cp, dp;
 
358
 
 
359
                        r = journal_file_find_data_object_with_hash(f, m->data, m->size, m->le_hash, &d, &dp);
 
360
                        if (r <= 0)
 
361
                                return r;
 
362
 
 
363
                        if (j->current_location.type == LOCATION_HEAD)
 
364
                                r = journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, &c, &cp);
 
365
                        else if (j->current_location.type == LOCATION_TAIL)
 
366
                                r = journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, &c, &cp);
 
367
                        else if (j->current_location.seqnum_set &&
 
368
                                 sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
 
369
                                r = journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, &c, &cp);
 
370
                        else if (j->current_location.realtime_set)
 
371
                                r = journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, &c, &cp);
 
372
                        else
 
373
                                r = journal_file_next_entry_for_data(f, NULL, 0, dp, direction, &c, &cp);
 
374
 
 
375
                        if (r < 0)
 
376
                                return r;
 
377
 
 
378
                        if (!term_match) {
 
379
                                term_match = m;
 
380
 
 
381
                                if (r > 0) {
 
382
                                        to = c;
 
383
                                        tp = cp;
 
384
                                }
 
385
                        } else if (same_field(term_match->data, term_match->size, m->data, m->size)) {
 
386
 
 
387
                                /* Same field as previous match... */
 
388
                                if (r > 0) {
 
389
 
 
390
                                        /* Find the earliest of the OR matches */
 
391
 
 
392
                                        if (!to ||
 
393
                                            (direction == DIRECTION_DOWN && cp < tp) ||
 
394
                                            (direction == DIRECTION_UP && cp > tp)) {
 
395
                                                to = c;
 
396
                                                tp = cp;
 
397
                                        }
 
398
 
 
399
                                }
 
400
 
 
401
                        } else {
 
402
 
 
403
                                /* Previous term is finished, did anything match? */
 
404
                                if (!to)
 
405
                                        return 0;
 
406
 
 
407
                                /* Find the last of the AND matches */
 
408
                                if (!o ||
 
409
                                    (direction == DIRECTION_DOWN && tp > p) ||
 
410
                                    (direction == DIRECTION_UP && tp < p)) {
 
411
                                        o = to;
 
412
                                        p = tp;
 
413
                                }
 
414
 
 
415
                                term_match = m;
 
416
 
 
417
                                if (r > 0) {
 
418
                                        to = c;
 
419
                                        tp = cp;
 
420
                                } else {
 
421
                                        to = NULL;
 
422
                                        tp = 0;
 
423
                                }
 
424
                        }
 
425
                }
 
426
 
 
427
                /* Last term is finished, did anything match? */
 
428
                if (!to)
 
429
                        return 0;
 
430
 
 
431
                if (!o ||
 
432
                    (direction == DIRECTION_DOWN && tp > p) ||
 
433
                    (direction == DIRECTION_UP && tp < p)) {
 
434
                        o = to;
 
435
                        p = tp;
 
436
                }
 
437
 
 
438
                if (!o)
 
439
                        return 0;
 
440
        }
 
441
 
 
442
        if (ret)
 
443
                *ret = o;
 
444
 
 
445
        if (offset)
 
446
                *offset = p;
 
447
 
 
448
        return 1;
 
449
}
 
450
 
 
451
static int next_with_matches(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
 
452
        int r;
 
453
        uint64_t cp;
 
454
        Object *c;
 
455
 
 
456
        assert(j);
 
457
        assert(f);
 
458
        assert(ret);
 
459
        assert(offset);
 
460
 
 
461
        c = *ret;
 
462
        cp = *offset;
 
463
 
 
464
        if (!j->matches) {
 
465
                /* No matches is easy */
 
466
 
 
467
                r = journal_file_next_entry(f, c, cp, direction, &c, &cp);
 
468
                if (r <= 0)
 
469
                        return r;
 
470
 
 
471
                if (ret)
 
472
                        *ret = c;
 
473
                if (offset)
 
474
                        *offset = cp;
 
475
                return 1;
 
476
        }
 
477
 
 
478
        /* So there are matches we have to adhere to, let's find the
 
479
         * first entry that matches all of them */
 
480
 
 
481
        for (;;) {
 
482
                uint64_t np, n;
 
483
                bool found, term_result = false;
 
484
                Match *m, *term_match = NULL;
 
485
                Object *npo = NULL;
 
486
 
 
487
                n = journal_file_entry_n_items(c);
 
488
 
 
489
                /* Make sure we don't match the entry we are starting
 
490
                 * from. */
 
491
                found = cp != *offset;
 
492
 
 
493
                np = 0;
 
494
                LIST_FOREACH(matches, m, j->matches) {
 
495
                        uint64_t q, k;
 
496
                        Object *qo = NULL;
 
497
 
 
498
                        /* Let's check if this is the beginning of a
 
499
                         * new term, i.e. has a different field prefix
 
500
                         * as the preceeding match. */
 
501
                        if (!term_match) {
 
502
                                term_match = m;
 
503
                                term_result = false;
 
504
                        } else if (!same_field(term_match->data, term_match->size, m->data, m->size)) {
 
505
                                if (!term_result)
 
506
                                        found = false;
 
507
 
 
508
                                term_match = m;
 
509
                                term_result = false;
 
510
                        }
 
511
 
 
512
                        for (k = 0; k < n; k++)
 
513
                                if (c->entry.items[k].hash == m->le_hash)
 
514
                                        break;
 
515
 
 
516
                        if (k >= n) {
 
517
                                /* Hmm, didn't find any field that
 
518
                                 * matched this rule, so ignore this
 
519
                                 * match. Go on with next match */
 
520
                                continue;
 
521
                        }
 
522
 
 
523
                        term_result = true;
 
524
 
 
525
                        /* Hmm, so, this field matched, let's remember
 
526
                         * where we'd have to try next, in case the other
 
527
                         * matches are not OK */
 
528
 
 
529
                        r = journal_file_next_entry_for_data(f, c, cp, le64toh(c->entry.items[k].object_offset), direction, &qo, &q);
 
530
                        /* This pointer is invalidated if the window was
 
531
                         * remapped. May need to re-fetch it later */
 
532
                        c = NULL;
 
533
                        if (r < 0)
 
534
                                return r;
 
535
 
 
536
                        if (r > 0) {
 
537
 
 
538
                                if (direction == DIRECTION_DOWN) {
 
539
                                        if (q > np) {
 
540
                                                np = q;
 
541
                                                npo = qo;
 
542
                                        }
 
543
                                } else {
 
544
                                        if (np == 0 || q < np) {
 
545
                                                np = q;
 
546
                                                npo = qo;
 
547
                                        }
 
548
                                }
 
549
                        }
 
550
                }
 
551
 
 
552
                /* Check the last term */
 
553
                if (term_match && !term_result)
 
554
                        found = false;
 
555
 
 
556
                /* Did this entry match against all matches? */
 
557
                if (found) {
 
558
                        if (ret) {
 
559
                                if (c == NULL) {
 
560
                                        /* Re-fetch the entry */
 
561
                                        r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
 
562
                                        if (r < 0)
 
563
                                                return r;
 
564
                                }
 
565
                                *ret = c;
 
566
                        }
 
567
                        if (offset)
 
568
                                *offset = cp;
 
569
                        return 1;
 
570
                }
 
571
 
 
572
                /* Did we find a subsequent entry? */
 
573
                if (np == 0)
 
574
                        return 0;
 
575
 
 
576
                /* Hmm, ok, this entry only matched partially, so
 
577
                 * let's try another one */
 
578
                cp = np;
 
579
                c = npo;
 
580
        }
 
581
}
 
582
 
 
583
static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
 
584
        Object *c;
 
585
        uint64_t cp;
 
586
        int compare_value, r;
 
587
 
 
588
        assert(j);
 
589
        assert(f);
 
590
 
 
591
        if (f->current_offset > 0) {
 
592
                cp = f->current_offset;
 
593
 
 
594
                r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
 
595
                if (r < 0)
 
596
                        return r;
 
597
 
 
598
                r = next_with_matches(j, f, direction, &c, &cp);
 
599
                if (r <= 0)
 
600
                        return r;
 
601
 
 
602
                compare_value = 1;
 
603
        } else {
 
604
                r = find_location(j, f, direction, &c, &cp);
 
605
                if (r <= 0)
 
606
                        return r;
 
607
 
 
608
                compare_value = 0;
 
609
        }
 
610
 
 
611
        for (;;) {
 
612
                bool found;
 
613
 
 
614
                if (j->current_location.type == LOCATION_DISCRETE) {
 
615
                        int k;
 
616
 
 
617
                        k = compare_with_location(f, c, &j->current_location);
 
618
                        if (direction == DIRECTION_DOWN)
 
619
                                found = k >= compare_value;
 
620
                        else
 
621
                                found = k <= -compare_value;
 
622
                } else
 
623
                        found = true;
 
624
 
 
625
                if (found) {
 
626
                        if (ret)
 
627
                                *ret = c;
 
628
                        if (offset)
 
629
                                *offset = cp;
 
630
                        return 1;
 
631
                }
 
632
 
 
633
                r = next_with_matches(j, f, direction, &c, &cp);
 
634
                if (r <= 0)
 
635
                        return r;
 
636
        }
 
637
}
 
638
 
 
639
static int real_journal_next(sd_journal *j, direction_t direction) {
 
640
        JournalFile *f, *new_current = NULL;
 
641
        Iterator i;
 
642
        int r;
 
643
        uint64_t new_offset = 0;
 
644
        Object *new_entry = NULL;
 
645
 
 
646
        if (!j)
 
647
                return -EINVAL;
 
648
 
 
649
        HASHMAP_FOREACH(f, j->files, i) {
 
650
                Object *o;
 
651
                uint64_t p;
 
652
                bool found;
 
653
 
 
654
                r = next_beyond_location(j, f, direction, &o, &p);
 
655
                if (r < 0)
 
656
                        return r;
 
657
                else if (r == 0)
 
658
                        continue;
 
659
 
 
660
                if (!new_current)
 
661
                        found = true;
 
662
                else {
 
663
                        int k;
 
664
 
 
665
                        k = compare_order(f, o, new_current, new_entry);
 
666
 
 
667
                        if (direction == DIRECTION_DOWN)
 
668
                                found = k < 0;
 
669
                        else
 
670
                                found = k > 0;
 
671
                }
 
672
 
 
673
                if (found) {
 
674
                        new_current = f;
 
675
                        new_entry = o;
 
676
                        new_offset = p;
 
677
                }
 
678
        }
 
679
 
 
680
        if (!new_current)
 
681
                return 0;
 
682
 
 
683
        set_location(j, new_current, new_entry, new_offset);
 
684
 
 
685
        return 1;
 
686
}
 
687
 
 
688
_public_ int sd_journal_next(sd_journal *j) {
 
689
        return real_journal_next(j, DIRECTION_DOWN);
 
690
}
 
691
 
 
692
_public_ int sd_journal_previous(sd_journal *j) {
 
693
        return real_journal_next(j, DIRECTION_UP);
 
694
}
 
695
 
 
696
static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
 
697
        int c = 0, r;
 
698
 
 
699
        if (!j)
 
700
                return -EINVAL;
 
701
 
 
702
        if (skip == 0) {
 
703
                /* If this is not a discrete skip, then at least
 
704
                 * resolve the current location */
 
705
                if (j->current_location.type != LOCATION_DISCRETE)
 
706
                        return real_journal_next(j, direction);
 
707
 
 
708
                return 0;
 
709
        }
 
710
 
 
711
        do {
 
712
                r = real_journal_next(j, direction);
 
713
                if (r < 0)
 
714
                        return r;
 
715
 
 
716
                if (r == 0)
 
717
                        return c;
 
718
 
 
719
                skip--;
 
720
                c++;
 
721
        } while (skip > 0);
 
722
 
 
723
        return c;
 
724
}
 
725
 
 
726
_public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
 
727
        return real_journal_next_skip(j, DIRECTION_DOWN, skip);
 
728
}
 
729
 
 
730
_public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
 
731
        return real_journal_next_skip(j, DIRECTION_UP, skip);
 
732
}
 
733
 
 
734
_public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
 
735
        Object *o;
 
736
        int r;
 
737
        char bid[33], sid[33];
 
738
 
 
739
        if (!j)
 
740
                return -EINVAL;
 
741
        if (!cursor)
 
742
                return -EINVAL;
 
743
 
 
744
        if (!j->current_file || j->current_file->current_offset <= 0)
 
745
                return -EADDRNOTAVAIL;
 
746
 
 
747
        r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
 
748
        if (r < 0)
 
749
                return r;
 
750
 
 
751
        sd_id128_to_string(j->current_file->header->seqnum_id, sid);
 
752
        sd_id128_to_string(o->entry.boot_id, bid);
 
753
 
 
754
        if (asprintf(cursor,
 
755
                     "s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx;p=%s",
 
756
                     sid, (unsigned long long) le64toh(o->entry.seqnum),
 
757
                     bid, (unsigned long long) le64toh(o->entry.monotonic),
 
758
                     (unsigned long long) le64toh(o->entry.realtime),
 
759
                     (unsigned long long) le64toh(o->entry.xor_hash),
 
760
                     file_name_from_path(j->current_file->path)) < 0)
 
761
                return -ENOMEM;
 
762
 
 
763
        return 1;
 
764
}
 
765
 
 
766
_public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
 
767
        char *w;
 
768
        size_t l;
 
769
        char *state;
 
770
        unsigned long long seqnum, monotonic, realtime, xor_hash;
 
771
        bool
 
772
                seqnum_id_set = false,
 
773
                seqnum_set = false,
 
774
                boot_id_set = false,
 
775
                monotonic_set = false,
 
776
                realtime_set = false,
 
777
                xor_hash_set = false;
 
778
        sd_id128_t seqnum_id, boot_id;
 
779
 
 
780
        if (!j)
 
781
                return -EINVAL;
 
782
        if (!cursor)
 
783
                return -EINVAL;
 
784
 
 
785
        FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
 
786
                char *item;
 
787
                int k = 0;
 
788
 
 
789
                if (l < 2 || w[1] != '=')
 
790
                        return -EINVAL;
 
791
 
 
792
                item = strndup(w, l);
 
793
                if (!item)
 
794
                        return -ENOMEM;
 
795
 
 
796
                switch (w[0]) {
 
797
 
 
798
                case 's':
 
799
                        seqnum_id_set = true;
 
800
                        k = sd_id128_from_string(w+2, &seqnum_id);
 
801
                        break;
 
802
 
 
803
                case 'i':
 
804
                        seqnum_set = true;
 
805
                        if (sscanf(w+2, "%llx", &seqnum) != 1)
 
806
                                k = -EINVAL;
 
807
                        break;
 
808
 
 
809
                case 'b':
 
810
                        boot_id_set = true;
 
811
                        k = sd_id128_from_string(w+2, &boot_id);
 
812
                        break;
 
813
 
 
814
                case 'm':
 
815
                        monotonic_set = true;
 
816
                        if (sscanf(w+2, "%llx", &monotonic) != 1)
 
817
                                k = -EINVAL;
 
818
                        break;
 
819
 
 
820
                case 't':
 
821
                        realtime_set = true;
 
822
                        if (sscanf(w+2, "%llx", &realtime) != 1)
 
823
                                k = -EINVAL;
 
824
                        break;
 
825
 
 
826
                case 'x':
 
827
                        xor_hash_set = true;
 
828
                        if (sscanf(w+2, "%llx", &xor_hash) != 1)
 
829
                                k = -EINVAL;
 
830
                        break;
 
831
                }
 
832
 
 
833
                free(item);
 
834
 
 
835
                if (k < 0)
 
836
                        return k;
 
837
        }
 
838
 
 
839
        if ((!seqnum_set || !seqnum_id_set) &&
 
840
            (!monotonic_set || !boot_id_set) &&
 
841
            !realtime_set)
 
842
                return -EINVAL;
 
843
 
 
844
        reset_location(j);
 
845
 
 
846
        j->current_location.type = LOCATION_DISCRETE;
 
847
 
 
848
        if (realtime_set) {
 
849
                j->current_location.realtime = (uint64_t) realtime;
 
850
                j->current_location.realtime_set = true;
 
851
        }
 
852
 
 
853
        if (seqnum_set && seqnum_id_set) {
 
854
                j->current_location.seqnum = (uint64_t) seqnum;
 
855
                j->current_location.seqnum_id = seqnum_id;
 
856
                j->current_location.seqnum_set = true;
 
857
        }
 
858
 
 
859
        if (monotonic_set && boot_id_set) {
 
860
                j->current_location.monotonic = (uint64_t) monotonic;
 
861
                j->current_location.boot_id = boot_id;
 
862
                j->current_location.monotonic_set = true;
 
863
        }
 
864
 
 
865
        if (xor_hash_set) {
 
866
                j->current_location.xor_hash = (uint64_t) xor_hash;
 
867
                j->current_location.xor_hash_set = true;
 
868
        }
 
869
 
 
870
        return 0;
 
871
}
 
872
 
 
873
_public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
 
874
        if (!j)
 
875
                return -EINVAL;
 
876
 
 
877
        reset_location(j);
 
878
        j->current_location.type = LOCATION_DISCRETE;
 
879
        j->current_location.boot_id = boot_id;
 
880
        j->current_location.monotonic = usec;
 
881
        j->current_location.monotonic_set = true;
 
882
 
 
883
        return 0;
 
884
}
 
885
 
 
886
_public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
 
887
        if (!j)
 
888
                return -EINVAL;
 
889
 
 
890
        reset_location(j);
 
891
        j->current_location.type = LOCATION_DISCRETE;
 
892
        j->current_location.realtime = usec;
 
893
        j->current_location.realtime_set = true;
 
894
 
 
895
        return 0;
 
896
}
 
897
 
 
898
_public_ int sd_journal_seek_head(sd_journal *j) {
 
899
        if (!j)
 
900
                return -EINVAL;
 
901
 
 
902
        reset_location(j);
 
903
        j->current_location.type = LOCATION_HEAD;
 
904
 
 
905
        return 0;
 
906
}
 
907
 
 
908
_public_ int sd_journal_seek_tail(sd_journal *j) {
 
909
        if (!j)
 
910
                return -EINVAL;
 
911
 
 
912
        reset_location(j);
 
913
        j->current_location.type = LOCATION_TAIL;
 
914
 
 
915
        return 0;
 
916
}
 
917
 
 
918
static int add_file(sd_journal *j, const char *prefix, const char *dir, const char *filename) {
 
919
        char *fn;
 
920
        int r;
 
921
        JournalFile *f;
 
922
 
 
923
        assert(j);
 
924
        assert(prefix);
 
925
        assert(filename);
 
926
 
 
927
        if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) &&
 
928
            !startswith(filename, "system.journal"))
 
929
                return 0;
 
930
 
 
931
        if (dir)
 
932
                fn = join(prefix, "/", dir, "/", filename, NULL);
 
933
        else
 
934
                fn = join(prefix, "/", filename, NULL);
 
935
 
 
936
        if (!fn)
 
937
                return -ENOMEM;
 
938
 
 
939
        if (hashmap_get(j->files, fn)) {
 
940
                free(fn);
 
941
                return 0;
 
942
        }
 
943
 
 
944
        if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
 
945
                log_debug("Too many open journal files, not adding %s, ignoring.", fn);
 
946
                free(fn);
 
947
                return 0;
 
948
        }
 
949
 
 
950
        r = journal_file_open(fn, O_RDONLY, 0, NULL, &f);
 
951
        free(fn);
 
952
 
 
953
        if (r < 0) {
 
954
                if (errno == ENOENT)
 
955
                        return 0;
 
956
 
 
957
                return r;
 
958
        }
 
959
 
 
960
        /* journal_file_dump(f); */
 
961
 
 
962
        r = hashmap_put(j->files, f->path, f);
 
963
        if (r < 0) {
 
964
                journal_file_close(f);
 
965
                return r;
 
966
        }
 
967
 
 
968
        log_debug("File %s got added.", f->path);
 
969
 
 
970
        return 0;
 
971
}
 
972
 
 
973
static int remove_file(sd_journal *j, const char *prefix, const char *dir, const char *filename) {
 
974
        char *fn;
 
975
        JournalFile *f;
 
976
 
 
977
        assert(j);
 
978
        assert(prefix);
 
979
        assert(filename);
 
980
 
 
981
        if (dir)
 
982
                fn = join(prefix, "/", dir, "/", filename, NULL);
 
983
        else
 
984
                fn = join(prefix, "/", filename, NULL);
 
985
 
 
986
        if (!fn)
 
987
                return -ENOMEM;
 
988
 
 
989
        f = hashmap_get(j->files, fn);
 
990
        free(fn);
 
991
 
 
992
        if (!f)
 
993
                return 0;
 
994
 
 
995
        hashmap_remove(j->files, f->path);
 
996
        journal_file_close(f);
 
997
 
 
998
        log_debug("File %s got removed.", f->path);
 
999
        return 0;
 
1000
}
 
1001
 
 
1002
static int add_directory(sd_journal *j, const char *prefix, const char *dir) {
 
1003
        char *fn;
 
1004
        int r;
 
1005
        DIR *d;
 
1006
        int wd;
 
1007
        sd_id128_t id, mid;
 
1008
 
 
1009
        assert(j);
 
1010
        assert(prefix);
 
1011
        assert(dir);
 
1012
 
 
1013
        if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
 
1014
            (sd_id128_from_string(dir, &id) < 0 ||
 
1015
             sd_id128_get_machine(&mid) < 0 ||
 
1016
             !sd_id128_equal(id, mid)))
 
1017
            return 0;
 
1018
 
 
1019
        fn = join(prefix, "/", dir, NULL);
 
1020
        if (!fn)
 
1021
                return -ENOMEM;
 
1022
 
 
1023
        d = opendir(fn);
 
1024
 
 
1025
        if (!d) {
 
1026
                free(fn);
 
1027
                if (errno == ENOENT)
 
1028
                        return 0;
 
1029
 
 
1030
                return -errno;
 
1031
        }
 
1032
 
 
1033
        wd = inotify_add_watch(j->inotify_fd, fn,
 
1034
                               IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
 
1035
                               IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|
 
1036
                               IN_DONT_FOLLOW|IN_ONLYDIR);
 
1037
        if (wd > 0) {
 
1038
                if (hashmap_put(j->inotify_wd_dirs, INT_TO_PTR(wd), fn) < 0)
 
1039
                        inotify_rm_watch(j->inotify_fd, wd);
 
1040
                else
 
1041
                        fn = NULL;
 
1042
        }
 
1043
 
 
1044
        free(fn);
 
1045
 
 
1046
        for (;;) {
 
1047
                struct dirent buf, *de;
 
1048
 
 
1049
                r = readdir_r(d, &buf, &de);
 
1050
                if (r != 0 || !de)
 
1051
                        break;
 
1052
 
 
1053
                if (!dirent_is_file_with_suffix(de, ".journal"))
 
1054
                        continue;
 
1055
 
 
1056
                r = add_file(j, prefix, dir, de->d_name);
 
1057
                if (r < 0)
 
1058
                        log_debug("Failed to add file %s/%s/%s: %s", prefix, dir, de->d_name, strerror(-r));
 
1059
        }
 
1060
 
 
1061
        closedir(d);
 
1062
 
 
1063
        log_debug("Directory %s/%s got added.", prefix, dir);
 
1064
 
 
1065
        return 0;
 
1066
}
 
1067
 
 
1068
static void remove_directory_wd(sd_journal *j, int wd) {
 
1069
        char *p;
 
1070
 
 
1071
        assert(j);
 
1072
        assert(wd > 0);
 
1073
 
 
1074
        if (j->inotify_fd >= 0)
 
1075
                inotify_rm_watch(j->inotify_fd, wd);
 
1076
 
 
1077
        p = hashmap_remove(j->inotify_wd_dirs, INT_TO_PTR(wd));
 
1078
 
 
1079
        if (p) {
 
1080
                log_debug("Directory %s got removed.", p);
 
1081
                free(p);
 
1082
        }
 
1083
}
 
1084
 
 
1085
static void add_root_wd(sd_journal *j, const char *p) {
 
1086
        int wd;
 
1087
        char *k;
 
1088
 
 
1089
        assert(j);
 
1090
        assert(p);
 
1091
 
 
1092
        wd = inotify_add_watch(j->inotify_fd, p,
 
1093
                               IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
 
1094
                               IN_DONT_FOLLOW|IN_ONLYDIR);
 
1095
        if (wd <= 0)
 
1096
                return;
 
1097
 
 
1098
        k = strdup(p);
 
1099
        if (!k || hashmap_put(j->inotify_wd_roots, INT_TO_PTR(wd), k) < 0) {
 
1100
                inotify_rm_watch(j->inotify_fd, wd);
 
1101
                free(k);
 
1102
        }
 
1103
}
 
1104
 
 
1105
static void remove_root_wd(sd_journal *j, int wd) {
 
1106
        char *p;
 
1107
 
 
1108
        assert(j);
 
1109
        assert(wd > 0);
 
1110
 
 
1111
        if (j->inotify_fd >= 0)
 
1112
                inotify_rm_watch(j->inotify_fd, wd);
 
1113
 
 
1114
        p = hashmap_remove(j->inotify_wd_roots, INT_TO_PTR(wd));
 
1115
 
 
1116
        if (p) {
 
1117
                log_debug("Root %s got removed.", p);
 
1118
                free(p);
 
1119
        }
 
1120
}
 
1121
 
 
1122
_public_ int sd_journal_open(sd_journal **ret, int flags) {
 
1123
        sd_journal *j;
 
1124
        const char *p;
 
1125
        const char search_paths[] =
 
1126
                "/run/log/journal\0"
 
1127
                "/var/log/journal\0";
 
1128
        int r;
 
1129
 
 
1130
        if (!ret)
 
1131
                return -EINVAL;
 
1132
 
 
1133
        if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
 
1134
                      SD_JOURNAL_RUNTIME_ONLY|
 
1135
                      SD_JOURNAL_SYSTEM_ONLY))
 
1136
                return -EINVAL;
 
1137
 
 
1138
        j = new0(sd_journal, 1);
 
1139
        if (!j)
 
1140
                return -ENOMEM;
 
1141
 
 
1142
        j->flags = flags;
 
1143
 
 
1144
        j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
 
1145
        if (j->inotify_fd < 0) {
 
1146
                r = -errno;
 
1147
                goto fail;
 
1148
        }
 
1149
 
 
1150
        j->files = hashmap_new(string_hash_func, string_compare_func);
 
1151
        if (!j->files) {
 
1152
                r = -ENOMEM;
 
1153
                goto fail;
 
1154
        }
 
1155
 
 
1156
        j->inotify_wd_dirs = hashmap_new(trivial_hash_func, trivial_compare_func);
 
1157
        j->inotify_wd_roots = hashmap_new(trivial_hash_func, trivial_compare_func);
 
1158
 
 
1159
        if (!j->inotify_wd_dirs || !j->inotify_wd_roots) {
 
1160
                r = -ENOMEM;
 
1161
                goto fail;
 
1162
        }
 
1163
 
 
1164
        /* We ignore most errors here, since the idea is to only open
 
1165
         * what's actually accessible, and ignore the rest. */
 
1166
 
 
1167
        NULSTR_FOREACH(p, search_paths) {
 
1168
                DIR *d;
 
1169
 
 
1170
                if ((flags & SD_JOURNAL_RUNTIME_ONLY) &&
 
1171
                    !path_startswith(p, "/run"))
 
1172
                        continue;
 
1173
 
 
1174
                d = opendir(p);
 
1175
                if (!d) {
 
1176
                        if (errno != ENOENT)
 
1177
                                log_debug("Failed to open %s: %m", p);
 
1178
                        continue;
 
1179
                }
 
1180
 
 
1181
                add_root_wd(j, p);
 
1182
 
 
1183
                for (;;) {
 
1184
                        struct dirent buf, *de;
 
1185
                        sd_id128_t id;
 
1186
 
 
1187
                        r = readdir_r(d, &buf, &de);
 
1188
                        if (r != 0 || !de)
 
1189
                                break;
 
1190
 
 
1191
                        if (dirent_is_file_with_suffix(de, ".journal")) {
 
1192
                                r = add_file(j, p, NULL, de->d_name);
 
1193
                                if (r < 0)
 
1194
                                        log_debug("Failed to add file %s/%s: %s", p, de->d_name, strerror(-r));
 
1195
 
 
1196
                        } else if ((de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) &&
 
1197
                                   sd_id128_from_string(de->d_name, &id) >= 0) {
 
1198
 
 
1199
                                r = add_directory(j, p, de->d_name);
 
1200
                                if (r < 0)
 
1201
                                        log_debug("Failed to add directory %s/%s: %s", p, de->d_name, strerror(-r));
 
1202
                        }
 
1203
                }
 
1204
 
 
1205
                closedir(d);
 
1206
        }
 
1207
 
 
1208
        *ret = j;
 
1209
        return 0;
 
1210
 
 
1211
fail:
 
1212
        sd_journal_close(j);
 
1213
 
 
1214
        return r;
 
1215
};
 
1216
 
 
1217
_public_ void sd_journal_close(sd_journal *j) {
 
1218
        if (!j)
 
1219
                return;
 
1220
 
 
1221
        if (j->inotify_wd_dirs) {
 
1222
                void *k;
 
1223
 
 
1224
                while ((k = hashmap_first_key(j->inotify_wd_dirs)))
 
1225
                        remove_directory_wd(j, PTR_TO_INT(k));
 
1226
 
 
1227
                hashmap_free(j->inotify_wd_dirs);
 
1228
        }
 
1229
 
 
1230
        if (j->inotify_wd_roots) {
 
1231
                void *k;
 
1232
 
 
1233
                while ((k = hashmap_first_key(j->inotify_wd_roots)))
 
1234
                        remove_root_wd(j, PTR_TO_INT(k));
 
1235
 
 
1236
                hashmap_free(j->inotify_wd_roots);
 
1237
        }
 
1238
 
 
1239
        if (j->files) {
 
1240
                JournalFile *f;
 
1241
 
 
1242
                while ((f = hashmap_steal_first(j->files)))
 
1243
                        journal_file_close(f);
 
1244
 
 
1245
                hashmap_free(j->files);
 
1246
        }
 
1247
 
 
1248
        sd_journal_flush_matches(j);
 
1249
 
 
1250
        if (j->inotify_fd >= 0)
 
1251
                close_nointr_nofail(j->inotify_fd);
 
1252
 
 
1253
        free(j);
 
1254
}
 
1255
 
 
1256
_public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
 
1257
        Object *o;
 
1258
        JournalFile *f;
 
1259
        int r;
 
1260
 
 
1261
        if (!j)
 
1262
                return -EINVAL;
 
1263
        if (!ret)
 
1264
                return -EINVAL;
 
1265
 
 
1266
        f = j->current_file;
 
1267
        if (!f)
 
1268
                return -EADDRNOTAVAIL;
 
1269
 
 
1270
        if (f->current_offset <= 0)
 
1271
                return -EADDRNOTAVAIL;
 
1272
 
 
1273
        r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
 
1274
        if (r < 0)
 
1275
                return r;
 
1276
 
 
1277
        *ret = le64toh(o->entry.realtime);
 
1278
        return 0;
 
1279
}
 
1280
 
 
1281
_public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
 
1282
        Object *o;
 
1283
        JournalFile *f;
 
1284
        int r;
 
1285
        sd_id128_t id;
 
1286
 
 
1287
        if (!j)
 
1288
                return -EINVAL;
 
1289
        if (!ret)
 
1290
                return -EINVAL;
 
1291
 
 
1292
        f = j->current_file;
 
1293
        if (!f)
 
1294
                return -EADDRNOTAVAIL;
 
1295
 
 
1296
        if (f->current_offset <= 0)
 
1297
                return -EADDRNOTAVAIL;
 
1298
 
 
1299
        r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
 
1300
        if (r < 0)
 
1301
                return r;
 
1302
 
 
1303
        if (ret_boot_id)
 
1304
                *ret_boot_id = o->entry.boot_id;
 
1305
        else {
 
1306
                r = sd_id128_get_boot(&id);
 
1307
                if (r < 0)
 
1308
                        return r;
 
1309
 
 
1310
                if (!sd_id128_equal(id, o->entry.boot_id))
 
1311
                        return -ESTALE;
 
1312
        }
 
1313
 
 
1314
        *ret = le64toh(o->entry.monotonic);
 
1315
        return 0;
 
1316
}
 
1317
 
 
1318
_public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
 
1319
        JournalFile *f;
 
1320
        uint64_t i, n;
 
1321
        size_t field_length;
 
1322
        int r;
 
1323
        Object *o;
 
1324
 
 
1325
        if (!j)
 
1326
                return -EINVAL;
 
1327
        if (!field)
 
1328
                return -EINVAL;
 
1329
        if (!data)
 
1330
                return -EINVAL;
 
1331
        if (!size)
 
1332
                return -EINVAL;
 
1333
 
 
1334
        if (isempty(field) || strchr(field, '='))
 
1335
                return -EINVAL;
 
1336
 
 
1337
        f = j->current_file;
 
1338
        if (!f)
 
1339
                return -EADDRNOTAVAIL;
 
1340
 
 
1341
        if (f->current_offset <= 0)
 
1342
                return -EADDRNOTAVAIL;
 
1343
 
 
1344
        r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
 
1345
        if (r < 0)
 
1346
                return r;
 
1347
 
 
1348
        field_length = strlen(field);
 
1349
 
 
1350
        n = journal_file_entry_n_items(o);
 
1351
        for (i = 0; i < n; i++) {
 
1352
                uint64_t p, l, le_hash;
 
1353
                size_t t;
 
1354
 
 
1355
                p = le64toh(o->entry.items[i].object_offset);
 
1356
                le_hash = o->entry.items[i].hash;
 
1357
                r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
 
1358
                if (r < 0)
 
1359
                        return r;
 
1360
 
 
1361
                if (le_hash != o->data.hash)
 
1362
                        return -EBADMSG;
 
1363
 
 
1364
                l = le64toh(o->object.size) - offsetof(Object, data.payload);
 
1365
 
 
1366
                if (o->object.flags & OBJECT_COMPRESSED) {
 
1367
 
 
1368
#ifdef HAVE_XZ
 
1369
                        if (uncompress_startswith(o->data.payload, l,
 
1370
                                                  &f->compress_buffer, &f->compress_buffer_size,
 
1371
                                                  field, field_length, '=')) {
 
1372
 
 
1373
                                uint64_t rsize;
 
1374
 
 
1375
                                if (!uncompress_blob(o->data.payload, l,
 
1376
                                                     &f->compress_buffer, &f->compress_buffer_size, &rsize))
 
1377
                                        return -EBADMSG;
 
1378
 
 
1379
                                *data = f->compress_buffer;
 
1380
                                *size = (size_t) rsize;
 
1381
 
 
1382
                                return 0;
 
1383
                        }
 
1384
#else
 
1385
                        return -EPROTONOSUPPORT;
 
1386
#endif
 
1387
 
 
1388
                } else if (l >= field_length+1 &&
 
1389
                           memcmp(o->data.payload, field, field_length) == 0 &&
 
1390
                           o->data.payload[field_length] == '=') {
 
1391
 
 
1392
                        t = (size_t) l;
 
1393
 
 
1394
                        if ((uint64_t) t != l)
 
1395
                                return -E2BIG;
 
1396
 
 
1397
                        *data = o->data.payload;
 
1398
                        *size = t;
 
1399
 
 
1400
                        return 0;
 
1401
                }
 
1402
 
 
1403
                r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
 
1404
                if (r < 0)
 
1405
                        return r;
 
1406
        }
 
1407
 
 
1408
        return -ENOENT;
 
1409
}
 
1410
 
 
1411
_public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
 
1412
        JournalFile *f;
 
1413
        uint64_t p, l, n, le_hash;
 
1414
        int r;
 
1415
        Object *o;
 
1416
        size_t t;
 
1417
 
 
1418
        if (!j)
 
1419
                return -EINVAL;
 
1420
        if (!data)
 
1421
                return -EINVAL;
 
1422
        if (!size)
 
1423
                return -EINVAL;
 
1424
 
 
1425
        f = j->current_file;
 
1426
        if (!f)
 
1427
                return -EADDRNOTAVAIL;
 
1428
 
 
1429
        if (f->current_offset <= 0)
 
1430
                return -EADDRNOTAVAIL;
 
1431
 
 
1432
        r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
 
1433
        if (r < 0)
 
1434
                return r;
 
1435
 
 
1436
        n = journal_file_entry_n_items(o);
 
1437
        if (j->current_field >= n)
 
1438
                return 0;
 
1439
 
 
1440
        p = le64toh(o->entry.items[j->current_field].object_offset);
 
1441
        le_hash = o->entry.items[j->current_field].hash;
 
1442
        r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
 
1443
        if (r < 0)
 
1444
                return r;
 
1445
 
 
1446
        if (le_hash != o->data.hash)
 
1447
                return -EBADMSG;
 
1448
 
 
1449
        l = le64toh(o->object.size) - offsetof(Object, data.payload);
 
1450
        t = (size_t) l;
 
1451
 
 
1452
        /* We can't read objects larger than 4G on a 32bit machine */
 
1453
        if ((uint64_t) t != l)
 
1454
                return -E2BIG;
 
1455
 
 
1456
        if (o->object.flags & OBJECT_COMPRESSED) {
 
1457
#ifdef HAVE_XZ
 
1458
                uint64_t rsize;
 
1459
 
 
1460
                if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize))
 
1461
                        return -EBADMSG;
 
1462
 
 
1463
                *data = f->compress_buffer;
 
1464
                *size = (size_t) rsize;
 
1465
#else
 
1466
                return -EPROTONOSUPPORT;
 
1467
#endif
 
1468
        } else {
 
1469
                *data = o->data.payload;
 
1470
                *size = t;
 
1471
        }
 
1472
 
 
1473
        j->current_field ++;
 
1474
 
 
1475
        return 1;
 
1476
}
 
1477
 
 
1478
_public_ void sd_journal_restart_data(sd_journal *j) {
 
1479
        if (!j)
 
1480
                return;
 
1481
 
 
1482
        j->current_field = 0;
 
1483
}
 
1484
 
 
1485
_public_ int sd_journal_get_fd(sd_journal *j) {
 
1486
        if (!j)
 
1487
                return -EINVAL;
 
1488
 
 
1489
        return j->inotify_fd;
 
1490
}
 
1491
 
 
1492
static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
 
1493
        char *p;
 
1494
        int r;
 
1495
 
 
1496
        assert(j);
 
1497
        assert(e);
 
1498
 
 
1499
        /* Is this a subdirectory we watch? */
 
1500
        p = hashmap_get(j->inotify_wd_dirs, INT_TO_PTR(e->wd));
 
1501
        if (p) {
 
1502
 
 
1503
                if (!(e->mask & IN_ISDIR) && e->len > 0 && endswith(e->name, ".journal")) {
 
1504
 
 
1505
                        /* Event for a journal file */
 
1506
 
 
1507
                        if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
 
1508
                                r = add_file(j, p, NULL, e->name);
 
1509
                                if (r < 0)
 
1510
                                        log_debug("Failed to add file %s/%s: %s", p, e->name, strerror(-r));
 
1511
                        } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
 
1512
 
 
1513
                                r = remove_file(j, p, NULL, e->name);
 
1514
                                if (r < 0)
 
1515
                                        log_debug("Failed to remove file %s/%s: %s", p, e->name, strerror(-r));
 
1516
                        }
 
1517
 
 
1518
                } else if (e->len == 0) {
 
1519
 
 
1520
                        /* Event for the directory itself */
 
1521
 
 
1522
                        if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT))
 
1523
                                remove_directory_wd(j, e->wd);
 
1524
                }
 
1525
 
 
1526
                return;
 
1527
        }
 
1528
 
 
1529
        /* Must be the root directory then? */
 
1530
        p = hashmap_get(j->inotify_wd_roots, INT_TO_PTR(e->wd));
 
1531
        if (p) {
 
1532
                sd_id128_t id;
 
1533
 
 
1534
                if (!(e->mask & IN_ISDIR) && e->len > 0 && endswith(e->name, ".journal")) {
 
1535
 
 
1536
                        /* Event for a journal file */
 
1537
 
 
1538
                        if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
 
1539
                                r = add_file(j, p, NULL, e->name);
 
1540
                                if (r < 0)
 
1541
                                        log_debug("Failed to add file %s/%s: %s", p, e->name, strerror(-r));
 
1542
                        } else if (e->mask & (IN_DELETE|IN_UNMOUNT)) {
 
1543
 
 
1544
                                r = remove_file(j, p, NULL, e->name);
 
1545
                                if (r < 0)
 
1546
                                        log_debug("Failed to remove file %s/%s: %s", p, e->name, strerror(-r));
 
1547
                        }
 
1548
 
 
1549
                } else if ((e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
 
1550
 
 
1551
                        /* Event for subdirectory */
 
1552
 
 
1553
                        if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
 
1554
 
 
1555
                                r = add_directory(j, p, e->name);
 
1556
                                if (r < 0)
 
1557
                                        log_debug("Failed to add directory %s/%s: %s", p, e->name, strerror(-r));
 
1558
                        }
 
1559
                }
 
1560
 
 
1561
                return;
 
1562
        }
 
1563
 
 
1564
        if (e->mask & IN_IGNORED)
 
1565
                return;
 
1566
 
 
1567
        log_warning("Unknown inotify event.");
 
1568
}
 
1569
 
 
1570
_public_ int sd_journal_process(sd_journal *j) {
 
1571
        uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX];
 
1572
 
 
1573
        if (!j)
 
1574
                return -EINVAL;
 
1575
 
 
1576
        for (;;) {
 
1577
                struct inotify_event *e;
 
1578
                ssize_t l;
 
1579
 
 
1580
                l = read(j->inotify_fd, buffer, sizeof(buffer));
 
1581
                if (l < 0) {
 
1582
                        if (errno == EINTR || errno == EAGAIN)
 
1583
                                return 0;
 
1584
 
 
1585
                        return -errno;
 
1586
                }
 
1587
 
 
1588
                e = (struct inotify_event*) buffer;
 
1589
                while (l > 0) {
 
1590
                        size_t step;
 
1591
 
 
1592
                        process_inotify_event(j, e);
 
1593
 
 
1594
                        step = sizeof(struct inotify_event) + e->len;
 
1595
                        assert(step <= (size_t) l);
 
1596
 
 
1597
                        e = (struct inotify_event*) ((uint8_t*) e + step);
 
1598
                        l -= step;
 
1599
                }
 
1600
        }
 
1601
}
 
1602
 
 
1603
/* _public_ int sd_journal_query_unique(sd_journal *j, const char *field) { */
 
1604
/*         if (!j) */
 
1605
/*                 return -EINVAL; */
 
1606
/*         if (!field) */
 
1607
/*                 return -EINVAL; */
 
1608
 
 
1609
/*         return -ENOTSUP; */
 
1610
/* } */
 
1611
 
 
1612
/* _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { */
 
1613
/*         if (!j) */
 
1614
/*                 return -EINVAL; */
 
1615
/*         if (!data) */
 
1616
/*                 return -EINVAL; */
 
1617
/*         if (!l) */
 
1618
/*                 return -EINVAL; */
 
1619
 
 
1620
/*         return -ENOTSUP; */
 
1621
/* } */
 
1622
 
 
1623
/* _public_ void sd_journal_restart_unique(sd_journal *j) { */
 
1624
/*         if (!j) */
 
1625
/*                 return; */
 
1626
/* } */