diff --git a/man/journalctl.xml b/man/journalctl.xml
index 7c1b094ee0..58f3aa205a 100644
--- a/man/journalctl.xml
+++ b/man/journalctl.xml
@@ -749,32 +749,28 @@
- Removes the oldest archived journal files until the disk
- space they use falls below the specified size (specified with
- the usual K, M,
- G and T suffixes), or all
- archived journal files contain no data older than the specified
- timespan (specified with the usual s,
- m, h,
- days, months,
- weeks and years suffixes),
- or no more than the specified number of separate journal files
- remain. Note that running has
- only an indirect effect on the output shown by
- , as the latter includes active
- journal files, while the vacuuming operation only operates
- on archived journal files. Similarly,
- might not actually reduce the
- number of journal files to below the specified number, as it
- will not remove active journal
- files. ,
- and
- 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.
+ Removes the oldest archived journal files until the disk space they use falls below the
+ specified size (specified with the usual K, M, G and
+ T suffixes), or all archived journal files contain no data older than the specified timespan
+ (specified with the usual s, m, h,
+ days, months, weeks and years
+ suffixes), or no more than the specified number of separate journal files remain. Note that running
+ has only an indirect effect on the output shown by
+ , as the latter includes active journal files, while the vacuuming operation only
+ operates on archived journal files. Similarly, might not actually reduce the
+ number of journal files to below the specified number, as it will not remove active journal
+ files.
+
+ , and
+ 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.
+
+ These three switches may also be combined with into one command. If so, all
+ active files are rotated first, and the requested vacuuming operation is executed right after. The rotation has
+ the effect that all currently active files are archived (and potentially new, empty journal files opened as
+ replacement), and hence the vacuuming operation has the greatest effect as it can take all log data written so
+ far into account.
@@ -896,9 +892,12 @@
- Asks the journal daemon to rotate journal
- files. This call does not return until the rotation operation
- is complete.
+ Asks the journal daemon to rotate journal files. This call does not return until the rotation
+ operation is complete. Journal file rotation has the effect that all currently active journal files are marked
+ as archived and renamed, so that they are never written to in future. New (empty) journal files are then
+ created in their place. This operation may be combined with ,
+ and into a single command, see
+ above.
diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c
index 3b19d3c444..c4addc6283 100644
--- a/src/journal/journal-file.c
+++ b/src/journal/journal-file.c
@@ -3224,30 +3224,30 @@ int journal_file_open(
if (fname && (flags & O_CREAT) && !endswith(fname, ".journal"))
return -EINVAL;
- f = new0(JournalFile, 1);
+ f = new(JournalFile, 1);
if (!f)
return -ENOMEM;
- f->fd = fd;
- f->mode = mode;
+ *f = (JournalFile) {
+ .fd = fd,
+ .mode = mode,
+
+ .flags = flags,
+ .prot = prot_from_flags(flags),
+ .writable = (flags & O_ACCMODE) != O_RDONLY,
- f->flags = flags;
- f->prot = prot_from_flags(flags);
- f->writable = (flags & O_ACCMODE) != O_RDONLY;
#if HAVE_LZ4
- f->compress_lz4 = compress;
+ .compress_lz4 = compress,
#elif HAVE_XZ
- f->compress_xz = compress;
+ .compress_xz = compress,
#endif
-
- if (compress_threshold_bytes == (uint64_t) -1)
- f->compress_threshold_bytes = DEFAULT_COMPRESS_THRESHOLD;
- else
- f->compress_threshold_bytes = MAX(MIN_COMPRESS_THRESHOLD, compress_threshold_bytes);
-
+ .compress_threshold_bytes = compress_threshold_bytes == (uint64_t) -1 ?
+ DEFAULT_COMPRESS_THRESHOLD :
+ MAX(MIN_COMPRESS_THRESHOLD, compress_threshold_bytes),
#if HAVE_GCRYPT
- f->seal = seal;
+ .seal = seal,
#endif
+ };
log_debug("Journal effective settings seal=%s compress=%s compress_threshold_bytes=%s",
yes_no(f->seal), yes_no(JOURNAL_FILE_COMPRESS(f)),
@@ -3437,72 +3437,143 @@ fail:
return r;
}
-int journal_file_rotate(JournalFile **f, bool compress, uint64_t compress_threshold_bytes, bool seal, Set *deferred_closes) {
+int journal_file_archive(JournalFile *f) {
_cleanup_free_ char *p = NULL;
- size_t l;
- JournalFile *old_file, *new_file = NULL;
+
+ assert(f);
+
+ if (!f->writable)
+ return -EINVAL;
+
+ /* Is this a journal file that was passed to us as fd? If so, we synthesized a path name for it, and we refuse
+ * rotation, since we don't know the actual path, and couldn't rename the file hence. */
+ if (path_startswith(f->path, "/proc/self/fd"))
+ return -EINVAL;
+
+ if (!endswith(f->path, ".journal"))
+ return -EINVAL;
+
+ if (asprintf(&p, "%.*s@" SD_ID128_FORMAT_STR "-%016"PRIx64"-%016"PRIx64".journal",
+ (int) strlen(f->path) - 8, f->path,
+ SD_ID128_FORMAT_VAL(f->header->seqnum_id),
+ le64toh(f->header->head_entry_seqnum),
+ le64toh(f->header->head_entry_realtime)) < 0)
+ return -ENOMEM;
+
+ /* Try to rename the file to the archived version. If the file already was deleted, we'll get ENOENT, let's
+ * ignore that case. */
+ if (rename(f->path, p) < 0 && errno != ENOENT)
+ return -errno;
+
+ /* Sync the rename to disk */
+ (void) fsync_directory_of_file(f->fd);
+
+ /* Set as archive so offlining commits w/state=STATE_ARCHIVED. Previously we would set old_file->header->state
+ * to STATE_ARCHIVED directly here, but journal_file_set_offline() short-circuits when state != STATE_ONLINE,
+ * which would result in the rotated journal never getting fsync() called before closing. Now we simply queue
+ * the archive state by setting an archive bit, leaving the state as STATE_ONLINE so proper offlining
+ * occurs. */
+ f->archive = true;
+
+ /* Currently, btrfs is not very good with out write patterns and fragments heavily. Let's defrag our journal
+ * files when we archive them */
+ f->defrag_on_close = true;
+
+ return 0;
+}
+
+JournalFile* journal_initiate_close(
+ JournalFile *f,
+ Set *deferred_closes) {
+
+ int r;
+
+ assert(f);
+
+ if (deferred_closes) {
+
+ r = set_put(deferred_closes, f);
+ if (r < 0)
+ log_debug_errno(r, "Failed to add file to deferred close set, closing immediately.");
+ else {
+ (void) journal_file_set_offline(f, false);
+ return NULL;
+ }
+ }
+
+ return journal_file_close(f);
+}
+
+int journal_file_rotate(
+ JournalFile **f,
+ bool compress,
+ uint64_t compress_threshold_bytes,
+ bool seal,
+ Set *deferred_closes) {
+
+ JournalFile *new_file = NULL;
int r;
assert(f);
assert(*f);
- old_file = *f;
-
- if (!old_file->writable)
- return -EINVAL;
-
- /* Is this a journal file that was passed to us as fd? If so, we synthesized a path name for it, and we refuse
- * rotation, since we don't know the actual path, and couldn't rename the file hence. */
- if (path_startswith(old_file->path, "/proc/self/fd"))
- return -EINVAL;
-
- if (!endswith(old_file->path, ".journal"))
- return -EINVAL;
-
- l = strlen(old_file->path);
- r = asprintf(&p, "%.*s@" SD_ID128_FORMAT_STR "-%016"PRIx64"-%016"PRIx64".journal",
- (int) l - 8, old_file->path,
- SD_ID128_FORMAT_VAL(old_file->header->seqnum_id),
- le64toh((*f)->header->head_entry_seqnum),
- le64toh((*f)->header->head_entry_realtime));
+ r = journal_file_archive(*f);
if (r < 0)
+ return r;
+
+ r = journal_file_open(
+ -1,
+ (*f)->path,
+ (*f)->flags,
+ (*f)->mode,
+ compress,
+ compress_threshold_bytes,
+ seal,
+ NULL, /* metrics */
+ (*f)->mmap,
+ deferred_closes,
+ *f, /* template */
+ &new_file);
+
+ journal_initiate_close(*f, deferred_closes);
+ *f = new_file;
+
+ return r;
+}
+
+int journal_file_dispose(int dir_fd, const char *fname) {
+ _cleanup_free_ char *p = NULL;
+ _cleanup_close_ int fd = -1;
+
+ assert(fname);
+
+ /* Renames a journal file to *.journal~, i.e. to mark it as corruped or otherwise uncleanly shutdown. Note that
+ * this is done without looking into the file or changing any of its contents. The idea is that this is called
+ * whenever something is suspicious and we want to move the file away and make clear that it is not accessed
+ * for writing anymore. */
+
+ if (!endswith(fname, ".journal"))
+ return -EINVAL;
+
+ if (asprintf(&p, "%.*s@%016" PRIx64 "-%016" PRIx64 ".journal~",
+ (int) strlen(fname) - 8, fname,
+ now(CLOCK_REALTIME),
+ random_u64()) < 0)
return -ENOMEM;
- /* Try to rename the file to the archived version. If the file
- * already was deleted, we'll get ENOENT, let's ignore that
- * case. */
- r = rename(old_file->path, p);
- if (r < 0 && errno != ENOENT)
+ if (renameat(dir_fd, fname, dir_fd, p) < 0)
return -errno;
- /* Sync the rename to disk */
- (void) fsync_directory_of_file(old_file->fd);
+ /* btrfs doesn't cope well with our write pattern and fragments heavily. Let's defrag all files we rotate */
+ fd = openat(dir_fd, p, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+ if (fd < 0)
+ log_debug_errno(errno, "Failed to open file for defragmentation/FS_NOCOW_FL, ignoring: %m");
+ else {
+ (void) chattr_fd(fd, 0, FS_NOCOW_FL, NULL);
+ (void) btrfs_defrag_fd(fd);
+ }
- /* Set as archive so offlining commits w/state=STATE_ARCHIVED.
- * Previously we would set old_file->header->state to STATE_ARCHIVED directly here,
- * but journal_file_set_offline() short-circuits when state != STATE_ONLINE, which
- * would result in the rotated journal never getting fsync() called before closing.
- * Now we simply queue the archive state by setting an archive bit, leaving the state
- * as STATE_ONLINE so proper offlining occurs. */
- old_file->archive = true;
-
- /* Currently, btrfs is not very good with out write patterns
- * and fragments heavily. Let's defrag our journal files when
- * we archive them */
- old_file->defrag_on_close = true;
-
- r = journal_file_open(-1, old_file->path, old_file->flags, old_file->mode, compress,
- compress_threshold_bytes, seal, NULL, old_file->mmap, deferred_closes,
- old_file, &new_file);
-
- if (deferred_closes &&
- set_put(deferred_closes, old_file) >= 0)
- (void) journal_file_set_offline(old_file, false);
- else
- (void) journal_file_close(old_file);
-
- *f = new_file;
- return r;
+ return 0;
}
int journal_file_open_reliably(
@@ -3518,9 +3589,8 @@ int journal_file_open_reliably(
JournalFile *template,
JournalFile **ret) {
- int r;
- size_t l;
_cleanup_free_ char *p = NULL;
+ int r;
r = journal_file_open(-1, fname, flags, mode, compress, compress_threshold_bytes, seal, metrics, mmap_cache,
deferred_closes, template, ret);
@@ -3546,25 +3616,12 @@ int journal_file_open_reliably(
return r;
/* The file is corrupted. Rotate it away and try it again (but only once) */
-
- l = strlen(fname);
- if (asprintf(&p, "%.*s@%016"PRIx64 "-%016"PRIx64 ".journal~",
- (int) l - 8, fname,
- now(CLOCK_REALTIME),
- random_u64()) < 0)
- return -ENOMEM;
-
- if (rename(fname, p) < 0)
- return -errno;
-
- /* btrfs doesn't cope well with our write pattern and
- * fragments heavily. Let's defrag all files we rotate */
-
- (void) chattr_path(p, 0, FS_NOCOW_FL, NULL);
- (void) btrfs_defrag(p);
-
log_warning_errno(r, "File %s corrupted or uncleanly shut down, renaming and replacing.", fname);
+ r = journal_file_dispose(AT_FDCWD, fname);
+ if (r < 0)
+ return r;
+
return journal_file_open(-1, fname, flags, mode, compress, compress_threshold_bytes, seal, metrics, mmap_cache,
deferred_closes, template, ret);
}
diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h
index c8114ee2d0..09b7ba090d 100644
--- a/src/journal/journal-file.h
+++ b/src/journal/journal-file.h
@@ -235,8 +235,12 @@ int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint6
void journal_file_dump(JournalFile *f);
void journal_file_print_header(JournalFile *f);
+int journal_file_archive(JournalFile *f);
+JournalFile* journal_initiate_close(JournalFile *f, Set *deferred_closes);
int journal_file_rotate(JournalFile **f, bool compress, uint64_t compress_threshold_bytes, bool seal, Set *deferred_closes);
+int journal_file_dispose(int dir_fd, const char *fname);
+
void journal_file_post_change(JournalFile *f);
int journal_file_enable_post_change_timer(JournalFile *f, sd_event *e, usec_t t);
diff --git a/src/journal/journal-vacuum.c b/src/journal/journal-vacuum.c
index 840fb69ee0..2778ce40c5 100644
--- a/src/journal/journal-vacuum.c
+++ b/src/journal/journal-vacuum.c
@@ -15,6 +15,7 @@
#include "journal-vacuum.h"
#include "parse-util.h"
#include "string-util.h"
+#include "time-util.h"
#include "util.h"
#include "xattr-util.h"
@@ -125,11 +126,10 @@ int journal_directory_vacuum(
usec_t *oldest_usec,
bool verbose) {
+ uint64_t sum = 0, freed = 0, n_active_files = 0;
+ size_t n_list = 0, n_allocated = 0, i;
_cleanup_closedir_ DIR *d = NULL;
struct vacuum_info *list = NULL;
- unsigned n_list = 0, i, n_active_files = 0;
- size_t n_allocated = 0;
- uint64_t sum = 0, freed = 0;
usec_t retention_limit = 0;
char sbytes[FORMAT_BYTES_MAX];
struct dirent *de;
@@ -140,13 +140,8 @@ int journal_directory_vacuum(
if (max_use <= 0 && max_retention_usec <= 0 && n_max_files <= 0)
return 0;
- if (max_retention_usec > 0) {
- retention_limit = now(CLOCK_REALTIME);
- if (retention_limit > max_retention_usec)
- retention_limit -= max_retention_usec;
- else
- max_retention_usec = retention_limit = 0;
- }
+ if (max_retention_usec > 0)
+ retention_limit = usec_sub_unsigned(now(CLOCK_REALTIME), max_retention_usec);
d = opendir(directory);
if (!d)
@@ -272,13 +267,14 @@ int journal_directory_vacuum(
goto finish;
}
- list[n_list].filename = TAKE_PTR(p);
- list[n_list].usage = size;
- list[n_list].seqnum = seqnum;
- list[n_list].realtime = realtime;
- list[n_list].seqnum_id = seqnum_id;
- list[n_list].have_seqnum = have_seqnum;
- n_list++;
+ list[n_list++] = (struct vacuum_info) {
+ .filename = TAKE_PTR(p),
+ .usage = size,
+ .seqnum = seqnum,
+ .realtime = realtime,
+ .seqnum_id = seqnum_id,
+ .have_seqnum = have_seqnum,
+ };
sum += size;
}
@@ -286,7 +282,7 @@ int journal_directory_vacuum(
typesafe_qsort(list, n_list, vacuum_compare);
for (i = 0; i < n_list; i++) {
- unsigned left;
+ uint64_t left;
left = n_active_files + n_list - i;
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 9bd2d9a150..93466c3d4f 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -167,6 +167,7 @@ static enum {
ACTION_SYNC,
ACTION_ROTATE,
ACTION_VACUUM,
+ ACTION_ROTATE_AND_VACUUM,
ACTION_LIST_FIELDS,
ACTION_LIST_FIELD_NAMES,
} arg_action = ACTION_SHOW;
@@ -687,7 +688,7 @@ static int parse_argv(int argc, char *argv[]) {
return r;
}
- arg_action = ACTION_VACUUM;
+ arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
break;
case ARG_VACUUM_FILES:
@@ -697,7 +698,7 @@ static int parse_argv(int argc, char *argv[]) {
return r;
}
- arg_action = ACTION_VACUUM;
+ arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
break;
case ARG_VACUUM_TIME:
@@ -707,7 +708,7 @@ static int parse_argv(int argc, char *argv[]) {
return r;
}
- arg_action = ACTION_VACUUM;
+ arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
break;
#if HAVE_GCRYPT
@@ -896,7 +897,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_ROTATE:
- arg_action = ACTION_ROTATE;
+ arg_action = arg_action == ACTION_VACUUM ? ACTION_ROTATE_AND_VACUUM : ACTION_ROTATE;
break;
case ARG_SYNC:
@@ -1970,7 +1971,7 @@ static int send_signal_and_wait(int sig, const char *watch_path) {
/* See if a sync happened by now. */
r = read_timestamp_file(watch_path, &tstamp);
if (r < 0 && r != -ENOENT)
- return log_error_errno(errno, "Failed to read %s: %m", watch_path);
+ return log_error_errno(r, "Failed to read %s: %m", watch_path);
if (r >= 0 && tstamp >= start)
return 0;
@@ -2120,6 +2121,7 @@ int main(int argc, char *argv[]) {
case ACTION_DISK_USAGE:
case ACTION_LIST_BOOTS:
case ACTION_VACUUM:
+ case ACTION_ROTATE_AND_VACUUM:
case ACTION_LIST_FIELDS:
case ACTION_LIST_FIELD_NAMES:
/* These ones require access to the journal files, continue below. */
@@ -2237,6 +2239,14 @@ int main(int argc, char *argv[]) {
r = list_boots(j);
goto finish;
+ case ACTION_ROTATE_AND_VACUUM:
+
+ r = rotate();
+ if (r < 0)
+ goto finish;
+
+ _fallthrough_;
+
case ACTION_VACUUM: {
Directory *d;
Iterator i;
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index e0ec438b1e..cd89886414 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -74,6 +74,8 @@
* for a bit of additional metadata. */
#define DEFAULT_LINE_MAX (48*1024)
+#define DEFERRED_CLOSES_MAX (4096)
+
static int determine_path_usage(Server *s, const char *path, uint64_t *ret_used, uint64_t *ret_free) {
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
@@ -252,8 +254,9 @@ static int open_journal(
bool seal,
JournalMetrics *metrics,
JournalFile **ret) {
- int r;
+
JournalFile *f;
+ int r;
assert(s);
assert(fname);
@@ -399,17 +402,21 @@ static JournalFile* find_journal(Server *s, uid_t uid) {
if (uid_for_system_journal(uid))
return s->system_journal;
- r = sd_id128_get_machine(&machine);
- if (r < 0)
- return s->system_journal;
-
f = ordered_hashmap_get(s->user_journals, UID_TO_PTR(uid));
if (f)
return f;
- if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/user-"UID_FMT".journal",
- SD_ID128_FORMAT_VAL(machine), uid) < 0)
+ r = sd_id128_get_machine(&machine);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to determine machine ID, using system log: %m");
return s->system_journal;
+ }
+
+ if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/user-"UID_FMT".journal",
+ SD_ID128_FORMAT_VAL(machine), uid) < 0) {
+ log_oom();
+ return s->system_journal;
+ }
while (ordered_hashmap_size(s->user_journals) >= USER_JOURNALS_MAX) {
/* Too many open? Then let's close one */
@@ -459,17 +466,81 @@ static int do_rotate(
return r;
}
-void server_rotate(Server *s) {
+static void server_process_deferred_closes(Server *s) {
JournalFile *f;
- void *k;
Iterator i;
+
+ /* Perform any deferred closes which aren't still offlining. */
+ SET_FOREACH(f, s->deferred_closes, i) {
+ if (journal_file_is_offlining(f))
+ continue;
+
+ (void) set_remove(s->deferred_closes, f);
+ (void) journal_file_close(f);
+ }
+}
+
+static void server_vacuum_deferred_closes(Server *s) {
+ assert(s);
+
+ /* Make some room in the deferred closes list, so that it doesn't grow without bounds */
+ if (set_size(s->deferred_closes) < DEFERRED_CLOSES_MAX)
+ return;
+
+ /* Let's first remove all journal files that might already have completed closing */
+ server_process_deferred_closes(s);
+
+ /* And now, let's close some more until we reach the limit again. */
+ while (set_size(s->deferred_closes) >= DEFERRED_CLOSES_MAX) {
+ JournalFile *f;
+
+ assert_se(f = set_steal_first(s->deferred_closes));
+ journal_file_close(f);
+ }
+}
+
+static int open_user_journal_directory(Server *s, DIR **ret_dir, char **ret_path) {
+ _cleanup_closedir_ DIR *dir = NULL;
+ _cleanup_free_ char *path = NULL;
+ sd_id128_t machine;
+ int r;
+
+ assert(s);
+
+ r = sd_id128_get_machine(&machine);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine machine ID, ignoring: %m");
+
+ if (asprintf(&path, "/var/log/journal/" SD_ID128_FORMAT_STR "/", SD_ID128_FORMAT_VAL(machine)) < 0)
+ return log_oom();
+
+ dir = opendir(path);
+ if (!dir)
+ return log_error_errno(errno, "Failed to open user journal directory '%s': %m", path);
+
+ if (ret_dir)
+ *ret_dir = TAKE_PTR(dir);
+ if (ret_path)
+ *ret_path = TAKE_PTR(path);
+
+ return 0;
+}
+
+void server_rotate(Server *s) {
+ _cleanup_free_ char *path = NULL;
+ _cleanup_closedir_ DIR *d = NULL;
+ JournalFile *f;
+ Iterator i;
+ void *k;
int r;
log_debug("Rotating...");
+ /* First, rotate the system journal (either in its runtime flavour or in its runtime flavour) */
(void) do_rotate(s, &s->runtime_journal, "runtime", false, 0);
(void) do_rotate(s, &s->system_journal, "system", s->seal, 0);
+ /* Then, rotate all user journals we have open (keeping them open) */
ORDERED_HASHMAP_FOREACH_KEY(f, k, s->user_journals, i) {
r = do_rotate(s, &f, "user", s->seal, PTR_TO_UID(k));
if (r >= 0)
@@ -479,12 +550,92 @@ void server_rotate(Server *s) {
ordered_hashmap_remove(s->user_journals, k);
}
- /* Perform any deferred closes which aren't still offlining. */
- SET_FOREACH(f, s->deferred_closes, i)
- if (!journal_file_is_offlining(f)) {
- (void) set_remove(s->deferred_closes, f);
- (void) journal_file_close(f);
+ /* Finally, also rotate all user journals we currently do not have open. */
+ r = open_user_journal_directory(s, &d, &path);
+ if (r >= 0) {
+ struct dirent *de;
+
+ FOREACH_DIRENT(de, d, log_warning_errno(errno, "Failed to enumerate %s, ignoring: %m", path)) {
+ _cleanup_free_ char *u = NULL, *full = NULL;
+ _cleanup_close_ int fd = -1;
+ const char *a, *b;
+ uid_t uid;
+
+ a = startswith(de->d_name, "user-");
+ if (!a)
+ continue;
+ b = endswith(de->d_name, ".journal");
+ if (!b)
+ continue;
+
+ u = strndup(a, b-a);
+ if (!u) {
+ log_oom();
+ break;
+ }
+
+ r = parse_uid(u, &uid);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to parse UID from file name '%s', ignoring: %m", de->d_name);
+ continue;
+ }
+
+ /* Already rotated in the above loop? i.e. is it an open user journal? */
+ if (ordered_hashmap_contains(s->user_journals, UID_TO_PTR(uid)))
+ continue;
+
+ full = strjoin(path, de->d_name);
+ if (!full) {
+ log_oom();
+ break;
+ }
+
+ fd = openat(dirfd(d), de->d_name, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK);
+ if (fd < 0) {
+ log_full_errno(IN_SET(errno, ELOOP, ENOENT) ? LOG_DEBUG : LOG_WARNING, errno,
+ "Failed to open journal file '%s' for rotation: %m", full);
+ continue;
+ }
+
+ /* Make some room in the set of deferred close()s */
+ server_vacuum_deferred_closes(s);
+
+ /* Open the file briefly, so that we can archive it */
+ r = journal_file_open(fd,
+ full,
+ O_RDWR,
+ 0640,
+ s->compress.enabled,
+ s->compress.threshold_bytes,
+ s->seal,
+ &s->system_storage.metrics,
+ s->mmap,
+ s->deferred_closes,
+ NULL,
+ &f);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to read journal file %s for rotation, trying to move it out of the way: %m", full);
+
+ r = journal_file_dispose(dirfd(d), de->d_name);
+ if (r < 0)
+ log_warning_errno(r, "Failed to move %s out of the way, ignoring: %m", full);
+ else
+ log_debug("Successfully moved %s out of the way.", full);
+
+ continue;
+ }
+
+ TAKE_FD(fd); /* Donated to journal_file_open() */
+
+ r = journal_file_archive(f);
+ if (r < 0)
+ log_debug_errno(r, "Failed to archive journal file '%s', ignoring: %m", full);
+
+ f = journal_initiate_close(f, s->deferred_closes);
}
+ }
+
+ server_process_deferred_closes(s);
}
void server_sync(Server *s) {