qemu: support VIR_DOMAIN_DESTROY_REMOVE_LOGS flag

Note that we attempt to remove logs only if virtlogd is in use.
Otherwise we do not know the pattern for rotated files.

For example for VM named "foo" we can not use "foo.log*" pattern to
remove rotated logs as we can have VM named "foo.log" with log
"foo.log.log".  We can add extra check that filename does not end with
".log" but for VM "foo.log" we can have rotated log "foo.log.log.1". Ok
let's check we don't have "log" in filename part corresponging to * but
what if someone will use logrotate with "%Y.log-%m-%d" 'dateformat'
option. In this case the check will exclude proper rotated files.

Yes, the last example if quite artificial but it shows it is difficult
to find out correctly rotated files when rotated files pattern is not
known. Thus the above decision only to support case with virtlogd when
we know the pattern.

Another reason for not removing log files when logrotate is present is
that due to races some files can escape deletion. For example foo.log.3
will be rotated to foo.log.4 after removing function will read directory
files and thus foo.log.4 will not be deleted.

Signed-off-by: Nikolay Shirokovskiy <nshirokovskiy@virtuozzo.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
This commit is contained in:
Nikolay Shirokovskiy 2022-02-14 15:19:52 +03:00 committed by Nikolay Shirokovskiy
parent ce3f707af5
commit a414cb76e3
3 changed files with 52 additions and 1 deletions

View File

@ -11820,3 +11820,44 @@ qemuDomainDeviceBackendChardevForeach(virDomainDef *def,
DOMAIN_DEVICE_ITERATE_MISSING_INFO,
&data);
}
int
qemuDomainRemoveLogs(virQEMUDriver *driver,
const char *name)
{
g_autoptr(virQEMUDriverConfig) cfg = NULL;
g_autofree char *format = NULL;
g_autofree char *main = NULL;
g_autoptr(DIR) dir = NULL;
struct dirent *entry;
int rc;
cfg = virQEMUDriverGetConfig(driver);
if (!cfg->stdioLogD)
return 0;
if (virDirOpen(&dir, cfg->logDir) < 0)
return -1;
main = g_strdup_printf("%s.log", name);
format = g_strdup_printf("%s.log.%%u", name);
while ((rc = virDirRead(dir, &entry, cfg->logDir)) > 0) {
unsigned int u;
if (STREQ(entry->d_name, main) ||
sscanf(entry->d_name, format, &u) == 1) {
g_autofree char *path = NULL;
path = g_strdup_printf("%s/%s", cfg->logDir, entry->d_name);
if (unlink(path) && errno != ENOENT)
VIR_WARN("unlink(%s) error: %s", path, g_strerror(errno));
}
}
if (rc < 0)
return -1;
return 0;
}

View File

@ -1084,3 +1084,7 @@ int
qemuDomainDeviceBackendChardevForeach(virDomainDef *def,
qemuDomainDeviceBackendChardevForeachCallback cb,
void *opaque);
int
qemuDomainRemoveLogs(virQEMUDriver *driver,
const char *name);

View File

@ -2072,7 +2072,8 @@ qemuDomainDestroyFlags(virDomainPtr dom,
int reason;
bool starting;
virCheckFlags(VIR_DOMAIN_DESTROY_GRACEFUL, -1);
virCheckFlags(VIR_DOMAIN_DESTROY_GRACEFUL |
VIR_DOMAIN_DESTROY_REMOVE_LOGS, -1);
if (!(vm = qemuDomainObjFromDomain(dom)))
return -1;
@ -2112,6 +2113,11 @@ qemuDomainDestroyFlags(virDomainPtr dom,
qemuProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_DESTROYED,
VIR_ASYNC_JOB_NONE, stopFlags);
if ((flags & VIR_DOMAIN_DESTROY_REMOVE_LOGS) &&
qemuDomainRemoveLogs(driver, vm->def->name) < 0)
VIR_WARN("Failed to remove logs for VM '%s'", vm->def->name);
event = virDomainEventLifecycleNewFromObj(vm,
VIR_DOMAIN_EVENT_STOPPED,
VIR_DOMAIN_EVENT_STOPPED_DESTROYED);