mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-03-20 07:59:00 +00:00
logging: introduce log handling protocol
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>
This commit is contained in:
parent
323a329b26
commit
19e5db4ae2
@ -80,6 +80,8 @@ src/locking/lock_manager.c
|
|||||||
src/locking/sanlock_helper.c
|
src/locking/sanlock_helper.c
|
||||||
src/logging/log_daemon.c
|
src/logging/log_daemon.c
|
||||||
src/logging/log_daemon_config.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_cgroup.c
|
||||||
src/lxc/lxc_fuse.c
|
src/lxc/lxc_fuse.c
|
||||||
src/lxc/lxc_hostdev.c
|
src/lxc/lxc_hostdev.c
|
||||||
|
@ -268,6 +268,8 @@ LOG_PROTOCOL_GENERATED = \
|
|||||||
logging/log_protocol.c \
|
logging/log_protocol.c \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
|
DRIVER_SOURCES += $(LOG_PROTOCOL_GENERATED)
|
||||||
|
|
||||||
LOG_PROTOCOL = $(srcdir)/logging/log_protocol.x
|
LOG_PROTOCOL = $(srcdir)/logging/log_protocol.x
|
||||||
EXTRA_DIST += $(LOG_PROTOCOL) \
|
EXTRA_DIST += $(LOG_PROTOCOL) \
|
||||||
$(LOG_PROTOCOL_GENERATED)
|
$(LOG_PROTOCOL_GENERATED)
|
||||||
@ -289,6 +291,8 @@ LOG_DAEMON_SOURCES = \
|
|||||||
logging/log_daemon_config.c \
|
logging/log_daemon_config.c \
|
||||||
logging/log_daemon_dispatch.c \
|
logging/log_daemon_dispatch.c \
|
||||||
logging/log_daemon_dispatch.h \
|
logging/log_daemon_dispatch.h \
|
||||||
|
logging/log_handler.c \
|
||||||
|
logging/log_handler.h \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
logging/log_daemon_dispatch_stubs.h: $(LOG_PROTOCOL) \
|
logging/log_daemon_dispatch_stubs.h: $(LOG_PROTOCOL) \
|
||||||
|
@ -60,6 +60,7 @@ struct _virLogDaemon {
|
|||||||
virMutex lock;
|
virMutex lock;
|
||||||
virNetDaemonPtr dmn;
|
virNetDaemonPtr dmn;
|
||||||
virNetServerPtr srv;
|
virNetServerPtr srv;
|
||||||
|
virLogHandlerPtr handler;
|
||||||
};
|
};
|
||||||
|
|
||||||
virLogDaemonPtr logDaemon = NULL;
|
virLogDaemonPtr logDaemon = NULL;
|
||||||
@ -114,6 +115,7 @@ virLogDaemonFree(virLogDaemonPtr logd)
|
|||||||
if (!logd)
|
if (!logd)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
virObjectUnref(logd->handler);
|
||||||
virMutexDestroy(&logd->lock);
|
virMutexDestroy(&logd->lock);
|
||||||
virObjectUnref(logd->srv);
|
virObjectUnref(logd->srv);
|
||||||
virObjectUnref(logd->dmn);
|
virObjectUnref(logd->dmn);
|
||||||
@ -150,6 +152,9 @@ virLogDaemonNew(virLogDaemonConfigPtr config, bool privileged)
|
|||||||
virNetDaemonAddServer(logd->dmn, logd->srv) < 0)
|
virNetDaemonAddServer(logd->dmn, logd->srv) < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
if (!(logd->handler = virLogHandlerNew(privileged)))
|
||||||
|
goto error;
|
||||||
|
|
||||||
return logd;
|
return logd;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
@ -158,6 +163,13 @@ virLogDaemonNew(virLogDaemonConfigPtr config, bool privileged)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
virLogHandlerPtr
|
||||||
|
virLogDaemonGetHandler(virLogDaemonPtr daemon)
|
||||||
|
{
|
||||||
|
return daemon->handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static virLogDaemonPtr
|
static virLogDaemonPtr
|
||||||
virLogDaemonNewPostExecRestart(virJSONValuePtr object, bool privileged)
|
virLogDaemonNewPostExecRestart(virJSONValuePtr object, bool privileged)
|
||||||
{
|
{
|
||||||
@ -191,6 +203,16 @@ virLogDaemonNewPostExecRestart(virJSONValuePtr object, bool privileged)
|
|||||||
(void*)(intptr_t)(privileged ? 0x1 : 0x0))))
|
(void*)(intptr_t)(privileged ? 0x1 : 0x0))))
|
||||||
goto error;
|
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;
|
return logd;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
@ -774,6 +796,15 @@ virLogDaemonPreExecRestart(const char *state_file,
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!(child = virLogHandlerPreExecRestart(logDaemon->handler)))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (virJSONValueObjectAppend(object, "handler", child) < 0) {
|
||||||
|
virJSONValueFree(child);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!(state = virJSONValueToString(object, true)))
|
if (!(state = virJSONValueToString(object, true)))
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
# define __VIR_LOG_DAEMON_H__
|
# define __VIR_LOG_DAEMON_H__
|
||||||
|
|
||||||
# include "virthread.h"
|
# include "virthread.h"
|
||||||
|
# include "log_handler.h"
|
||||||
|
|
||||||
typedef struct _virLogDaemon virLogDaemon;
|
typedef struct _virLogDaemon virLogDaemon;
|
||||||
typedef virLogDaemon *virLogDaemonPtr;
|
typedef virLogDaemon *virLogDaemonPtr;
|
||||||
@ -39,4 +40,6 @@ struct _virLogDaemonClient {
|
|||||||
|
|
||||||
extern virLogDaemonPtr logDaemon;
|
extern virLogDaemonPtr logDaemon;
|
||||||
|
|
||||||
|
virLogHandlerPtr virLogDaemonGetHandler(virLogDaemonPtr daemon);
|
||||||
|
|
||||||
#endif /* __VIR_LOG_DAEMON_H__ */
|
#endif /* __VIR_LOG_DAEMON_H__ */
|
||||||
|
@ -29,9 +29,115 @@
|
|||||||
#include "log_daemon.h"
|
#include "log_daemon.h"
|
||||||
#include "log_protocol.h"
|
#include "log_protocol.h"
|
||||||
#include "virerror.h"
|
#include "virerror.h"
|
||||||
|
#include "virthreadjob.h"
|
||||||
|
#include "virfile.h"
|
||||||
|
|
||||||
#define VIR_FROM_THIS VIR_FROM_RPC
|
#define VIR_FROM_THIS VIR_FROM_RPC
|
||||||
|
|
||||||
VIR_LOG_INIT("logging.log_daemon_dispatch");
|
VIR_LOG_INIT("logging.log_daemon_dispatch");
|
||||||
|
|
||||||
#include "log_daemon_dispatch_stubs.h"
|
#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;
|
||||||
|
}
|
||||||
|
555
src/logging/log_handler.c
Normal file
555
src/logging/log_handler.c
Normal file
@ -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;
|
||||||
|
}
|
63
src/logging/log_handler.h
Normal file
63
src/logging/log_handler.h
Normal file
@ -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__ */
|
@ -17,6 +17,99 @@ typedef string virLogManagerProtocolNonNullString<VIR_LOG_MANAGER_PROTOCOL_STRIN
|
|||||||
/* A long string, which may be NULL. */
|
/* A long string, which may be NULL. */
|
||||||
typedef virLogManagerProtocolNonNullString *virLogManagerProtocolString;
|
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. */
|
/* Define the program number, protocol version and procedure numbers here. */
|
||||||
const VIR_LOG_MANAGER_PROTOCOL_PROGRAM = 0x87539319;
|
const VIR_LOG_MANAGER_PROTOCOL_PROGRAM = 0x87539319;
|
||||||
const VIR_LOG_MANAGER_PROTOCOL_PROGRAM_VERSION = 1;
|
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
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user