]> xenbits.xensource.com Git - libvirt.git/commitdiff
logging: introduce log handling protocol
authorDaniel P. Berrange <berrange@redhat.com>
Tue, 3 Nov 2015 11:01:21 +0000 (11:01 +0000)
committerDaniel P. Berrange <berrange@redhat.com>
Thu, 26 Nov 2015 14:28:55 +0000 (14:28 +0000)
Define a new RPC protocol for the virtlogd daemon that provides
for handling of logs. The initial RPC method defined allows a
client to obtain a file handle to use for writing to a log
file for a guest domain. The file handle passed back will not
actually refer to the log file, but rather an anonymous pipe.
The virtlogd daemon will forward I/O between them, ensuring
file rotation happens when required.

Initially the log setup is hardcoded to cap log files at
128 KB, and keep 3 backups when rolling over, which gives
a max usage of 512 KB per guest.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
po/POTFILES.in
src/Makefile.am
src/logging/log_daemon.c
src/logging/log_daemon.h
src/logging/log_daemon_dispatch.c
src/logging/log_handler.c [new file with mode: 0644]
src/logging/log_handler.h [new file with mode: 0644]
src/logging/log_protocol.x

index 33bc25869550f07696c124562a2986a699b03e9a..725ec668ce2680d387a9ffb716a19b6316734050 100644 (file)
@@ -80,6 +80,8 @@ src/locking/lock_manager.c
 src/locking/sanlock_helper.c
 src/logging/log_daemon.c
 src/logging/log_daemon_config.c
+src/logging/log_daemon_dispatch.c
+src/logging/log_handler.c
 src/lxc/lxc_cgroup.c
 src/lxc/lxc_fuse.c
 src/lxc/lxc_hostdev.c
index 3323bd1383b2b4c8c43f33c7fdaff2d9f6438dfd..c658204b4c099d1e6e1eef4324c797458e1d670e 100644 (file)
@@ -268,6 +268,8 @@ LOG_PROTOCOL_GENERATED = \
                logging/log_protocol.c \
                $(NULL)
 
+DRIVER_SOURCES += $(LOG_PROTOCOL_GENERATED)
+
 LOG_PROTOCOL = $(srcdir)/logging/log_protocol.x
 EXTRA_DIST += $(LOG_PROTOCOL) \
        $(LOG_PROTOCOL_GENERATED)
@@ -289,6 +291,8 @@ LOG_DAEMON_SOURCES = \
                logging/log_daemon_config.c \
                logging/log_daemon_dispatch.c \
                logging/log_daemon_dispatch.h \
+               logging/log_handler.c \
+               logging/log_handler.h \
                $(NULL)
 
 logging/log_daemon_dispatch_stubs.h: $(LOG_PROTOCOL) \
index c3d7cbc83c1873082ac748b5cf516d22702c0913..7a1afecffb9ffda9704b486014a29cba295d274a 100644 (file)
@@ -60,6 +60,7 @@ struct _virLogDaemon {
     virMutex lock;
     virNetDaemonPtr dmn;
     virNetServerPtr srv;
+    virLogHandlerPtr handler;
 };
 
 virLogDaemonPtr logDaemon = NULL;
@@ -114,6 +115,7 @@ virLogDaemonFree(virLogDaemonPtr logd)
     if (!logd)
         return;
 
+    virObjectUnref(logd->handler);
     virMutexDestroy(&logd->lock);
     virObjectUnref(logd->srv);
     virObjectUnref(logd->dmn);
@@ -150,6 +152,9 @@ virLogDaemonNew(virLogDaemonConfigPtr config, bool privileged)
         virNetDaemonAddServer(logd->dmn, logd->srv) < 0)
         goto error;
 
+    if (!(logd->handler = virLogHandlerNew(privileged)))
+        goto error;
+
     return logd;
 
  error:
@@ -158,6 +163,13 @@ virLogDaemonNew(virLogDaemonConfigPtr config, bool privileged)
 }
 
 
+virLogHandlerPtr
+virLogDaemonGetHandler(virLogDaemonPtr daemon)
+{
+    return daemon->handler;
+}
+
+
 static virLogDaemonPtr
 virLogDaemonNewPostExecRestart(virJSONValuePtr object, bool privileged)
 {
@@ -191,6 +203,16 @@ virLogDaemonNewPostExecRestart(virJSONValuePtr object, bool privileged)
                                                     (void*)(intptr_t)(privileged ? 0x1 : 0x0))))
         goto error;
 
+    if (!(child = virJSONValueObjectGet(object, "handler"))) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Malformed daemon data from JSON file"));
+        goto error;
+    }
+
+    if (!(logd->handler = virLogHandlerNewPostExecRestart(child,
+                                                          privileged)))
+        goto error;
+
     return logd;
 
  error:
@@ -774,6 +796,15 @@ virLogDaemonPreExecRestart(const char *state_file,
         goto cleanup;
     }
 
+    if (!(child = virLogHandlerPreExecRestart(logDaemon->handler)))
+        goto cleanup;
+
+    if (virJSONValueObjectAppend(object, "handler", child) < 0) {
+        virJSONValueFree(child);
+        goto cleanup;
+    }
+
+
     if (!(state = virJSONValueToString(object, true)))
         goto cleanup;
 
index a1531604d5b5168b3f9066167828e4f8c7c63619..b076a4f3b47e254f7bbbf201572329021c168af0 100644 (file)
@@ -24,6 +24,7 @@
 # define __VIR_LOG_DAEMON_H__
 
 # include "virthread.h"
+# include "log_handler.h"
 
 typedef struct _virLogDaemon virLogDaemon;
 typedef virLogDaemon *virLogDaemonPtr;
@@ -39,4 +40,6 @@ struct _virLogDaemonClient {
 
 extern virLogDaemonPtr logDaemon;
 
+virLogHandlerPtr virLogDaemonGetHandler(virLogDaemonPtr daemon);
+
 #endif /* __VIR_LOG_DAEMON_H__ */
index 98df178b763b67d5d198719c278ceeb61ec01123..269255ad5f431f1d8bc28492cd60b2188983f889 100644 (file)
 #include "log_daemon.h"
 #include "log_protocol.h"
 #include "virerror.h"
+#include "virthreadjob.h"
+#include "virfile.h"
 
 #define VIR_FROM_THIS VIR_FROM_RPC
 
 VIR_LOG_INIT("logging.log_daemon_dispatch");
 
 #include "log_daemon_dispatch_stubs.h"
+
+static int
+virLogManagerProtocolDispatchDomainOpenLogFile(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                               virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                               virNetMessagePtr msg,
+                                               virNetMessageErrorPtr rerr,
+                                               virLogManagerProtocolDomainOpenLogFileArgs *args,
+                                               virLogManagerProtocolDomainOpenLogFileRet *ret)
+{
+    int fd = -1;
+    int rv = -1;
+    off_t offset;
+    ino_t inode;
+
+    if ((fd = virLogHandlerDomainOpenLogFile(virLogDaemonGetHandler(logDaemon),
+                                             args->driver,
+                                             (unsigned char *)args->dom.uuid,
+                                             args->dom.name,
+                                             &inode, &offset)) < 0)
+        goto cleanup;
+
+    ret->pos.inode = inode;
+    ret->pos.offset = offset;
+
+    if (virNetMessageAddFD(msg, fd) < 0)
+        goto cleanup;
+
+    rv = 1; /* '1' tells caller we added some FDs */
+
+ cleanup:
+    VIR_FORCE_CLOSE(fd);
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    return rv;
+}
+
+
+static int
+virLogManagerProtocolDispatchDomainGetLogFilePosition(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                                      virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                                      virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                                      virNetMessageErrorPtr rerr,
+                                                      virLogManagerProtocolDomainGetLogFilePositionArgs *args,
+                                                      virLogManagerProtocolDomainGetLogFilePositionRet *ret)
+{
+    int rv = -1;
+    off_t offset;
+    ino_t inode;
+
+    if (virLogHandlerDomainGetLogFilePosition(virLogDaemonGetHandler(logDaemon),
+                                              args->driver,
+                                              (unsigned char *)args->dom.uuid,
+                                              args->dom.name,
+                                              &inode, &offset) < 0)
+        goto cleanup;
+
+    ret->pos.inode = inode;
+    ret->pos.offset = offset;
+
+    rv = 0;
+ cleanup:
+
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    return rv;
+}
+
+
+static int
+virLogManagerProtocolDispatchDomainReadLogFile(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                               virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                               virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                               virNetMessageErrorPtr rerr,
+                                               virLogManagerProtocolDomainReadLogFileArgs *args,
+                                               virLogManagerProtocolDomainReadLogFileRet *ret)
+{
+    int rv = -1;
+    char *data;
+
+    if (args->maxlen > VIR_LOG_MANAGER_PROTOCOL_STRING_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Requested data len %zu is larger than maximum %d"),
+                       args->maxlen, VIR_LOG_MANAGER_PROTOCOL_STRING_MAX);
+        goto cleanup;
+    }
+
+    if ((data = virLogHandlerDomainReadLogFile(virLogDaemonGetHandler(logDaemon),
+                                               args->driver,
+                                               (unsigned char *)args->dom.uuid,
+                                               args->dom.name,
+                                               args->pos.inode,
+                                               args->pos.offset,
+                                               args->maxlen)) == NULL)
+        goto cleanup;
+
+    ret->data = data;
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    return rv;
+}
diff --git a/src/logging/log_handler.c b/src/logging/log_handler.c
new file mode 100644 (file)
index 0000000..2acbca7
--- /dev/null
@@ -0,0 +1,555 @@
+/*
+ * log_handler.c: log management daemon handler
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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/>.
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+#include <config.h>
+
+#include "log_handler.h"
+#include "virerror.h"
+#include "virobject.h"
+#include "virfile.h"
+#include "viralloc.h"
+#include "virstring.h"
+#include "virlog.h"
+#include "virrotatingfile.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "configmake.h"
+
+VIR_LOG_INIT("logging.log_handler");
+
+#define VIR_FROM_THIS VIR_FROM_LOGGING
+
+#define DEFAULT_FILE_SIZE (128 * 1024)
+#define DEFAULT_MAX_BACKUP 3
+#define DEFAULT_MODE 0600
+
+typedef struct _virLogHandlerLogFile virLogHandlerLogFile;
+typedef virLogHandlerLogFile *virLogHandlerLogFilePtr;
+
+struct _virLogHandlerLogFile {
+    virRotatingFileWriterPtr file;
+    int watch;
+    int pipefd; /* Read from QEMU via this */
+};
+
+struct _virLogHandler {
+    virObjectLockable parent;
+
+    bool privileged;
+    virLogHandlerLogFilePtr *files;
+    size_t nfiles;
+};
+
+static virClassPtr virLogHandlerClass;
+static void virLogHandlerDispose(void *obj);
+
+static int
+virLogHandlerOnceInit(void)
+{
+    if (!(virLogHandlerClass = virClassNew(virClassForObjectLockable(),
+                                          "virLogHandler",
+                                          sizeof(virLogHandler),
+                                          virLogHandlerDispose)))
+        return -1;
+
+    return 0;
+}
+
+VIR_ONCE_GLOBAL_INIT(virLogHandler)
+
+
+static void
+virLogHandlerLogFileFree(virLogHandlerLogFilePtr file)
+{
+    if (!file)
+        return;
+
+    VIR_FORCE_CLOSE(file->pipefd);
+    virRotatingFileWriterFree(file->file);
+
+    if (file->watch != -1)
+        virEventRemoveHandle(file->watch);
+    VIR_FREE(file);
+}
+
+
+static void
+virLogHandlerLogFileClose(virLogHandlerPtr handler,
+                          virLogHandlerLogFilePtr file)
+{
+    size_t i;
+
+    for (i = 0; i < handler->nfiles; i++) {
+        if (handler->files[i] == file) {
+            VIR_DELETE_ELEMENT(handler->files, i, handler->nfiles);
+            virLogHandlerLogFileFree(file);
+            break;
+        }
+    }
+}
+
+
+static virLogHandlerLogFilePtr
+virLogHandlerGetLogFileFromWatch(virLogHandlerPtr handler,
+                                 int watch)
+{
+    size_t i;
+
+    for (i = 0; i < handler->nfiles; i++) {
+        if (handler->files[i]->watch == watch)
+            return handler->files[i];
+    }
+
+    return NULL;
+}
+
+
+static void
+virLogHandlerDomainLogFileEvent(int watch,
+                                int fd,
+                                int events,
+                                void *opaque)
+{
+    virLogHandlerPtr handler = opaque;
+    virLogHandlerLogFilePtr logfile;
+    char buf[1024];
+    ssize_t len;
+
+    virObjectLock(handler);
+    logfile = virLogHandlerGetLogFileFromWatch(handler, watch);
+    if (!logfile || logfile->pipefd != fd) {
+        virEventRemoveHandle(watch);
+        virObjectUnlock(handler);
+        return;
+    }
+
+ reread:
+    len = read(fd, buf, sizeof(buf));
+    if (len < 0) {
+        if (errno == EINTR)
+            goto reread;
+
+        virReportSystemError(errno, "%s",
+                             _("Unable to read from log pipe"));
+        goto error;
+    }
+
+    if (virRotatingFileWriterAppend(logfile->file, buf, len) != len)
+        goto error;
+
+    if (events & VIR_EVENT_HANDLE_HANGUP)
+        goto error;
+
+    virObjectUnlock(handler);
+    return;
+
+ error:
+    virLogHandlerLogFileClose(handler, logfile);
+    virObjectUnlock(handler);
+}
+
+
+virLogHandlerPtr
+virLogHandlerNew(bool privileged)
+{
+    virLogHandlerPtr handler;
+
+    if (virLogHandlerInitialize() < 0)
+        goto error;
+
+    if (!(handler = virObjectLockableNew(virLogHandlerClass)))
+        goto error;
+
+    handler->privileged = privileged;
+
+    return handler;
+
+ error:
+    return NULL;
+}
+
+
+static virLogHandlerLogFilePtr
+virLogHandlerLogFilePostExecRestart(virJSONValuePtr object)
+{
+    virLogHandlerLogFilePtr file;
+    const char *path;
+
+    if (VIR_ALLOC(file) < 0)
+        return NULL;
+
+    if ((path = virJSONValueObjectGetString(object, "path")) == NULL) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Missing file path in JSON document"));
+        goto error;
+    }
+
+    if ((file->file = virRotatingFileWriterNew(path,
+                                               DEFAULT_FILE_SIZE,
+                                               DEFAULT_MAX_BACKUP,
+                                               false,
+                                               DEFAULT_MODE)) == NULL)
+        goto error;
+
+    if (virJSONValueObjectGetNumberInt(object, "pipefd", &file->pipefd) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Missing file pipefd in JSON document"));
+        goto error;
+    }
+    if (virSetInherit(file->pipefd, false) < 0) {
+        virReportSystemError(errno, "%s",
+                             _("Cannot enable close-on-exec flag"));
+        goto error;
+    }
+
+    return file;
+
+ error:
+    virLogHandlerLogFileFree(file);
+    return NULL;
+}
+
+
+virLogHandlerPtr
+virLogHandlerNewPostExecRestart(virJSONValuePtr object,
+                                bool privileged)
+{
+    virLogHandlerPtr handler;
+    virJSONValuePtr files;
+    ssize_t n;
+    size_t i;
+
+    if (!(handler = virLogHandlerNew(privileged)))
+        return NULL;
+
+    if (!(files = virJSONValueObjectGet(object, "files"))) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Missing files data from JSON file"));
+        goto error;
+    }
+
+    if ((n = virJSONValueArraySize(files)) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Malformed files data from JSON file"));
+        goto error;
+    }
+
+    for (i = 0; i < n; i++) {
+        virLogHandlerLogFilePtr file;
+        virJSONValuePtr child = virJSONValueArrayGet(files, i);
+
+        if (!(file = virLogHandlerLogFilePostExecRestart(child)))
+            goto error;
+
+        if (VIR_APPEND_ELEMENT_COPY(handler->files, handler->nfiles, file) < 0)
+            goto error;
+
+        if ((file->watch = virEventAddHandle(file->pipefd,
+                                             VIR_EVENT_HANDLE_READABLE,
+                                             virLogHandlerDomainLogFileEvent,
+                                             handler,
+                                             NULL)) < 0) {
+            VIR_DELETE_ELEMENT(handler->files, handler->nfiles - 1, handler->nfiles);
+            goto error;
+        }
+    }
+
+
+    return handler;
+
+ error:
+    virObjectUnref(handler);
+    return NULL;
+}
+
+
+static void
+virLogHandlerDispose(void *obj)
+{
+    virLogHandlerPtr handler = obj;
+    size_t i;
+
+    for (i = 0; i < handler->nfiles; i++)
+        virLogHandlerLogFileFree(handler->files[i]);
+    VIR_FREE(handler->files);
+}
+
+
+static char *
+virLogHandlerGetLogFilePathForDomain(virLogHandlerPtr handler,
+                                     const char *driver,
+                                     const unsigned char *domuuid ATTRIBUTE_UNUSED,
+                                     const char *domname)
+{
+    char *path;
+    if (handler->privileged) {
+        if (virAsprintf(&path,
+                        LOCALSTATEDIR "/log/libvirt/%s/%s.log",
+                        driver, domname) < 0)
+            return NULL;
+    } else {
+        char *cachedir;
+
+        cachedir = virGetUserCacheDirectory();
+        if (!cachedir)
+            return NULL;
+
+        if (virAsprintf(&path,
+                        "%s/%s/log/%s.log", cachedir, driver, domname) < 0) {
+            VIR_FREE(cachedir);
+            return NULL;
+        }
+
+    }
+    return path;
+}
+
+
+int
+virLogHandlerDomainOpenLogFile(virLogHandlerPtr handler,
+                               const char *driver,
+                               const unsigned char *domuuid ATTRIBUTE_UNUSED,
+                               const char *domname,
+                               ino_t *inode,
+                               off_t *offset)
+{
+    size_t i;
+    virLogHandlerLogFilePtr file = NULL;
+    int pipefd[2] = { -1, -1 };
+    char *path;
+
+    virObjectLock(handler);
+
+    if (!(path = virLogHandlerGetLogFilePathForDomain(handler,
+                                                      driver,
+                                                      domuuid,
+                                                      domname)))
+        goto error;
+
+    for (i = 0; i < handler->nfiles; i++) {
+        if (STREQ(virRotatingFileWriterGetPath(handler->files[i]->file),
+                  path)) {
+            virReportSystemError(EBUSY,
+                                 _("Cannot open log file: '%s'"),
+                                 path);
+            goto error;
+        }
+    }
+
+    if (pipe(pipefd) < 0) {
+        virReportSystemError(errno, "%s",
+                             _("Cannot open fifo pipe"));
+        goto error;
+    }
+    if (VIR_ALLOC(file) < 0)
+        goto error;
+
+    file->watch = -1;
+    file->pipefd = pipefd[0];
+    pipefd[0] = -1;
+
+    if ((file->file = virRotatingFileWriterNew(path,
+                                               DEFAULT_FILE_SIZE,
+                                               DEFAULT_MAX_BACKUP,
+                                               false,
+                                               DEFAULT_MODE)) == NULL)
+        goto error;
+
+    if (VIR_APPEND_ELEMENT_COPY(handler->files, handler->nfiles, file) < 0)
+        goto error;
+
+    if ((file->watch = virEventAddHandle(file->pipefd,
+                                         VIR_EVENT_HANDLE_READABLE,
+                                         virLogHandlerDomainLogFileEvent,
+                                         handler,
+                                         NULL)) < 0) {
+        VIR_DELETE_ELEMENT(handler->files, handler->nfiles - 1, handler->nfiles);
+        goto error;
+    }
+
+    VIR_FREE(path);
+
+    *inode = virRotatingFileWriterGetINode(file->file);
+    *offset = virRotatingFileWriterGetOffset(file->file);
+
+    virObjectUnlock(handler);
+    return pipefd[1];
+
+ error:
+    VIR_FREE(path);
+    VIR_FORCE_CLOSE(pipefd[0]);
+    VIR_FORCE_CLOSE(pipefd[1]);
+    virLogHandlerLogFileFree(file);
+    virObjectUnlock(handler);
+    return -1;
+}
+
+
+int
+virLogHandlerDomainGetLogFilePosition(virLogHandlerPtr handler,
+                                      const char *driver,
+                                      const unsigned char *domuuid,
+                                      const char *domname,
+                                      ino_t *inode,
+                                      off_t *offset)
+{
+    char *path;
+    virLogHandlerLogFilePtr file = NULL;
+    int ret = -1;
+    size_t i;
+
+    virObjectLock(handler);
+
+    if (!(path = virLogHandlerGetLogFilePathForDomain(handler,
+                                                      driver,
+                                                      domuuid,
+                                                      domname)))
+        goto cleanup;
+
+    for (i = 0; i < handler->nfiles; i++) {
+        if (STREQ(virRotatingFileWriterGetPath(handler->files[i]->file),
+                  path)) {
+            file = handler->files[i];
+            break;
+        }
+    }
+
+    if (!file) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("No open log file for domain %s"),
+                       domname);
+        goto cleanup;
+    }
+
+    *inode = virRotatingFileWriterGetINode(file->file);
+    *offset = virRotatingFileWriterGetOffset(file->file);
+
+    ret = 0;
+
+ cleanup:
+    VIR_FREE(path);
+    virObjectUnlock(handler);
+    return ret;
+}
+
+
+char *
+virLogHandlerDomainReadLogFile(virLogHandlerPtr handler,
+                               const char *driver,
+                               const unsigned char *domuuid,
+                               const char *domname,
+                               ino_t inode,
+                               off_t offset,
+                               size_t maxlen)
+{
+    char *path;
+    virRotatingFileReaderPtr file = NULL;
+    char *data = NULL;
+    ssize_t got;
+
+    virObjectLock(handler);
+
+    if (!(path = virLogHandlerGetLogFilePathForDomain(handler,
+                                                      driver,
+                                                      domuuid,
+                                                      domname)))
+        goto error;
+
+    if (!(file = virRotatingFileReaderNew(path, DEFAULT_MAX_BACKUP)))
+        goto error;
+
+    if (virRotatingFileReaderSeek(file, inode, offset) < 0)
+        goto error;
+
+    if (VIR_ALLOC_N(data, maxlen + 1) < 0)
+        goto error;
+
+    got = virRotatingFileReaderConsume(file, data, maxlen);
+    if (got < 0)
+        goto error;
+    data[got] = '\0';
+
+    virRotatingFileReaderFree(file);
+    virObjectUnlock(handler);
+    VIR_FREE(path);
+    return data;
+
+ error:
+    VIR_FREE(path);
+    VIR_FREE(data);
+    virRotatingFileReaderFree(file);
+    virObjectUnlock(handler);
+    return NULL;
+}
+
+
+virJSONValuePtr
+virLogHandlerPreExecRestart(virLogHandlerPtr handler)
+{
+    virJSONValuePtr ret = virJSONValueNewObject();
+    virJSONValuePtr files;
+    size_t i;
+
+    if (!ret)
+        return NULL;
+
+    if (!(files = virJSONValueNewArray()))
+        goto error;
+
+    if (virJSONValueObjectAppend(ret, "files", files) < 0) {
+        virJSONValueFree(files);
+        goto error;
+    }
+
+    for (i = 0; i < handler->nfiles; i++) {
+        virJSONValuePtr file = virJSONValueNewObject();
+        if (!file)
+            goto error;
+
+        if (virJSONValueArrayAppend(files, file) < 0) {
+            virJSONValueFree(file);
+            goto error;
+        }
+
+        if (virJSONValueObjectAppendNumberInt(file, "pipefd",
+                                              handler->files[i]->pipefd) < 0)
+            goto error;
+
+        if (virJSONValueObjectAppendString(file, "path",
+                                           virRotatingFileWriterGetPath(handler->files[i]->file)) < 0)
+            goto error;
+
+        if (virSetInherit(handler->files[i]->pipefd, true) < 0) {
+            virReportSystemError(errno, "%s",
+                                 _("Cannot disable close-on-exec flag"));
+            goto error;
+        }
+    }
+
+    return ret;
+
+ error:
+    virJSONValueFree(ret);
+    return NULL;
+}
diff --git a/src/logging/log_handler.h b/src/logging/log_handler.h
new file mode 100644 (file)
index 0000000..1ad755e
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * log_handler.h: log management daemon handler
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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/>.
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+#ifndef __VIR_LOG_HANDLER_H__
+# define __VIR_LOG_HANDLER_H__
+
+# include "internal.h"
+# include "virjson.h"
+
+typedef struct _virLogHandler virLogHandler;
+typedef virLogHandler *virLogHandlerPtr;
+
+
+virLogHandlerPtr virLogHandlerNew(bool privileged);
+virLogHandlerPtr virLogHandlerNewPostExecRestart(virJSONValuePtr child,
+                                                 bool privileged);
+
+void virLogHandlerFree(virLogHandlerPtr handler);
+
+int virLogHandlerDomainOpenLogFile(virLogHandlerPtr handler,
+                                   const char *driver,
+                                   const unsigned char *domuuid,
+                                   const char *domname,
+                                   ino_t *inode,
+                                   off_t *offset);
+
+int virLogHandlerDomainGetLogFilePosition(virLogHandlerPtr handler,
+                                          const char *driver,
+                                          const unsigned char *domuuid,
+                                          const char *domname,
+                                          ino_t *inode,
+                                          off_t *offset);
+
+char *virLogHandlerDomainReadLogFile(virLogHandlerPtr handler,
+                                     const char *driver,
+                                     const unsigned char *domuuid,
+                                     const char *domname,
+                                     ino_t inode,
+                                     off_t offset,
+                                     size_t maxlen);
+
+virJSONValuePtr virLogHandlerPreExecRestart(virLogHandlerPtr handler);
+
+#endif /** __VIR_LOG_HANDLER_H__ */
index 1481c301fa45d3b182e5f5dd8b05a165076133d7..de57c69dfabc73b11b3a6aa09acac37bcc27066f 100644 (file)
@@ -17,6 +17,99 @@ typedef string virLogManagerProtocolNonNullString<VIR_LOG_MANAGER_PROTOCOL_STRIN
 /* A long string, which may be NULL. */
 typedef virLogManagerProtocolNonNullString *virLogManagerProtocolString;
 
+struct virLogManagerProtocolDomain {
+    virLogManagerProtocolUUID uuid;
+    virLogManagerProtocolNonNullString name;
+};
+typedef struct virLogManagerProtocolDomain virLogManagerProtocolDomain;
+
+struct virLogManagerProtocolLogFilePosition {
+    unsigned hyper inode;
+    unsigned hyper offset;
+};
+typedef struct virLogManagerProtocolLogFilePosition virLogManagerProtocolLogFilePosition;
+
+/* Obtain a file handle suitable for writing to a
+ * log file for a domain
+ */
+struct virLogManagerProtocolDomainOpenLogFileArgs {
+    virLogManagerProtocolNonNullString driver;
+    virLogManagerProtocolDomain dom;
+    unsigned int flags;
+};
+
+struct virLogManagerProtocolDomainOpenLogFileRet {
+    virLogManagerProtocolLogFilePosition pos;
+};
+
+struct virLogManagerProtocolDomainGetLogFilePositionArgs {
+    virLogManagerProtocolNonNullString driver;
+    virLogManagerProtocolDomain dom;
+    unsigned int flags;
+};
+
+struct virLogManagerProtocolDomainGetLogFilePositionRet {
+    virLogManagerProtocolLogFilePosition pos;
+};
+
+struct virLogManagerProtocolDomainReadLogFileArgs {
+    virLogManagerProtocolNonNullString driver;
+    virLogManagerProtocolDomain dom;
+    virLogManagerProtocolLogFilePosition pos;
+    unsigned hyper maxlen;
+    unsigned int flags;
+};
+
+struct virLogManagerProtocolDomainReadLogFileRet {
+    virLogManagerProtocolNonNullString data;
+};
+
 /* Define the program number, protocol version and procedure numbers here. */
 const VIR_LOG_MANAGER_PROTOCOL_PROGRAM = 0x87539319;
 const VIR_LOG_MANAGER_PROTOCOL_PROGRAM_VERSION = 1;
+
+enum virLogManagerProtocolProcedure {
+    /* Each function must be preceded by a comment providing one or
+     * more annotations:
+     *
+     * - @generate: none|client|server|both
+     *
+     *   Whether to generate the dispatch stubs for the server
+     *   and/or client code.
+     *
+     * - @readstream: paramnumber
+     * - @writestream: paramnumber
+     *
+     *   The @readstream or @writestream annotations let daemon and src/remote
+     *   create a stream.  The direction is defined from the src/remote point
+     *   of view.  A readstream transfers data from daemon to src/remote.  The
+     *   <paramnumber> specifies at which offset the stream parameter is inserted
+     *   in the function parameter list.
+     *
+     * - @priority: low|high
+     *
+     *   Each API that might eventually access hypervisor's  monitor (and thus
+     *   block) MUST fall into low priority. However, there are some exceptions
+     *   to this rule, e.g. domainDestroy. Other APIs MAY  be marked as high
+     *   priority. If in doubt, it's safe to choose low. Low is taken as default,
+     *   and thus can be left out.
+     */
+
+    /**
+     * @generate: none
+     * @acl: none
+     */
+    VIR_LOG_MANAGER_PROTOCOL_PROC_DOMAIN_OPEN_LOG_FILE = 1,
+
+    /**
+     * @generate: none
+     * @acl: none
+     */
+    VIR_LOG_MANAGER_PROTOCOL_PROC_DOMAIN_GET_LOG_FILE_POSITION = 2,
+
+    /**
+     * @generate: none
+     * @acl: none
+     */
+    VIR_LOG_MANAGER_PROTOCOL_PROC_DOMAIN_READ_LOG_FILE = 3
+};