1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 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/types.h>
25 #include <sys/statvfs.h>
28 #include "journal-def.h"
29 #include "journal-file.h"
30 #include "journal-vacuum.h"
45 static int vacuum_compare(const void *_a, const void *_b) {
46 const struct vacuum_info *a, *b;
51 if (a->have_seqnum && b->have_seqnum &&
52 sd_id128_equal(a->seqnum_id, b->seqnum_id)) {
53 if (a->seqnum < b->seqnum)
55 else if (a->seqnum > b->seqnum)
61 if (a->realtime < b->realtime)
63 else if (a->realtime > b->realtime)
65 else if (a->have_seqnum && b->have_seqnum)
66 return memcmp(&a->seqnum_id, &b->seqnum_id, 16);
68 return strcmp(a->filename, b->filename);
71 int journal_directory_vacuum(const char *directory, uint64_t max_use, uint64_t min_free) {
74 struct vacuum_info *list = NULL;
75 unsigned n_list = 0, n_allocated = 0, i;
83 d = opendir(directory);
89 struct dirent buf, *de;
93 unsigned long long seqnum = 0, realtime;
97 k = readdir_r(d, &buf, &de);
106 if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0)
109 if (!S_ISREG(st.st_mode))
112 q = strlen(de->d_name);
114 if (endswith(de->d_name, ".journal")) {
116 /* Vacuum archived files */
118 if (q < 1 + 32 + 1 + 16 + 1 + 16 + 8)
121 if (de->d_name[q-8-16-1] != '-' ||
122 de->d_name[q-8-16-1-16-1] != '-' ||
123 de->d_name[q-8-16-1-16-1-32-1] != '@')
126 p = strdup(de->d_name);
132 de->d_name[q-8-16-1-16-1] = 0;
133 if (sd_id128_from_string(de->d_name + q-8-16-1-16-1-32, &seqnum_id) < 0) {
138 if (sscanf(de->d_name + q-8-16-1-16, "%16llx-%16llx.journal", &seqnum, &realtime) != 2) {
145 } else if (endswith(de->d_name, ".journal~")) {
146 unsigned long long tmp;
148 /* Vacuum corrupted files */
150 if (q < 1 + 16 + 1 + 16 + 8 + 1)
153 if (de->d_name[q-1-8-16-1] != '-' ||
154 de->d_name[q-1-8-16-1-16-1] != '@')
157 p = strdup(de->d_name);
163 if (sscanf(de->d_name + q-1-8-16-1-16, "%16llx-%16llx.journal~", &realtime, &tmp) != 2) {
172 if (n_list >= n_allocated) {
173 struct vacuum_info *j;
175 n_allocated = MAX(n_allocated * 2U, 8U);
176 j = realloc(list, n_allocated * sizeof(struct vacuum_info));
186 list[n_list].filename = p;
187 list[n_list].usage = 512UL * (uint64_t) st.st_blocks;
188 list[n_list].seqnum = seqnum;
189 list[n_list].realtime = realtime;
190 list[n_list].seqnum_id = seqnum_id;
191 list[n_list].have_seqnum = have_seqnum;
193 sum += list[n_list].usage;
199 qsort(list, n_list, sizeof(struct vacuum_info), vacuum_compare);
201 for(i = 0; i < n_list; i++) {
204 if (fstatvfs(dirfd(d), &ss) < 0) {
209 if (sum <= max_use &&
210 (uint64_t) ss.f_bavail * (uint64_t) ss.f_bsize >= min_free)
213 if (unlinkat(dirfd(d), list[i].filename, 0) >= 0) {
214 log_debug("Deleted archived journal %s/%s.", directory, list[i].filename);
215 sum -= list[i].usage;
216 } else if (errno != ENOENT)
217 log_warning("Failed to delete %s/%s: %m", directory, list[i].filename);
221 for (i = 0; i < n_list; i++)
222 free(list[i].filename);