]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/coredump/coredump-vacuum.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / coredump / coredump-vacuum.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
0dc5d23c
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2014 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
21#include <sys/statvfs.h>
22
b5efdb8a 23#include "alloc-util.h"
3ffd4af2 24#include "coredump-vacuum.h"
a0956174 25#include "dirent-util.h"
3ffd4af2 26#include "fd-util.h"
0dc5d23c
LP
27#include "hashmap.h"
28#include "macro.h"
07630cea
LP
29#include "string-util.h"
30#include "time-util.h"
b1d4f8e1 31#include "user-util.h"
07630cea 32#include "util.h"
0dc5d23c 33
59f448cf
LP
34#define DEFAULT_MAX_USE_LOWER (uint64_t) (1ULL*1024ULL*1024ULL) /* 1 MiB */
35#define DEFAULT_MAX_USE_UPPER (uint64_t) (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */
36#define DEFAULT_KEEP_FREE_UPPER (uint64_t) (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */
37#define DEFAULT_KEEP_FREE (uint64_t) (1024ULL*1024ULL) /* 1 MB */
0dc5d23c
LP
38
39struct vacuum_candidate {
40 unsigned n_files;
41 char *oldest_file;
42 usec_t oldest_mtime;
43};
44
45static void vacuum_candidate_free(struct vacuum_candidate *c) {
46 if (!c)
47 return;
48
49 free(c->oldest_file);
50 free(c);
51}
52
53DEFINE_TRIVIAL_CLEANUP_FUNC(struct vacuum_candidate*, vacuum_candidate_free);
54
55static void vacuum_candidate_hasmap_free(Hashmap *h) {
56 struct vacuum_candidate *c;
57
58 while ((c = hashmap_steal_first(h)))
59 vacuum_candidate_free(c);
60
61 hashmap_free(h);
62}
63
64DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, vacuum_candidate_hasmap_free);
65
66static int uid_from_file_name(const char *filename, uid_t *uid) {
67 const char *p, *e, *u;
68
69 p = startswith(filename, "core.");
70 if (!p)
71 return -EINVAL;
72
73 /* Skip the comm field */
74 p = strchr(p, '.');
75 if (!p)
76 return -EINVAL;
77 p++;
78
79 /* Find end up UID */
80 e = strchr(p, '.');
81 if (!e)
82 return -EINVAL;
83
84 u = strndupa(p, e-p);
85 return parse_uid(u, uid);
86}
87
59f448cf
LP
88static bool vacuum_necessary(int fd, uint64_t sum, uint64_t keep_free, uint64_t max_use) {
89 uint64_t fs_size = 0, fs_free = (uint64_t) -1;
0dc5d23c
LP
90 struct statvfs sv;
91
92 assert(fd >= 0);
93
94 if (fstatvfs(fd, &sv) >= 0) {
95 fs_size = sv.f_frsize * sv.f_blocks;
96 fs_free = sv.f_frsize * sv.f_bfree;
97 }
98
59f448cf 99 if (max_use == (uint64_t) -1) {
0dc5d23c
LP
100
101 if (fs_size > 0) {
102 max_use = PAGE_ALIGN(fs_size / 10); /* 10% */
103
104 if (max_use > DEFAULT_MAX_USE_UPPER)
105 max_use = DEFAULT_MAX_USE_UPPER;
106
107 if (max_use < DEFAULT_MAX_USE_LOWER)
108 max_use = DEFAULT_MAX_USE_LOWER;
5470c03b 109 } else
cd4ba18a 110 max_use = DEFAULT_MAX_USE_LOWER;
0dc5d23c
LP
111 } else
112 max_use = PAGE_ALIGN(max_use);
113
114 if (max_use > 0 && sum > max_use)
115 return true;
116
59f448cf 117 if (keep_free == (uint64_t) -1) {
0dc5d23c
LP
118
119 if (fs_size > 0) {
120 keep_free = PAGE_ALIGN((fs_size * 3) / 20); /* 15% */
121
122 if (keep_free > DEFAULT_KEEP_FREE_UPPER)
123 keep_free = DEFAULT_KEEP_FREE_UPPER;
124 } else
125 keep_free = DEFAULT_KEEP_FREE;
126 } else
127 keep_free = PAGE_ALIGN(keep_free);
128
129 if (keep_free > 0 && fs_free < keep_free)
130 return true;
131
132 return false;
133}
134
59f448cf 135int coredump_vacuum(int exclude_fd, uint64_t keep_free, uint64_t max_use) {
0dc5d23c
LP
136 _cleanup_closedir_ DIR *d = NULL;
137 struct stat exclude_st;
138 int r;
139
5470c03b 140 if (keep_free == 0 && max_use == 0)
0dc5d23c
LP
141 return 0;
142
143 if (exclude_fd >= 0) {
4a62c710
MS
144 if (fstat(exclude_fd, &exclude_st) < 0)
145 return log_error_errno(errno, "Failed to fstat(): %m");
0dc5d23c
LP
146 }
147
148 /* This algorithm will keep deleting the oldest file of the
149 * user with the most coredumps until we are back in the size
150 * limits. Note that vacuuming for journal files is different,
151 * because we rely on rate-limiting of the messages there,
152 * to avoid being flooded. */
153
154 d = opendir("/var/lib/systemd/coredump");
155 if (!d) {
156 if (errno == ENOENT)
157 return 0;
158
c292d9e9 159 return log_error_errno(errno, "Can't open coredump directory: %m");
0dc5d23c
LP
160 }
161
162 for (;;) {
163 _cleanup_(vacuum_candidate_hasmap_freep) Hashmap *h = NULL;
164 struct vacuum_candidate *worst = NULL;
165 struct dirent *de;
59f448cf 166 uint64_t sum = 0;
0dc5d23c
LP
167
168 rewinddir(d);
169
170 FOREACH_DIRENT(de, d, goto fail) {
171 struct vacuum_candidate *c;
172 struct stat st;
173 uid_t uid;
174 usec_t t;
175
176 r = uid_from_file_name(de->d_name, &uid);
177 if (r < 0)
178 continue;
179
180 if (fstatat(dirfd(d), de->d_name, &st, AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW) < 0) {
181 if (errno == ENOENT)
182 continue;
183
c292d9e9 184 log_warning_errno(errno, "Failed to stat /var/lib/systemd/coredump/%s: %m", de->d_name);
0dc5d23c
LP
185 continue;
186 }
187
188 if (!S_ISREG(st.st_mode))
189 continue;
190
191 if (exclude_fd >= 0 &&
192 exclude_st.st_dev == st.st_dev &&
193 exclude_st.st_ino == st.st_ino)
194 continue;
195
d5099efc 196 r = hashmap_ensure_allocated(&h, NULL);
0dc5d23c
LP
197 if (r < 0)
198 return log_oom();
199
200 t = timespec_load(&st.st_mtim);
201
4a0b58c4 202 c = hashmap_get(h, UID_TO_PTR(uid));
0dc5d23c
LP
203 if (c) {
204
205 if (t < c->oldest_mtime) {
206 char *n;
207
208 n = strdup(de->d_name);
209 if (!n)
210 return log_oom();
211
212 free(c->oldest_file);
213 c->oldest_file = n;
214 c->oldest_mtime = t;
215 }
216
217 } else {
218 _cleanup_(vacuum_candidate_freep) struct vacuum_candidate *n = NULL;
219
220 n = new0(struct vacuum_candidate, 1);
221 if (!n)
222 return log_oom();
223
224 n->oldest_file = strdup(de->d_name);
225 if (!n->oldest_file)
226 return log_oom();
227
228 n->oldest_mtime = t;
229
4a0b58c4 230 r = hashmap_put(h, UID_TO_PTR(uid), n);
0dc5d23c
LP
231 if (r < 0)
232 return log_oom();
233
234 c = n;
235 n = NULL;
236 }
237
238 c->n_files++;
239
240 if (!worst ||
241 worst->n_files < c->n_files ||
242 (worst->n_files == c->n_files && c->oldest_mtime < worst->oldest_mtime))
243 worst = c;
244
245 sum += st.st_blocks * 512;
246 }
247
248 if (!worst)
249 break;
250
251 r = vacuum_necessary(dirfd(d), sum, keep_free, max_use);
252 if (r <= 0)
253 return r;
254
255 if (unlinkat(dirfd(d), worst->oldest_file, 0) < 0) {
256
257 if (errno == ENOENT)
258 continue;
259
c292d9e9 260 return log_error_errno(errno, "Failed to remove file %s: %m", worst->oldest_file);
0dc5d23c
LP
261 } else
262 log_info("Removed old coredump %s.", worst->oldest_file);
263 }
264
265 return 0;
266
267fail:
c292d9e9 268 return log_error_errno(errno, "Failed to read directory: %m");
0dc5d23c 269}