chiark / gitweb /
journal: implementation rotation
[elogind.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
25 #include "sd-journal.h"
26 #include "journal-def.h"
27 #include "journal-file.h"
28 #include "hashmap.h"
29 #include "list.h"
30
31 typedef struct Match Match;
32
33 struct Match {
34         char *data;
35         size_t size;
36         uint64_t hash;
37
38         LIST_FIELDS(Match, matches);
39 };
40
41 struct sd_journal {
42         Hashmap *files;
43
44         JournalFile *current_file;
45
46         LIST_HEAD(Match, matches);
47 };
48
49 int sd_journal_add_match(sd_journal *j, const char *field, const void *data, size_t size) {
50         Match *m;
51         char *e;
52
53         assert(j);
54         assert(field);
55         assert(data || size == 0);
56
57         m = new0(Match, 1);
58         if (!m)
59                 return -ENOMEM;
60
61         m->size = strlen(field) + 1 + size;
62         m->data = malloc(m->size);
63         if (!m->data) {
64                 free(m);
65                 return -ENOMEM;
66         }
67
68         e = stpcpy(m->data, field);
69         *(e++) = '=';
70         memcpy(e, data, size);
71
72         LIST_PREPEND(Match, matches, j->matches, m);
73         return 0;
74 }
75
76 void sd_journal_flush_matches(sd_journal *j) {
77         assert(j);
78
79         while (j->matches) {
80                 Match *m = j->matches;
81
82                 LIST_REMOVE(Match, matches, j->matches, m);
83                 free(m->data);
84                 free(m);
85         }
86 }
87
88 static int compare_order(JournalFile *af, Object *ao, uint64_t ap,
89                             JournalFile *bf, Object *bo, uint64_t bp) {
90
91         uint64_t a, b;
92
93         if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
94
95                 /* If this is from the same seqnum source, compare
96                  * seqnums */
97                 a = le64toh(ao->entry.seqnum);
98                 b = le64toh(bo->entry.seqnum);
99
100
101         } else if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
102
103                 /* If the boot id matches compare monotonic time */
104                 a = le64toh(ao->entry.monotonic);
105                 b = le64toh(bo->entry.monotonic);
106
107         } else {
108
109                 /* Otherwise compare UTC time */
110                 a = le64toh(ao->entry.realtime);
111                 b = le64toh(ao->entry.realtime);
112         }
113
114         return
115                 a < b ? -1 :
116                 a > b ? +1 : 0;
117 }
118
119 int sd_journal_next(sd_journal *j) {
120         JournalFile *f, *new_current = NULL;
121         Iterator i;
122         int r;
123         uint64_t new_offset = 0;
124         Object *new_entry = NULL;
125
126         assert(j);
127
128         HASHMAP_FOREACH(f, j->files, i) {
129                 Object *o;
130                 uint64_t p;
131
132                 if (f->current_offset > 0) {
133                         r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &o);
134                         if (r < 0)
135                                 return r;
136                 } else
137                         o = NULL;
138
139                 r = journal_file_next_entry(f, o, &o, &p);
140                 if (r < 0)
141                         return r;
142                 else if (r == 0)
143                         continue;
144
145                 if (!new_current || compare_order(new_current, new_entry, new_offset, f, o, p) > 0) {
146                         new_current = f;
147                         new_entry = o;
148                         new_offset = p;
149                 }
150         }
151
152         if (new_current) {
153                 j->current_file = new_current;
154                 f->current_offset = new_offset;
155                 return 1;
156         }
157
158         return 0;
159 }
160
161 int sd_journal_previous(sd_journal *j) {
162         JournalFile *f, *new_current = NULL;
163         Iterator i;
164         int r;
165         uint64_t new_offset = 0;
166         Object *new_entry = NULL;
167
168         assert(j);
169
170         HASHMAP_FOREACH(f, j->files, i) {
171                 Object *o;
172                 uint64_t p;
173
174                 if (f->current_offset > 0) {
175                         r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &o);
176                         if (r < 0)
177                                 return r;
178                 } else
179                         o = NULL;
180
181                 r = journal_file_prev_entry(f, o, &o, &p);
182                 if (r < 0)
183                         return r;
184                 else if (r == 0)
185                         continue;
186
187                 if (!new_current || compare_order(new_current, new_entry, new_offset, f, o, p) > 0) {
188                         new_current = f;
189                         new_entry = o;
190                         new_offset = p;
191                 }
192         }
193
194         if (new_current) {
195                 j->current_file = new_current;
196                 f->current_offset = new_offset;
197                 return 1;
198         }
199
200         return 0;
201 }
202
203 int sd_journal_get_cursor(sd_journal *j, void **cursor, size_t *size) {
204         JournalCursor *c;
205         Object *o;
206         int r;
207
208         assert(j);
209         assert(cursor);
210         assert(size);
211
212         if (!j->current_file || !j->current_file->current_offset <= 0)
213                 return 0;
214
215         r = journal_file_move_to_object(j->current_file, j->current_file->current_offset, OBJECT_ENTRY, &o);
216         if (r < 0)
217                 return r;
218
219         c = new0(JournalCursor, 1);
220         if (!c)
221                 return -ENOMEM;
222
223         c->version = 1;
224         c->seqnum = o->entry.seqnum;
225         c->seqnum_id = j->current_file->header->seqnum_id;
226         c->boot_id = o->entry.boot_id;
227         c->monotonic = o->entry.monotonic;
228         c->realtime = o->entry.realtime;
229         c->xor_hash = o->entry.xor_hash;
230
231         *cursor = c;
232         *size = sizeof(JournalCursor);
233
234         return 1;
235 }
236
237 int sd_journal_set_cursor(sd_journal *j, const void *cursor, size_t size) {
238         return -EINVAL;
239 }
240
241 int sd_journal_open(sd_journal **ret) {
242         sd_journal *j;
243         char *fn;
244         const char *p;
245         int r = 0;
246         const char search_paths[] =
247                 "/run/log/journal\0"
248                 "/var/log/journal\0";
249
250         assert(ret);
251
252         j = new0(sd_journal, 1);
253         if (!j)
254                 return -ENOMEM;
255
256         j->files = hashmap_new(string_hash_func, string_compare_func);
257         if (!j->files)
258                 goto fail;
259
260         NULSTR_FOREACH(p, search_paths) {
261                 DIR *d;
262
263                 d = opendir(p);
264                 if (!d) {
265                         if (errno != ENOENT && r == 0)
266                                 r = -errno;
267
268                         continue;
269                 }
270
271                 for (;;) {
272                         struct dirent buf, *de;
273                         int k;
274                         JournalFile *f;
275
276                         k = readdir_r(d, &buf, &de);
277                         if (k != 0) {
278                                 if (r == 0)
279                                         r = -k;
280
281                                 break;
282                         }
283
284                         if (!de)
285                                 break;
286
287                         if (!dirent_is_file_with_suffix(de, ".journal"))
288                                 continue;
289
290                         fn = join(p, "/", de->d_name, NULL);
291                         if (!fn) {
292                                 r = -ENOMEM;
293                                 closedir(d);
294                                 goto fail;
295                         }
296
297                         k = journal_file_open(fn, O_RDONLY, 0, NULL, &f);
298                         free(fn);
299
300                         if (k < 0) {
301
302                                 if (r == 0)
303                                         r = -k;
304                         } else {
305                                 k = hashmap_put(j->files, f->path, f);
306                                 if (k < 0) {
307                                         journal_file_close(f);
308                                         closedir(d);
309
310                                         r = k;
311                                         goto fail;
312                                 }
313                         }
314                 }
315         }
316
317         *ret = j;
318         return 0;
319
320 fail:
321         sd_journal_close(j);
322
323         return r;
324 };
325
326 void sd_journal_close(sd_journal *j) {
327         assert(j);
328
329         if (j->files) {
330                 JournalFile *f;
331
332                 while ((f = hashmap_steal_first(j->files)))
333                         journal_file_close(f);
334
335                 hashmap_free(j->files);
336         }
337
338         free(j);
339 }