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