]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/test-journal-interleaving.c
util-lib: split string parsing related calls from util.[ch] into parse-util.[ch]
[thirdparty/systemd.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 "sd-journal.h"
27
28 #include "journal-file.h"
29 #include "journal-vacuum.h"
30 #include "log.h"
31 #include "parse-util.h"
32 #include "rm-rf.h"
33 #include "util.h"
34
35 /* This program tests skipping around in a multi-file journal.
36 */
37
38 static bool arg_keep = false;
39
40 noreturn static void log_assert_errno(const char *text, int eno, const char *file, int line, const char *func) {
41 log_internal(LOG_CRIT, 0, file, line, func,
42 "'%s' failed at %s:%u (%s): %s.",
43 text, file, line, func, strerror(eno));
44 abort();
45 }
46
47 #define assert_ret(expr) \
48 do { \
49 int _r_ = (expr); \
50 if (_unlikely_(_r_ < 0)) \
51 log_assert_errno(#expr, -_r_, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
52 } while (false)
53
54 static JournalFile *test_open(const char *name) {
55 JournalFile *f;
56 assert_ret(journal_file_open(name, O_RDWR|O_CREAT, 0644, true, false, NULL, NULL, NULL, &f));
57 return f;
58 }
59
60 static void test_close(JournalFile *f) {
61 journal_file_close (f);
62 }
63
64 static void append_number(JournalFile *f, int n, uint64_t *seqnum) {
65 char *p;
66 dual_timestamp ts;
67 static dual_timestamp previous_ts = {};
68 struct iovec iovec[1];
69
70 dual_timestamp_get(&ts);
71
72 if (ts.monotonic <= previous_ts.monotonic)
73 ts.monotonic = previous_ts.monotonic + 1;
74
75 if (ts.realtime <= previous_ts.realtime)
76 ts.realtime = previous_ts.realtime + 1;
77
78 previous_ts = ts;
79
80 assert_se(asprintf(&p, "NUMBER=%d", n) >= 0);
81 iovec[0].iov_base = p;
82 iovec[0].iov_len = strlen(p);
83 assert_ret(journal_file_append_entry(f, &ts, iovec, 1, seqnum, NULL, NULL));
84 free(p);
85 }
86
87 static void test_check_number (sd_journal *j, int n) {
88 const void *d;
89 _cleanup_free_ char *k;
90 size_t l;
91 int x;
92
93 assert_ret(sd_journal_get_data(j, "NUMBER", &d, &l));
94 assert_se(k = strndup(d, l));
95 printf("%s\n", k);
96
97 assert_se(safe_atoi(k + 7, &x) >= 0);
98 assert_se(n == x);
99 }
100
101 static void test_check_numbers_down (sd_journal *j, int count) {
102 int i;
103
104 for (i = 1; i <= count; i++) {
105 int r;
106 test_check_number(j, i);
107 assert_ret(r = sd_journal_next(j));
108 if (i == count)
109 assert_se(r == 0);
110 else
111 assert_se(r == 1);
112 }
113
114 }
115
116 static void test_check_numbers_up (sd_journal *j, int count) {
117 for (int i = count; i >= 1; i--) {
118 int r;
119 test_check_number(j, i);
120 assert_ret(r = sd_journal_previous(j));
121 if (i == 1)
122 assert_se(r == 0);
123 else
124 assert_se(r == 1);
125 }
126
127 }
128
129 static void setup_sequential(void) {
130 JournalFile *one, *two;
131 one = test_open("one.journal");
132 two = test_open("two.journal");
133 append_number(one, 1, NULL);
134 append_number(one, 2, NULL);
135 append_number(two, 3, NULL);
136 append_number(two, 4, NULL);
137 test_close(one);
138 test_close(two);
139 }
140
141 static void setup_interleaved(void) {
142 JournalFile *one, *two;
143 one = test_open("one.journal");
144 two = test_open("two.journal");
145 append_number(one, 1, NULL);
146 append_number(two, 2, NULL);
147 append_number(one, 3, NULL);
148 append_number(two, 4, NULL);
149 test_close(one);
150 test_close(two);
151 }
152
153 static void test_skip(void (*setup)(void)) {
154 char t[] = "/tmp/journal-skip-XXXXXX";
155 sd_journal *j;
156 int r;
157
158 assert_se(mkdtemp(t));
159 assert_se(chdir(t) >= 0);
160
161 setup();
162
163 /* Seek to head, iterate down.
164 */
165 assert_ret(sd_journal_open_directory(&j, t, 0));
166 assert_ret(sd_journal_seek_head(j));
167 assert_ret(sd_journal_next(j));
168 test_check_numbers_down(j, 4);
169 sd_journal_close(j);
170
171 /* Seek to tail, iterate up.
172 */
173 assert_ret(sd_journal_open_directory(&j, t, 0));
174 assert_ret(sd_journal_seek_tail(j));
175 assert_ret(sd_journal_previous(j));
176 test_check_numbers_up(j, 4);
177 sd_journal_close(j);
178
179 /* Seek to tail, skip to head, iterate down.
180 */
181 assert_ret(sd_journal_open_directory(&j, t, 0));
182 assert_ret(sd_journal_seek_tail(j));
183 assert_ret(r = sd_journal_previous_skip(j, 4));
184 assert_se(r == 4);
185 test_check_numbers_down(j, 4);
186 sd_journal_close(j);
187
188 /* Seek to head, skip to tail, iterate up.
189 */
190 assert_ret(sd_journal_open_directory(&j, t, 0));
191 assert_ret(sd_journal_seek_head(j));
192 assert_ret(r = sd_journal_next_skip(j, 4));
193 assert_se(r == 4);
194 test_check_numbers_up(j, 4);
195 sd_journal_close(j);
196
197 log_info("Done...");
198
199 if (arg_keep)
200 log_info("Not removing %s", t);
201 else {
202 journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
203
204 assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
205 }
206
207 puts("------------------------------------------------------------");
208 }
209
210 static void test_sequence_numbers(void) {
211
212 char t[] = "/tmp/journal-seq-XXXXXX";
213 JournalFile *one, *two;
214 uint64_t seqnum = 0;
215 sd_id128_t seqnum_id;
216
217 assert_se(mkdtemp(t));
218 assert_se(chdir(t) >= 0);
219
220 assert_se(journal_file_open("one.journal", O_RDWR|O_CREAT, 0644,
221 true, false, NULL, NULL, NULL, &one) == 0);
222
223 append_number(one, 1, &seqnum);
224 printf("seqnum=%"PRIu64"\n", seqnum);
225 assert_se(seqnum == 1);
226 append_number(one, 2, &seqnum);
227 printf("seqnum=%"PRIu64"\n", seqnum);
228 assert_se(seqnum == 2);
229
230 assert_se(one->header->state == STATE_ONLINE);
231 assert_se(!sd_id128_equal(one->header->file_id, one->header->machine_id));
232 assert_se(!sd_id128_equal(one->header->file_id, one->header->boot_id));
233 assert_se(sd_id128_equal(one->header->file_id, one->header->seqnum_id));
234
235 memcpy(&seqnum_id, &one->header->seqnum_id, sizeof(sd_id128_t));
236
237 assert_se(journal_file_open("two.journal", O_RDWR|O_CREAT, 0644,
238 true, false, NULL, NULL, one, &two) == 0);
239
240 assert_se(two->header->state == STATE_ONLINE);
241 assert_se(!sd_id128_equal(two->header->file_id, one->header->file_id));
242 assert_se(sd_id128_equal(one->header->machine_id, one->header->machine_id));
243 assert_se(sd_id128_equal(one->header->boot_id, one->header->boot_id));
244 assert_se(sd_id128_equal(one->header->seqnum_id, one->header->seqnum_id));
245
246 append_number(two, 3, &seqnum);
247 printf("seqnum=%"PRIu64"\n", seqnum);
248 assert_se(seqnum == 3);
249 append_number(two, 4, &seqnum);
250 printf("seqnum=%"PRIu64"\n", seqnum);
251 assert_se(seqnum == 4);
252
253 test_close(two);
254
255 append_number(one, 5, &seqnum);
256 printf("seqnum=%"PRIu64"\n", seqnum);
257 assert_se(seqnum == 5);
258
259 append_number(one, 6, &seqnum);
260 printf("seqnum=%"PRIu64"\n", seqnum);
261 assert_se(seqnum == 6);
262
263 test_close(one);
264
265 /* restart server */
266 seqnum = 0;
267
268 assert_se(journal_file_open("two.journal", O_RDWR, 0,
269 true, false, NULL, NULL, NULL, &two) == 0);
270
271 assert_se(sd_id128_equal(two->header->seqnum_id, seqnum_id));
272
273 append_number(two, 7, &seqnum);
274 printf("seqnum=%"PRIu64"\n", seqnum);
275 assert_se(seqnum == 5);
276
277 /* So..., here we have the same seqnum in two files with the
278 * same seqnum_id. */
279
280 test_close(two);
281
282 log_info("Done...");
283
284 if (arg_keep)
285 log_info("Not removing %s", t);
286 else {
287 journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
288
289 assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
290 }
291 }
292
293 int main(int argc, char *argv[]) {
294 log_set_max_level(LOG_DEBUG);
295
296 /* journal_file_open requires a valid machine id */
297 if (access("/etc/machine-id", F_OK) != 0)
298 return EXIT_TEST_SKIP;
299
300 arg_keep = argc > 1;
301
302 test_skip(setup_sequential);
303 test_skip(setup_interleaved);
304
305 test_sequence_numbers();
306
307 return 0;
308 }