chiark / gitweb /
5b747147827e2cb0643e30451478802b617250dd
[elogind.git] / src / journal / test-journal-interleaving.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 Marius Vollmer
7   Copyright 2013 Zbigniew JÄ™drzejewski-Szmek
8
9   systemd is free software; you can redistribute it and/or modify it
10   under the terms of the GNU Lesser General Public License as published by
11   the Free Software Foundation; either version 2.1 of the License, or
12   (at your option) any later version.
13
14   systemd is distributed in the hope that it will be useful, but
15   WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   Lesser General Public License for more details.
18
19   You should have received a copy of the GNU Lesser General Public License
20   along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 ***/
22
23 #include <unistd.h>
24 #include <fcntl.h>
25
26 #include <systemd/sd-journal.h>
27
28 #include "journal-file.h"
29 #include "journal-internal.h"
30 #include "journal-vacuum.h"
31 #include "util.h"
32 #include "log.h"
33
34 /* This program tests skipping around in a multi-file journal.
35  */
36
37 static bool arg_keep = false;
38
39 noreturn static void log_assert_errno(const char *text, int eno, const char *file, int line, const char *func) {
40         log_meta(LOG_CRIT, file, line, func,
41                  "'%s' failed at %s:%u (%s): %s.",
42                  text, file, line, func, strerror(eno));
43         abort();
44 }
45
46 #define assert_ret(expr)                                                \
47         do {                                                            \
48                 int _r_ = (expr);                                       \
49                 if (_unlikely_(_r_ < 0))                                \
50                         log_assert_errno(#expr, -_r_, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
51         } while (false)
52
53 static JournalFile *test_open(const char *name) {
54         JournalFile *f;
55         assert_ret(journal_file_open(name, O_RDWR|O_CREAT, 0644, true, false, NULL, NULL, NULL, &f));
56         return f;
57 }
58
59 static void test_close(JournalFile *f) {
60         journal_file_close (f);
61 }
62
63 static void append_number(JournalFile *f, int n, uint64_t *seqnum) {
64         char *p;
65         dual_timestamp ts;
66         struct iovec iovec[1];
67
68         dual_timestamp_get(&ts);
69
70         assert_se(asprintf(&p, "NUMBER=%d", n) >= 0);
71         iovec[0].iov_base = p;
72         iovec[0].iov_len = strlen(p);
73         assert_ret(journal_file_append_entry(f, &ts, iovec, 1, seqnum, NULL, NULL));
74         free(p);
75 }
76
77 static void test_check_number (sd_journal *j, int n) {
78         const void *d;
79         _cleanup_free_ char *k;
80         size_t l;
81         int x;
82
83         assert_ret(sd_journal_get_data(j, "NUMBER", &d, &l));
84         assert_se(k = strndup(d, l));
85         printf("%s\n", k);
86
87         assert_se(safe_atoi(k + 7, &x) >= 0);
88         assert_se(n == x);
89 }
90
91 static void test_check_numbers_down (sd_journal *j, int count) {
92         int i;
93
94         for (i = 1; i <= count; i++) {
95                 int r;
96                 test_check_number(j, i);
97                 assert_ret(r = sd_journal_next(j));
98                 if (i == count)
99                         assert_se(r == 0);
100                 else
101                         assert_se(r == 1);
102         }
103
104 }
105
106 static void test_check_numbers_up (sd_journal *j, int count) {
107         for (int i = count; i >= 1; i--) {
108                 int r;
109                 test_check_number(j, i);
110                 assert_ret(r = sd_journal_previous(j));
111                 if (i == 1)
112                         assert_se(r == 0);
113                 else
114                         assert_se(r == 1);
115         }
116
117 }
118
119 static void setup_sequential(void) {
120         JournalFile *one, *two;
121         one = test_open("one.journal");
122         two = test_open("two.journal");
123         append_number(one, 1, NULL);
124         append_number(one, 2, NULL);
125         append_number(two, 3, NULL);
126         append_number(two, 4, NULL);
127         test_close(one);
128         test_close(two);
129 }
130
131 static void setup_interleaved(void) {
132         JournalFile *one, *two;
133         one = test_open("one.journal");
134         two = test_open("two.journal");
135         append_number(one, 1, NULL);
136         append_number(two, 2, NULL);
137         append_number(one, 3, NULL);
138         append_number(two, 4, NULL);
139         test_close(one);
140         test_close(two);
141 }
142
143 static void test_skip(void (*setup)(void)) {
144         char t[] = "/tmp/journal-skip-XXXXXX";
145         sd_journal *j;
146         int r;
147
148         assert_se(mkdtemp(t));
149         assert_se(chdir(t) >= 0);
150
151         setup();
152
153         /* Seek to head, iterate down.
154          */
155         assert_ret(sd_journal_open_directory(&j, t, 0));
156         assert_ret(sd_journal_seek_head(j));
157         assert_ret(sd_journal_next(j));
158         test_check_numbers_down(j, 4);
159         sd_journal_close(j);
160
161         /* Seek to tail, iterate up.
162          */
163         assert_ret(sd_journal_open_directory(&j, t, 0));
164         assert_ret(sd_journal_seek_tail(j));
165         assert_ret(sd_journal_previous(j));
166         test_check_numbers_up(j, 4);
167         sd_journal_close(j);
168
169         /* Seek to tail, skip to head, iterate down.
170          */
171         assert_ret(sd_journal_open_directory(&j, t, 0));
172         assert_ret(sd_journal_seek_tail(j));
173         assert_ret(r = sd_journal_previous_skip(j, 4));
174         assert_se(r == 4);
175         test_check_numbers_down(j, 4);
176         sd_journal_close(j);
177
178         /* Seek to head, skip to tail, iterate up.
179          */
180         assert_ret(sd_journal_open_directory(&j, t, 0));
181         assert_ret(sd_journal_seek_head(j));
182         assert_ret(r = sd_journal_next_skip(j, 4));
183         assert_se(r == 4);
184         test_check_numbers_up(j, 4);
185         sd_journal_close(j);
186
187         log_info("Done...");
188
189         if (arg_keep)
190                 log_info("Not removing %s", t);
191         else {
192                 journal_directory_vacuum(".", 3000000, 0, 0, NULL);
193
194                 assert_se(rm_rf_dangerous(t, false, true, false) >= 0);
195         }
196
197         puts("------------------------------------------------------------");
198 }
199
200 static void test_sequence_numbers(void) {
201
202         char t[] = "/tmp/journal-seq-XXXXXX";
203         JournalFile *one, *two;
204         uint64_t seqnum = 0;
205         sd_id128_t seqnum_id;
206
207         assert_se(mkdtemp(t));
208         assert_se(chdir(t) >= 0);
209
210         assert_se(journal_file_open("one.journal", O_RDWR|O_CREAT, 0644,
211                                     true, false, NULL, NULL, NULL, &one) == 0);
212
213         append_number(one, 1, &seqnum);
214         printf("seqnum=%"PRIu64"\n", seqnum);
215         assert(seqnum == 1);
216         append_number(one, 2, &seqnum);
217         printf("seqnum=%"PRIu64"\n", seqnum);
218         assert(seqnum == 2);
219
220         assert(one->header->state == STATE_ONLINE);
221         assert(!sd_id128_equal(one->header->file_id, one->header->machine_id));
222         assert(!sd_id128_equal(one->header->file_id, one->header->boot_id));
223         assert(sd_id128_equal(one->header->file_id, one->header->seqnum_id));
224
225         memcpy(&seqnum_id, &one->header->seqnum_id, sizeof(sd_id128_t));
226
227         assert_se(journal_file_open("two.journal", O_RDWR|O_CREAT, 0644,
228                                     true, false, NULL, NULL, one, &two) == 0);
229
230         assert(two->header->state == STATE_ONLINE);
231         assert(!sd_id128_equal(two->header->file_id, one->header->file_id));
232         assert(sd_id128_equal(one->header->machine_id, one->header->machine_id));
233         assert(sd_id128_equal(one->header->boot_id, one->header->boot_id));
234         assert(sd_id128_equal(one->header->seqnum_id, one->header->seqnum_id));
235
236         append_number(two, 3, &seqnum);
237         printf("seqnum=%"PRIu64"\n", seqnum);
238         assert(seqnum == 3);
239         append_number(two, 4, &seqnum);
240         printf("seqnum=%"PRIu64"\n", seqnum);
241         assert(seqnum == 4);
242
243         test_close(two);
244
245         append_number(one, 5, &seqnum);
246         printf("seqnum=%"PRIu64"\n", seqnum);
247         assert(seqnum == 5);
248
249         append_number(one, 6, &seqnum);
250         printf("seqnum=%"PRIu64"\n", seqnum);
251         assert(seqnum == 6);
252
253         test_close(one);
254
255         /* restart server */
256         seqnum = 0;
257
258         assert_se(journal_file_open("two.journal", O_RDWR, 0,
259                                     true, false, NULL, NULL, NULL, &two) == 0);
260
261         assert(sd_id128_equal(two->header->seqnum_id, seqnum_id));
262
263         append_number(two, 7, &seqnum);
264         printf("seqnum=%"PRIu64"\n", seqnum);
265         assert(seqnum == 5);
266
267         /* So..., here we have the same seqnum in two files with the
268          * same seqnum_id. */
269
270         test_close(two);
271
272         log_info("Done...");
273
274         if (arg_keep)
275                 log_info("Not removing %s", t);
276         else {
277                 journal_directory_vacuum(".", 3000000, 0, 0, NULL);
278
279                 assert_se(rm_rf_dangerous(t, false, true, false) >= 0);
280         }
281 }
282
283 int main(int argc, char *argv[]) {
284         log_set_max_level(LOG_DEBUG);
285
286         /* journal_file_open requires a valid machine id */
287         if (access("/etc/machine-id", F_OK) != 0)
288                 return EXIT_TEST_SKIP;
289
290         arg_keep = argc > 1;
291
292         test_skip(setup_sequential);
293         test_skip(setup_interleaved);
294
295         test_sequence_numbers();
296
297         return 0;
298 }