]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journal-vacuum.c
journal: downgrade vaccuum message to debug level
[thirdparty/systemd.git] / src / journal / journal-vacuum.c
CommitLineData
0284adc6
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
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 <sys/types.h>
23#include <fcntl.h>
24#include <sys/stat.h>
25#include <sys/statvfs.h>
26#include <unistd.h>
27
fb0951b0
LP
28#ifdef HAVE_XATTR
29#include <attr/xattr.h>
30#endif
31
0284adc6
LP
32#include "journal-def.h"
33#include "journal-file.h"
34#include "journal-vacuum.h"
35#include "sd-id128.h"
36#include "util.h"
37
38struct vacuum_info {
6c142648 39 uint64_t usage;
0284adc6
LP
40 char *filename;
41
42 uint64_t realtime;
43 sd_id128_t seqnum_id;
44 uint64_t seqnum;
45
46 bool have_seqnum;
47};
48
49static int vacuum_compare(const void *_a, const void *_b) {
50 const struct vacuum_info *a, *b;
51
52 a = _a;
53 b = _b;
54
55 if (a->have_seqnum && b->have_seqnum &&
56 sd_id128_equal(a->seqnum_id, b->seqnum_id)) {
57 if (a->seqnum < b->seqnum)
58 return -1;
59 else if (a->seqnum > b->seqnum)
60 return 1;
61 else
62 return 0;
63 }
64
65 if (a->realtime < b->realtime)
66 return -1;
67 else if (a->realtime > b->realtime)
68 return 1;
69 else if (a->have_seqnum && b->have_seqnum)
70 return memcmp(&a->seqnum_id, &b->seqnum_id, 16);
71 else
72 return strcmp(a->filename, b->filename);
73}
74
fb0951b0
LP
75static void patch_realtime(
76 const char *dir,
77 const char *fn,
78 const struct stat *st,
79 unsigned long long *realtime) {
80
81 usec_t x;
82
83#ifdef HAVE_XATTR
84 uint64_t crtime;
85 _cleanup_free_ const char *path = NULL;
86#endif
87
88 /* The timestamp was determined by the file name, but let's
89 * see if the file might actually be older than the file name
90 * suggested... */
91
92 assert(dir);
93 assert(fn);
94 assert(st);
95 assert(realtime);
96
97 x = timespec_load(&st->st_ctim);
98 if (x > 0 && x != (usec_t) -1 && x < *realtime)
99 *realtime = x;
100
101 x = timespec_load(&st->st_atim);
102 if (x > 0 && x != (usec_t) -1 && x < *realtime)
103 *realtime = x;
104
105 x = timespec_load(&st->st_mtim);
106 if (x > 0 && x != (usec_t) -1 && x < *realtime)
107 *realtime = x;
108
109#ifdef HAVE_XATTR
110 /* Let's read the original creation time, if possible. Ideally
111 * we'd just query the creation time the FS might provide, but
112 * unfortunately there's currently no sane API to query
113 * it. Hence let's implement this manually... */
114
115 /* Unfortunately there is is not fgetxattrat(), so we need to
116 * go via path here. :-( */
117
118 path = strjoin(dir, "/", fn, NULL);
119 if (!path)
120 return;
121
122 if (getxattr(path, "user.crtime_usec", &crtime, sizeof(crtime)) == sizeof(crtime)) {
123 crtime = le64toh(crtime);
124
125 if (crtime > 0 && crtime != (uint64_t) -1 && crtime < *realtime)
126 *realtime = crtime;
127 }
128#endif
129}
130
9d647740 131static int journal_file_empty(int dir_fd, const char *name) {
48979861 132 int r;
9d647740 133 le64_t n_entries;
48979861 134 _cleanup_close_ int fd;
9d647740
ZJS
135
136 fd = openat(dir_fd, name, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK);
137 if (fd < 0)
138 return -errno;
139
140 if (lseek(fd, offsetof(Header, n_entries), SEEK_SET) < 0)
141 return -errno;
142
143 r = read(fd, &n_entries, sizeof(n_entries));
144 if (r != sizeof(n_entries))
145 return r == 0 ? -EINVAL : -errno;
146
147 return le64toh(n_entries) == 0;
148}
149
fb0951b0
LP
150int journal_directory_vacuum(
151 const char *directory,
152 uint64_t max_use,
fb0951b0
LP
153 usec_t max_retention_usec,
154 usec_t *oldest_usec) {
155
30cb029b 156 _cleanup_closedir_ DIR *d = NULL;
0284adc6
LP
157 int r = 0;
158 struct vacuum_info *list = NULL;
30cb029b
ZJS
159 unsigned n_list = 0, i;
160 size_t n_allocated = 0;
289f910e 161 uint64_t sum = 0, freed = 0;
fb0951b0 162 usec_t retention_limit = 0;
0284adc6
LP
163
164 assert(directory);
165
348ced90 166 if (max_use <= 0 && max_retention_usec <= 0)
0284adc6
LP
167 return 0;
168
fb0951b0
LP
169 if (max_retention_usec > 0) {
170 retention_limit = now(CLOCK_REALTIME);
171 if (retention_limit > max_retention_usec)
172 retention_limit -= max_retention_usec;
173 else
174 max_retention_usec = retention_limit = 0;
175 }
176
0284adc6
LP
177 d = opendir(directory);
178 if (!d)
179 return -errno;
180
181 for (;;) {
7d5e9c0f 182 struct dirent *de;
0284adc6
LP
183 size_t q;
184 struct stat st;
185 char *p;
186 unsigned long long seqnum = 0, realtime;
187 sd_id128_t seqnum_id;
188 bool have_seqnum;
189
78a5d04d
FW
190 errno = 0;
191 de = readdir(d);
192 if (!de && errno != 0) {
193 r = -errno;
0284adc6
LP
194 goto finish;
195 }
196
197 if (!de)
198 break;
199
200 if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0)
201 continue;
202
203 if (!S_ISREG(st.st_mode))
204 continue;
205
206 q = strlen(de->d_name);
207
208 if (endswith(de->d_name, ".journal")) {
209
210 /* Vacuum archived files */
211
212 if (q < 1 + 32 + 1 + 16 + 1 + 16 + 8)
213 continue;
214
215 if (de->d_name[q-8-16-1] != '-' ||
216 de->d_name[q-8-16-1-16-1] != '-' ||
217 de->d_name[q-8-16-1-16-1-32-1] != '@')
218 continue;
219
220 p = strdup(de->d_name);
221 if (!p) {
222 r = -ENOMEM;
223 goto finish;
224 }
225
226 de->d_name[q-8-16-1-16-1] = 0;
227 if (sd_id128_from_string(de->d_name + q-8-16-1-16-1-32, &seqnum_id) < 0) {
228 free(p);
229 continue;
230 }
231
232 if (sscanf(de->d_name + q-8-16-1-16, "%16llx-%16llx.journal", &seqnum, &realtime) != 2) {
233 free(p);
234 continue;
235 }
236
237 have_seqnum = true;
238
239 } else if (endswith(de->d_name, ".journal~")) {
240 unsigned long long tmp;
241
242 /* Vacuum corrupted files */
243
244 if (q < 1 + 16 + 1 + 16 + 8 + 1)
245 continue;
246
247 if (de->d_name[q-1-8-16-1] != '-' ||
248 de->d_name[q-1-8-16-1-16-1] != '@')
249 continue;
250
251 p = strdup(de->d_name);
252 if (!p) {
253 r = -ENOMEM;
254 goto finish;
255 }
256
257 if (sscanf(de->d_name + q-1-8-16-1-16, "%16llx-%16llx.journal~", &realtime, &tmp) != 2) {
258 free(p);
259 continue;
260 }
261
262 have_seqnum = false;
263 } else
4fa25d62 264 /* We do not vacuum active files or unknown files! */
0284adc6
LP
265 continue;
266
629bfc5a 267 if (journal_file_empty(dirfd(d), p)) {
9d647740
ZJS
268 /* Always vacuum empty non-online files. */
269
289f910e
ZJS
270 uint64_t size = 512UL * (uint64_t) st.st_blocks;
271
272 if (unlinkat(dirfd(d), p, 0) >= 0) {
273 log_info("Deleted empty journal %s/%s (%"PRIu64" bytes).",
274 directory, p, size);
275 freed += size;
276 } else if (errno != ENOENT)
629bfc5a 277 log_warning("Failed to delete %s/%s: %m", directory, p);
289f910e 278
2ee0591d
LP
279 free(p);
280
9d647740
ZJS
281 continue;
282 }
283
629bfc5a 284 patch_realtime(directory, p, &st, &realtime);
fb0951b0 285
30cb029b 286 GREEDY_REALLOC(list, n_allocated, n_list + 1);
0284adc6
LP
287
288 list[n_list].filename = p;
289 list[n_list].usage = 512UL * (uint64_t) st.st_blocks;
290 list[n_list].seqnum = seqnum;
291 list[n_list].realtime = realtime;
292 list[n_list].seqnum_id = seqnum_id;
293 list[n_list].have_seqnum = have_seqnum;
294
295 sum += list[n_list].usage;
296
297 n_list ++;
298 }
299
7ff7394d 300 qsort_safe(list, n_list, sizeof(struct vacuum_info), vacuum_compare);
0284adc6 301
fb0951b0 302 for (i = 0; i < n_list; i++) {
0284adc6
LP
303 struct statvfs ss;
304
305 if (fstatvfs(dirfd(d), &ss) < 0) {
306 r = -errno;
307 goto finish;
308 }
309
fb0951b0 310 if ((max_retention_usec <= 0 || list[i].realtime >= retention_limit) &&
348ced90 311 (max_use <= 0 || sum <= max_use))
0284adc6
LP
312 break;
313
314 if (unlinkat(dirfd(d), list[i].filename, 0) >= 0) {
289f910e
ZJS
315 log_debug("Deleted archived journal %s/%s (%"PRIu64" bytes).",
316 directory, list[i].filename, list[i].usage);
317 freed += list[i].usage;
4fa25d62 318
6c142648 319 if (list[i].usage < sum)
4fa25d62
LP
320 sum -= list[i].usage;
321 else
322 sum = 0;
323
0284adc6
LP
324 } else if (errno != ENOENT)
325 log_warning("Failed to delete %s/%s: %m", directory, list[i].filename);
326 }
327
fb0951b0
LP
328 if (oldest_usec && i < n_list && (*oldest_usec == 0 || list[i].realtime < *oldest_usec))
329 *oldest_usec = list[i].realtime;
330
0284adc6
LP
331finish:
332 for (i = 0; i < n_list; i++)
333 free(list[i].filename);
0284adc6
LP
334 free(list);
335
3bb621e1 336 log_debug("Vacuuming done, freed %"PRIu64" bytes", freed);
289f910e 337
0284adc6
LP
338 return r;
339}