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/>.
28 #include "alloc-util.h"
29 #include "dirent-util.h"
31 #include "journal-def.h"
32 #include "journal-file.h"
33 #include "journal-vacuum.h"
34 #include "parse-util.h"
35 #include "string-util.h"
37 #include "xattr-util.h"
50 static int vacuum_compare(const void *_a
, const void *_b
) {
51 const struct vacuum_info
*a
, *b
;
56 if (a
->have_seqnum
&& b
->have_seqnum
&&
57 sd_id128_equal(a
->seqnum_id
, b
->seqnum_id
)) {
58 if (a
->seqnum
< b
->seqnum
)
60 else if (a
->seqnum
> b
->seqnum
)
66 if (a
->realtime
< b
->realtime
)
68 else if (a
->realtime
> b
->realtime
)
70 else if (a
->have_seqnum
&& b
->have_seqnum
)
71 return memcmp(&a
->seqnum_id
, &b
->seqnum_id
, 16);
73 return strcmp(a
->filename
, b
->filename
);
76 static void patch_realtime(
79 const struct stat
*st
,
80 unsigned long long *realtime
) {
84 /* The timestamp was determined by the file name, but let's
85 * see if the file might actually be older than the file name
93 x
= timespec_load(&st
->st_ctim
);
94 if (x
> 0 && x
!= USEC_INFINITY
&& x
< *realtime
)
97 x
= timespec_load(&st
->st_atim
);
98 if (x
> 0 && x
!= USEC_INFINITY
&& x
< *realtime
)
101 x
= timespec_load(&st
->st_mtim
);
102 if (x
> 0 && x
!= USEC_INFINITY
&& x
< *realtime
)
105 /* Let's read the original creation time, if possible. Ideally
106 * we'd just query the creation time the FS might provide, but
107 * unfortunately there's currently no sane API to query
108 * it. Hence let's implement this manually... */
110 if (fd_getcrtime_at(fd
, fn
, &crtime
, 0) >= 0) {
111 if (crtime
< *realtime
)
116 static int journal_file_empty(int dir_fd
, const char *name
) {
117 _cleanup_close_
int fd
;
122 fd
= openat(dir_fd
, name
, O_RDONLY
|O_CLOEXEC
|O_NOFOLLOW
|O_NONBLOCK
|O_NOATIME
);
124 /* Maybe failed due to O_NOATIME and lack of privileges? */
125 fd
= openat(dir_fd
, name
, O_RDONLY
|O_CLOEXEC
|O_NOFOLLOW
|O_NONBLOCK
);
130 if (fstat(fd
, &st
) < 0)
133 /* If an offline file doesn't even have a header we consider it empty */
134 if (st
.st_size
< (off_t
) sizeof(Header
))
137 /* If the number of entries is empty, we consider it empty, too */
138 n
= pread(fd
, &n_entries
, sizeof(n_entries
), offsetof(Header
, n_entries
));
141 if (n
!= sizeof(n_entries
))
144 return le64toh(n_entries
) <= 0;
147 int journal_directory_vacuum(
148 const char *directory
,
150 uint64_t n_max_files
,
151 usec_t max_retention_usec
,
155 _cleanup_closedir_
DIR *d
= NULL
;
156 struct vacuum_info
*list
= NULL
;
157 unsigned n_list
= 0, i
, n_active_files
= 0;
158 size_t n_allocated
= 0;
159 uint64_t sum
= 0, freed
= 0;
160 usec_t retention_limit
= 0;
161 char sbytes
[FORMAT_BYTES_MAX
];
167 if (max_use
<= 0 && max_retention_usec
<= 0 && n_max_files
<= 0)
170 if (max_retention_usec
> 0) {
171 retention_limit
= now(CLOCK_REALTIME
);
172 if (retention_limit
> max_retention_usec
)
173 retention_limit
-= max_retention_usec
;
175 max_retention_usec
= retention_limit
= 0;
178 d
= opendir(directory
);
182 FOREACH_DIRENT_ALL(de
, d
, r
= -errno
; goto finish
) {
184 unsigned long long seqnum
= 0, realtime
;
185 _cleanup_free_
char *p
= NULL
;
186 sd_id128_t seqnum_id
;
192 if (fstatat(dirfd(d
), de
->d_name
, &st
, AT_SYMLINK_NOFOLLOW
) < 0) {
193 log_debug_errno(errno
, "Failed to stat file %s while vacuuming, ignoring: %m", de
->d_name
);
197 if (!S_ISREG(st
.st_mode
))
200 q
= strlen(de
->d_name
);
202 if (endswith(de
->d_name
, ".journal")) {
204 /* Vacuum archived files. Active files are
207 if (q
< 1 + 32 + 1 + 16 + 1 + 16 + 8) {
212 if (de
->d_name
[q
-8-16-1] != '-' ||
213 de
->d_name
[q
-8-16-1-16-1] != '-' ||
214 de
->d_name
[q
-8-16-1-16-1-32-1] != '@') {
219 p
= strdup(de
->d_name
);
225 de
->d_name
[q
-8-16-1-16-1] = 0;
226 if (sd_id128_from_string(de
->d_name
+ q
-8-16-1-16-1-32, &seqnum_id
) < 0) {
231 if (sscanf(de
->d_name
+ q
-8-16-1-16, "%16llx-%16llx.journal", &seqnum
, &realtime
) != 2) {
238 } else if (endswith(de
->d_name
, ".journal~")) {
239 unsigned long long tmp
;
241 /* Vacuum corrupted files */
243 if (q
< 1 + 16 + 1 + 16 + 8 + 1) {
248 if (de
->d_name
[q
-1-8-16-1] != '-' ||
249 de
->d_name
[q
-1-8-16-1-16-1] != '@') {
254 p
= strdup(de
->d_name
);
260 if (sscanf(de
->d_name
+ q
-1-8-16-1-16, "%16llx-%16llx.journal~", &realtime
, &tmp
) != 2) {
267 /* We do not vacuum unknown files! */
268 log_debug("Not vacuuming unknown file %s.", de
->d_name
);
272 size
= 512UL * (uint64_t) st
.st_blocks
;
274 r
= journal_file_empty(dirfd(d
), p
);
276 log_debug_errno(r
, "Failed check if %s is empty, ignoring: %m", p
);
280 /* Always vacuum empty non-online files. */
282 if (unlinkat(dirfd(d
), p
, 0) >= 0) {
284 log_full(verbose
? LOG_INFO
: LOG_DEBUG
,
285 "Deleted empty archived journal %s/%s (%s).", directory
, p
, format_bytes(sbytes
, sizeof(sbytes
), size
));
288 } else if (errno
!= ENOENT
)
289 log_warning_errno(errno
, "Failed to delete empty archived journal %s/%s: %m", directory
, p
);
294 patch_realtime(dirfd(d
), p
, &st
, &realtime
);
296 if (!GREEDY_REALLOC(list
, n_allocated
, n_list
+ 1)) {
301 list
[n_list
].filename
= p
;
302 list
[n_list
].usage
= size
;
303 list
[n_list
].seqnum
= seqnum
;
304 list
[n_list
].realtime
= realtime
;
305 list
[n_list
].seqnum_id
= seqnum_id
;
306 list
[n_list
].have_seqnum
= have_seqnum
;
313 qsort_safe(list
, n_list
, sizeof(struct vacuum_info
), vacuum_compare
);
315 for (i
= 0; i
< n_list
; i
++) {
318 left
= n_active_files
+ n_list
- i
;
320 if ((max_retention_usec
<= 0 || list
[i
].realtime
>= retention_limit
) &&
321 (max_use
<= 0 || sum
<= max_use
) &&
322 (n_max_files
<= 0 || left
<= n_max_files
))
325 if (unlinkat(dirfd(d
), list
[i
].filename
, 0) >= 0) {
326 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
));
327 freed
+= list
[i
].usage
;
329 if (list
[i
].usage
< sum
)
330 sum
-= list
[i
].usage
;
334 } else if (errno
!= ENOENT
)
335 log_warning_errno(errno
, "Failed to delete archived journal %s/%s: %m", directory
, list
[i
].filename
);
338 if (oldest_usec
&& i
< n_list
&& (*oldest_usec
== 0 || list
[i
].realtime
< *oldest_usec
))
339 *oldest_usec
= list
[i
].realtime
;
344 for (i
= 0; i
< n_list
; i
++)
345 free(list
[i
].filename
);
348 log_full(verbose
? LOG_INFO
: LOG_DEBUG
, "Vacuuming done, freed %s of archived journals on disk.", format_bytes(sbytes
, sizeof(sbytes
), freed
));