1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <sys/statvfs.h>
8 #include "alloc-util.h"
9 #include "coredump-vacuum.h"
10 #include "dirent-util.h"
15 #include "memory-util.h"
16 #include "stat-util.h"
17 #include "string-util.h"
18 #include "time-util.h"
19 #include "user-util.h"
21 #define DEFAULT_MAX_USE_LOWER (uint64_t) (1ULL*1024ULL*1024ULL) /* 1 MiB */
22 #define DEFAULT_MAX_USE_UPPER (uint64_t) (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */
23 #define DEFAULT_KEEP_FREE_UPPER (uint64_t) (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */
24 #define DEFAULT_KEEP_FREE (uint64_t) (1024ULL*1024ULL) /* 1 MB */
26 typedef struct VacuumCandidate
{
32 static VacuumCandidate
* vacuum_candidate_free(VacuumCandidate
*c
) {
39 DEFINE_TRIVIAL_CLEANUP_FUNC(VacuumCandidate
*, vacuum_candidate_free
);
41 static Hashmap
* vacuum_candidate_hashmap_free(Hashmap
*h
) {
42 return hashmap_free_with_destructor(h
, vacuum_candidate_free
);
45 DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap
*, vacuum_candidate_hashmap_free
);
47 static int uid_from_file_name(const char *filename
, uid_t
*uid
) {
48 const char *p
, *e
, *u
;
50 p
= startswith(filename
, "core.");
54 /* Skip the comm field */
65 u
= strndupa_safe(p
, e
- p
);
66 return parse_uid(u
, uid
);
69 static bool vacuum_necessary(int fd
, uint64_t sum
, uint64_t keep_free
, uint64_t max_use
) {
70 uint64_t fs_size
= 0, fs_free
= UINT64_MAX
;
75 if (fstatvfs(fd
, &sv
) >= 0) {
76 fs_size
= sv
.f_frsize
* sv
.f_blocks
;
77 fs_free
= sv
.f_frsize
* sv
.f_bfree
;
80 if (max_use
== UINT64_MAX
) {
83 max_use
= PAGE_ALIGN(fs_size
/ 10); /* 10% */
85 if (max_use
> DEFAULT_MAX_USE_UPPER
)
86 max_use
= DEFAULT_MAX_USE_UPPER
;
88 if (max_use
< DEFAULT_MAX_USE_LOWER
)
89 max_use
= DEFAULT_MAX_USE_LOWER
;
91 max_use
= DEFAULT_MAX_USE_LOWER
;
93 max_use
= PAGE_ALIGN(max_use
);
95 if (max_use
> 0 && sum
> max_use
)
98 if (keep_free
== UINT64_MAX
) {
101 keep_free
= PAGE_ALIGN((fs_size
* 3) / 20); /* 15% */
103 if (keep_free
> DEFAULT_KEEP_FREE_UPPER
)
104 keep_free
= DEFAULT_KEEP_FREE_UPPER
;
106 keep_free
= DEFAULT_KEEP_FREE
;
108 keep_free
= PAGE_ALIGN(keep_free
);
110 if (keep_free
> 0 && fs_free
< keep_free
)
116 int coredump_vacuum(int exclude_fd
, uint64_t keep_free
, uint64_t max_use
) {
117 _cleanup_closedir_
DIR *d
= NULL
;
118 struct stat exclude_st
;
121 if (keep_free
== 0 && max_use
== 0)
124 if (exclude_fd
>= 0) {
125 if (fstat(exclude_fd
, &exclude_st
) < 0)
126 return log_error_errno(errno
, "Failed to fstat(): %m");
129 /* This algorithm will keep deleting the oldest file of the
130 * user with the most coredumps until we are back in the size
131 * limits. Note that vacuuming for journal files is different,
132 * because we rely on rate-limiting of the messages there,
133 * to avoid being flooded. */
135 d
= opendir("/var/lib/systemd/coredump");
140 return log_error_errno(errno
, "Can't open coredump directory: %m");
144 _cleanup_(vacuum_candidate_hashmap_freep
) Hashmap
*h
= NULL
;
145 VacuumCandidate
*worst
= NULL
;
150 FOREACH_DIRENT(de
, d
, goto fail
) {
156 r
= uid_from_file_name(de
->d_name
, &uid
);
160 if (fstatat(dirfd(d
), de
->d_name
, &st
, AT_NO_AUTOMOUNT
|AT_SYMLINK_NOFOLLOW
) < 0) {
164 log_warning_errno(errno
, "Failed to stat /var/lib/systemd/coredump/%s: %m", de
->d_name
);
168 if (!S_ISREG(st
.st_mode
))
171 if (exclude_fd
>= 0 && stat_inode_same(&exclude_st
, &st
))
174 r
= hashmap_ensure_allocated(&h
, NULL
);
178 t
= timespec_load(&st
.st_mtim
);
180 c
= hashmap_get(h
, UID_TO_PTR(uid
));
182 if (t
< c
->oldest_mtime
) {
183 r
= free_and_strdup_warn(&c
->oldest_file
, de
->d_name
);
189 _cleanup_(vacuum_candidate_freep
) VacuumCandidate
*n
= NULL
;
191 n
= new0(VacuumCandidate
, 1);
195 r
= free_and_strdup_warn(&n
->oldest_file
, de
->d_name
);
200 r
= hashmap_put(h
, UID_TO_PTR(uid
), n
);
210 worst
->n_files
< c
->n_files
||
211 (worst
->n_files
== c
->n_files
&& c
->oldest_mtime
< worst
->oldest_mtime
))
214 sum
+= st
.st_blocks
* 512;
220 r
= vacuum_necessary(dirfd(d
), sum
, keep_free
, max_use
);
224 r
= unlinkat_deallocate(dirfd(d
), worst
->oldest_file
, 0);
228 return log_error_errno(r
, "Failed to remove file %s: %m", worst
->oldest_file
);
230 log_info("Removed old coredump %s.", worst
->oldest_file
);
236 return log_error_errno(errno
, "Failed to read directory: %m");