]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/sd-journal.c
journal: implement parallel file traversal
[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>
87d2c1ff
LP
25
26#include "sd-journal.h"
27#include "journal-def.h"
cec736d2 28#include "journal-file.h"
260a2be4 29#include "hashmap.h"
cec736d2 30#include "list.h"
87d2c1ff 31
cec736d2 32typedef struct Match Match;
87d2c1ff 33
cec736d2
LP
34struct Match {
35 char *data;
36 size_t size;
37 uint64_t hash;
87d2c1ff 38
cec736d2
LP
39 LIST_FIELDS(Match, matches);
40};
87d2c1ff 41
87d2c1ff 42struct sd_journal {
260a2be4 43 Hashmap *files;
87d2c1ff 44
cec736d2 45 JournalFile *current_file;
87d2c1ff 46
cec736d2
LP
47 LIST_HEAD(Match, matches);
48};
87d2c1ff 49
cec736d2
LP
50int sd_journal_add_match(sd_journal *j, const char *field, const void *data, size_t size) {
51 Match *m;
52 char *e;
87d2c1ff 53
cec736d2
LP
54 assert(j);
55 assert(field);
56 assert(data || size == 0);
87d2c1ff 57
cec736d2
LP
58 m = new0(Match, 1);
59 if (!m)
60 return -ENOMEM;
87d2c1ff 61
cec736d2
LP
62 m->size = strlen(field) + 1 + size;
63 m->data = malloc(m->size);
64 if (!m->data) {
65 free(m);
66 return -ENOMEM;
87d2c1ff
LP
67 }
68
cec736d2
LP
69 e = stpcpy(m->data, field);
70 *(e++) = '=';
71 memcpy(e, data, size);
87d2c1ff 72
cec736d2 73 LIST_PREPEND(Match, matches, j->matches, m);
87d2c1ff
LP
74 return 0;
75}
76
cec736d2
LP
77void sd_journal_flush_matches(sd_journal *j) {
78 assert(j);
87d2c1ff 79
cec736d2
LP
80 while (j->matches) {
81 Match *m = j->matches;
87d2c1ff 82
cec736d2
LP
83 LIST_REMOVE(Match, matches, j->matches, m);
84 free(m->data);
85 free(m);
87d2c1ff 86 }
87d2c1ff
LP
87}
88
cec736d2
LP
89static int compare_order(JournalFile *af, Object *ao, uint64_t ap,
90 JournalFile *bf, Object *bo, uint64_t bp) {
87d2c1ff 91
cec736d2 92 uint64_t a, b;
87d2c1ff 93
cec736d2 94 if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
87d2c1ff 95
cec736d2
LP
96 /* If this is from the same seqnum source, compare
97 * seqnums */
98 a = le64toh(ao->entry.seqnum);
99 b = le64toh(bo->entry.seqnum);
87d2c1ff 100
87d2c1ff 101
cec736d2 102 } else if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
87d2c1ff 103
cec736d2
LP
104 /* If the boot id matches compare monotonic time */
105 a = le64toh(ao->entry.monotonic);
106 b = le64toh(bo->entry.monotonic);
87d2c1ff 107
87d2c1ff 108 } else {
87d2c1ff 109
cec736d2
LP
110 /* Otherwise compare UTC time */
111 a = le64toh(ao->entry.realtime);
112 b = le64toh(ao->entry.realtime);
87d2c1ff
LP
113 }
114
cec736d2
LP
115 return
116 a < b ? -1 :
117 a > b ? +1 : 0;
87d2c1ff
LP
118}
119
cec736d2
LP
120int sd_journal_next(sd_journal *j) {
121 JournalFile *f, *new_current = NULL;
122 Iterator i;
87d2c1ff 123 int r;
cec736d2
LP
124 uint64_t new_offset = 0;
125 Object *new_entry = NULL;
87d2c1ff 126
cec736d2 127 assert(j);
87d2c1ff 128
cec736d2
LP
129 HASHMAP_FOREACH(f, j->files, i) {
130 Object *o;
131 uint64_t p;
87d2c1ff 132
cec736d2
LP
133 if (f->current_offset > 0) {
134 r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &o);
135 if (r < 0)
136 return r;
137 } else
138 o = NULL;
87d2c1ff 139
cec736d2 140 r = journal_file_next_entry(f, o, &o, &p);
87d2c1ff
LP
141 if (r < 0)
142 return r;
cec736d2
LP
143 else if (r == 0)
144 continue;
87d2c1ff 145
cec736d2
LP
146 if (!new_current || compare_order(new_current, new_entry, new_offset, f, o, p) > 0) {
147 new_current = f;
148 new_entry = o;
149 new_offset = p;
87d2c1ff 150 }
87d2c1ff
LP
151 }
152
cec736d2
LP
153 if (new_current) {
154 j->current_file = new_current;
3fbf9cbb
LP
155 j->current_file->current_offset = new_offset;
156 j->current_file->current_field = 0;
cec736d2 157 return 1;
87d2c1ff
LP
158 }
159
87d2c1ff
LP
160 return 0;
161}
162
cec736d2
LP
163int sd_journal_previous(sd_journal *j) {
164 JournalFile *f, *new_current = NULL;
165 Iterator i;
87d2c1ff 166 int r;
cec736d2
LP
167 uint64_t new_offset = 0;
168 Object *new_entry = NULL;
87d2c1ff 169
cec736d2 170 assert(j);
87d2c1ff 171
cec736d2 172 HASHMAP_FOREACH(f, j->files, i) {
dad50316 173 Object *o;
cec736d2 174 uint64_t p;
87d2c1ff 175
cec736d2
LP
176 if (f->current_offset > 0) {
177 r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &o);
178 if (r < 0)
179 return r;
180 } else
181 o = NULL;
87d2c1ff 182
cec736d2 183 r = journal_file_prev_entry(f, o, &o, &p);
87d2c1ff
LP
184 if (r < 0)
185 return r;
cec736d2
LP
186 else if (r == 0)
187 continue;
87d2c1ff 188
cec736d2
LP
189 if (!new_current || compare_order(new_current, new_entry, new_offset, f, o, p) > 0) {
190 new_current = f;
191 new_entry = o;
192 new_offset = p;
193 }
87d2c1ff
LP
194 }
195
cec736d2
LP
196 if (new_current) {
197 j->current_file = new_current;
3fbf9cbb
LP
198 j->current_file->current_offset = new_offset;
199 j->current_file->current_field = 0;
cec736d2 200 return 1;
87d2c1ff
LP
201 }
202
203 return 0;
204}
205
3fbf9cbb 206int sd_journal_get_cursor(sd_journal *j, char **cursor) {
cec736d2 207 Object *o;
87d2c1ff 208 int r;
3fbf9cbb 209 char bid[33], sid[33];
87d2c1ff 210
cec736d2
LP
211 assert(j);
212 assert(cursor);
87d2c1ff 213
3fbf9cbb
LP
214 if (!j->current_file || j->current_file->current_offset <= 0)
215 return -EADDRNOTAVAIL;
87d2c1ff 216
cec736d2 217 r = journal_file_move_to_object(j->current_file, j->current_file->current_offset, OBJECT_ENTRY, &o);
87d2c1ff
LP
218 if (r < 0)
219 return r;
220
3fbf9cbb
LP
221 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
222 sd_id128_to_string(o->entry.boot_id, bid);
87d2c1ff 223
3fbf9cbb
LP
224 if (asprintf(cursor,
225 "s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx;p=%s",
226 sid, (unsigned long long) le64toh(o->entry.seqnum),
227 bid, (unsigned long long) le64toh(o->entry.monotonic),
228 (unsigned long long) le64toh(o->entry.realtime),
229 (unsigned long long) le64toh(o->entry.xor_hash),
230 file_name_from_path(j->current_file->path)) < 0)
231 return -ENOMEM;
87d2c1ff
LP
232
233 return 1;
234}
235
3fbf9cbb 236int sd_journal_set_cursor(sd_journal *j, const char *cursor) {
cec736d2 237 return -EINVAL;
87d2c1ff
LP
238}
239
3fbf9cbb
LP
240static int add_file(sd_journal *j, const char *prefix, const char *dir, const char *filename) {
241 char *fn;
242 int r;
243 JournalFile *f;
244
245 assert(j);
246 assert(prefix);
247 assert(filename);
248
249 if (dir)
250 fn = join(prefix, "/", dir, "/", filename, NULL);
251 else
252 fn = join(prefix, "/", filename, NULL);
253
254 if (!fn)
255 return -ENOMEM;
256
257 r = journal_file_open(fn, O_RDONLY, 0, NULL, &f);
258 free(fn);
259
260 if (r < 0) {
261 if (errno == ENOENT)
262 return 0;
263
264 return r;
265 }
266
267 r = hashmap_put(j->files, f->path, f);
268 if (r < 0) {
269 journal_file_close(f);
270 return r;
271 }
272
273 return 0;
274}
275
276static int add_directory(sd_journal *j, const char *prefix, const char *dir) {
277 char *fn;
278 int r;
279 DIR *d;
280
281 assert(j);
282 assert(prefix);
283 assert(dir);
284
285 fn = join(prefix, "/", dir, NULL);
286 if (!fn)
287 return -ENOMEM;
288
289 d = opendir(fn);
290 free(fn);
291
292 if (!d) {
293 if (errno == ENOENT)
294 return 0;
295
296 return -errno;
297 }
298
299 for (;;) {
300 struct dirent buf, *de;
301
302 r = readdir_r(d, &buf, &de);
303 if (r != 0 || !de)
304 break;
305
306 if (!dirent_is_file_with_suffix(de, ".journal"))
307 continue;
308
309 r = add_file(j, prefix, dir, de->d_name);
310 if (r < 0)
311 log_debug("Failed to add file %s/%s/%s: %s", prefix, dir, de->d_name, strerror(-r));
312 }
313
314 closedir(d);
315
316 return 0;
317}
318
87d2c1ff
LP
319int sd_journal_open(sd_journal **ret) {
320 sd_journal *j;
87d2c1ff 321 const char *p;
87d2c1ff
LP
322 const char search_paths[] =
323 "/run/log/journal\0"
324 "/var/log/journal\0";
3fbf9cbb 325 int r;
87d2c1ff
LP
326
327 assert(ret);
328
329 j = new0(sd_journal, 1);
330 if (!j)
331 return -ENOMEM;
332
260a2be4 333 j->files = hashmap_new(string_hash_func, string_compare_func);
3fbf9cbb
LP
334 if (!j->files) {
335 r = -ENOMEM;
260a2be4 336 goto fail;
3fbf9cbb
LP
337 }
338
339 /* We ignore most errors here, since the idea is to only open
340 * what's actually accessible, and ignore the rest. */
260a2be4 341
87d2c1ff
LP
342 NULSTR_FOREACH(p, search_paths) {
343 DIR *d;
344
345 d = opendir(p);
346 if (!d) {
3fbf9cbb
LP
347 if (errno != ENOENT)
348 log_debug("Failed to open %s: %m", p);
87d2c1ff
LP
349 continue;
350 }
351
352 for (;;) {
353 struct dirent buf, *de;
3fbf9cbb 354 sd_id128_t id;
87d2c1ff 355
3fbf9cbb
LP
356 r = readdir_r(d, &buf, &de);
357 if (r != 0 || !de)
87d2c1ff
LP
358 break;
359
3fbf9cbb
LP
360 if (dirent_is_file_with_suffix(de, ".journal")) {
361 r = add_file(j, p, NULL, de->d_name);
362 if (r < 0)
363 log_debug("Failed to add file %s/%s: %s", p, de->d_name, strerror(-r));
87d2c1ff 364
3fbf9cbb
LP
365 } else if ((de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) &&
366 sd_id128_from_string(de->d_name, &id) >= 0) {
87d2c1ff 367
3fbf9cbb
LP
368 r = add_directory(j, p, de->d_name);
369 if (r < 0)
370 log_debug("Failed to add directory %s/%s: %s", p, de->d_name, strerror(-r));
260a2be4
LP
371 }
372 }
3fbf9cbb
LP
373
374 closedir(d);
87d2c1ff
LP
375 }
376
377 *ret = j;
378 return 0;
379
380fail:
381 sd_journal_close(j);
382
383 return r;
384};
385
386void sd_journal_close(sd_journal *j) {
387 assert(j);
388
260a2be4
LP
389 if (j->files) {
390 JournalFile *f;
391
392 while ((f = hashmap_steal_first(j->files)))
393 journal_file_close(f);
394
395 hashmap_free(j->files);
396 }
87d2c1ff
LP
397
398 free(j);
399}
3fbf9cbb
LP
400
401int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
402 Object *o;
403 JournalFile *f;
404 int r;
405
406 assert(j);
407 assert(ret);
408
409 f = j->current_file;
410 if (!f)
411 return 0;
412
413 if (f->current_offset <= 0)
414 return 0;
415
416 r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &o);
417 if (r < 0)
418 return r;
419
420 *ret = le64toh(o->entry.realtime);
421 return 1;
422}
423
424int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret) {
425 Object *o;
426 JournalFile *f;
427 int r;
428 sd_id128_t id;
429
430 assert(j);
431 assert(ret);
432
433 f = j->current_file;
434 if (!f)
435 return 0;
436
437 if (f->current_offset <= 0)
438 return 0;
439
440 r = sd_id128_get_machine(&id);
441 if (r < 0)
442 return r;
443
444 r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &o);
445 if (r < 0)
446 return r;
447
448 if (!sd_id128_equal(id, o->entry.boot_id))
449 return 0;
450
451 *ret = le64toh(o->entry.monotonic);
452 return 1;
453
454}
455
456int sd_journal_get_field(sd_journal *j, const char *field, const void **data, size_t *size) {
457 JournalFile *f;
458 uint64_t i, n;
459 size_t field_length;
460 int r;
461 Object *o;
462
463 assert(j);
464 assert(field);
465 assert(data);
466 assert(size);
467
468 if (isempty(field) || strchr(field, '='))
469 return -EINVAL;
470
471 f = j->current_file;
472 if (!f)
473 return 0;
474
475 if (f->current_offset <= 0)
476 return 0;
477
478 r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &o);
479 if (r < 0)
480 return r;
481
482 field_length = strlen(field);
483
484 n = journal_file_entry_n_items(o);
485 for (i = 0; i < n; i++) {
486 uint64_t p, l;
487 size_t t;
488
489 p = le64toh(o->entry.items[i].object_offset);
490 r = journal_file_move_to_object(f, p, OBJECT_DATA, &o);
491 if (r < 0)
492 return r;
493
494 l = le64toh(o->object.size) - offsetof(Object, data.payload);
495
496 if (field_length+1 > l)
497 continue;
498
499 if (memcmp(o->data.payload, field, field_length) ||
500 o->data.payload[field_length] != '=')
501 continue;
502
503 t = (size_t) l;
504
505 if ((uint64_t) t != l)
506 return -E2BIG;
507
508 *data = o->data.payload;
509 *size = t;
510
511 return 1;
512 }
513
514 return 0;
515}
516
517int sd_journal_iterate_fields(sd_journal *j, const void **data, size_t *size) {
518 JournalFile *f;
519 uint64_t p, l, n;
520 size_t t;
521 int r;
522 Object *o;
523
524 assert(j);
525 assert(data);
526 assert(size);
527
528 f = j->current_file;
529 if (!f)
530 return 0;
531
532 if (f->current_offset <= 0)
533 return 0;
534
535 r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &o);
536 if (r < 0)
537 return r;
538
539 n = journal_file_entry_n_items(o);
540 if (f->current_field >= n)
541 return 0;
542
543 p = le64toh(o->entry.items[f->current_field].object_offset);
544 r = journal_file_move_to_object(f, p, OBJECT_DATA, &o);
545 if (r < 0)
546 return r;
547
548 l = le64toh(o->object.size) - offsetof(Object, data.payload);
549 t = (size_t) l;
550
551 /* We can't read objects larger than 4G on a 32bit machine */
552 if ((uint64_t) t != l)
553 return -E2BIG;
554
555 *data = o->data.payload;
556 *size = t;
557
558 f->current_field ++;
559
560 return 1;
561}