1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include <sys/statvfs.h>
5 #include "alloc-util.h"
6 #include "coredump-vacuum.h"
7 #include "dirent-util.h"
12 #include "string-util.h"
13 #include "time-util.h"
14 #include "user-util.h"
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 */
22 struct vacuum_candidate
{
28 static void vacuum_candidate_free(struct vacuum_candidate
*c
) {
36 DEFINE_TRIVIAL_CLEANUP_FUNC(struct vacuum_candidate
*, vacuum_candidate_free
);
38 static void vacuum_candidate_hashmap_free(Hashmap
*h
) {
39 hashmap_free_with_destructor(h
, vacuum_candidate_free
);
42 DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap
*, vacuum_candidate_hashmap_free
);
44 static int uid_from_file_name(const char *filename
, uid_t
*uid
) {
45 const char *p
, *e
, *u
;
47 p
= startswith(filename
, "core.");
51 /* Skip the comm field */
63 return parse_uid(u
, uid
);
66 static 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;
72 if (fstatvfs(fd
, &sv
) >= 0) {
73 fs_size
= sv
.f_frsize
* sv
.f_blocks
;
74 fs_free
= sv
.f_frsize
* sv
.f_bfree
;
77 if (max_use
== (uint64_t) -1) {
80 max_use
= PAGE_ALIGN(fs_size
/ 10); /* 10% */
82 if (max_use
> DEFAULT_MAX_USE_UPPER
)
83 max_use
= DEFAULT_MAX_USE_UPPER
;
85 if (max_use
< DEFAULT_MAX_USE_LOWER
)
86 max_use
= DEFAULT_MAX_USE_LOWER
;
88 max_use
= DEFAULT_MAX_USE_LOWER
;
90 max_use
= PAGE_ALIGN(max_use
);
92 if (max_use
> 0 && sum
> max_use
)
95 if (keep_free
== (uint64_t) -1) {
98 keep_free
= PAGE_ALIGN((fs_size
* 3) / 20); /* 15% */
100 if (keep_free
> DEFAULT_KEEP_FREE_UPPER
)
101 keep_free
= DEFAULT_KEEP_FREE_UPPER
;
103 keep_free
= DEFAULT_KEEP_FREE
;
105 keep_free
= PAGE_ALIGN(keep_free
);
107 if (keep_free
> 0 && fs_free
< keep_free
)
113 int coredump_vacuum(int exclude_fd
, uint64_t keep_free
, uint64_t max_use
) {
114 _cleanup_closedir_
DIR *d
= NULL
;
115 struct stat exclude_st
;
118 if (keep_free
== 0 && max_use
== 0)
121 if (exclude_fd
>= 0) {
122 if (fstat(exclude_fd
, &exclude_st
) < 0)
123 return log_error_errno(errno
, "Failed to fstat(): %m");
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. */
132 d
= opendir("/var/lib/systemd/coredump");
137 return log_error_errno(errno
, "Can't open coredump directory: %m");
141 _cleanup_(vacuum_candidate_hashmap_freep
) Hashmap
*h
= NULL
;
142 struct vacuum_candidate
*worst
= NULL
;
148 FOREACH_DIRENT(de
, d
, goto fail
) {
149 struct vacuum_candidate
*c
;
154 r
= uid_from_file_name(de
->d_name
, &uid
);
158 if (fstatat(dirfd(d
), de
->d_name
, &st
, AT_NO_AUTOMOUNT
|AT_SYMLINK_NOFOLLOW
) < 0) {
162 log_warning_errno(errno
, "Failed to stat /var/lib/systemd/coredump/%s: %m", de
->d_name
);
166 if (!S_ISREG(st
.st_mode
))
169 if (exclude_fd
>= 0 &&
170 exclude_st
.st_dev
== st
.st_dev
&&
171 exclude_st
.st_ino
== st
.st_ino
)
174 r
= hashmap_ensure_allocated(&h
, NULL
);
178 t
= timespec_load(&st
.st_mtim
);
180 c
= hashmap_get(h
, UID_TO_PTR(uid
));
183 if (t
< c
->oldest_mtime
) {
186 n
= strdup(de
->d_name
);
190 free(c
->oldest_file
);
196 _cleanup_(vacuum_candidate_freep
) struct vacuum_candidate
*n
= NULL
;
198 n
= new0(struct vacuum_candidate
, 1);
202 n
->oldest_file
= strdup(de
->d_name
);
208 r
= hashmap_put(h
, UID_TO_PTR(uid
), n
);
218 worst
->n_files
< c
->n_files
||
219 (worst
->n_files
== c
->n_files
&& c
->oldest_mtime
< worst
->oldest_mtime
))
222 sum
+= st
.st_blocks
* 512;
228 r
= vacuum_necessary(dirfd(d
), sum
, keep_free
, max_use
);
232 r
= unlinkat_deallocate(dirfd(d
), worst
->oldest_file
, 0);
236 return log_error_errno(r
, "Failed to remove file %s: %m", worst
->oldest_file
);
238 log_info("Removed old coredump %s.", worst
->oldest_file
);
244 return log_error_errno(errno
, "Failed to read directory: %m");