chiark / gitweb /
journalctl: immeidately terminate on invalid seed
[elogind.git] / src / journal / journal-vacuum.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2011 Lennart Poettering
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include <sys/types.h>
23 #include <fcntl.h>
24 #include <sys/stat.h>
25 #include <sys/statvfs.h>
26 #include <unistd.h>
27
28 #include "journal-def.h"
29 #include "journal-file.h"
30 #include "journal-vacuum.h"
31 #include "sd-id128.h"
32 #include "util.h"
33
34 struct vacuum_info {
35         off_t usage;
36         char *filename;
37
38         uint64_t realtime;
39         sd_id128_t seqnum_id;
40         uint64_t seqnum;
41
42         bool have_seqnum;
43 };
44
45 static int vacuum_compare(const void *_a, const void *_b) {
46         const struct vacuum_info *a, *b;
47
48         a = _a;
49         b = _b;
50
51         if (a->have_seqnum && b->have_seqnum &&
52             sd_id128_equal(a->seqnum_id, b->seqnum_id)) {
53                 if (a->seqnum < b->seqnum)
54                         return -1;
55                 else if (a->seqnum > b->seqnum)
56                         return 1;
57                 else
58                         return 0;
59         }
60
61         if (a->realtime < b->realtime)
62                 return -1;
63         else if (a->realtime > b->realtime)
64                 return 1;
65         else if (a->have_seqnum && b->have_seqnum)
66                 return memcmp(&a->seqnum_id, &b->seqnum_id, 16);
67         else
68                 return strcmp(a->filename, b->filename);
69 }
70
71 int journal_directory_vacuum(const char *directory, uint64_t max_use, uint64_t min_free) {
72         DIR *d;
73         int r = 0;
74         struct vacuum_info *list = NULL;
75         unsigned n_list = 0, n_allocated = 0, i;
76         uint64_t sum = 0;
77
78         assert(directory);
79
80         if (max_use <= 0)
81                 return 0;
82
83         d = opendir(directory);
84         if (!d)
85                 return -errno;
86
87         for (;;) {
88                 int k;
89                 struct dirent buf, *de;
90                 size_t q;
91                 struct stat st;
92                 char *p;
93                 unsigned long long seqnum = 0, realtime;
94                 sd_id128_t seqnum_id;
95                 bool have_seqnum;
96
97                 k = readdir_r(d, &buf, &de);
98                 if (k != 0) {
99                         r = -k;
100                         goto finish;
101                 }
102
103                 if (!de)
104                         break;
105
106                 if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0)
107                         continue;
108
109                 if (!S_ISREG(st.st_mode))
110                         continue;
111
112                 q = strlen(de->d_name);
113
114                 if (endswith(de->d_name, ".journal")) {
115
116                         /* Vacuum archived files */
117
118                         if (q < 1 + 32 + 1 + 16 + 1 + 16 + 8)
119                                 continue;
120
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] != '@')
124                                 continue;
125
126                         p = strdup(de->d_name);
127                         if (!p) {
128                                 r = -ENOMEM;
129                                 goto finish;
130                         }
131
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) {
134                                 free(p);
135                                 continue;
136                         }
137
138                         if (sscanf(de->d_name + q-8-16-1-16, "%16llx-%16llx.journal", &seqnum, &realtime) != 2) {
139                                 free(p);
140                                 continue;
141                         }
142
143                         have_seqnum = true;
144
145                 } else if (endswith(de->d_name, ".journal~")) {
146                         unsigned long long tmp;
147
148                         /* Vacuum corrupted files */
149
150                         if (q < 1 + 16 + 1 + 16 + 8 + 1)
151                                 continue;
152
153                         if (de->d_name[q-1-8-16-1] != '-' ||
154                             de->d_name[q-1-8-16-1-16-1] != '@')
155                                 continue;
156
157                         p = strdup(de->d_name);
158                         if (!p) {
159                                 r = -ENOMEM;
160                                 goto finish;
161                         }
162
163                         if (sscanf(de->d_name + q-1-8-16-1-16, "%16llx-%16llx.journal~", &realtime, &tmp) != 2) {
164                                 free(p);
165                                 continue;
166                         }
167
168                         have_seqnum = false;
169                 } else
170                         continue;
171
172                 if (n_list >= n_allocated) {
173                         struct vacuum_info *j;
174
175                         n_allocated = MAX(n_allocated * 2U, 8U);
176                         j = realloc(list, n_allocated * sizeof(struct vacuum_info));
177                         if (!j) {
178                                 free(p);
179                                 r = -ENOMEM;
180                                 goto finish;
181                         }
182
183                         list = j;
184                 }
185
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;
192
193                 sum += list[n_list].usage;
194
195                 n_list ++;
196         }
197
198         if (n_list > 0)
199                 qsort(list, n_list, sizeof(struct vacuum_info), vacuum_compare);
200
201         for(i = 0; i < n_list; i++) {
202                 struct statvfs ss;
203
204                 if (fstatvfs(dirfd(d), &ss) < 0) {
205                         r = -errno;
206                         goto finish;
207                 }
208
209                 if (sum <= max_use &&
210                     (uint64_t) ss.f_bavail * (uint64_t) ss.f_bsize >= min_free)
211                         break;
212
213                 if (unlinkat(dirfd(d), list[i].filename, 0) >= 0) {
214                         log_info("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);
218         }
219
220 finish:
221         for (i = 0; i < n_list; i++)
222                 free(list[i].filename);
223
224         free(list);
225
226         if (d)
227                 closedir(d);
228
229         return r;
230 }