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