]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journal-vacuum.c
libudev: Use correct type for sizeof
[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
131int journal_directory_vacuum(
132 const char *directory,
133 uint64_t max_use,
134 uint64_t min_free,
135 usec_t max_retention_usec,
136 usec_t *oldest_usec) {
137
0284adc6
LP
138 DIR *d;
139 int r = 0;
140 struct vacuum_info *list = NULL;
141 unsigned n_list = 0, n_allocated = 0, i;
142 uint64_t sum = 0;
fb0951b0 143 usec_t retention_limit = 0;
0284adc6
LP
144
145 assert(directory);
146
fb0951b0 147 if (max_use <= 0 && min_free <= 0 && max_retention_usec <= 0)
0284adc6
LP
148 return 0;
149
fb0951b0
LP
150 if (max_retention_usec > 0) {
151 retention_limit = now(CLOCK_REALTIME);
152 if (retention_limit > max_retention_usec)
153 retention_limit -= max_retention_usec;
154 else
155 max_retention_usec = retention_limit = 0;
156 }
157
0284adc6
LP
158 d = opendir(directory);
159 if (!d)
160 return -errno;
161
162 for (;;) {
163 int k;
7d5e9c0f
LP
164 struct dirent *de;
165 union dirent_storage buf;
0284adc6
LP
166 size_t q;
167 struct stat st;
168 char *p;
169 unsigned long long seqnum = 0, realtime;
170 sd_id128_t seqnum_id;
171 bool have_seqnum;
172
7d5e9c0f 173 k = readdir_r(d, &buf.de, &de);
0284adc6
LP
174 if (k != 0) {
175 r = -k;
176 goto finish;
177 }
178
179 if (!de)
180 break;
181
182 if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0)
183 continue;
184
185 if (!S_ISREG(st.st_mode))
186 continue;
187
188 q = strlen(de->d_name);
189
190 if (endswith(de->d_name, ".journal")) {
191
192 /* Vacuum archived files */
193
194 if (q < 1 + 32 + 1 + 16 + 1 + 16 + 8)
195 continue;
196
197 if (de->d_name[q-8-16-1] != '-' ||
198 de->d_name[q-8-16-1-16-1] != '-' ||
199 de->d_name[q-8-16-1-16-1-32-1] != '@')
200 continue;
201
202 p = strdup(de->d_name);
203 if (!p) {
204 r = -ENOMEM;
205 goto finish;
206 }
207
208 de->d_name[q-8-16-1-16-1] = 0;
209 if (sd_id128_from_string(de->d_name + q-8-16-1-16-1-32, &seqnum_id) < 0) {
210 free(p);
211 continue;
212 }
213
214 if (sscanf(de->d_name + q-8-16-1-16, "%16llx-%16llx.journal", &seqnum, &realtime) != 2) {
215 free(p);
216 continue;
217 }
218
219 have_seqnum = true;
220
221 } else if (endswith(de->d_name, ".journal~")) {
222 unsigned long long tmp;
223
224 /* Vacuum corrupted files */
225
226 if (q < 1 + 16 + 1 + 16 + 8 + 1)
227 continue;
228
229 if (de->d_name[q-1-8-16-1] != '-' ||
230 de->d_name[q-1-8-16-1-16-1] != '@')
231 continue;
232
233 p = strdup(de->d_name);
234 if (!p) {
235 r = -ENOMEM;
236 goto finish;
237 }
238
239 if (sscanf(de->d_name + q-1-8-16-1-16, "%16llx-%16llx.journal~", &realtime, &tmp) != 2) {
240 free(p);
241 continue;
242 }
243
244 have_seqnum = false;
245 } else
4fa25d62 246 /* We do not vacuum active files or unknown files! */
0284adc6
LP
247 continue;
248
fb0951b0
LP
249 patch_realtime(directory, de->d_name, &st, &realtime);
250
0284adc6
LP
251 if (n_list >= n_allocated) {
252 struct vacuum_info *j;
253
254 n_allocated = MAX(n_allocated * 2U, 8U);
255 j = realloc(list, n_allocated * sizeof(struct vacuum_info));
256 if (!j) {
257 free(p);
258 r = -ENOMEM;
259 goto finish;
260 }
261
262 list = j;
263 }
264
265 list[n_list].filename = p;
266 list[n_list].usage = 512UL * (uint64_t) st.st_blocks;
267 list[n_list].seqnum = seqnum;
268 list[n_list].realtime = realtime;
269 list[n_list].seqnum_id = seqnum_id;
270 list[n_list].have_seqnum = have_seqnum;
271
272 sum += list[n_list].usage;
273
274 n_list ++;
275 }
276
277 if (n_list > 0)
278 qsort(list, n_list, sizeof(struct vacuum_info), vacuum_compare);
279
fb0951b0 280 for (i = 0; i < n_list; i++) {
0284adc6
LP
281 struct statvfs ss;
282
283 if (fstatvfs(dirfd(d), &ss) < 0) {
284 r = -errno;
285 goto finish;
286 }
287
fb0951b0
LP
288 if ((max_retention_usec <= 0 || list[i].realtime >= retention_limit) &&
289 (max_use <= 0 || sum <= max_use) &&
290 (min_free <= 0 || (uint64_t) ss.f_bavail * (uint64_t) ss.f_bsize >= min_free))
0284adc6
LP
291 break;
292
293 if (unlinkat(dirfd(d), list[i].filename, 0) >= 0) {
2b43f939 294 log_debug("Deleted archived journal %s/%s.", directory, list[i].filename);
4fa25d62 295
6c142648 296 if (list[i].usage < sum)
4fa25d62
LP
297 sum -= list[i].usage;
298 else
299 sum = 0;
300
0284adc6
LP
301 } else if (errno != ENOENT)
302 log_warning("Failed to delete %s/%s: %m", directory, list[i].filename);
303 }
304
fb0951b0
LP
305 if (oldest_usec && i < n_list && (*oldest_usec == 0 || list[i].realtime < *oldest_usec))
306 *oldest_usec = list[i].realtime;
307
0284adc6
LP
308finish:
309 for (i = 0; i < n_list; i++)
310 free(list[i].filename);
311
312 free(list);
313
314 if (d)
315 closedir(d);
316
317 return r;
318}