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);
90 union dirent_storage buf;
94 unsigned long long seqnum = 0, realtime;
98 k = readdir_r(d, &buf.de, &de);
107 if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0)
110 if (!S_ISREG(st.st_mode))
113 q = strlen(de->d_name);
115 if (endswith(de->d_name, ".journal")) {
117 /* Vacuum archived files */
119 if (q < 1 + 32 + 1 + 16 + 1 + 16 + 8)
122 if (de->d_name[q-8-16-1] != '-' ||
123 de->d_name[q-8-16-1-16-1] != '-' ||
124 de->d_name[q-8-16-1-16-1-32-1] != '@')
127 p = strdup(de->d_name);
133 de->d_name[q-8-16-1-16-1] = 0;
134 if (sd_id128_from_string(de->d_name + q-8-16-1-16-1-32, &seqnum_id) < 0) {
139 if (sscanf(de->d_name + q-8-16-1-16, "%16llx-%16llx.journal", &seqnum, &realtime) != 2) {
146 } else if (endswith(de->d_name, ".journal~")) {
147 unsigned long long tmp;
149 /* Vacuum corrupted files */
151 if (q < 1 + 16 + 1 + 16 + 8 + 1)
154 if (de->d_name[q-1-8-16-1] != '-' ||
155 de->d_name[q-1-8-16-1-16-1] != '@')
158 p = strdup(de->d_name);
164 if (sscanf(de->d_name + q-1-8-16-1-16, "%16llx-%16llx.journal~", &realtime, &tmp) != 2) {
173 if (n_list >= n_allocated) {
174 struct vacuum_info *j;
176 n_allocated = MAX(n_allocated * 2U, 8U);
177 j = realloc(list, n_allocated * sizeof(struct vacuum_info));
187 list[n_list].filename = p;
188 list[n_list].usage = 512UL * (uint64_t) st.st_blocks;
189 list[n_list].seqnum = seqnum;
190 list[n_list].realtime = realtime;
191 list[n_list].seqnum_id = seqnum_id;
192 list[n_list].have_seqnum = have_seqnum;
194 sum += list[n_list].usage;
200 qsort(list, n_list, sizeof(struct vacuum_info), vacuum_compare);
202 for(i = 0; i < n_list; i++) {
205 if (fstatvfs(dirfd(d), &ss) < 0) {
210 if (sum <= max_use &&
211 (uint64_t) ss.f_bavail * (uint64_t) ss.f_bsize >= min_free)
214 if (unlinkat(dirfd(d), list[i].filename, 0) >= 0) {
215 log_debug("Deleted archived journal %s/%s.", directory, list[i].filename);
216 sum -= list[i].usage;
217 } else if (errno != ENOENT)
218 log_warning("Failed to delete %s/%s: %m", directory, list[i].filename);
222 for (i = 0; i < n_list; i++)
223 free(list[i].filename);