1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
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/>.
26 #include "journal-def.h"
27 #include "journal-file.h"
28 #include "journal-vacuum.h"
43 static int vacuum_compare(const void *_a
, const void *_b
) {
44 const struct vacuum_info
*a
, *b
;
49 if (a
->have_seqnum
&& b
->have_seqnum
&&
50 sd_id128_equal(a
->seqnum_id
, b
->seqnum_id
)) {
51 if (a
->seqnum
< b
->seqnum
)
53 else if (a
->seqnum
> b
->seqnum
)
59 if (a
->realtime
< b
->realtime
)
61 else if (a
->realtime
> b
->realtime
)
63 else if (a
->have_seqnum
&& b
->have_seqnum
)
64 return memcmp(&a
->seqnum_id
, &b
->seqnum_id
, 16);
66 return strcmp(a
->filename
, b
->filename
);
69 static void patch_realtime(
72 const struct stat
*st
,
73 unsigned long long *realtime
) {
77 /* The timestamp was determined by the file name, but let's
78 * see if the file might actually be older than the file name
86 x
= timespec_load(&st
->st_ctim
);
87 if (x
> 0 && x
!= USEC_INFINITY
&& x
< *realtime
)
90 x
= timespec_load(&st
->st_atim
);
91 if (x
> 0 && x
!= USEC_INFINITY
&& x
< *realtime
)
94 x
= timespec_load(&st
->st_mtim
);
95 if (x
> 0 && x
!= USEC_INFINITY
&& x
< *realtime
)
98 /* Let's read the original creation time, if possible. Ideally
99 * we'd just query the creation time the FS might provide, but
100 * unfortunately there's currently no sane API to query
101 * it. Hence let's implement this manually... */
103 if (fd_getcrtime_at(fd
, fn
, &crtime
, 0) >= 0) {
104 if (crtime
< *realtime
)
109 static int journal_file_empty(int dir_fd
, const char *name
) {
110 _cleanup_close_
int fd
;
115 fd
= openat(dir_fd
, name
, O_RDONLY
|O_CLOEXEC
|O_NOFOLLOW
|O_NONBLOCK
|O_NOATIME
);
117 /* Maybe failed due to O_NOATIME and lack of privileges? */
118 fd
= openat(dir_fd
, name
, O_RDONLY
|O_CLOEXEC
|O_NOFOLLOW
|O_NONBLOCK
);
123 if (fstat(fd
, &st
) < 0)
126 /* If an offline file doesn't even have a header we consider it empty */
127 if (st
.st_size
< (off_t
) sizeof(Header
))
130 /* If the number of entries is empty, we consider it empty, too */
131 n
= pread(fd
, &n_entries
, sizeof(n_entries
), offsetof(Header
, n_entries
));
134 if (n
!= sizeof(n_entries
))
137 return le64toh(n_entries
) <= 0;
140 int journal_directory_vacuum(
141 const char *directory
,
143 uint64_t n_max_files
,
144 usec_t max_retention_usec
,
148 _cleanup_closedir_
DIR *d
= NULL
;
149 struct vacuum_info
*list
= NULL
;
150 unsigned n_list
= 0, i
, n_active_files
= 0;
151 size_t n_allocated
= 0;
152 uint64_t sum
= 0, freed
= 0;
153 usec_t retention_limit
= 0;
154 char sbytes
[FORMAT_BYTES_MAX
];
160 if (max_use
<= 0 && max_retention_usec
<= 0 && n_max_files
<= 0)
163 if (max_retention_usec
> 0) {
164 retention_limit
= now(CLOCK_REALTIME
);
165 if (retention_limit
> max_retention_usec
)
166 retention_limit
-= max_retention_usec
;
168 max_retention_usec
= retention_limit
= 0;
171 d
= opendir(directory
);
175 FOREACH_DIRENT_ALL(de
, d
, r
= -errno
; goto finish
) {
177 unsigned long long seqnum
= 0, realtime
;
178 _cleanup_free_
char *p
= NULL
;
179 sd_id128_t seqnum_id
;
185 if (fstatat(dirfd(d
), de
->d_name
, &st
, AT_SYMLINK_NOFOLLOW
) < 0) {
186 log_debug_errno(errno
, "Failed to stat file %s while vacuuming, ignoring: %m", de
->d_name
);
190 if (!S_ISREG(st
.st_mode
))
193 q
= strlen(de
->d_name
);
195 if (endswith(de
->d_name
, ".journal")) {
197 /* Vacuum archived files. Active files are
200 if (q
< 1 + 32 + 1 + 16 + 1 + 16 + 8) {
205 if (de
->d_name
[q
-8-16-1] != '-' ||
206 de
->d_name
[q
-8-16-1-16-1] != '-' ||
207 de
->d_name
[q
-8-16-1-16-1-32-1] != '@') {
212 p
= strdup(de
->d_name
);
218 de
->d_name
[q
-8-16-1-16-1] = 0;
219 if (sd_id128_from_string(de
->d_name
+ q
-8-16-1-16-1-32, &seqnum_id
) < 0) {
224 if (sscanf(de
->d_name
+ q
-8-16-1-16, "%16llx-%16llx.journal", &seqnum
, &realtime
) != 2) {
231 } else if (endswith(de
->d_name
, ".journal~")) {
232 unsigned long long tmp
;
234 /* Vacuum corrupted files */
236 if (q
< 1 + 16 + 1 + 16 + 8 + 1) {
241 if (de
->d_name
[q
-1-8-16-1] != '-' ||
242 de
->d_name
[q
-1-8-16-1-16-1] != '@') {
247 p
= strdup(de
->d_name
);
253 if (sscanf(de
->d_name
+ q
-1-8-16-1-16, "%16llx-%16llx.journal~", &realtime
, &tmp
) != 2) {
260 /* We do not vacuum unknown files! */
261 log_debug("Not vacuuming unknown file %s.", de
->d_name
);
265 size
= 512UL * (uint64_t) st
.st_blocks
;
267 r
= journal_file_empty(dirfd(d
), p
);
269 log_debug_errno(r
, "Failed check if %s is empty, ignoring: %m", p
);
273 /* Always vacuum empty non-online files. */
275 if (unlinkat(dirfd(d
), p
, 0) >= 0) {
277 log_full(verbose
? LOG_INFO
: LOG_DEBUG
,
278 "Deleted empty archived journal %s/%s (%s).", directory
, p
, format_bytes(sbytes
, sizeof(sbytes
), size
));
281 } else if (errno
!= ENOENT
)
282 log_warning_errno(errno
, "Failed to delete empty archived journal %s/%s: %m", directory
, p
);
287 patch_realtime(dirfd(d
), p
, &st
, &realtime
);
289 if (!GREEDY_REALLOC(list
, n_allocated
, n_list
+ 1)) {
294 list
[n_list
].filename
= p
;
295 list
[n_list
].usage
= size
;
296 list
[n_list
].seqnum
= seqnum
;
297 list
[n_list
].realtime
= realtime
;
298 list
[n_list
].seqnum_id
= seqnum_id
;
299 list
[n_list
].have_seqnum
= have_seqnum
;
306 qsort_safe(list
, n_list
, sizeof(struct vacuum_info
), vacuum_compare
);
308 for (i
= 0; i
< n_list
; i
++) {
311 left
= n_active_files
+ n_list
- i
;
313 if ((max_retention_usec
<= 0 || list
[i
].realtime
>= retention_limit
) &&
314 (max_use
<= 0 || sum
<= max_use
) &&
315 (n_max_files
<= 0 || left
<= n_max_files
))
318 if (unlinkat(dirfd(d
), list
[i
].filename
, 0) >= 0) {
319 log_full(verbose
? LOG_INFO
: LOG_DEBUG
, "Deleted archived journal %s/%s (%s).", directory
, list
[i
].filename
, format_bytes(sbytes
, sizeof(sbytes
), list
[i
].usage
));
320 freed
+= list
[i
].usage
;
322 if (list
[i
].usage
< sum
)
323 sum
-= list
[i
].usage
;
327 } else if (errno
!= ENOENT
)
328 log_warning_errno(errno
, "Failed to delete archived journal %s/%s: %m", directory
, list
[i
].filename
);
331 if (oldest_usec
&& i
< n_list
&& (*oldest_usec
== 0 || list
[i
].realtime
< *oldest_usec
))
332 *oldest_usec
= list
[i
].realtime
;
337 for (i
= 0; i
< n_list
; i
++)
338 free(list
[i
].filename
);
341 log_full(verbose
? LOG_INFO
: LOG_DEBUG
, "Vacuuming done, freed %s of archived journals on disk.", format_bytes(sbytes
, sizeof(sbytes
), freed
));