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