1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
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.
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.
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/>.
22 #include <sys/statvfs.h>
26 #include "string-util.h"
27 #include "time-util.h"
29 #include "coredump-vacuum.h"
31 #define DEFAULT_MAX_USE_LOWER (uint64_t) (1ULL*1024ULL*1024ULL) /* 1 MiB */
32 #define DEFAULT_MAX_USE_UPPER (uint64_t) (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */
33 #define DEFAULT_KEEP_FREE_UPPER (uint64_t) (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */
34 #define DEFAULT_KEEP_FREE (uint64_t) (1024ULL*1024ULL) /* 1 MB */
36 struct vacuum_candidate
{
42 static void vacuum_candidate_free(struct vacuum_candidate
*c
) {
50 DEFINE_TRIVIAL_CLEANUP_FUNC(struct vacuum_candidate
*, vacuum_candidate_free
);
52 static void vacuum_candidate_hasmap_free(Hashmap
*h
) {
53 struct vacuum_candidate
*c
;
55 while ((c
= hashmap_steal_first(h
)))
56 vacuum_candidate_free(c
);
61 DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap
*, vacuum_candidate_hasmap_free
);
63 static int uid_from_file_name(const char *filename
, uid_t
*uid
) {
64 const char *p
, *e
, *u
;
66 p
= startswith(filename
, "core.");
70 /* Skip the comm field */
82 return parse_uid(u
, uid
);
85 static bool vacuum_necessary(int fd
, uint64_t sum
, uint64_t keep_free
, uint64_t max_use
) {
86 uint64_t fs_size
= 0, fs_free
= (uint64_t) -1;
91 if (fstatvfs(fd
, &sv
) >= 0) {
92 fs_size
= sv
.f_frsize
* sv
.f_blocks
;
93 fs_free
= sv
.f_frsize
* sv
.f_bfree
;
96 if (max_use
== (uint64_t) -1) {
99 max_use
= PAGE_ALIGN(fs_size
/ 10); /* 10% */
101 if (max_use
> DEFAULT_MAX_USE_UPPER
)
102 max_use
= DEFAULT_MAX_USE_UPPER
;
104 if (max_use
< DEFAULT_MAX_USE_LOWER
)
105 max_use
= DEFAULT_MAX_USE_LOWER
;
107 max_use
= DEFAULT_MAX_USE_LOWER
;
109 max_use
= PAGE_ALIGN(max_use
);
111 if (max_use
> 0 && sum
> max_use
)
114 if (keep_free
== (uint64_t) -1) {
117 keep_free
= PAGE_ALIGN((fs_size
* 3) / 20); /* 15% */
119 if (keep_free
> DEFAULT_KEEP_FREE_UPPER
)
120 keep_free
= DEFAULT_KEEP_FREE_UPPER
;
122 keep_free
= DEFAULT_KEEP_FREE
;
124 keep_free
= PAGE_ALIGN(keep_free
);
126 if (keep_free
> 0 && fs_free
< keep_free
)
132 int coredump_vacuum(int exclude_fd
, uint64_t keep_free
, uint64_t max_use
) {
133 _cleanup_closedir_
DIR *d
= NULL
;
134 struct stat exclude_st
;
137 if (keep_free
== 0 && max_use
== 0)
140 if (exclude_fd
>= 0) {
141 if (fstat(exclude_fd
, &exclude_st
) < 0)
142 return log_error_errno(errno
, "Failed to fstat(): %m");
145 /* This algorithm will keep deleting the oldest file of the
146 * user with the most coredumps until we are back in the size
147 * limits. Note that vacuuming for journal files is different,
148 * because we rely on rate-limiting of the messages there,
149 * to avoid being flooded. */
151 d
= opendir("/var/lib/systemd/coredump");
156 log_error_errno(errno
, "Can't open coredump directory: %m");
161 _cleanup_(vacuum_candidate_hasmap_freep
) Hashmap
*h
= NULL
;
162 struct vacuum_candidate
*worst
= NULL
;
168 FOREACH_DIRENT(de
, d
, goto fail
) {
169 struct vacuum_candidate
*c
;
174 r
= uid_from_file_name(de
->d_name
, &uid
);
178 if (fstatat(dirfd(d
), de
->d_name
, &st
, AT_NO_AUTOMOUNT
|AT_SYMLINK_NOFOLLOW
) < 0) {
182 log_warning("Failed to stat /var/lib/systemd/coredump/%s", de
->d_name
);
186 if (!S_ISREG(st
.st_mode
))
189 if (exclude_fd
>= 0 &&
190 exclude_st
.st_dev
== st
.st_dev
&&
191 exclude_st
.st_ino
== st
.st_ino
)
194 r
= hashmap_ensure_allocated(&h
, NULL
);
198 t
= timespec_load(&st
.st_mtim
);
200 c
= hashmap_get(h
, UINT32_TO_PTR(uid
));
203 if (t
< c
->oldest_mtime
) {
206 n
= strdup(de
->d_name
);
210 free(c
->oldest_file
);
216 _cleanup_(vacuum_candidate_freep
) struct vacuum_candidate
*n
= NULL
;
218 n
= new0(struct vacuum_candidate
, 1);
222 n
->oldest_file
= strdup(de
->d_name
);
228 r
= hashmap_put(h
, UINT32_TO_PTR(uid
), n
);
239 worst
->n_files
< c
->n_files
||
240 (worst
->n_files
== c
->n_files
&& c
->oldest_mtime
< worst
->oldest_mtime
))
243 sum
+= st
.st_blocks
* 512;
249 r
= vacuum_necessary(dirfd(d
), sum
, keep_free
, max_use
);
253 if (unlinkat(dirfd(d
), worst
->oldest_file
, 0) < 0) {
258 log_error_errno(errno
, "Failed to remove file %s: %m", worst
->oldest_file
);
261 log_info("Removed old coredump %s.", worst
->oldest_file
);
267 log_error_errno(errno
, "Failed to read directory: %m");