1
0
mirror of https://gitlab.com/libvirt/libvirt.git synced 2025-03-07 17:28:15 +00:00

Make use of private data structure for monitor state

Introduce a new qemuDomainObjPrivate object which is used to store
the private QEMU specific data associated with each virDomainObjPtr
instance. This contains a single member, an instance of the new
qemuMonitorPtr object which encapsulates the QEMU monitor state.
The internals of the latter are private to the qemu_monitor* files,
not to be shown to qemu_driver.c

* src/qemu/qemu_conf.h: Definition of qemuDomainObjPrivate.
* src/qemu/qemu_driver.c: Register a functions for creating
  and freeing qemuDomainObjPrivate instances with the domain
  capabilities. Remove the qemudDispatchVMEvent() watch since
  I/O watches are now handled by the monitor code itself. Pass
  a new qemuHandleMonitorEOF() callback into qemuMonitorOpen
  to allow notification when the monitor quits.
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Introduce
  the 'qemuMonitor' object. Temporarily add new APIs
  qemuMonitorWrite, qemuMonitorRead, qemuMonitorWaitForInput
  to allow text based monitor impl to perform I/O.
* src/qemu/qemu_monitor_text.c: Call APIs for reading/writing
  to monitor instead of accessing the file handle directly.
This commit is contained in:
Daniel P. Berrange 2009-10-09 20:13:29 +01:00
parent ff26194143
commit 1cfd5a00eb
5 changed files with 292 additions and 161 deletions

View File

@ -37,6 +37,7 @@
#include "security/security_driver.h"
#include "cgroup.h"
#include "pci.h"
#include "qemu_monitor.h"
#define qemudDebug(fmt, ...) do {} while(0)
@ -133,6 +134,16 @@ struct qemud_driver {
pciDeviceList *activePciHostdevs;
};
/* XXX temporarily exposed.
* This will be moved back into qemu_driver.c, once the
* qemu_monitor* code is refactored a little more
*/
typedef struct _qemuDomainObjPrivate qemuDomainObjPrivate;
typedef qemuDomainObjPrivate *qemuDomainObjPrivatePtr;
struct _qemuDomainObjPrivate {
qemuMonitorPtr mon;
};
/* Port numbers used for KVM migration. */
#define QEMUD_MIGRATION_FIRST_PORT 49152

View File

@ -93,11 +93,6 @@ static void qemuDomainEventFlush(int timer, void *opaque);
static void qemuDomainEventQueue(struct qemud_driver *driver,
virDomainEventPtr event);
static void qemudDispatchVMEvent(int watch,
int fd,
int events,
void *opaque);
static int qemudStartVMDaemon(virConnectPtr conn,
struct qemud_driver *driver,
virDomainObjPtr vm,
@ -118,6 +113,30 @@ static int qemuUpdateActivePciHostdevs(struct qemud_driver *driver,
static struct qemud_driver *qemu_driver = NULL;
static void *qemuDomainObjPrivateAlloc(void)
{
qemuDomainObjPrivatePtr priv;
if (VIR_ALLOC(priv) < 0)
return NULL;
return priv;
}
static void qemuDomainObjPrivateFree(void *data)
{
qemuDomainObjPrivatePtr priv = data;
/* This should never be non-NULL if we get here, but just in case... */
if (priv->mon) {
VIR_ERROR0("Unexpected QEMU monitor still active during domain deletion");
qemuMonitorClose(priv->mon);
}
VIR_FREE(priv);
}
static int qemuCgroupControllerActive(struct qemud_driver *driver,
int controller)
{
@ -292,22 +311,54 @@ qemudRemoveDomainStatus(virConnectPtr conn,
return 0;
}
/*
* This is a callback registered with a qemuMonitorPtr instance,
* and to be invoked when the monitor console hits an end of file
* condition, or error, thus indicating VM shutdown should be
* performed
*/
static void
qemuHandleMonitorEOF(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
virDomainObjPtr vm,
int hasError) {
struct qemud_driver *driver = qemu_driver;
virDomainEventPtr event = NULL;
qemuDriverLock(driver);
virDomainObjLock(vm);
qemuDriverUnlock(driver);
event = virDomainEventNewFromObj(vm,
VIR_DOMAIN_EVENT_STOPPED,
hasError ?
VIR_DOMAIN_EVENT_STOPPED_FAILED :
VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN);
qemudShutdownVMDaemon(NULL, driver, vm);
if (!vm->persistent)
virDomainRemoveInactive(&driver->domains, vm);
else
virDomainObjUnlock(vm);
if (event) {
qemuDriverLock(driver);
qemuDomainEventQueue(driver, event);
qemuDriverUnlock(driver);
}
}
static int
qemuConnectMonitor(virDomainObjPtr vm, int reconnect)
{
int rc;
if ((rc = qemuMonitorOpen(vm, reconnect)) != 0) {
VIR_ERROR(_("Failed to connect monitor for %s: %d\n"),
vm->def->name, rc);
qemuDomainObjPrivatePtr priv = vm->privateData;
if ((priv->mon = qemuMonitorOpen(vm, reconnect, qemuHandleMonitorEOF)) == NULL) {
VIR_ERROR(_("Failed to connect monitor for %s\n"), vm->def->name);
return -1;
}
if ((vm->monitorWatch = virEventAddHandle(vm->monitor,
VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR,
qemudDispatchVMEvent,
vm, NULL)) < 0)
return -1;
return 0;
}
@ -351,7 +402,7 @@ error:
}
/**
* qemudReconnectVMs
* qemudReconnectDomains
*
* Try to re-open the resources for live VMs that we care
* about.
@ -549,6 +600,9 @@ qemudStartup(int privileged) {
if ((qemu_driver->caps = qemudCapsInit(NULL)) == NULL)
goto out_of_memory;
qemu_driver->caps->privateDataAllocFunc = qemuDomainObjPrivateAlloc;
qemu_driver->caps->privateDataFreeFunc = qemuDomainObjPrivateFree;
if ((qemu_driver->activePciHostdevs = pciDeviceListNew(NULL)) == NULL)
goto error;
@ -1952,6 +2006,7 @@ static void qemudShutdownVMDaemon(virConnectPtr conn,
virDomainObjPtr vm) {
int ret;
int retries = 0;
qemuDomainObjPrivatePtr priv = vm->privateData;
if (!virDomainObjIsActive(vm))
return;
@ -1980,15 +2035,11 @@ static void qemudShutdownVMDaemon(virConnectPtr conn,
_("Failed to send SIGTERM to %s (%d)"),
vm->def->name, vm->pid);
if (vm->monitorWatch != -1) {
virEventRemoveHandle(vm->monitorWatch);
vm->monitorWatch = -1;
if (priv->mon) {
qemuMonitorClose(priv->mon);
priv->mon = NULL;
}
if (vm->monitor != -1)
close(vm->monitor);
vm->monitor = -1;
if (vm->monitor_chr) {
if (vm->monitor_chr->type == VIR_DOMAIN_CHR_TYPE_UNIX)
unlink(vm->monitor_chr->data.nix.path);
@ -2043,59 +2094,6 @@ retry:
}
static void
qemudDispatchVMEvent(int watch, int fd, int events, void *opaque) {
struct qemud_driver *driver = qemu_driver;
virDomainObjPtr vm = opaque;
virDomainEventPtr event = NULL;
int quit = 0, failed = 0;
/* XXX Normally we have to lock the driver first, to protect
* against someone adding/removing the domain. We know,
* however, then if we're getting data in this callback
* the VM must be running. Nowhere is allowed to remove
* a domain while it is running, so it is safe to not
* lock the driver here... */
qemuDriverLock(driver);
virDomainObjLock(vm);
qemuDriverUnlock(driver);
if (vm->monitor != fd || vm->monitorWatch != watch) {
failed = 1;
} else {
if (events & (VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR))
quit = 1;
else {
VIR_ERROR(_("unhandled fd event %d for %s"),
events, vm->def->name);
failed = 1;
}
}
if (failed || quit) {
event = virDomainEventNewFromObj(vm,
VIR_DOMAIN_EVENT_STOPPED,
quit ?
VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN :
VIR_DOMAIN_EVENT_STOPPED_FAILED);
qemudShutdownVMDaemon(NULL, driver, vm);
if (!vm->persistent) {
virDomainRemoveInactive(&driver->domains,
vm);
vm = NULL;
}
}
virDomainObjUnlock(vm);
if (event) {
qemuDriverLock(driver);
qemuDomainEventQueue(driver, event);
qemuDriverUnlock(driver);
}
}
static virDrvOpenStatus qemudOpen(virConnectPtr conn,
virConnectAuthPtr auth ATTRIBUTE_UNUSED,
int flags ATTRIBUTE_UNUSED) {
@ -2229,6 +2227,9 @@ static char *qemudGetCapabilities(virConnectPtr conn) {
goto cleanup;
}
caps->privateDataAllocFunc = qemuDomainObjPrivateAlloc;
caps->privateDataFreeFunc = qemuDomainObjPrivateFree;
if (qemu_driver->securityDriver &&
qemudSecurityCapsInit(qemu_driver->securityDriver, caps) < 0) {
virCapabilitiesFree(caps);

View File

@ -32,13 +32,24 @@
#include "qemu_conf.h"
#include "event.h"
#include "virterror_internal.h"
#include "memory.h"
#include "logging.h"
#define VIR_FROM_THIS VIR_FROM_QEMU
struct _qemuMonitor {
int fd;
int watch;
int hasSendFD;
virDomainObjPtr vm;
qemuMonitorEOFNotify eofCB;
};
/* Return -1 for error, 1 to continue reading and 0 for success */
typedef int qemuMonitorHandleOutput(virDomainObjPtr vm,
const char *output,
int fd);
const char *output);
/*
* Returns -1 for error, 0 on end-of-file, 1 for success
@ -101,7 +112,7 @@ qemuMonitorReadOutput(virDomainObjPtr vm,
} else {
got += ret;
buf[got] = '\0';
ret = func(vm, buf, fd);
ret = func(vm, buf);
if (ret == -1)
return -1;
if (ret == 1)
@ -117,15 +128,12 @@ qemuMonitorReadOutput(virDomainObjPtr vm,
}
static int
qemuMonitorCheckPrompt(virDomainObjPtr vm,
const char *output,
int fd)
qemuMonitorCheckPrompt(virDomainObjPtr vm ATTRIBUTE_UNUSED,
const char *output)
{
if (strstr(output, "(qemu) ") == NULL)
return 1; /* keep reading */
vm->monitor = fd;
return 0;
}
@ -157,14 +165,10 @@ qemuMonitorOpenCommon(virDomainObjPtr vm,
else
ret = 0;
} else {
vm->monitor = monfd;
ret = 0;
}
if (ret != 0)
return ret;
return 0;
return ret;
}
static int
@ -218,7 +222,7 @@ qemuMonitorOpenUnix(virDomainObjPtr vm,
if (qemuMonitorOpenCommon(vm, monfd, reconnect) < 0)
goto error;
return 0;
return monfd;
error:
close(monfd);
@ -241,28 +245,168 @@ qemuMonitorOpenPty(virDomainObjPtr vm,
if (qemuMonitorOpenCommon(vm, monfd, reconnect) < 0)
goto error;
return 0;
return monfd;
error:
close(monfd);
return -1;
}
int
static void
qemuMonitorIO(int watch, int fd, int events, void *opaque) {
qemuMonitorPtr mon = opaque;
int quit = 0, failed = 0;
if (mon->fd != fd || mon->watch != watch) {
VIR_ERROR0(_("event from unexpected fd/watch"));
failed = 1;
} else {
if (events & (VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR))
quit = 1;
else {
VIR_ERROR(_("unhandled fd event %d for monitor fd %d"),
events, mon->fd);
failed = 1;
}
}
mon->eofCB(mon, mon->vm, failed);
}
qemuMonitorPtr
qemuMonitorOpen(virDomainObjPtr vm,
int reconnect)
int reconnect,
qemuMonitorEOFNotify eofCB)
{
qemuMonitorPtr mon;
if (VIR_ALLOC(mon) < 0) {
virReportOOMError(NULL);
return NULL;
}
mon->fd = -1;
mon->vm = vm;
mon->eofCB = eofCB;
switch (vm->monitor_chr->type) {
case VIR_DOMAIN_CHR_TYPE_UNIX:
return qemuMonitorOpenUnix(vm, vm->monitor_chr->data.nix.path,
reconnect);
mon->hasSendFD = 1;
mon->fd = qemuMonitorOpenUnix(vm, vm->monitor_chr->data.nix.path,
reconnect);
break;
case VIR_DOMAIN_CHR_TYPE_PTY:
return qemuMonitorOpenPty(vm, vm->monitor_chr->data.file.path,
reconnect);
mon->fd = qemuMonitorOpenPty(vm, vm->monitor_chr->data.file.path,
reconnect);
break;
default:
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("unable to handle monitor type: %s"),
virDomainChrTypeToString(vm->monitor_chr->type));
goto cleanup;
}
if ((mon->watch = virEventAddHandle(mon->fd,
VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR,
qemuMonitorIO,
mon, NULL)) < 0) {
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s",
_("unable to register monitor events"));
goto cleanup;
}
return mon;
cleanup:
qemuMonitorClose(mon);
return NULL;
}
void qemuMonitorClose(qemuMonitorPtr mon)
{
if (!mon)
return;
if (mon->watch)
virEventRemoveHandle(mon->watch);
if (mon->fd != -1)
close(mon->fd);
VIR_FREE(mon);
}
int qemuMonitorWrite(qemuMonitorPtr mon,
const char *data,
size_t len)
{
return safewrite(mon->fd, data, len);
}
int qemuMonitorWriteWithFD(qemuMonitorPtr mon,
const char *data,
size_t len,
int fd)
{
struct msghdr msg;
struct iovec iov[1];
ssize_t ret;
char control[CMSG_SPACE(sizeof(int))];
struct cmsghdr *cmsg;
if (!mon->hasSendFD) {
errno = EINVAL;
return -1;
}
memset(&msg, 0, sizeof(msg));
iov[0].iov_base = (void *)data;
iov[0].iov_len = len;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = control;
msg.msg_controllen = sizeof(control);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
do {
ret = sendmsg(mon->fd, &msg, 0);
} while (ret < 0 && errno == EINTR);
return ret == len ? 0 : -1;
}
int qemuMonitorRead(qemuMonitorPtr mon,
char *data,
size_t len)
{
return read(mon->fd, data, len);
}
int qemuMonitorWaitForInput(qemuMonitorPtr mon)
{
struct pollfd fd = { mon->fd, POLLIN | POLLERR | POLLHUP, 0 };
retry:
if (poll(&fd, 1, -1) < 0) {
if (errno == EINTR)
goto retry;
return -1;
}
return 0;
}

View File

@ -29,9 +29,33 @@
#include "domain_conf.h"
int qemuMonitorOpen(virDomainObjPtr vm,
int reconnect);
typedef struct _qemuMonitor qemuMonitor;
typedef qemuMonitor *qemuMonitorPtr;
typedef void (*qemuMonitorEOFNotify)(qemuMonitorPtr mon,
virDomainObjPtr vm,
int withError);
qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm,
int reconnect,
qemuMonitorEOFNotify eofCB);
void qemuMonitorClose(qemuMonitorPtr mon);
int qemuMonitorWrite(qemuMonitorPtr mon,
const char *data,
size_t len);
int qemuMonitorWriteWithFD(qemuMonitorPtr mon,
const char *data,
size_t len,
int fd);
int qemuMonitorRead(qemuMonitorPtr mon,
char *data,
size_t len);
int qemuMonitorWaitForInput(qemuMonitorPtr mon);
#endif /* QEMU_MONITOR_H */

View File

@ -142,6 +142,7 @@ static char *qemuMonitorEscapeShell(const char *in)
*/
static void
qemuMonitorDiscardPendingData(virDomainObjPtr vm) {
qemuDomainObjPrivatePtr priv = vm->privateData;
char buf[1024];
int ret = 0;
@ -149,54 +150,17 @@ qemuMonitorDiscardPendingData(virDomainObjPtr vm) {
* get -1 or 0. Don't bother with detecting
* errors, since we'll deal with that better later */
do {
ret = read(vm->monitor, buf, sizeof (buf)-1);
ret = qemuMonitorRead(priv->mon, buf, sizeof (buf)-1);
} while (ret > 0);
}
static int
qemuMonitorSendUnix(const virDomainObjPtr vm,
const char *cmd,
size_t cmdlen,
int scm_fd)
{
struct msghdr msg;
struct iovec iov[1];
ssize_t ret;
memset(&msg, 0, sizeof(msg));
iov[0].iov_base = (void *)cmd;
iov[0].iov_len = cmdlen;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
if (scm_fd != -1) {
char control[CMSG_SPACE(sizeof(int))];
struct cmsghdr *cmsg;
msg.msg_control = control;
msg.msg_controllen = sizeof(control);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
memcpy(CMSG_DATA(cmsg), &scm_fd, sizeof(int));
}
do {
ret = sendmsg(vm->monitor, &msg, 0);
} while (ret < 0 && errno == EINTR);
return ret == cmdlen ? 0 : -1;
}
static int
qemuMonitorSend(const virDomainObjPtr vm,
const char *cmd,
int scm_fd)
{
qemuDomainObjPrivatePtr priv = vm->privateData;
char *full;
size_t len;
int ret = -1;
@ -208,20 +172,11 @@ qemuMonitorSend(const virDomainObjPtr vm,
len = strlen(full);
switch (vm->monitor_chr->type) {
case VIR_DOMAIN_CHR_TYPE_UNIX:
if (qemuMonitorSendUnix(vm, full, len, scm_fd) < 0)
goto out;
break;
default:
case VIR_DOMAIN_CHR_TYPE_PTY:
if (safewrite(vm->monitor, full, len) != len)
goto out;
break;
}
if (scm_fd == -1)
ret = qemuMonitorWrite(priv->mon, full, len);
else
ret = qemuMonitorWriteWithFD(priv->mon, full, len, scm_fd);
ret = 0;
out:
VIR_FREE(full);
return ret;
}
@ -234,12 +189,13 @@ qemuMonitorCommandWithHandler(const virDomainObjPtr vm,
void *handlerData,
int scm_fd,
char **reply) {
qemuDomainObjPrivatePtr priv = vm->privateData;
int size = 0;
char *buf = NULL;
/* Should never happen, but just in case, protect
* against null monitor (ocurrs when VM is inactive) */
if (!vm->monitor_chr)
if (!priv->mon)
return -1;
qemuMonitorDiscardPendingData(vm);
@ -251,13 +207,10 @@ qemuMonitorCommandWithHandler(const virDomainObjPtr vm,
*reply = NULL;
for (;;) {
struct pollfd fd = { vm->monitor, POLLIN | POLLERR | POLLHUP, 0 };
char *tmp;
/* Read all the data QEMU has sent thus far */
for (;;) {
char data[1024];
int got = read(vm->monitor, data, sizeof(data));
int got = qemuMonitorRead(priv->mon, data, sizeof(data));
if (got == 0)
goto error;
@ -279,6 +232,7 @@ qemuMonitorCommandWithHandler(const virDomainObjPtr vm,
/* Look for QEMU prompt to indicate completion */
if (buf) {
char *foundPrompt;
char *tmp;
if (extraPrompt &&
(foundPrompt = strstr(buf, extraPrompt)) != NULL) {
@ -314,13 +268,10 @@ qemuMonitorCommandWithHandler(const virDomainObjPtr vm,
break;
}
}
pollagain:
/* Need to wait for more data */
if (poll(&fd, 1, -1) < 0) {
if (errno == EINTR)
goto pollagain;
if (qemuMonitorWaitForInput(priv->mon) < 0)
goto error;
}
}
*reply = buf;
DEBUG("reply='%s'", buf);