journal: rework vacuuming logic

Implement a maximum limit on number of journal files to keep around.
Enforcing a limit is useful on this since our performance when viewing
pays a heavy penalty for each journal file to interleve. This setting is
turned on now by default, and set to 100.

Also, actully implement what 348ced9097
promised: use whatever we find on disk at startup as lower bound on how
much disk space we can use. That commit introduced some provisions to
implement this, but actually never did.

This also adds "journalctl --vacuum-files=" to vacuum files on disk by
their number explicitly.
This commit is contained in:
Lennart Poettering 2015-10-02 23:21:59 +02:00
parent 0fb398316c
commit 8580d1f73d
14 changed files with 367 additions and 240 deletions

View File

@ -649,6 +649,7 @@
<varlistentry> <varlistentry>
<term><option>--vacuum-size=</option></term> <term><option>--vacuum-size=</option></term>
<term><option>--vacuum-time=</option></term> <term><option>--vacuum-time=</option></term>
<term><option>--vacuum-files=</option></term>
<listitem><para>Removes archived journal files until the disk <listitem><para>Removes archived journal files until the disk
space they use falls below the specified size (specified with space they use falls below the specified size (specified with
@ -658,15 +659,24 @@
timespan (specified with the usual <literal>s</literal>, timespan (specified with the usual <literal>s</literal>,
<literal>min</literal>, <literal>h</literal>, <literal>min</literal>, <literal>h</literal>,
<literal>days</literal>, <literal>months</literal>, <literal>days</literal>, <literal>months</literal>,
<literal>weeks</literal>, <literal>years</literal> <literal>weeks</literal>, <literal>years</literal> suffixes),
suffixes). Note that running <option>--vacuum-size=</option> or no more than the specified number of separate journal files
has only indirect effect on the output shown by remain. Note that running <option>--vacuum-size=</option> has
only indirect effect on the output shown by
<option>--disk-usage</option> as the latter includes active <option>--disk-usage</option> as the latter includes active
journal files, while the former only operates on archived journal files, while the the vacuuming operation only operates
journal files. <option>--vacuum-size=</option> and on archived journal files. Similar,
<option>--vacuum-time=</option> may be combined in a single <option>--vacuum-files=</option> might not actually reduce the
invocation to enforce both a size and time limit on the number of journal files to below the specified number, as it
archived journal files.</para></listitem> will not remove active journal
files. <option>--vacuum-size=</option>,
<option>--vacuum-time=</option> and
<option>--vacuum-files=</option> may be combined in a single
invocation to enforce any combination of a size, a time and a
number of files limit on the archived journal
files. Specifying any of these three parameters as zero is
equivalent to not enforcing the specific limit, and is thus
redundant.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>

View File

@ -173,9 +173,11 @@
<term><varname>SystemMaxUse=</varname></term> <term><varname>SystemMaxUse=</varname></term>
<term><varname>SystemKeepFree=</varname></term> <term><varname>SystemKeepFree=</varname></term>
<term><varname>SystemMaxFileSize=</varname></term> <term><varname>SystemMaxFileSize=</varname></term>
<term><varname>SystemMaxFiles=</varname></term>
<term><varname>RuntimeMaxUse=</varname></term> <term><varname>RuntimeMaxUse=</varname></term>
<term><varname>RuntimeKeepFree=</varname></term> <term><varname>RuntimeKeepFree=</varname></term>
<term><varname>RuntimeMaxFileSize=</varname></term> <term><varname>RuntimeMaxFileSize=</varname></term>
<term><varname>RuntimeMaxFiles=</varname></term>
<listitem><para>Enforce size limits on the journal files <listitem><para>Enforce size limits on the journal files
stored. The options prefixed with <literal>System</literal> stored. The options prefixed with <literal>System</literal>
@ -197,8 +199,7 @@
names not ending with <literal>.journal</literal> or names not ending with <literal>.journal</literal> or
<literal>.journal~</literal>, so only such files, located in <literal>.journal~</literal>, so only such files, located in
the appropriate directories, are taken into account when the appropriate directories, are taken into account when
calculating current disk usage. calculating current disk usage.</para>
</para>
<para><varname>SystemMaxUse=</varname> and <para><varname>SystemMaxUse=</varname> and
<varname>RuntimeMaxUse=</varname> control how much disk space <varname>RuntimeMaxUse=</varname> control how much disk space
@ -212,13 +213,14 @@
<para>The first pair defaults to 10% and the second to 15% of <para>The first pair defaults to 10% and the second to 15% of
the size of the respective file system. If the file system is the size of the respective file system. If the file system is
nearly full and either <varname>SystemKeepFree=</varname> or nearly full and either <varname>SystemKeepFree=</varname> or
<varname>RuntimeKeepFree=</varname> is violated when <varname>RuntimeKeepFree=</varname> are violated when
systemd-journald is started, the value will be raised to systemd-journald is started, the limit will be raised to the
percentage that is actually free. This means that if there was percentage that is actually free. This means that if there was
enough free space before and journal files were created, and enough free space before and journal files were created, and
subsequently something else causes the file system to fill up, subsequently something else causes the file system to fill up,
journald will stop using more space, but it will not be journald will stop using more space, but it will not be
removing existing files to go reduce footprint either.</para> removing existing files to reduce footprint again
either.</para>
<para><varname>SystemMaxFileSize=</varname> and <para><varname>SystemMaxFileSize=</varname> and
<varname>RuntimeMaxFileSize=</varname> control how large <varname>RuntimeMaxFileSize=</varname> control how large
@ -228,13 +230,22 @@
eighth of the values configured with eighth of the values configured with
<varname>SystemMaxUse=</varname> and <varname>SystemMaxUse=</varname> and
<varname>RuntimeMaxUse=</varname>, so that usually seven <varname>RuntimeMaxUse=</varname>, so that usually seven
rotated journal files are kept as history.</para></listitem> rotated journal files are kept as history.</para>
<para>Specify values in bytes or use K, M, G, T, P, E as <para>Specify values in bytes or use K, M, G, T, P, E as
units for the specified sizes (equal to 1024, 1024²,... bytes). units for the specified sizes (equal to 1024, 1024²,... bytes).
Note that size limits are enforced synchronously when journal Note that size limits are enforced synchronously when journal
files are extended, and no explicit rotation step triggered by files are extended, and no explicit rotation step triggered by
time is needed.</para> time is needed.</para>
<para><varname>SystemMaxFiles=</varname> and
<varname>RuntimeMaxFiles=</varname> control how many
individual journal files to keep at maximum. Note that only
archived files are deleted to reduce the number of files until
this limit is reached; active files will stay around. This
means that in effect there might still be more journal files
around in total than this limit after a vacuuming operation is
complete. This setting defaults to 100.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>

View File

@ -49,6 +49,9 @@
#define DEFAULT_MAX_USE_LOWER (1ULL*1024ULL*1024ULL) /* 1 MiB */ #define DEFAULT_MAX_USE_LOWER (1ULL*1024ULL*1024ULL) /* 1 MiB */
#define DEFAULT_MAX_USE_UPPER (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */ #define DEFAULT_MAX_USE_UPPER (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */
/* This is the default minimal use limit, how much we'll use even if keep_free suggests otherwise. */
#define DEFAULT_MIN_USE (1ULL*1024ULL*1024ULL) /* 1 MiB */
/* This is the upper bound if we deduce max_size from max_use */ /* This is the upper bound if we deduce max_size from max_use */
#define DEFAULT_MAX_SIZE_UPPER (128ULL*1024ULL*1024ULL) /* 128 MiB */ #define DEFAULT_MAX_SIZE_UPPER (128ULL*1024ULL*1024ULL) /* 128 MiB */
@ -60,6 +63,9 @@
* size */ * size */
#define DEFAULT_KEEP_FREE (1024ULL*1024ULL) /* 1 MB */ #define DEFAULT_KEEP_FREE (1024ULL*1024ULL) /* 1 MB */
/* This is the default maximum number of journal files to keep around. */
#define DEFAULT_N_MAX_FILES (100)
/* n_data was the first entry we added after the initial file format design */ /* n_data was the first entry we added after the initial file format design */
#define HEADER_SIZE_MIN ALIGN64(offsetof(Header, n_data)) #define HEADER_SIZE_MIN ALIGN64(offsetof(Header, n_data))
@ -2957,16 +2963,35 @@ int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint6
return r; return r;
} }
void journal_reset_metrics(JournalMetrics *m) {
assert(m);
/* Set everything to "pick automatic values". */
*m = (JournalMetrics) {
.min_use = (uint64_t) -1,
.max_use = (uint64_t) -1,
.min_size = (uint64_t) -1,
.max_size = (uint64_t) -1,
.keep_free = (uint64_t) -1,
.n_max_files = (uint64_t) -1,
};
}
void journal_default_metrics(JournalMetrics *m, int fd) { void journal_default_metrics(JournalMetrics *m, int fd) {
uint64_t fs_size = 0; char a[FORMAT_BYTES_MAX], b[FORMAT_BYTES_MAX], c[FORMAT_BYTES_MAX], d[FORMAT_BYTES_MAX], e[FORMAT_BYTES_MAX];
struct statvfs ss; struct statvfs ss;
char a[FORMAT_BYTES_MAX], b[FORMAT_BYTES_MAX], c[FORMAT_BYTES_MAX], d[FORMAT_BYTES_MAX]; uint64_t fs_size;
assert(m); assert(m);
assert(fd >= 0); assert(fd >= 0);
if (fstatvfs(fd, &ss) >= 0) if (fstatvfs(fd, &ss) >= 0)
fs_size = ss.f_frsize * ss.f_blocks; fs_size = ss.f_frsize * ss.f_blocks;
else {
log_debug_errno(errno, "Failed to detremine disk size: %m");
fs_size = 0;
}
if (m->max_use == (uint64_t) -1) { if (m->max_use == (uint64_t) -1) {
@ -2983,10 +3008,16 @@ void journal_default_metrics(JournalMetrics *m, int fd) {
} else { } else {
m->max_use = PAGE_ALIGN(m->max_use); m->max_use = PAGE_ALIGN(m->max_use);
if (m->max_use < JOURNAL_FILE_SIZE_MIN*2) if (m->max_use != 0 && m->max_use < JOURNAL_FILE_SIZE_MIN*2)
m->max_use = JOURNAL_FILE_SIZE_MIN*2; m->max_use = JOURNAL_FILE_SIZE_MIN*2;
} }
if (m->min_use == (uint64_t) -1)
m->min_use = DEFAULT_MIN_USE;
if (m->min_use > m->max_use)
m->min_use = m->max_use;
if (m->max_size == (uint64_t) -1) { if (m->max_size == (uint64_t) -1) {
m->max_size = PAGE_ALIGN(m->max_use / 8); /* 8 chunks */ m->max_size = PAGE_ALIGN(m->max_use / 8); /* 8 chunks */
@ -2995,11 +3026,13 @@ void journal_default_metrics(JournalMetrics *m, int fd) {
} else } else
m->max_size = PAGE_ALIGN(m->max_size); m->max_size = PAGE_ALIGN(m->max_size);
if (m->max_size < JOURNAL_FILE_SIZE_MIN) if (m->max_size != 0) {
m->max_size = JOURNAL_FILE_SIZE_MIN; if (m->max_size < JOURNAL_FILE_SIZE_MIN)
m->max_size = JOURNAL_FILE_SIZE_MIN;
if (m->max_size*2 > m->max_use) if (m->max_use != 0 && m->max_size*2 > m->max_use)
m->max_use = m->max_size*2; m->max_use = m->max_size*2;
}
if (m->min_size == (uint64_t) -1) if (m->min_size == (uint64_t) -1)
m->min_size = JOURNAL_FILE_SIZE_MIN; m->min_size = JOURNAL_FILE_SIZE_MIN;
@ -3009,7 +3042,7 @@ void journal_default_metrics(JournalMetrics *m, int fd) {
if (m->min_size < JOURNAL_FILE_SIZE_MIN) if (m->min_size < JOURNAL_FILE_SIZE_MIN)
m->min_size = JOURNAL_FILE_SIZE_MIN; m->min_size = JOURNAL_FILE_SIZE_MIN;
if (m->min_size > m->max_size) if (m->max_size != 0 && m->min_size > m->max_size)
m->max_size = m->min_size; m->max_size = m->min_size;
} }
@ -3025,11 +3058,16 @@ void journal_default_metrics(JournalMetrics *m, int fd) {
m->keep_free = DEFAULT_KEEP_FREE; m->keep_free = DEFAULT_KEEP_FREE;
} }
log_debug("Fixed max_use=%s max_size=%s min_size=%s keep_free=%s", if (m->n_max_files == (uint64_t) -1)
format_bytes(a, sizeof(a), m->max_use), m->n_max_files = DEFAULT_N_MAX_FILES;
format_bytes(b, sizeof(b), m->max_size),
format_bytes(c, sizeof(c), m->min_size), log_debug("Fixed min_use=%s max_use=%s max_size=%s min_size=%s keep_free=%s n_max_files=%" PRIu64,
format_bytes(d, sizeof(d), m->keep_free)); format_bytes(a, sizeof(a), m->min_use),
format_bytes(b, sizeof(b), m->max_use),
format_bytes(c, sizeof(c), m->max_size),
format_bytes(d, sizeof(d), m->min_size),
format_bytes(e, sizeof(e), m->keep_free),
m->n_max_files);
} }
int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t *to) { int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t *to) {

View File

@ -36,11 +36,13 @@
#include "hashmap.h" #include "hashmap.h"
typedef struct JournalMetrics { typedef struct JournalMetrics {
uint64_t max_use; /* For all these: -1 means "pick automatically", and 0 means "no limit enforced" */
uint64_t use; uint64_t max_size; /* how large journal files grow at max */
uint64_t max_size; uint64_t min_size; /* how large journal files grow at least */
uint64_t min_size; uint64_t max_use; /* how much disk space to use in total at max, keep_free permitting */
uint64_t keep_free; uint64_t min_use; /* how much disk space to use in total at least, even if keep_free says not to */
uint64_t keep_free; /* how much to keep free on disk */
uint64_t n_max_files; /* how many files to keep around at max */
} JournalMetrics; } JournalMetrics;
typedef enum direction { typedef enum direction {
@ -223,6 +225,7 @@ int journal_file_rotate(JournalFile **f, bool compress, bool seal);
void journal_file_post_change(JournalFile *f); void journal_file_post_change(JournalFile *f);
void journal_reset_metrics(JournalMetrics *m);
void journal_default_metrics(JournalMetrics *m, int fd); void journal_default_metrics(JournalMetrics *m, int fd);
int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t *to); int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t *to);

View File

@ -140,22 +140,24 @@ static int journal_file_empty(int dir_fd, const char *name) {
int journal_directory_vacuum( int journal_directory_vacuum(
const char *directory, const char *directory,
uint64_t max_use, uint64_t max_use,
uint64_t n_max_files,
usec_t max_retention_usec, usec_t max_retention_usec,
usec_t *oldest_usec, usec_t *oldest_usec,
bool verbose) { bool verbose) {
_cleanup_closedir_ DIR *d = NULL; _cleanup_closedir_ DIR *d = NULL;
int r = 0;
struct vacuum_info *list = NULL; struct vacuum_info *list = NULL;
unsigned n_list = 0, i; unsigned n_list = 0, i, n_active_files = 0;
size_t n_allocated = 0; size_t n_allocated = 0;
uint64_t sum = 0, freed = 0; uint64_t sum = 0, freed = 0;
usec_t retention_limit = 0; usec_t retention_limit = 0;
char sbytes[FORMAT_BYTES_MAX]; char sbytes[FORMAT_BYTES_MAX];
struct dirent *de;
int r;
assert(directory); assert(directory);
if (max_use <= 0 && max_retention_usec <= 0) if (max_use <= 0 && max_retention_usec <= 0 && n_max_files <= 0)
return 0; return 0;
if (max_retention_usec > 0) { if (max_retention_usec > 0) {
@ -170,27 +172,20 @@ int journal_directory_vacuum(
if (!d) if (!d)
return -errno; return -errno;
for (;;) { FOREACH_DIRENT_ALL(de, d, r = -errno; goto finish) {
struct dirent *de;
size_t q;
struct stat st;
char *p;
unsigned long long seqnum = 0, realtime; unsigned long long seqnum = 0, realtime;
_cleanup_free_ char *p = NULL;
sd_id128_t seqnum_id; sd_id128_t seqnum_id;
bool have_seqnum; bool have_seqnum;
uint64_t size;
struct stat st;
size_t q;
errno = 0; if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
de = readdir(d); log_debug_errno(errno, "Failed to stat file %s while vacuuming, ignoring: %m", de->d_name);
if (!de && errno != 0) {
r = -errno;
goto finish;
}
if (!de)
break;
if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0)
continue; continue;
}
if (!S_ISREG(st.st_mode)) if (!S_ISREG(st.st_mode))
continue; continue;
@ -199,15 +194,20 @@ int journal_directory_vacuum(
if (endswith(de->d_name, ".journal")) { if (endswith(de->d_name, ".journal")) {
/* Vacuum archived files */ /* Vacuum archived files. Active files are
* left around */
if (q < 1 + 32 + 1 + 16 + 1 + 16 + 8) if (q < 1 + 32 + 1 + 16 + 1 + 16 + 8) {
n_active_files++;
continue; continue;
}
if (de->d_name[q-8-16-1] != '-' || if (de->d_name[q-8-16-1] != '-' ||
de->d_name[q-8-16-1-16-1] != '-' || de->d_name[q-8-16-1-16-1] != '-' ||
de->d_name[q-8-16-1-16-1-32-1] != '@') de->d_name[q-8-16-1-16-1-32-1] != '@') {
n_active_files++;
continue; continue;
}
p = strdup(de->d_name); p = strdup(de->d_name);
if (!p) { if (!p) {
@ -218,11 +218,13 @@ int journal_directory_vacuum(
de->d_name[q-8-16-1-16-1] = 0; de->d_name[q-8-16-1-16-1] = 0;
if (sd_id128_from_string(de->d_name + q-8-16-1-16-1-32, &seqnum_id) < 0) { if (sd_id128_from_string(de->d_name + q-8-16-1-16-1-32, &seqnum_id) < 0) {
free(p); free(p);
n_active_files++;
continue; continue;
} }
if (sscanf(de->d_name + q-8-16-1-16, "%16llx-%16llx.journal", &seqnum, &realtime) != 2) { if (sscanf(de->d_name + q-8-16-1-16, "%16llx-%16llx.journal", &seqnum, &realtime) != 2) {
free(p); free(p);
n_active_files++;
continue; continue;
} }
@ -233,12 +235,16 @@ int journal_directory_vacuum(
/* Vacuum corrupted files */ /* Vacuum corrupted files */
if (q < 1 + 16 + 1 + 16 + 8 + 1) if (q < 1 + 16 + 1 + 16 + 8 + 1) {
n_active_files ++;
continue; continue;
}
if (de->d_name[q-1-8-16-1] != '-' || if (de->d_name[q-1-8-16-1] != '-' ||
de->d_name[q-1-8-16-1-16-1] != '@') de->d_name[q-1-8-16-1-16-1] != '@') {
n_active_files ++;
continue; continue;
}
p = strdup(de->d_name); p = strdup(de->d_name);
if (!p) { if (!p) {
@ -248,54 +254,68 @@ int journal_directory_vacuum(
if (sscanf(de->d_name + q-1-8-16-1-16, "%16llx-%16llx.journal~", &realtime, &tmp) != 2) { if (sscanf(de->d_name + q-1-8-16-1-16, "%16llx-%16llx.journal~", &realtime, &tmp) != 2) {
free(p); free(p);
n_active_files ++;
continue; continue;
} }
have_seqnum = false; have_seqnum = false;
} else } else {
/* We do not vacuum active files or unknown files! */ /* We do not vacuum unknown files! */
log_debug("Not vacuuming unknown file %s.", de->d_name);
continue; continue;
}
if (journal_file_empty(dirfd(d), p)) { size = 512UL * (uint64_t) st.st_blocks;
r = journal_file_empty(dirfd(d), p);
if (r < 0) {
log_debug_errno(r, "Failed check if %s is empty, ignoring: %m", p);
continue;
}
if (r > 0) {
/* Always vacuum empty non-online files. */ /* Always vacuum empty non-online files. */
uint64_t size = 512UL * (uint64_t) st.st_blocks;
if (unlinkat(dirfd(d), p, 0) >= 0) { if (unlinkat(dirfd(d), p, 0) >= 0) {
log_full(verbose ? LOG_INFO : LOG_DEBUG, "Deleted empty archived journal %s/%s (%s).", directory, p, format_bytes(sbytes, sizeof(sbytes), size));
log_full(verbose ? LOG_INFO : LOG_DEBUG,
"Deleted empty archived journal %s/%s (%s).", directory, p, format_bytes(sbytes, sizeof(sbytes), size));
freed += size; freed += size;
} else if (errno != ENOENT) } else if (errno != ENOENT)
log_warning_errno(errno, "Failed to delete empty archived journal %s/%s: %m", directory, p); log_warning_errno(errno, "Failed to delete empty archived journal %s/%s: %m", directory, p);
free(p);
continue; continue;
} }
patch_realtime(directory, p, &st, &realtime); patch_realtime(dirfd(d), p, &st, &realtime);
if (!GREEDY_REALLOC(list, n_allocated, n_list + 1)) { if (!GREEDY_REALLOC(list, n_allocated, n_list + 1)) {
free(p);
r = -ENOMEM; r = -ENOMEM;
goto finish; goto finish;
} }
list[n_list].filename = p; list[n_list].filename = p;
list[n_list].usage = 512UL * (uint64_t) st.st_blocks; list[n_list].usage = size;
list[n_list].seqnum = seqnum; list[n_list].seqnum = seqnum;
list[n_list].realtime = realtime; list[n_list].realtime = realtime;
list[n_list].seqnum_id = seqnum_id; list[n_list].seqnum_id = seqnum_id;
list[n_list].have_seqnum = have_seqnum; list[n_list].have_seqnum = have_seqnum;
sum += list[n_list].usage;
n_list ++; n_list ++;
p = NULL;
sum += size;
} }
qsort_safe(list, n_list, sizeof(struct vacuum_info), vacuum_compare); qsort_safe(list, n_list, sizeof(struct vacuum_info), vacuum_compare);
for (i = 0; i < n_list; i++) { for (i = 0; i < n_list; i++) {
unsigned left;
left = n_active_files + n_list - i;
if ((max_retention_usec <= 0 || list[i].realtime >= retention_limit) && if ((max_retention_usec <= 0 || list[i].realtime >= retention_limit) &&
(max_use <= 0 || sum <= max_use)) (max_use <= 0 || sum <= max_use) &&
(n_max_files <= 0 || left <= n_max_files))
break; break;
if (unlinkat(dirfd(d), list[i].filename, 0) >= 0) { if (unlinkat(dirfd(d), list[i].filename, 0) >= 0) {
@ -314,6 +334,8 @@ int journal_directory_vacuum(
if (oldest_usec && i < n_list && (*oldest_usec == 0 || list[i].realtime < *oldest_usec)) if (oldest_usec && i < n_list && (*oldest_usec == 0 || list[i].realtime < *oldest_usec))
*oldest_usec = list[i].realtime; *oldest_usec = list[i].realtime;
r = 0;
finish: finish:
for (i = 0; i < n_list; i++) for (i = 0; i < n_list; i++)
free(list[i].filename); free(list[i].filename);

View File

@ -21,5 +21,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>. along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/ ***/
#include <inttypes.h>
#include <stdbool.h>
int journal_directory_vacuum(const char *directory, uint64_t max_use, usec_t max_retention_usec, usec_t *oldest_usec, bool vacuum); #include "time-util.h"
int journal_directory_vacuum(const char *directory, uint64_t max_use, uint64_t n_max_files, usec_t max_retention_usec, usec_t *oldest_usec, bool verbose);

View File

@ -106,8 +106,9 @@ static bool arg_reverse = false;
static int arg_journal_type = 0; static int arg_journal_type = 0;
static const char *arg_root = NULL; static const char *arg_root = NULL;
static const char *arg_machine = NULL; static const char *arg_machine = NULL;
static uint64_t arg_vacuum_size = (uint64_t) -1; static uint64_t arg_vacuum_size = 0;
static usec_t arg_vacuum_time = USEC_INFINITY; static uint64_t arg_vacuum_n_files = 0;
static usec_t arg_vacuum_time = 0;
static enum { static enum {
ACTION_SHOW, ACTION_SHOW,
@ -235,7 +236,8 @@ static void help(void) {
" --new-id128 Generate a new 128-bit ID\n" " --new-id128 Generate a new 128-bit ID\n"
" --disk-usage Show total disk usage of all journal files\n" " --disk-usage Show total disk usage of all journal files\n"
" --vacuum-size=BYTES Reduce disk usage below specified size\n" " --vacuum-size=BYTES Reduce disk usage below specified size\n"
" --vacuum-time=TIME Remove journal files older than specified date\n" " --vacuum-files=INT Leave only the specified number of journal files\n"
" --vacuum-time=TIME Remove journal files older than specified time\n"
" --flush Flush all journal data from /run into /var\n" " --flush Flush all journal data from /run into /var\n"
" --rotate Request immediate rotation of the journal files\n" " --rotate Request immediate rotation of the journal files\n"
" --header Show journal header information\n" " --header Show journal header information\n"
@ -281,6 +283,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_FLUSH, ARG_FLUSH,
ARG_ROTATE, ARG_ROTATE,
ARG_VACUUM_SIZE, ARG_VACUUM_SIZE,
ARG_VACUUM_FILES,
ARG_VACUUM_TIME, ARG_VACUUM_TIME,
}; };
@ -335,6 +338,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "flush", no_argument, NULL, ARG_FLUSH }, { "flush", no_argument, NULL, ARG_FLUSH },
{ "rotate", no_argument, NULL, ARG_ROTATE }, { "rotate", no_argument, NULL, ARG_ROTATE },
{ "vacuum-size", required_argument, NULL, ARG_VACUUM_SIZE }, { "vacuum-size", required_argument, NULL, ARG_VACUUM_SIZE },
{ "vacuum-files", required_argument, NULL, ARG_VACUUM_FILES },
{ "vacuum-time", required_argument, NULL, ARG_VACUUM_TIME }, { "vacuum-time", required_argument, NULL, ARG_VACUUM_TIME },
{} {}
}; };
@ -540,6 +544,16 @@ static int parse_argv(int argc, char *argv[]) {
arg_action = ACTION_VACUUM; arg_action = ACTION_VACUUM;
break; break;
case ARG_VACUUM_FILES:
r = safe_atou64(optarg, &arg_vacuum_n_files);
if (r < 0) {
log_error("Failed to parse vacuum files: %s", optarg);
return r;
}
arg_action = ACTION_VACUUM;
break;
case ARG_VACUUM_TIME: case ARG_VACUUM_TIME:
r = parse_sec(optarg, &arg_vacuum_time); r = parse_sec(optarg, &arg_vacuum_time);
if (r < 0) { if (r < 0) {
@ -1929,9 +1943,9 @@ int main(int argc, char *argv[]) {
if (d->is_root) if (d->is_root)
continue; continue;
q = journal_directory_vacuum(d->path, arg_vacuum_size, arg_vacuum_time, NULL, true); q = journal_directory_vacuum(d->path, arg_vacuum_size, arg_vacuum_n_files, arg_vacuum_time, NULL, true);
if (q < 0) { if (q < 0) {
log_error_errno(q, "Failed to vacuum: %m"); log_error_errno(q, "Failed to vacuum %s: %m", d->path);
r = q; r = q;
} }
} }

View File

@ -24,9 +24,11 @@ Journal.RateLimitBurst, config_parse_unsigned, 0, offsetof(Server, rate_li
Journal.SystemMaxUse, config_parse_iec_uint64, 0, offsetof(Server, system_metrics.max_use) Journal.SystemMaxUse, config_parse_iec_uint64, 0, offsetof(Server, system_metrics.max_use)
Journal.SystemMaxFileSize, config_parse_iec_uint64, 0, offsetof(Server, system_metrics.max_size) Journal.SystemMaxFileSize, config_parse_iec_uint64, 0, offsetof(Server, system_metrics.max_size)
Journal.SystemKeepFree, config_parse_iec_uint64, 0, offsetof(Server, system_metrics.keep_free) Journal.SystemKeepFree, config_parse_iec_uint64, 0, offsetof(Server, system_metrics.keep_free)
Journal.SystemMaxFiles, config_parse_uint64, 0, offsetof(Server, system_metrics.n_max_files)
Journal.RuntimeMaxUse, config_parse_iec_uint64, 0, offsetof(Server, runtime_metrics.max_use) Journal.RuntimeMaxUse, config_parse_iec_uint64, 0, offsetof(Server, runtime_metrics.max_use)
Journal.RuntimeMaxFileSize, config_parse_iec_uint64, 0, offsetof(Server, runtime_metrics.max_size) Journal.RuntimeMaxFileSize, config_parse_iec_uint64, 0, offsetof(Server, runtime_metrics.max_size)
Journal.RuntimeKeepFree, config_parse_iec_uint64, 0, offsetof(Server, runtime_metrics.keep_free) Journal.RuntimeKeepFree, config_parse_iec_uint64, 0, offsetof(Server, runtime_metrics.keep_free)
Journal.RuntimeMaxFiles, config_parse_uint64, 0, offsetof(Server, runtime_metrics.n_max_files)
Journal.MaxRetentionSec, config_parse_sec, 0, offsetof(Server, max_retention_usec) Journal.MaxRetentionSec, config_parse_sec, 0, offsetof(Server, max_retention_usec)
Journal.MaxFileSec, config_parse_sec, 0, offsetof(Server, max_file_usec) Journal.MaxFileSec, config_parse_sec, 0, offsetof(Server, max_file_usec)
Journal.ForwardToSyslog, config_parse_bool, 0, offsetof(Server, forward_to_syslog) Journal.ForwardToSyslog, config_parse_bool, 0, offsetof(Server, forward_to_syslog)

View File

@ -19,45 +19,44 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>. along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/ ***/
#include <sys/signalfd.h>
#include <sys/ioctl.h>
#include <linux/sockios.h> #include <linux/sockios.h>
#include <sys/statvfs.h>
#include <sys/mman.h>
#ifdef HAVE_SELINUX #ifdef HAVE_SELINUX
#include <selinux/selinux.h> #include <selinux/selinux.h>
#endif #endif
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/signalfd.h>
#include <sys/statvfs.h>
#include <libudev.h> #include "libudev.h"
#include "sd-daemon.h"
#include "sd-journal.h" #include "sd-journal.h"
#include "sd-messages.h" #include "sd-messages.h"
#include "sd-daemon.h"
#include "mkdir.h"
#include "rm-rf.h"
#include "hashmap.h"
#include "journal-file.h"
#include "socket-util.h"
#include "cgroup-util.h"
#include "missing.h"
#include "conf-parser.h"
#include "selinux-util.h"
#include "acl-util.h" #include "acl-util.h"
#include "cgroup-util.h"
#include "conf-parser.h"
#include "formats-util.h" #include "formats-util.h"
#include "process-util.h" #include "hashmap.h"
#include "hostname-util.h" #include "hostname-util.h"
#include "missing.h"
#include "mkdir.h"
#include "process-util.h"
#include "rm-rf.h"
#include "selinux-util.h"
#include "signal-util.h" #include "signal-util.h"
#include "socket-util.h"
#include "journal-authenticate.h"
#include "journal-file.h"
#include "journal-internal.h" #include "journal-internal.h"
#include "journal-vacuum.h" #include "journal-vacuum.h"
#include "journal-authenticate.h"
#include "journald-rate-limit.h"
#include "journald-kmsg.h"
#include "journald-syslog.h"
#include "journald-stream.h"
#include "journald-native.h"
#include "journald-audit.h" #include "journald-audit.h"
#include "journald-kmsg.h"
#include "journald-native.h"
#include "journald-rate-limit.h"
#include "journald-server.h" #include "journald-server.h"
#include "journald-stream.h"
#include "journald-syslog.h"
#define USER_JOURNALS_MAX 1024 #define USER_JOURNALS_MAX 1024
@ -66,88 +65,61 @@
#define DEFAULT_RATE_LIMIT_BURST 1000 #define DEFAULT_RATE_LIMIT_BURST 1000
#define DEFAULT_MAX_FILE_USEC USEC_PER_MONTH #define DEFAULT_MAX_FILE_USEC USEC_PER_MONTH
#define RECHECK_AVAILABLE_SPACE_USEC (30*USEC_PER_SEC) #define RECHECK_SPACE_USEC (30*USEC_PER_SEC)
static const char* const storage_table[_STORAGE_MAX] = { static int determine_space_for(
[STORAGE_AUTO] = "auto", Server *s,
[STORAGE_VOLATILE] = "volatile", JournalMetrics *metrics,
[STORAGE_PERSISTENT] = "persistent", const char *path,
[STORAGE_NONE] = "none" const char *name,
}; bool verbose,
bool patch_min_use,
uint64_t *available,
uint64_t *limit) {
DEFINE_STRING_TABLE_LOOKUP(storage, Storage); uint64_t sum = 0, ss_avail, avail;
DEFINE_CONFIG_PARSE_ENUM(config_parse_storage, storage, Storage, "Failed to parse storage setting");
static const char* const split_mode_table[_SPLIT_MAX] = {
[SPLIT_LOGIN] = "login",
[SPLIT_UID] = "uid",
[SPLIT_NONE] = "none",
};
DEFINE_STRING_TABLE_LOOKUP(split_mode, SplitMode);
DEFINE_CONFIG_PARSE_ENUM(config_parse_split_mode, split_mode, SplitMode, "Failed to parse split mode setting");
static uint64_t available_space(Server *s, bool verbose) {
char ids[33];
_cleanup_free_ char *p = NULL;
sd_id128_t machine;
struct statvfs ss;
uint64_t sum = 0, ss_avail = 0, avail = 0;
int r;
_cleanup_closedir_ DIR *d = NULL; _cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
struct statvfs ss;
const char *p;
usec_t ts; usec_t ts;
const char *f;
JournalMetrics *m; assert(s);
assert(metrics);
assert(path);
assert(name);
ts = now(CLOCK_MONOTONIC); ts = now(CLOCK_MONOTONIC);
if (s->cached_available_space_timestamp + RECHECK_AVAILABLE_SPACE_USEC > ts if (!verbose && s->cached_space_timestamp + RECHECK_SPACE_USEC > ts) {
&& !verbose)
return s->cached_available_space; if (available)
*available = s->cached_space_available;
if (limit)
*limit = s->cached_space_limit;
r = sd_id128_get_machine(&machine);
if (r < 0)
return 0; return 0;
if (s->system_journal) {
f = "/var/log/journal/";
m = &s->system_metrics;
} else {
f = "/run/log/journal/";
m = &s->runtime_metrics;
} }
assert(m); p = strjoina(path, SERVER_MACHINE_ID(s));
p = strappend(f, sd_id128_to_string(machine, ids));
if (!p)
return 0;
d = opendir(p); d = opendir(p);
if (!d) if (!d)
return 0; return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to open %s: %m", p);
if (fstatvfs(dirfd(d), &ss) < 0) if (fstatvfs(dirfd(d), &ss) < 0)
return 0; return log_error_errno(errno, "Failed to fstatvfs(%s): %m", p);
for (;;) { FOREACH_DIRENT_ALL(de, d, break) {
struct stat st; struct stat st;
struct dirent *de;
errno = 0;
de = readdir(d);
if (!de && errno != 0)
return 0;
if (!de)
break;
if (!endswith(de->d_name, ".journal") && if (!endswith(de->d_name, ".journal") &&
!endswith(de->d_name, ".journal~")) !endswith(de->d_name, ".journal~"))
continue; continue;
if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
log_debug_errno(errno, "Failed to stat %s/%s, ignoring: %m", p, de->d_name);
continue; continue;
}
if (!S_ISREG(st.st_mode)) if (!S_ISREG(st.st_mode))
continue; continue;
@ -155,39 +127,66 @@ static uint64_t available_space(Server *s, bool verbose) {
sum += (uint64_t) st.st_blocks * 512UL; sum += (uint64_t) st.st_blocks * 512UL;
} }
/* If request, then let's bump the min_use limit to the
* current usage on disk. We do this when starting up and
* first opening the journal files. This way sudden spikes in
* disk usage will not cause journald to vacuum files without
* bounds. Note that this means that only a restart of
* journald will make it reset this value. */
if (patch_min_use)
metrics->min_use = MAX(metrics->min_use, sum);
ss_avail = ss.f_bsize * ss.f_bavail; ss_avail = ss.f_bsize * ss.f_bavail;
avail = LESS_BY(ss_avail, metrics->keep_free);
/* If we reached a high mark, we will always allow this much s->cached_space_limit = MIN(MAX(sum + avail, metrics->min_use), metrics->max_use);
* again, unless usage goes above max_use. This watermark s->cached_space_available = LESS_BY(s->cached_space_limit, sum);
* value is cached so that we don't give up space on pressure, s->cached_space_timestamp = ts;
* but hover below the maximum usage. */
if (m->use < sum)
m->use = sum;
avail = LESS_BY(ss_avail, m->keep_free);
s->cached_available_space = LESS_BY(MIN(m->max_use, avail), sum);
s->cached_available_space_timestamp = ts;
if (verbose) { if (verbose) {
char fb1[FORMAT_BYTES_MAX], fb2[FORMAT_BYTES_MAX], fb3[FORMAT_BYTES_MAX], char fb1[FORMAT_BYTES_MAX], fb2[FORMAT_BYTES_MAX], fb3[FORMAT_BYTES_MAX],
fb4[FORMAT_BYTES_MAX], fb5[FORMAT_BYTES_MAX]; fb4[FORMAT_BYTES_MAX], fb5[FORMAT_BYTES_MAX], fb6[FORMAT_BYTES_MAX];
server_driver_message(s, SD_MESSAGE_JOURNAL_USAGE, server_driver_message(s, SD_MESSAGE_JOURNAL_USAGE,
"%s is currently using %s.\n" "%s (%s) is currently using %s.\n"
"Maximum allowed usage is set to %s.\n" "Maximum allowed usage is set to %s.\n"
"Leaving at least %s free (of currently available %s of space).\n" "Leaving at least %s free (of currently available %s of space).\n"
"Enforced usage limit is thus %s.", "Enforced usage limit is thus %s, of which %s are still available.",
s->system_journal ? "Permanent journal (/var/log/journal/)" : "Runtime journal (/run/log/journal/)", name, path,
format_bytes(fb1, sizeof(fb1), sum), format_bytes(fb1, sizeof(fb1), sum),
format_bytes(fb2, sizeof(fb2), m->max_use), format_bytes(fb2, sizeof(fb2), metrics->max_use),
format_bytes(fb3, sizeof(fb3), m->keep_free), format_bytes(fb3, sizeof(fb3), metrics->keep_free),
format_bytes(fb4, sizeof(fb4), ss_avail), format_bytes(fb4, sizeof(fb4), ss_avail),
format_bytes(fb5, sizeof(fb5), s->cached_available_space + sum)); format_bytes(fb5, sizeof(fb5), s->cached_space_limit),
format_bytes(fb6, sizeof(fb6), s->cached_space_available));
} }
return s->cached_available_space; if (available)
*available = s->cached_space_available;
if (limit)
*limit = s->cached_space_limit;
return 1;
}
static int determine_space(Server *s, bool verbose, bool patch_min_use, uint64_t *available, uint64_t *limit) {
JournalMetrics *metrics;
const char *path, *name;
assert(s);
if (s->system_journal) {
path = "/var/log/journal/";
metrics = &s->system_metrics;
name = "System journal";
} else {
path = "/run/log/journal/";
metrics = &s->runtime_metrics;
name = "Runtime journal";
}
return determine_space_for(s, metrics, path, name, verbose, patch_min_use, available, limit);
} }
void server_fix_perms(Server *s, JournalFile *f, uid_t uid) { void server_fix_perms(Server *s, JournalFile *f, uid_t uid) {
@ -326,8 +325,8 @@ void server_rotate(Server *s) {
log_debug("Rotating..."); log_debug("Rotating...");
do_rotate(s, &s->runtime_journal, "runtime", false, 0); (void) do_rotate(s, &s->runtime_journal, "runtime", false, 0);
do_rotate(s, &s->system_journal, "system", s->seal, 0); (void) do_rotate(s, &s->system_journal, "system", s->seal, 0);
ORDERED_HASHMAP_FOREACH_KEY(f, k, s->user_journals, i) { ORDERED_HASHMAP_FOREACH_KEY(f, k, s->user_journals, i) {
r = do_rotate(s, &f, "user", s->seal, PTR_TO_UINT32(k)); r = do_rotate(s, &f, "user", s->seal, PTR_TO_UINT32(k));
@ -368,43 +367,50 @@ void server_sync(Server *s) {
static void do_vacuum( static void do_vacuum(
Server *s, Server *s,
const char *id,
JournalFile *f, JournalFile *f,
const char* path, JournalMetrics *metrics,
JournalMetrics *metrics) { const char *path,
const char *name,
bool verbose,
bool patch_min_use) {
const char *p; const char *p;
uint64_t limit;
int r; int r;
assert(s);
assert(metrics);
assert(path);
assert(name);
if (!f) if (!f)
return; return;
p = strjoina(path, id); p = strjoina(path, SERVER_MACHINE_ID(s));
r = journal_directory_vacuum(p, metrics->max_use, s->max_retention_usec, &s->oldest_file_usec, false);
limit = metrics->max_use;
(void) determine_space_for(s, metrics, path, name, verbose, patch_min_use, NULL, &limit);
r = journal_directory_vacuum(p, limit, metrics->n_max_files, s->max_retention_usec, &s->oldest_file_usec, verbose);
if (r < 0 && r != -ENOENT) if (r < 0 && r != -ENOENT)
log_error_errno(r, "Failed to vacuum %s: %m", p); log_warning_errno(r, "Failed to vacuum %s, ignoring: %m", p);
} }
void server_vacuum(Server *s) { int server_vacuum(Server *s, bool verbose, bool patch_min_use) {
char ids[33]; assert(s);
sd_id128_t machine;
int r;
log_debug("Vacuuming..."); log_debug("Vacuuming...");
s->oldest_file_usec = 0; s->oldest_file_usec = 0;
r = sd_id128_get_machine(&machine); do_vacuum(s, s->system_journal, &s->system_metrics, "/var/log/journal/", "System journal", verbose, patch_min_use);
if (r < 0) { do_vacuum(s, s->runtime_journal, &s->runtime_metrics, "/run/log/journal/", "Runtime journal", verbose, patch_min_use);
log_error_errno(r, "Failed to get machine ID: %m");
return;
}
sd_id128_to_string(machine, ids);
do_vacuum(s, ids, s->system_journal, "/var/log/journal/", &s->system_metrics); s->cached_space_limit = 0;
do_vacuum(s, ids, s->runtime_journal, "/run/log/journal/", &s->runtime_metrics); s->cached_space_available = 0;
s->cached_space_timestamp = 0;
s->cached_available_space_timestamp = 0; return 0;
} }
static void server_cache_machine_id(Server *s) { static void server_cache_machine_id(Server *s) {
@ -502,7 +508,7 @@ static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, unsigned
if (journal_file_rotate_suggested(f, s->max_file_usec)) { if (journal_file_rotate_suggested(f, s->max_file_usec)) {
log_debug("%s: Journal header limits reached or header out-of-date, rotating.", f->path); log_debug("%s: Journal header limits reached or header out-of-date, rotating.", f->path);
server_rotate(s); server_rotate(s);
server_vacuum(s); server_vacuum(s, false, false);
vacuumed = true; vacuumed = true;
f = find_journal(s, uid); f = find_journal(s, uid);
@ -522,7 +528,7 @@ static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, unsigned
} }
server_rotate(s); server_rotate(s);
server_vacuum(s); server_vacuum(s, false, false);
f = find_journal(s, uid); f = find_journal(s, uid);
if (!f) if (!f)
@ -867,6 +873,7 @@ void server_dispatch_message(
int rl, r; int rl, r;
_cleanup_free_ char *path = NULL; _cleanup_free_ char *path = NULL;
uint64_t available = 0;
char *c; char *c;
assert(s); assert(s);
@ -906,9 +913,8 @@ void server_dispatch_message(
} }
} }
rl = journal_rate_limit_test(s->rate_limit, path, (void) determine_space(s, false, false, &available, NULL);
priority & LOG_PRIMASK, available_space(s, false)); rl = journal_rate_limit_test(s->rate_limit, path, priority & LOG_PRIMASK, available);
if (rl == 0) if (rl == 0)
return; return;
@ -923,16 +929,8 @@ finish:
static int system_journal_open(Server *s, bool flush_requested) { static int system_journal_open(Server *s, bool flush_requested) {
int r;
const char *fn; const char *fn;
sd_id128_t machine; int r;
char ids[33];
r = sd_id128_get_machine(&machine);
if (r < 0)
return log_error_errno(r, "Failed to get machine id: %m");
sd_id128_to_string(machine, ids);
if (!s->system_journal && if (!s->system_journal &&
(s->storage == STORAGE_PERSISTENT || s->storage == STORAGE_AUTO) && (s->storage == STORAGE_PERSISTENT || s->storage == STORAGE_AUTO) &&
@ -948,15 +946,15 @@ static int system_journal_open(Server *s, bool flush_requested) {
if (s->storage == STORAGE_PERSISTENT) if (s->storage == STORAGE_PERSISTENT)
(void) mkdir_p("/var/log/journal/", 0755); (void) mkdir_p("/var/log/journal/", 0755);
fn = strjoina("/var/log/journal/", ids); fn = strjoina("/var/log/journal/", SERVER_MACHINE_ID(s));
(void) mkdir(fn, 0755); (void) mkdir(fn, 0755);
fn = strjoina(fn, "/system.journal"); fn = strjoina(fn, "/system.journal");
r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, s->compress, s->seal, &s->system_metrics, s->mmap, NULL, &s->system_journal); r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, s->compress, s->seal, &s->system_metrics, s->mmap, NULL, &s->system_journal);
if (r >= 0) {
if (r >= 0)
server_fix_perms(s, s->system_journal, 0); server_fix_perms(s, s->system_journal, 0);
else if (r < 0) { (void) determine_space_for(s, &s->system_metrics, "/var/log/journal/", "System journal", true, true, NULL, NULL);
} else if (r < 0) {
if (r != -ENOENT && r != -EROFS) if (r != -ENOENT && r != -EROFS)
log_warning_errno(r, "Failed to open system journal: %m"); log_warning_errno(r, "Failed to open system journal: %m");
@ -967,7 +965,7 @@ static int system_journal_open(Server *s, bool flush_requested) {
if (!s->runtime_journal && if (!s->runtime_journal &&
(s->storage != STORAGE_NONE)) { (s->storage != STORAGE_NONE)) {
fn = strjoina("/run/log/journal/", ids, "/system.journal", NULL); fn = strjoina("/run/log/journal/", SERVER_MACHINE_ID(s), "/system.journal");
if (s->system_journal) { if (s->system_journal) {
@ -997,12 +995,12 @@ static int system_journal_open(Server *s, bool flush_requested) {
return log_error_errno(r, "Failed to open runtime journal: %m"); return log_error_errno(r, "Failed to open runtime journal: %m");
} }
if (s->runtime_journal) if (s->runtime_journal) {
server_fix_perms(s, s->runtime_journal, 0); server_fix_perms(s, s->runtime_journal, 0);
(void) determine_space_for(s, &s->runtime_metrics, "/run/log/journal/", "Runtime journal", true, true, NULL, NULL);
}
} }
available_space(s, true);
return r; return r;
} }
@ -1023,7 +1021,7 @@ int server_flush_to_var(Server *s) {
if (!s->runtime_journal) if (!s->runtime_journal)
return 0; return 0;
system_journal_open(s, true); (void) system_journal_open(s, true);
if (!s->system_journal) if (!s->system_journal)
return 0; return 0;
@ -1067,7 +1065,7 @@ int server_flush_to_var(Server *s) {
} }
server_rotate(s); server_rotate(s);
server_vacuum(s); server_vacuum(s, false, false);
if (!s->system_journal) { if (!s->system_journal) {
log_notice("Didn't flush runtime journal since rotation of system journal wasn't successful."); log_notice("Didn't flush runtime journal since rotation of system journal wasn't successful.");
@ -1231,7 +1229,7 @@ static int dispatch_sigusr1(sd_event_source *es, const struct signalfd_siginfo *
server_flush_to_var(s); server_flush_to_var(s);
server_sync(s); server_sync(s);
server_vacuum(s); server_vacuum(s, false, false);
touch("/run/systemd/journal/flushed"); touch("/run/systemd/journal/flushed");
@ -1245,7 +1243,7 @@ static int dispatch_sigusr2(sd_event_source *es, const struct signalfd_siginfo *
log_info("Received request to rotate journal from PID %"PRIu32, si->ssi_pid); log_info("Received request to rotate journal from PID %"PRIu32, si->ssi_pid);
server_rotate(s); server_rotate(s);
server_vacuum(s); server_vacuum(s, true, true);
return 0; return 0;
} }
@ -1472,18 +1470,19 @@ int server_init(Server *s) {
s->max_level_console = LOG_INFO; s->max_level_console = LOG_INFO;
s->max_level_wall = LOG_EMERG; s->max_level_wall = LOG_EMERG;
memset(&s->system_metrics, 0xFF, sizeof(s->system_metrics)); journal_reset_metrics(&s->system_metrics);
memset(&s->runtime_metrics, 0xFF, sizeof(s->runtime_metrics)); journal_reset_metrics(&s->runtime_metrics);
server_parse_config_file(s); server_parse_config_file(s);
server_parse_proc_cmdline(s); server_parse_proc_cmdline(s);
if (!!s->rate_limit_interval ^ !!s->rate_limit_burst) { if (!!s->rate_limit_interval ^ !!s->rate_limit_burst) {
log_debug("Setting both rate limit interval and burst from "USEC_FMT",%u to 0,0", log_debug("Setting both rate limit interval and burst from "USEC_FMT",%u to 0,0",
s->rate_limit_interval, s->rate_limit_burst); s->rate_limit_interval, s->rate_limit_burst);
s->rate_limit_interval = s->rate_limit_burst = 0; s->rate_limit_interval = s->rate_limit_burst = 0;
} }
mkdir_p("/run/systemd/journal", 0755); (void) mkdir_p("/run/systemd/journal", 0755);
s->user_journals = ordered_hashmap_new(NULL); s->user_journals = ordered_hashmap_new(NULL);
if (!s->user_journals) if (!s->user_journals)
@ -1682,3 +1681,22 @@ void server_done(Server *s) {
udev_unref(s->udev); udev_unref(s->udev);
} }
static const char* const storage_table[_STORAGE_MAX] = {
[STORAGE_AUTO] = "auto",
[STORAGE_VOLATILE] = "volatile",
[STORAGE_PERSISTENT] = "persistent",
[STORAGE_NONE] = "none"
};
DEFINE_STRING_TABLE_LOOKUP(storage, Storage);
DEFINE_CONFIG_PARSE_ENUM(config_parse_storage, storage, Storage, "Failed to parse storage setting");
static const char* const split_mode_table[_SPLIT_MAX] = {
[SPLIT_LOGIN] = "login",
[SPLIT_UID] = "uid",
[SPLIT_NONE] = "none",
};
DEFINE_STRING_TABLE_LOOKUP(split_mode, SplitMode);
DEFINE_CONFIG_PARSE_ENUM(config_parse_split_mode, split_mode, SplitMode, "Failed to parse split mode setting");

View File

@ -100,8 +100,9 @@ typedef struct Server {
unsigned n_forward_syslog_missed; unsigned n_forward_syslog_missed;
usec_t last_warn_forward_syslog_missed; usec_t last_warn_forward_syslog_missed;
uint64_t cached_available_space; uint64_t cached_space_available;
usec_t cached_available_space_timestamp; uint64_t cached_space_limit;
usec_t cached_space_timestamp;
uint64_t var_available_timestamp; uint64_t var_available_timestamp;
@ -141,6 +142,8 @@ typedef struct Server {
char *cgroup_root; char *cgroup_root;
} Server; } Server;
#define SERVER_MACHINE_ID(s) ((s)->machine_id_field + strlen("_MACHINE_ID="))
#define N_IOVEC_META_FIELDS 20 #define N_IOVEC_META_FIELDS 20
#define N_IOVEC_KERNEL_FIELDS 64 #define N_IOVEC_KERNEL_FIELDS 64
#define N_IOVEC_UDEV_FIELDS 32 #define N_IOVEC_UDEV_FIELDS 32
@ -166,7 +169,7 @@ void server_fix_perms(Server *s, JournalFile *f, uid_t uid);
int server_init(Server *s); int server_init(Server *s);
void server_done(Server *s); void server_done(Server *s);
void server_sync(Server *s); void server_sync(Server *s);
void server_vacuum(Server *s); int server_vacuum(Server *s, bool verbose, bool patch_min_use);
void server_rotate(Server *s); void server_rotate(Server *s);
int server_schedule_sync(Server *s, int priority); int server_schedule_sync(Server *s, int priority);
int server_flush_to_var(Server *s); int server_flush_to_var(Server *s);

View File

@ -21,8 +21,8 @@
#include <unistd.h> #include <unistd.h>
#include "systemd/sd-messages.h" #include "sd-messages.h"
#include "systemd/sd-daemon.h" #include "sd-daemon.h"
#include "journal-authenticate.h" #include "journal-authenticate.h"
#include "journald-server.h" #include "journald-server.h"
@ -54,7 +54,7 @@ int main(int argc, char *argv[]) {
if (r < 0) if (r < 0)
goto finish; goto finish;
server_vacuum(&server); server_vacuum(&server, false, false);
server_flush_to_var(&server); server_flush_to_var(&server);
server_flush_dev_kmsg(&server); server_flush_dev_kmsg(&server);
@ -82,7 +82,7 @@ int main(int argc, char *argv[]) {
if (server.oldest_file_usec + server.max_retention_usec < n) { if (server.oldest_file_usec + server.max_retention_usec < n) {
log_info("Retention time reached."); log_info("Retention time reached.");
server_rotate(&server); server_rotate(&server);
server_vacuum(&server); server_vacuum(&server, false, false);
continue; continue;
} }

View File

@ -22,9 +22,11 @@
#SystemMaxUse= #SystemMaxUse=
#SystemKeepFree= #SystemKeepFree=
#SystemMaxFileSize= #SystemMaxFileSize=
#SystemMaxFiles=100
#RuntimeMaxUse= #RuntimeMaxUse=
#RuntimeKeepFree= #RuntimeKeepFree=
#RuntimeMaxFileSize= #RuntimeMaxFileSize=
#RuntimeMaxFiles=100
#MaxRetentionSec= #MaxRetentionSec=
#MaxFileSec=1month #MaxFileSec=1month
#ForwardToSyslog=no #ForwardToSyslog=no

View File

@ -197,7 +197,7 @@ static void test_skip(void (*setup)(void)) {
if (arg_keep) if (arg_keep)
log_info("Not removing %s", t); log_info("Not removing %s", t);
else { else {
journal_directory_vacuum(".", 3000000, 0, NULL, true); journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
} }
@ -282,7 +282,7 @@ static void test_sequence_numbers(void) {
if (arg_keep) if (arg_keep)
log_info("Not removing %s", t); log_info("Not removing %s", t);
else { else {
journal_directory_vacuum(".", 3000000, 0, NULL, true); journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
} }

View File

@ -116,7 +116,7 @@ static void test_non_empty(void) {
if (arg_keep) if (arg_keep)
log_info("Not removing %s", t); log_info("Not removing %s", t);
else { else {
journal_directory_vacuum(".", 3000000, 0, NULL, true); journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
} }
@ -155,7 +155,7 @@ static void test_empty(void) {
if (arg_keep) if (arg_keep)
log_info("Not removing %s", t); log_info("Not removing %s", t);
else { else {
journal_directory_vacuum(".", 3000000, 0, NULL, true); journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
} }