Before, logs from deleted machines have been piling up, since there were
no garbadge collection mechanism. Now virtlogd can be configured to
periodically scan log folder for orphan logs with no recent modfications
and delete it.
Signed-off-by: Oleg Vasilev <oleg.vasilev(a)virtuozzo.com>
---
src/logging/log_daemon_config.c | 9 +++
src/logging/log_daemon_config.h | 3 +
src/logging/log_handler.c | 108 +++++++++++++++++++++++++++++++
src/logging/test_virtlogd.aug.in | 2 +
src/logging/virtlogd.aug | 2 +
src/logging/virtlogd.conf | 7 ++
6 files changed, 131 insertions(+)
diff --git a/src/logging/log_daemon_config.c b/src/logging/log_daemon_config.c
index 4436745488..248bd927d3 100644
--- a/src/logging/log_daemon_config.c
+++ b/src/logging/log_daemon_config.c
@@ -28,6 +28,7 @@
#include "virutil.h"
#define VIR_FROM_THIS VIR_FROM_CONF
+#define DEFAULT_LOG_ROOT LOCALSTATEDIR "/log/libvirt/"
VIR_LOG_INIT("logging.log_daemon_config");
@@ -60,6 +61,7 @@ virLogDaemonConfigNew(bool privileged G_GNUC_UNUSED)
data->admin_max_clients = 5000;
data->max_size = 1024 * 1024 * 2;
data->max_backups = 3;
+ data->max_age_days = 0;
return data;
}
@@ -72,6 +74,7 @@ virLogDaemonConfigFree(virLogDaemonConfig *data)
g_free(data->log_filters);
g_free(data->log_outputs);
+ g_free(data->log_root);
g_free(data);
}
@@ -94,6 +97,12 @@ virLogDaemonConfigLoadOptions(virLogDaemonConfig *data,
return -1;
if (virConfGetValueSizeT(conf, "max_backups", &data->max_backups)
< 0)
return -1;
+ if (virConfGetValueSizeT(conf, "max_age_days", &data->max_age_days)
< 0)
+ return -1;
+ if (virConfGetValueString(conf, "log_root", &data->log_root) <
0)
+ return -1;
+ if (!data->log_root)
+ data->log_root = g_strdup(DEFAULT_LOG_ROOT);
return 0;
}
diff --git a/src/logging/log_daemon_config.h b/src/logging/log_daemon_config.h
index 2ab0f67c96..43922feedf 100644
--- a/src/logging/log_daemon_config.h
+++ b/src/logging/log_daemon_config.h
@@ -33,6 +33,9 @@ struct _virLogDaemonConfig {
size_t max_backups;
size_t max_size;
+
+ char *log_root;
+ size_t max_age_days;
};
diff --git a/src/logging/log_handler.c b/src/logging/log_handler.c
index 7342404b00..2690d7519b 100644
--- a/src/logging/log_handler.c
+++ b/src/logging/log_handler.c
@@ -33,6 +33,7 @@
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
+#include <fnmatch.h>
#include "configmake.h"
@@ -42,6 +43,10 @@ VIR_LOG_INIT("logging.log_handler");
#define DEFAULT_MODE 0600
+/* Cleanup log root (/var/log/libvirt) and all subfolders (e.g. /var/log/libvirt/qemu)
*/
+#define CLEANUP_OBSOLETE_LOG_DEPTH 1
+#define CLEANUP_OBSOLETE_LOG_TIMEOUT_MS (24 * 3600 * 1000) /* One day */
+
typedef struct _virLogHandlerLogFile virLogHandlerLogFile;
struct _virLogHandlerLogFile {
virRotatingFileWriter *file;
@@ -60,6 +65,8 @@ struct _virLogHandler {
bool privileged;
virLogDaemonConfig *config;
+ int cleanup_log_timer;
+
virLogHandlerLogFile **files;
size_t nfiles;
@@ -81,6 +88,93 @@ virLogHandlerOnceInit(void)
VIR_ONCE_GLOBAL_INIT(virLogHandler);
+static void
+virLogHandlerMaybeCleanupObsoleteLog(virLogHandler *handler,
+ const char* path) {
+ size_t i;
+ bool remove = true;
+
+ if (fnmatch("*.log*", path, 0))
+ return;
+
+ virObjectLock(handler);
+ for (i = 0; i < handler->nfiles; i++) {
+ virLogHandlerLogFile *file = handler->files[i];
+ if (STRPREFIX(path, virRotatingFileWriterGetPath(file->file))) {
+ remove = false;
+ break;
+ }
+ }
+ virObjectUnlock(handler);
+
+ if (!remove)
+ return;
+
+ if (unlink(path) < 0) {
+ virReportSystemError(errno, _("Unable to delete %s"), path);
+ }
+}
+
+static void
+virLogHandlerCleanupObsoleteLogsFolder(virLogHandler *handler,
+ time_t oldest_to_keep,
+ const char *path,
+ int depth_left)
+{
+ DIR *dir;
+ struct dirent *entry;
+ char *newpath;
+ struct stat sb;
+
+ if (virDirOpenIfExists(&dir, path) < 0)
+ return;
+
+ while (virDirRead(dir, &entry, path) > 0) {
+ if (STREQ(entry->d_name, "."))
+ continue;
+ if (STREQ(entry->d_name, ".."))
+ continue;
+
+ newpath = g_strdup_printf("%s/%s", path, entry->d_name);
+
+ if (stat(newpath, &sb) < 0) {
+ virReportSystemError(errno, _("Unable to stat %s"), newpath);
+ goto next;
+ }
+
+ if (S_ISDIR(sb.st_mode)) {
+ if (depth_left > 0)
+ virLogHandlerCleanupObsoleteLogsFolder(handler, oldest_to_keep, newpath,
depth_left - 1);
+ goto next;
+ }
+
+ if (!S_ISREG(sb.st_mode)) {
+ goto next;
+ }
+
+ if (sb.st_mtim.tv_sec > oldest_to_keep) {
+ goto next;
+ }
+
+ virLogHandlerMaybeCleanupObsoleteLog(handler, newpath);
+
+ next:
+ VIR_FREE(newpath);
+ }
+
+ virDirClose(dir);
+}
+
+static void
+virLogHandlerCleanupObsoleteLogs(int timer G_GNUC_UNUSED, void* opaque)
+{
+ virLogHandler *handler = opaque;
+ time_t oldest_to_keep = time(NULL) - 3600 * 24 *
handler->config->max_age_days;
+ const char *log_root = handler->config->log_root;
+
+ virLogHandlerCleanupObsoleteLogsFolder(handler, oldest_to_keep, log_root,
CLEANUP_OBSOLETE_LOG_DEPTH);
+}
+
static void
virLogHandlerLogFileFree(virLogHandlerLogFile *file)
@@ -201,7 +295,19 @@ virLogHandlerNew(bool privileged,
handler->inhibitor = inhibitor;
handler->opaque = opaque;
+ if (config->max_age_days > 0) {
+ handler->cleanup_log_timer =
virEventAddTimeout(CLEANUP_OBSOLETE_LOG_TIMEOUT_MS,
+
virLogHandlerCleanupObsoleteLogs,
+ handler, NULL);
+ if (handler->cleanup_log_timer < 0)
+ goto error;
+ }
+
return handler;
+
+ error:
+ virObjectUnref(handler);
+ return NULL;
}
@@ -344,6 +450,8 @@ virLogHandlerDispose(void *obj)
virLogHandlerLogFileFree(handler->files[i]);
}
g_free(handler->files);
+ if (handler->cleanup_log_timer != 0)
+ virEventRemoveTimeout(handler->cleanup_log_timer);
}
diff --git a/src/logging/test_virtlogd.aug.in b/src/logging/test_virtlogd.aug.in
index cd5b0d91f8..8dfad39506 100644
--- a/src/logging/test_virtlogd.aug.in
+++ b/src/logging/test_virtlogd.aug.in
@@ -9,3 +9,5 @@ module Test_virtlogd =
{ "admin_max_clients" = "5" }
{ "max_size" = "2097152" }
{ "max_backups" = "3" }
+ { "max_age_days" = "0" }
+ { "log_root" = "/var/log/libvirt" }
diff --git a/src/logging/virtlogd.aug b/src/logging/virtlogd.aug
index 0f1b290c72..bdf61dea6e 100644
--- a/src/logging/virtlogd.aug
+++ b/src/logging/virtlogd.aug
@@ -31,6 +31,8 @@ module Virtlogd =
| int_entry "admin_max_clients"
| int_entry "max_size"
| int_entry "max_backups"
+ | int_entry "max_age_days"
+ | str_entry "log_root"
(* Each entry in the config is one of the following three ... *)
let entry = logging_entry
diff --git a/src/logging/virtlogd.conf b/src/logging/virtlogd.conf
index c53a1112bd..d88ce31327 100644
--- a/src/logging/virtlogd.conf
+++ b/src/logging/virtlogd.conf
@@ -101,3 +101,10 @@
# Maximum number of backup files to keep. Defaults to 3,
# not including the primary active file
#max_backups = 3
+
+# Maximum age for log files to live after the last modification.
+# Defaults to 0, which means "forever".
+#max_age_days = 0
+
+# Root of all logs managed by virtlogd. Used to GC logs from obsolete machines.
+#log_root = "/var/log/libvirt"
--
2.38.1