]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/sd-journal.c
journal: fix field retrieval by name
[thirdparty/systemd.git] / src / journal / sd-journal.c
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
26 #include "sd-journal.h"
27 #include "journal-def.h"
28 #include "journal-file.h"
29 #include "hashmap.h"
30 #include "list.h"
31
32 typedef struct Match Match;
33
34 struct Match {
35 char *data;
36 size_t size;
37 uint64_t hash;
38
39 LIST_FIELDS(Match, matches);
40 };
41
42 struct sd_journal {
43 Hashmap *files;
44
45 JournalFile *current_file;
46
47 LIST_HEAD(Match, matches);
48 };
49
50 int sd_journal_add_match(sd_journal *j, const char *field, const void *data, size_t size) {
51 Match *m;
52 char *e;
53
54 assert(j);
55 assert(field);
56 assert(data || size == 0);
57
58 m = new0(Match, 1);
59 if (!m)
60 return -ENOMEM;
61
62 m->size = strlen(field) + 1 + size;
63 m->data = malloc(m->size);
64 if (!m->data) {
65 free(m);
66 return -ENOMEM;
67 }
68
69 e = stpcpy(m->data, field);
70 *(e++) = '=';
71 memcpy(e, data, size);
72
73 LIST_PREPEND(Match, matches, j->matches, m);
74 return 0;
75 }
76
77 void sd_journal_flush_matches(sd_journal *j) {
78 assert(j);
79
80 while (j->matches) {
81 Match *m = j->matches;
82
83 LIST_REMOVE(Match, matches, j->matches, m);
84 free(m->data);
85 free(m);
86 }
87 }
88
89 static int compare_order(JournalFile *af, Object *ao, uint64_t ap,
90 JournalFile *bf, Object *bo, uint64_t bp) {
91
92 uint64_t a, b;
93
94 if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
95
96 /* If this is from the same seqnum source, compare
97 * seqnums */
98 a = le64toh(ao->entry.seqnum);
99 b = le64toh(bo->entry.seqnum);
100
101
102 } else if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
103
104 /* If the boot id matches compare monotonic time */
105 a = le64toh(ao->entry.monotonic);
106 b = le64toh(bo->entry.monotonic);
107
108 } else {
109
110 /* Otherwise compare UTC time */
111 a = le64toh(ao->entry.realtime);
112 b = le64toh(ao->entry.realtime);
113 }
114
115 return
116 a < b ? -1 :
117 a > b ? +1 : 0;
118 }
119
120 int sd_journal_next(sd_journal *j) {
121 JournalFile *f, *new_current = NULL;
122 Iterator i;
123 int r;
124 uint64_t new_offset = 0;
125 Object *new_entry = NULL;
126
127 assert(j);
128
129 HASHMAP_FOREACH(f, j->files, i) {
130 Object *o;
131 uint64_t p;
132
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;
139
140 r = journal_file_next_entry(f, o, &o, &p);
141 if (r < 0)
142 return r;
143 else if (r == 0)
144 continue;
145
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;
150 }
151 }
152
153 if (new_current) {
154 j->current_file = new_current;
155 j->current_file->current_offset = new_offset;
156 j->current_file->current_field = 0;
157 return 1;
158 }
159
160 return 0;
161 }
162
163 int sd_journal_previous(sd_journal *j) {
164 JournalFile *f, *new_current = NULL;
165 Iterator i;
166 int r;
167 uint64_t new_offset = 0;
168 Object *new_entry = NULL;
169
170 assert(j);
171
172 HASHMAP_FOREACH(f, j->files, i) {
173 Object *o;
174 uint64_t p;
175
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;
182
183 r = journal_file_prev_entry(f, o, &o, &p);
184 if (r < 0)
185 return r;
186 else if (r == 0)
187 continue;
188
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 }
194 }
195
196 if (new_current) {
197 j->current_file = new_current;
198 j->current_file->current_offset = new_offset;
199 j->current_file->current_field = 0;
200 return 1;
201 }
202
203 return 0;
204 }
205
206 int sd_journal_get_cursor(sd_journal *j, char **cursor) {
207 Object *o;
208 int r;
209 char bid[33], sid[33];
210
211 assert(j);
212 assert(cursor);
213
214 if (!j->current_file || j->current_file->current_offset <= 0)
215 return -EADDRNOTAVAIL;
216
217 r = journal_file_move_to_object(j->current_file, j->current_file->current_offset, OBJECT_ENTRY, &o);
218 if (r < 0)
219 return r;
220
221 sd_id128_to_string(j->current_file->header->seqnum_id, sid);
222 sd_id128_to_string(o->entry.boot_id, bid);
223
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;
232
233 return 1;
234 }
235
236 int sd_journal_set_cursor(sd_journal *j, const char *cursor) {
237 return -EINVAL;
238 }
239
240 static 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
276 static 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
319 int sd_journal_open(sd_journal **ret) {
320 sd_journal *j;
321 const char *p;
322 const char search_paths[] =
323 "/run/log/journal\0"
324 "/var/log/journal\0";
325 int r;
326
327 assert(ret);
328
329 j = new0(sd_journal, 1);
330 if (!j)
331 return -ENOMEM;
332
333 j->files = hashmap_new(string_hash_func, string_compare_func);
334 if (!j->files) {
335 r = -ENOMEM;
336 goto fail;
337 }
338
339 /* We ignore most errors here, since the idea is to only open
340 * what's actually accessible, and ignore the rest. */
341
342 NULSTR_FOREACH(p, search_paths) {
343 DIR *d;
344
345 d = opendir(p);
346 if (!d) {
347 if (errno != ENOENT)
348 log_debug("Failed to open %s: %m", p);
349 continue;
350 }
351
352 for (;;) {
353 struct dirent buf, *de;
354 sd_id128_t id;
355
356 r = readdir_r(d, &buf, &de);
357 if (r != 0 || !de)
358 break;
359
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));
364
365 } else if ((de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) &&
366 sd_id128_from_string(de->d_name, &id) >= 0) {
367
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));
371 }
372 }
373
374 closedir(d);
375 }
376
377 *ret = j;
378 return 0;
379
380 fail:
381 sd_journal_close(j);
382
383 return r;
384 };
385
386 void sd_journal_close(sd_journal *j) {
387 assert(j);
388
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 }
397
398 free(j);
399 }
400
401 int 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
424 int 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
456 int 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 (l >= field_length+1 &&
497 memcmp(o->data.payload, field, field_length) == 0 &&
498 o->data.payload[field_length] == '=') {
499
500 t = (size_t) l;
501
502 if ((uint64_t) t != l)
503 return -E2BIG;
504
505 *data = o->data.payload;
506 *size = t;
507
508 return 1;
509 }
510
511 r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &o);
512 if (r < 0)
513 return r;
514 }
515
516 return 0;
517 }
518
519 int sd_journal_iterate_fields(sd_journal *j, const void **data, size_t *size) {
520 JournalFile *f;
521 uint64_t p, l, n;
522 size_t t;
523 int r;
524 Object *o;
525
526 assert(j);
527 assert(data);
528 assert(size);
529
530 f = j->current_file;
531 if (!f)
532 return 0;
533
534 if (f->current_offset <= 0)
535 return 0;
536
537 r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &o);
538 if (r < 0)
539 return r;
540
541 n = journal_file_entry_n_items(o);
542 if (f->current_field >= n)
543 return 0;
544
545 p = le64toh(o->entry.items[f->current_field].object_offset);
546 r = journal_file_move_to_object(f, p, OBJECT_DATA, &o);
547 if (r < 0)
548 return r;
549
550 l = le64toh(o->object.size) - offsetof(Object, data.payload);
551 t = (size_t) l;
552
553 /* We can't read objects larger than 4G on a 32bit machine */
554 if ((uint64_t) t != l)
555 return -E2BIG;
556
557 *data = o->data.payload;
558 *size = t;
559
560 f->current_field ++;
561
562 return 1;
563 }