1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2013 Marius Vollmer
6 Copyright 2013 Zbigniew Jędrzejewski-Szmek
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.
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.
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/>.
25 #include "sd-journal.h"
27 #include "alloc-util.h"
28 #include "journal-file.h"
29 #include "journal-vacuum.h"
31 #include "parse-util.h"
35 /* This program tests skipping around in a multi-file journal.
38 static bool arg_keep
= false;
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
);
46 #define assert_ret(expr) \
49 if (_unlikely_(_r_ < 0)) \
50 log_assert_errno(#expr, -_r_, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
53 static JournalFile
*test_open(const char *name
) {
55 assert_ret(journal_file_open(-1, name
, O_RDWR
|O_CREAT
, 0644, true, false, NULL
, NULL
, NULL
, NULL
, &f
));
59 static void test_close(JournalFile
*f
) {
60 (void) journal_file_close (f
);
63 static void append_number(JournalFile
*f
, int n
, uint64_t *seqnum
) {
66 static dual_timestamp previous_ts
= {};
67 struct iovec iovec
[1];
69 dual_timestamp_get(&ts
);
71 if (ts
.monotonic
<= previous_ts
.monotonic
)
72 ts
.monotonic
= previous_ts
.monotonic
+ 1;
74 if (ts
.realtime
<= previous_ts
.realtime
)
75 ts
.realtime
= previous_ts
.realtime
+ 1;
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
));
86 static void test_check_number (sd_journal
*j
, int n
) {
88 _cleanup_free_
char *k
;
92 assert_ret(sd_journal_get_data(j
, "NUMBER", &d
, &l
));
93 assert_se(k
= strndup(d
, l
));
96 assert_se(safe_atoi(k
+ 7, &x
) >= 0);
100 static void test_check_numbers_down (sd_journal
*j
, int count
) {
103 for (i
= 1; i
<= count
; i
++) {
105 test_check_number(j
, i
);
106 assert_ret(r
= sd_journal_next(j
));
115 static void test_check_numbers_up (sd_journal
*j
, int count
) {
116 for (int i
= count
; i
>= 1; i
--) {
118 test_check_number(j
, i
);
119 assert_ret(r
= sd_journal_previous(j
));
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
);
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
);
152 static void test_skip(void (*setup
)(void)) {
153 char t
[] = "/tmp/journal-skip-XXXXXX";
157 assert_se(mkdtemp(t
));
158 assert_se(chdir(t
) >= 0);
162 /* Seek to head, iterate down.
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);
170 /* Seek to tail, iterate up.
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);
178 /* Seek to tail, skip to head, iterate down.
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));
184 test_check_numbers_down(j
, 4);
187 /* Seek to head, skip to tail, iterate up.
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));
193 test_check_numbers_up(j
, 4);
199 log_info("Not removing %s", t
);
201 journal_directory_vacuum(".", 3000000, 0, 0, NULL
, true);
203 assert_se(rm_rf(t
, REMOVE_ROOT
|REMOVE_PHYSICAL
) >= 0);
206 puts("------------------------------------------------------------");
209 static void test_sequence_numbers(void) {
211 char t
[] = "/tmp/journal-seq-XXXXXX";
212 JournalFile
*one
, *two
;
214 sd_id128_t seqnum_id
;
216 assert_se(mkdtemp(t
));
217 assert_se(chdir(t
) >= 0);
219 assert_se(journal_file_open(-1, "one.journal", O_RDWR
|O_CREAT
, 0644,
220 true, false, NULL
, NULL
, NULL
, NULL
, &one
) == 0);
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);
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
));
234 memcpy(&seqnum_id
, &one
->header
->seqnum_id
, sizeof(sd_id128_t
));
236 assert_se(journal_file_open(-1, "two.journal", O_RDWR
|O_CREAT
, 0644,
237 true, false, NULL
, NULL
, NULL
, one
, &two
) == 0);
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
));
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);
254 append_number(one
, 5, &seqnum
);
255 printf("seqnum=%"PRIu64
"\n", seqnum
);
256 assert_se(seqnum
== 5);
258 append_number(one
, 6, &seqnum
);
259 printf("seqnum=%"PRIu64
"\n", seqnum
);
260 assert_se(seqnum
== 6);
267 assert_se(journal_file_open(-1, "two.journal", O_RDWR
, 0,
268 true, false, NULL
, NULL
, NULL
, NULL
, &two
) == 0);
270 assert_se(sd_id128_equal(two
->header
->seqnum_id
, seqnum_id
));
272 append_number(two
, 7, &seqnum
);
273 printf("seqnum=%"PRIu64
"\n", seqnum
);
274 assert_se(seqnum
== 5);
276 /* So..., here we have the same seqnum in two files with the
284 log_info("Not removing %s", t
);
286 journal_directory_vacuum(".", 3000000, 0, 0, NULL
, true);
288 assert_se(rm_rf(t
, REMOVE_ROOT
|REMOVE_PHYSICAL
) >= 0);
292 int main(int argc
, char *argv
[]) {
293 log_set_max_level(LOG_DEBUG
);
295 /* journal_file_open requires a valid machine id */
296 if (access("/etc/machine-id", F_OK
) != 0)
297 return EXIT_TEST_SKIP
;
301 test_skip(setup_sequential
);
302 test_skip(setup_interleaved
);
304 test_sequence_numbers();