[PATCH 01/10] hypervisor: copy qemu log context mgmt to hypervisor

Praveen K Paladugu posted 10 patches 2 days, 11 hours ago
There is a newer version of this series
[PATCH 01/10] hypervisor: copy qemu log context mgmt to hypervisor
Posted by Praveen K Paladugu 2 days, 11 hours ago
Signed-off-by: Praveen K Paladugu <praveenkpaladugu@gmail.com>
---
 po/POTFILES                        |   1 +
 src/hypervisor/domain_logcontext.c | 329 +++++++++++++++++++++++++++++
 src/hypervisor/domain_logcontext.h |  41 ++++
 3 files changed, 371 insertions(+)
 create mode 100644 src/hypervisor/domain_logcontext.c
 create mode 100644 src/hypervisor/domain_logcontext.h

diff --git a/po/POTFILES b/po/POTFILES
index 3514aa3dca..1d3c3c0a81 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -94,6 +94,7 @@ src/hyperv/hyperv_util.c
 src/hyperv/hyperv_wmi.c
 src/hypervisor/domain_cgroup.c
 src/hypervisor/domain_driver.c
+src/hypervisor/domain_logcontext.c
 src/hypervisor/domain_interface.c
 src/hypervisor/virhostdev.c
 src/interface/interface_backend_netcf.c
diff --git a/src/hypervisor/domain_logcontext.c b/src/hypervisor/domain_logcontext.c
new file mode 100644
index 0000000000..7932cf7712
--- /dev/null
+++ b/src/hypervisor/domain_logcontext.c
@@ -0,0 +1,329 @@
+/*
+ * domain_logcontext.c: QEMU log context
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include "qemu_logcontext.h"
+#include "viralloc.h"
+#include "virlog.h"
+#include "virstring.h"
+#include "virutil.h"
+
+#include <fcntl.h>
+
+#define VIR_FROM_THIS VIR_FROM_QEMU
+
+VIR_LOG_INIT("qemu.qemu_logcontext");
+
+
+struct _qemuLogContext {
+    GObject parent;
+
+    int writefd;
+    int readfd; /* Only used if manager == NULL */
+    off_t pos;
+    ino_t inode; /* Only used if manager != NULL */
+    char *path;
+    virLogManager *manager;
+};
+
+G_DEFINE_TYPE(qemuLogContext, qemu_log_context, G_TYPE_OBJECT);
+
+static void
+qemuLogContextFinalize(GObject *obj);
+
+
+static void
+qemu_log_context_init(qemuLogContext *logctxt G_GNUC_UNUSED)
+{
+}
+
+
+static void
+qemu_log_context_class_init(qemuLogContextClass *klass)
+{
+    GObjectClass *obj = G_OBJECT_CLASS(klass);
+
+    obj->finalize = qemuLogContextFinalize;
+}
+
+
+static void
+qemuLogContextFinalize(GObject *object)
+{
+    qemuLogContext *ctxt = QEMU_LOG_CONTEXT(object);
+    VIR_DEBUG("ctxt=%p", ctxt);
+
+    virLogManagerFree(ctxt->manager);
+    VIR_FREE(ctxt->path);
+    VIR_FORCE_CLOSE(ctxt->writefd);
+    VIR_FORCE_CLOSE(ctxt->readfd);
+    G_OBJECT_CLASS(qemu_log_context_parent_class)->finalize(object);
+}
+
+
+qemuLogContext *
+qemuLogContextNew(virQEMUDriver *driver,
+                  virDomainObj *vm,
+                  const char *basename)
+{
+    g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
+    qemuLogContext *ctxt = QEMU_LOG_CONTEXT(g_object_new(QEMU_TYPE_LOG_CONTEXT, NULL));
+
+    VIR_DEBUG("Context new %p stdioLogD=%d", ctxt, cfg->stdioLogD);
+    ctxt->writefd = -1;
+    ctxt->readfd = -1;
+
+    ctxt->path = g_strdup_printf("%s/%s.log", cfg->logDir, basename);
+
+    if (cfg->stdioLogD) {
+        ctxt->manager = virLogManagerNew(driver->privileged);
+        if (!ctxt->manager)
+            goto error;
+
+        ctxt->writefd = virLogManagerDomainOpenLogFile(ctxt->manager,
+                                                       "qemu",
+                                                       vm->def->uuid,
+                                                       vm->def->name,
+                                                       ctxt->path,
+                                                       0,
+                                                       &ctxt->inode,
+                                                       &ctxt->pos);
+        if (ctxt->writefd < 0)
+            goto error;
+    } else {
+        if ((ctxt->writefd = open(ctxt->path, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR)) < 0) {
+            virReportSystemError(errno, _("failed to create logfile %1$s"),
+                                 ctxt->path);
+            goto error;
+        }
+        if (virSetCloseExec(ctxt->writefd) < 0) {
+            virReportSystemError(errno, _("failed to set close-on-exec flag on %1$s"),
+                                 ctxt->path);
+            goto error;
+        }
+
+        /* For unprivileged startup we must truncate the file since
+         * we can't rely on logrotate. We don't use O_TRUNC since
+         * it is better for SELinux policy if we truncate afterwards */
+        if (!driver->privileged &&
+            ftruncate(ctxt->writefd, 0) < 0) {
+            virReportSystemError(errno, _("failed to truncate %1$s"),
+                                 ctxt->path);
+            goto error;
+        }
+
+        if ((ctxt->readfd = open(ctxt->path, O_RDONLY)) < 0) {
+            virReportSystemError(errno, _("failed to open logfile %1$s"),
+                                 ctxt->path);
+            goto error;
+        }
+        if (virSetCloseExec(ctxt->readfd) < 0) {
+            virReportSystemError(errno, _("failed to set close-on-exec flag on %1$s"),
+                                 ctxt->path);
+            goto error;
+        }
+
+        if ((ctxt->pos = lseek(ctxt->writefd, 0, SEEK_END)) < 0) {
+            virReportSystemError(errno, _("failed to seek in log file %1$s"),
+                                 ctxt->path);
+            goto error;
+        }
+    }
+
+    return ctxt;
+
+ error:
+    g_clear_object(&ctxt);
+    return NULL;
+}
+
+
+int
+qemuLogContextWrite(qemuLogContext *ctxt,
+                    const char *fmt, ...)
+{
+    va_list argptr;
+    g_autofree char *message = NULL;
+    int ret = -1;
+
+    va_start(argptr, fmt);
+
+    message = g_strdup_vprintf(fmt, argptr);
+    if (!ctxt->manager &&
+        lseek(ctxt->writefd, 0, SEEK_END) < 0) {
+        virReportSystemError(errno, "%s",
+                             _("Unable to seek to end of domain logfile"));
+        goto cleanup;
+    }
+    if (safewrite(ctxt->writefd, message, strlen(message)) < 0) {
+        virReportSystemError(errno, "%s",
+                             _("Unable to write to domain logfile"));
+        goto cleanup;
+    }
+
+    ret = 0;
+
+ cleanup:
+    va_end(argptr);
+    return ret;
+}
+
+
+ssize_t
+qemuLogContextRead(qemuLogContext *ctxt,
+                   char **msg)
+{
+    char *buf;
+    size_t buflen;
+
+    VIR_DEBUG("Context read %p manager=%p inode=%llu pos=%llu",
+              ctxt, ctxt->manager,
+              (unsigned long long)ctxt->inode,
+              (unsigned long long)ctxt->pos);
+
+    if (ctxt->manager) {
+        buf = virLogManagerDomainReadLogFile(ctxt->manager,
+                                             ctxt->path,
+                                             ctxt->inode,
+                                             ctxt->pos,
+                                             1024 * 128,
+                                             0);
+        if (!buf)
+            return -1;
+        buflen = strlen(buf);
+    } else {
+        ssize_t got;
+
+        buflen = 1024 * 128;
+
+        /* Best effort jump to start of messages */
+        ignore_value(lseek(ctxt->readfd, ctxt->pos, SEEK_SET));
+
+        buf = g_new0(char, buflen);
+
+        got = saferead(ctxt->readfd, buf, buflen - 1);
+        if (got < 0) {
+            VIR_FREE(buf);
+            virReportSystemError(errno, "%s",
+                                 _("Unable to read from log file"));
+            return -1;
+        }
+
+        buf[got] = '\0';
+
+        buf = g_renew(char, buf, got + 1);
+        buflen = got;
+    }
+
+    *msg = buf;
+
+    return buflen;
+}
+
+
+/**
+ * qemuLogContextFilter: Read and filter log for relevant messages
+ * @ctxt: the domain log context
+ * @msg: pointer to buffer to store the read messages in
+ * @max: maximum length of the message returned in @msg after filtering
+ *
+ * Reads log output from @ctxt and filters it. Skips messages not produced by
+ * the target executable or irrelevant messages. If @max is not zero, @buf will
+ * contain at most @max characters from the end of the log and @buf will start
+ * after a new line if possible.
+ */
+int
+qemuLogContextReadFiltered(qemuLogContext *ctxt,
+                           char **msg,
+                           size_t max)
+{
+    char *buf;
+    char *eol;
+    char *filter_next;
+    size_t skip;
+    ssize_t got;
+
+    if ((got = qemuLogContextRead(ctxt, &buf)) < 0)
+        return -1;
+
+    /* Filter out debug messages from intermediate libvirt process */
+    filter_next = buf;
+    while ((eol = strchr(filter_next, '\n'))) {
+        *eol = '\0';
+        if (virLogProbablyLogMessage(filter_next) ||
+            strstr(filter_next, "char device redirected to")) {
+            skip = (eol + 1) - filter_next;
+            memmove(filter_next, eol + 1, buf + got - eol);
+            got -= skip;
+        } else {
+            filter_next = eol + 1;
+            *eol = '\n';
+        }
+    }
+
+    if (got > 0 &&
+        buf[got - 1] == '\n') {
+        buf[got - 1] = '\0';
+        got--;
+    }
+
+    if (max > 0 && got > max) {
+        skip = got - max;
+
+        if (buf[skip - 1] != '\n' &&
+            (eol = strchr(buf + skip, '\n')) &&
+            !virStringIsEmpty(eol + 1))
+            skip = eol + 1 - buf;
+
+        memmove(buf, buf + skip, got - skip + 1);
+        got -= skip;
+    }
+
+    buf = g_renew(char, buf, got + 1);
+    *msg = buf;
+    return 0;
+}
+
+
+int
+qemuLogContextGetWriteFD(qemuLogContext *ctxt)
+{
+    return ctxt->writefd;
+}
+
+
+void
+qemuLogContextMarkPosition(qemuLogContext *ctxt)
+{
+    if (ctxt->manager)
+        virLogManagerDomainGetLogFilePosition(ctxt->manager,
+                                              ctxt->path,
+                                              0,
+                                              &ctxt->inode,
+                                              &ctxt->pos);
+    else
+        ctxt->pos = lseek(ctxt->writefd, 0, SEEK_END);
+}
+
+
+virLogManager *
+qemuLogContextGetManager(qemuLogContext *ctxt)
+{
+    return ctxt->manager;
+}
diff --git a/src/hypervisor/domain_logcontext.h b/src/hypervisor/domain_logcontext.h
new file mode 100644
index 0000000000..572ac1026e
--- /dev/null
+++ b/src/hypervisor/domain_logcontext.h
@@ -0,0 +1,41 @@
+/*
+ * domain_logcontext.h: QEMU log context
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <glib-object.h>
+#include "qemu_conf.h"
+#include "logging/log_manager.h"
+
+#define QEMU_TYPE_LOG_CONTEXT qemu_log_context_get_type()
+G_DECLARE_FINAL_TYPE(qemuLogContext, qemu_log_context, QEMU, LOG_CONTEXT, GObject);
+
+qemuLogContext *qemuLogContextNew(virQEMUDriver *driver,
+                                  virDomainObj *vm,
+                                  const char *basename);
+int qemuLogContextWrite(qemuLogContext *ctxt,
+                        const char *fmt, ...) G_GNUC_PRINTF(2, 3);
+ssize_t qemuLogContextRead(qemuLogContext *ctxt,
+                           char **msg);
+int qemuLogContextReadFiltered(qemuLogContext *ctxt,
+                               char **msg,
+                               size_t max);
+int qemuLogContextGetWriteFD(qemuLogContext *ctxt);
+void qemuLogContextMarkPosition(qemuLogContext *ctxt);
+
+virLogManager *qemuLogContextGetManager(qemuLogContext *ctxt);
-- 
2.47.0