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