]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/test-journal-interleaving.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / journal / test-journal-interleaving.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2013 Marius Vollmer
6 Copyright 2013 Zbigniew Jędrzejewski-Szmek
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <fcntl.h>
23 #include <unistd.h>
24
25 #include "sd-journal.h"
26
27 #include "alloc-util.h"
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 error, const char *file, int line, const char *func) {
41 log_internal(LOG_CRIT, error, file, line, func,
42 "'%s' failed at %s:%u (%s): %m", text, file, line, func);
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(-1, name, O_RDWR|O_CREAT, 0644, true, false, NULL, NULL, NULL, NULL, &f));
56 return f;
57 }
58
59 static void test_close(JournalFile *f) {
60 (void) 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 static dual_timestamp previous_ts = {};
67 struct iovec iovec[1];
68
69 dual_timestamp_get(&ts);
70
71 if (ts.monotonic <= previous_ts.monotonic)
72 ts.monotonic = previous_ts.monotonic + 1;
73
74 if (ts.realtime <= previous_ts.realtime)
75 ts.realtime = previous_ts.realtime + 1;
76
77 previous_ts = ts;
78
79 assert_se(asprintf(&p, "NUMBER=%d", n) >= 0);
80 iovec[0].iov_base = p;
81 iovec[0].iov_len = strlen(p);
82 assert_ret(journal_file_append_entry(f, &ts, iovec, 1, seqnum, NULL, NULL));
83 free(p);
84 }
85
86 static void test_check_number (sd_journal *j, int n) {
87 const void *d;
88 _cleanup_free_ char *k;
89 size_t l;
90 int x;
91
92 assert_ret(sd_journal_get_data(j, "NUMBER", &d, &l));
93 assert_se(k = strndup(d, l));
94 printf("%s\n", k);
95
96 assert_se(safe_atoi(k + 7, &x) >= 0);
97 assert_se(n == x);
98 }
99
100 static void test_check_numbers_down (sd_journal *j, int count) {
101 int i;
102
103 for (i = 1; i <= count; i++) {
104 int r;
105 test_check_number(j, i);
106 assert_ret(r = sd_journal_next(j));
107 if (i == count)
108 assert_se(r == 0);
109 else
110 assert_se(r == 1);
111 }
112
113 }
114
115 static void test_check_numbers_up (sd_journal *j, int count) {
116 for (int i = count; i >= 1; i--) {
117 int r;
118 test_check_number(j, i);
119 assert_ret(r = sd_journal_previous(j));
120 if (i == 1)
121 assert_se(r == 0);
122 else
123 assert_se(r == 1);
124 }
125
126 }
127
128 static void setup_sequential(void) {
129 JournalFile *one, *two;
130 one = test_open("one.journal");
131 two = test_open("two.journal");
132 append_number(one, 1, NULL);
133 append_number(one, 2, NULL);
134 append_number(two, 3, NULL);
135 append_number(two, 4, NULL);
136 test_close(one);
137 test_close(two);
138 }
139
140 static void setup_interleaved(void) {
141 JournalFile *one, *two;
142 one = test_open("one.journal");
143 two = test_open("two.journal");
144 append_number(one, 1, NULL);
145 append_number(two, 2, NULL);
146 append_number(one, 3, NULL);
147 append_number(two, 4, NULL);
148 test_close(one);
149 test_close(two);
150 }
151
152 static void test_skip(void (*setup)(void)) {
153 char t[] = "/tmp/journal-skip-XXXXXX";
154 sd_journal *j;
155 int r;
156
157 assert_se(mkdtemp(t));
158 assert_se(chdir(t) >= 0);
159
160 setup();
161
162 /* Seek to head, iterate down.
163 */
164 assert_ret(sd_journal_open_directory(&j, t, 0));
165 assert_ret(sd_journal_seek_head(j));
166 assert_ret(sd_journal_next(j));
167 test_check_numbers_down(j, 4);
168 sd_journal_close(j);
169
170 /* Seek to tail, iterate up.
171 */
172 assert_ret(sd_journal_open_directory(&j, t, 0));
173 assert_ret(sd_journal_seek_tail(j));
174 assert_ret(sd_journal_previous(j));
175 test_check_numbers_up(j, 4);
176 sd_journal_close(j);
177
178 /* Seek to tail, skip to head, iterate down.
179 */
180 assert_ret(sd_journal_open_directory(&j, t, 0));
181 assert_ret(sd_journal_seek_tail(j));
182 assert_ret(r = sd_journal_previous_skip(j, 4));
183 assert_se(r == 4);
184 test_check_numbers_down(j, 4);
185 sd_journal_close(j);
186
187 /* Seek to head, skip to tail, iterate up.
188 */
189 assert_ret(sd_journal_open_directory(&j, t, 0));
190 assert_ret(sd_journal_seek_head(j));
191 assert_ret(r = sd_journal_next_skip(j, 4));
192 assert_se(r == 4);
193 test_check_numbers_up(j, 4);
194 sd_journal_close(j);
195
196 log_info("Done...");
197
198 if (arg_keep)
199 log_info("Not removing %s", t);
200 else {
201 journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
202
203 assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
204 }
205
206 puts("------------------------------------------------------------");
207 }
208
209 static void test_sequence_numbers(void) {
210
211 char t[] = "/tmp/journal-seq-XXXXXX";
212 JournalFile *one, *two;
213 uint64_t seqnum = 0;
214 sd_id128_t seqnum_id;
215
216 assert_se(mkdtemp(t));
217 assert_se(chdir(t) >= 0);
218
219 assert_se(journal_file_open(-1, "one.journal", O_RDWR|O_CREAT, 0644,
220 true, false, NULL, NULL, NULL, NULL, &one) == 0);
221
222 append_number(one, 1, &seqnum);
223 printf("seqnum=%"PRIu64"\n", seqnum);
224 assert_se(seqnum == 1);
225 append_number(one, 2, &seqnum);
226 printf("seqnum=%"PRIu64"\n", seqnum);
227 assert_se(seqnum == 2);
228
229 assert_se(one->header->state == STATE_ONLINE);
230 assert_se(!sd_id128_equal(one->header->file_id, one->header->machine_id));
231 assert_se(!sd_id128_equal(one->header->file_id, one->header->boot_id));
232 assert_se(sd_id128_equal(one->header->file_id, one->header->seqnum_id));
233
234 memcpy(&seqnum_id, &one->header->seqnum_id, sizeof(sd_id128_t));
235
236 assert_se(journal_file_open(-1, "two.journal", O_RDWR|O_CREAT, 0644,
237 true, false, NULL, NULL, NULL, one, &two) == 0);
238
239 assert_se(two->header->state == STATE_ONLINE);
240 assert_se(!sd_id128_equal(two->header->file_id, one->header->file_id));
241 assert_se(sd_id128_equal(one->header->machine_id, one->header->machine_id));
242 assert_se(sd_id128_equal(one->header->boot_id, one->header->boot_id));
243 assert_se(sd_id128_equal(one->header->seqnum_id, one->header->seqnum_id));
244
245 append_number(two, 3, &seqnum);
246 printf("seqnum=%"PRIu64"\n", seqnum);
247 assert_se(seqnum == 3);
248 append_number(two, 4, &seqnum);
249 printf("seqnum=%"PRIu64"\n", seqnum);
250 assert_se(seqnum == 4);
251
252 test_close(two);
253
254 append_number(one, 5, &seqnum);
255 printf("seqnum=%"PRIu64"\n", seqnum);
256 assert_se(seqnum == 5);
257
258 append_number(one, 6, &seqnum);
259 printf("seqnum=%"PRIu64"\n", seqnum);
260 assert_se(seqnum == 6);
261
262 test_close(one);
263
264 /* restart server */
265 seqnum = 0;
266
267 assert_se(journal_file_open(-1, "two.journal", O_RDWR, 0,
268 true, false, NULL, NULL, NULL, NULL, &two) == 0);
269
270 assert_se(sd_id128_equal(two->header->seqnum_id, seqnum_id));
271
272 append_number(two, 7, &seqnum);
273 printf("seqnum=%"PRIu64"\n", seqnum);
274 assert_se(seqnum == 5);
275
276 /* So..., here we have the same seqnum in two files with the
277 * same seqnum_id. */
278
279 test_close(two);
280
281 log_info("Done...");
282
283 if (arg_keep)
284 log_info("Not removing %s", t);
285 else {
286 journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
287
288 assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
289 }
290 }
291
292 int main(int argc, char *argv[]) {
293 log_set_max_level(LOG_DEBUG);
294
295 /* journal_file_open requires a valid machine id */
296 if (access("/etc/machine-id", F_OK) != 0)
297 return EXIT_TEST_SKIP;
298
299 arg_keep = argc > 1;
300
301 test_skip(setup_sequential);
302 test_skip(setup_interleaved);
303
304 test_sequence_numbers();
305
306 return 0;
307 }