1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Marius Vollmer
7 Copyright 2013 Zbigniew Jędrzejewski-Szmek
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.
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.
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/>.
26 #include "sd-journal.h"
28 #include "alloc-util.h"
29 #include "journal-file.h"
30 #include "journal-vacuum.h"
32 #include "parse-util.h"
36 /* This program tests skipping around in a multi-file journal.
39 static bool arg_keep
= false;
41 noreturn
static void log_assert_errno(const char *text
, int eno
, const char *file
, int line
, const char *func
) {
42 log_internal(LOG_CRIT
, 0, file
, line
, func
,
43 "'%s' failed at %s:%u (%s): %s.",
44 text
, file
, line
, func
, strerror(eno
));
48 #define assert_ret(expr) \
51 if (_unlikely_(_r_ < 0)) \
52 log_assert_errno(#expr, -_r_, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
55 static JournalFile
*test_open(const char *name
) {
57 assert_ret(journal_file_open(name
, O_RDWR
|O_CREAT
, 0644, true, false, NULL
, NULL
, NULL
, &f
));
61 static void test_close(JournalFile
*f
) {
62 journal_file_close (f
);
65 static void append_number(JournalFile
*f
, int n
, uint64_t *seqnum
) {
68 static dual_timestamp previous_ts
= {};
69 struct iovec iovec
[1];
71 dual_timestamp_get(&ts
);
73 if (ts
.monotonic
<= previous_ts
.monotonic
)
74 ts
.monotonic
= previous_ts
.monotonic
+ 1;
76 if (ts
.realtime
<= previous_ts
.realtime
)
77 ts
.realtime
= previous_ts
.realtime
+ 1;
81 assert_se(asprintf(&p
, "NUMBER=%d", n
) >= 0);
82 iovec
[0].iov_base
= p
;
83 iovec
[0].iov_len
= strlen(p
);
84 assert_ret(journal_file_append_entry(f
, &ts
, iovec
, 1, seqnum
, NULL
, NULL
));
88 static void test_check_number (sd_journal
*j
, int n
) {
90 _cleanup_free_
char *k
;
94 assert_ret(sd_journal_get_data(j
, "NUMBER", &d
, &l
));
95 assert_se(k
= strndup(d
, l
));
98 assert_se(safe_atoi(k
+ 7, &x
) >= 0);
102 static void test_check_numbers_down (sd_journal
*j
, int count
) {
105 for (i
= 1; i
<= count
; i
++) {
107 test_check_number(j
, i
);
108 assert_ret(r
= sd_journal_next(j
));
117 static void test_check_numbers_up (sd_journal
*j
, int count
) {
118 for (int i
= count
; i
>= 1; i
--) {
120 test_check_number(j
, i
);
121 assert_ret(r
= sd_journal_previous(j
));
130 static void setup_sequential(void) {
131 JournalFile
*one
, *two
;
132 one
= test_open("one.journal");
133 two
= test_open("two.journal");
134 append_number(one
, 1, NULL
);
135 append_number(one
, 2, NULL
);
136 append_number(two
, 3, NULL
);
137 append_number(two
, 4, NULL
);
142 static void setup_interleaved(void) {
143 JournalFile
*one
, *two
;
144 one
= test_open("one.journal");
145 two
= test_open("two.journal");
146 append_number(one
, 1, NULL
);
147 append_number(two
, 2, NULL
);
148 append_number(one
, 3, NULL
);
149 append_number(two
, 4, NULL
);
154 static void test_skip(void (*setup
)(void)) {
155 char t
[] = "/tmp/journal-skip-XXXXXX";
159 assert_se(mkdtemp(t
));
160 assert_se(chdir(t
) >= 0);
164 /* Seek to head, iterate down.
166 assert_ret(sd_journal_open_directory(&j
, t
, 0));
167 assert_ret(sd_journal_seek_head(j
));
168 assert_ret(sd_journal_next(j
));
169 test_check_numbers_down(j
, 4);
172 /* Seek to tail, iterate up.
174 assert_ret(sd_journal_open_directory(&j
, t
, 0));
175 assert_ret(sd_journal_seek_tail(j
));
176 assert_ret(sd_journal_previous(j
));
177 test_check_numbers_up(j
, 4);
180 /* Seek to tail, skip to head, iterate down.
182 assert_ret(sd_journal_open_directory(&j
, t
, 0));
183 assert_ret(sd_journal_seek_tail(j
));
184 assert_ret(r
= sd_journal_previous_skip(j
, 4));
186 test_check_numbers_down(j
, 4);
189 /* Seek to head, skip to tail, iterate up.
191 assert_ret(sd_journal_open_directory(&j
, t
, 0));
192 assert_ret(sd_journal_seek_head(j
));
193 assert_ret(r
= sd_journal_next_skip(j
, 4));
195 test_check_numbers_up(j
, 4);
201 log_info("Not removing %s", t
);
203 journal_directory_vacuum(".", 3000000, 0, 0, NULL
, true);
205 assert_se(rm_rf(t
, REMOVE_ROOT
|REMOVE_PHYSICAL
) >= 0);
208 puts("------------------------------------------------------------");
211 static void test_sequence_numbers(void) {
213 char t
[] = "/tmp/journal-seq-XXXXXX";
214 JournalFile
*one
, *two
;
216 sd_id128_t seqnum_id
;
218 assert_se(mkdtemp(t
));
219 assert_se(chdir(t
) >= 0);
221 assert_se(journal_file_open("one.journal", O_RDWR
|O_CREAT
, 0644,
222 true, false, NULL
, NULL
, NULL
, &one
) == 0);
224 append_number(one
, 1, &seqnum
);
225 printf("seqnum=%"PRIu64
"\n", seqnum
);
226 assert_se(seqnum
== 1);
227 append_number(one
, 2, &seqnum
);
228 printf("seqnum=%"PRIu64
"\n", seqnum
);
229 assert_se(seqnum
== 2);
231 assert_se(one
->header
->state
== STATE_ONLINE
);
232 assert_se(!sd_id128_equal(one
->header
->file_id
, one
->header
->machine_id
));
233 assert_se(!sd_id128_equal(one
->header
->file_id
, one
->header
->boot_id
));
234 assert_se(sd_id128_equal(one
->header
->file_id
, one
->header
->seqnum_id
));
236 memcpy(&seqnum_id
, &one
->header
->seqnum_id
, sizeof(sd_id128_t
));
238 assert_se(journal_file_open("two.journal", O_RDWR
|O_CREAT
, 0644,
239 true, false, NULL
, NULL
, one
, &two
) == 0);
241 assert_se(two
->header
->state
== STATE_ONLINE
);
242 assert_se(!sd_id128_equal(two
->header
->file_id
, one
->header
->file_id
));
243 assert_se(sd_id128_equal(one
->header
->machine_id
, one
->header
->machine_id
));
244 assert_se(sd_id128_equal(one
->header
->boot_id
, one
->header
->boot_id
));
245 assert_se(sd_id128_equal(one
->header
->seqnum_id
, one
->header
->seqnum_id
));
247 append_number(two
, 3, &seqnum
);
248 printf("seqnum=%"PRIu64
"\n", seqnum
);
249 assert_se(seqnum
== 3);
250 append_number(two
, 4, &seqnum
);
251 printf("seqnum=%"PRIu64
"\n", seqnum
);
252 assert_se(seqnum
== 4);
256 append_number(one
, 5, &seqnum
);
257 printf("seqnum=%"PRIu64
"\n", seqnum
);
258 assert_se(seqnum
== 5);
260 append_number(one
, 6, &seqnum
);
261 printf("seqnum=%"PRIu64
"\n", seqnum
);
262 assert_se(seqnum
== 6);
269 assert_se(journal_file_open("two.journal", O_RDWR
, 0,
270 true, false, NULL
, NULL
, NULL
, &two
) == 0);
272 assert_se(sd_id128_equal(two
->header
->seqnum_id
, seqnum_id
));
274 append_number(two
, 7, &seqnum
);
275 printf("seqnum=%"PRIu64
"\n", seqnum
);
276 assert_se(seqnum
== 5);
278 /* So..., here we have the same seqnum in two files with the
286 log_info("Not removing %s", t
);
288 journal_directory_vacuum(".", 3000000, 0, 0, NULL
, true);
290 assert_se(rm_rf(t
, REMOVE_ROOT
|REMOVE_PHYSICAL
) >= 0);
294 int main(int argc
, char *argv
[]) {
295 log_set_max_level(LOG_DEBUG
);
297 /* journal_file_open requires a valid machine id */
298 if (access("/etc/machine-id", F_OK
) != 0)
299 return EXIT_TEST_SKIP
;
303 test_skip(setup_sequential
);
304 test_skip(setup_interleaved
);
306 test_sequence_numbers();