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