From f0835d3cc79f212378659eb50d076bd01c3c2967 Mon Sep 17 00:00:00 2001 Message-Id: <f0835d3cc79f212378659eb50d076bd01c3c2967.1289922177.git.jdenemar@redhat.com> From: Jiri Denemark <jdenemar@redhat.com> Date: Fri, 5 Nov 2010 13:51:50 +0100 Subject: [PATCH] qemu: Wire up text monitor events Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=574017 This patch wires up async events sent through qemu's text monitor to libvirt events. The main purpose is to update guest's state if it was stopped because of an I/O error. While I was at it, I wired up other events as well. Unfortunately, this is a RHEL-5-only patch since upstream qemu has no support for event notifications in text monitor. --- src/qemu/qemu_monitor.c | 2 +- src/qemu/qemu_monitor_text.c | 132 +++++++++++++++++++++++++++++++++++++++++- src/qemu/qemu_monitor_text.h | 4 +- 3 files changed, 134 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index b05032a..de25dc4 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -912,7 +912,7 @@ int qemuMonitorSetCapabilities(qemuMonitorPtr mon) if (mon->json) ret = qemuMonitorJSONSetCapabilities(mon); else - ret = 0; + ret = qemuMonitorTextSetCapabilities(mon); return ret; } diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index 569742a..4c5de55 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -68,12 +68,83 @@ typedef int qemuMonitorExtraPromptHandler(qemuMonitorPtr mon, #define DISK_ENCRYPTION_POSTFIX ") is encrypted." #define LINE_ENDING "\r\n" -int qemuMonitorTextIOProcess(qemuMonitorPtr mon ATTRIBUTE_UNUSED, - const char *data, +#define EVENT_PREFIX "# " +#define EVENT_SHUTDOWN "GUEST: Got shutdown request" +#define EVENT_POWERDOWN "GUEST: Got powerdown request" +#define EVENT_REBOOT "GUEST: Got reboot request" +#define EVENT_VMSTOP_PREFIX "VM is stopped due to disk write error: " +#define EVENT_RTC_PREFIX "RTC: new time is UTC" + + +static void +qemuMonitorTextHandleVMStop(qemuMonitorPtr mon, + char *event) +{ + char *device; + char *sep; + + device = event + strlen(EVENT_VMSTOP_PREFIX); + + sep = strstr(device, ": "); + if (sep) + *sep = '\0'; + + /* + * qemu-kvm in RHEL-5 provides human readable reason string in + * strerror(errno) form for VMSTOP event. However, qemu-kvm only sends + * this event on ENOSPC error so instead of trying to map the + * provided reason to a corresponding symbolic name we just hardcode it. + */ + qemuMonitorEmitIOError(mon, device, VIR_DOMAIN_EVENT_IO_ERROR_PAUSE, + "enospc"); + if (sep) + *sep = ':'; +} + + +static void +qemuMonitorTextHandleRTCChange(qemuMonitorPtr mon, + char *event) +{ + long offset; + + if (virStrToLong_i(event + strlen(EVENT_RTC_PREFIX), + NULL, 10, &offset) < 0) { + VIR_WARN("cannot parse offset in RTC change event: %s", event); + offset = 0; + } + qemuMonitorEmitRTCChange(mon, offset); +} + + +static void +qemuMonitorTextIOProcessEvent(qemuMonitorPtr mon, + char *event) +{ + VIR_DEBUG("mon=%p, event='%s'", mon, event); + + if (STREQ(event, EVENT_SHUTDOWN)) + qemuMonitorEmitShutdown(mon); + else if (STREQ(event, EVENT_POWERDOWN)) + qemuMonitorEmitPowerdown(mon); + else if (STREQ(event, EVENT_REBOOT)) + qemuMonitorEmitReset(mon); + else if (STRPREFIX(event, EVENT_VMSTOP_PREFIX)) + qemuMonitorTextHandleVMStop(mon, event); + else if (STRPREFIX(event, EVENT_RTC_PREFIX)) + qemuMonitorTextHandleRTCChange(mon, event); + else + VIR_DEBUG("No handler for event '%s'", event); +} + + +int qemuMonitorTextIOProcess(qemuMonitorPtr mon, + char *data, size_t len ATTRIBUTE_UNUSED, qemuMonitorMessagePtr msg) { int used = 0; + char *evstart; /* Check for & discard greeting */ if (STRPREFIX(data, GREETING_PREFIX)) { @@ -101,6 +172,42 @@ int qemuMonitorTextIOProcess(qemuMonitorPtr mon ATTRIBUTE_UNUSED, VIR_DEBUG("Process data %d byts of data", (int)(len - used)); #endif + /* First process all events and move them to the beginning of data */ + while ((evstart = strstr(data + used, EVENT_PREFIX))) { + char *start = data + used; + unsigned int evlen; + char *event; + char *nl; + char end; + + if (!(nl = strstr(evstart, LINE_ENDING))) { +#if DEBUG_IO + VIR_DEBUG0("Partial event, getting out and waiting for more"); +#endif + return used; + } + nl += strlen(LINE_ENDING); + end = *nl; + *nl = '\0'; + + evlen = strlen(evstart); + event = strdup(evstart); + *nl = end; + if (!event) { + virReportOOMError(); + return -1; + } + + memmove(start + evlen, start, evstart - start); + memcpy(start, event, evlen); + + event[evlen - strlen(LINE_ENDING)] = '\0'; + qemuMonitorTextIOProcessEvent(mon, event + strlen(EVENT_PREFIX)); + free(event); + + used += evlen; + } + /* Look for a non-zero reply followed by prompt */ if (msg && !msg->finished) { char *start = NULL; @@ -346,6 +453,27 @@ qemuMonitorSendDiskPassphrase(qemuMonitorPtr mon, return 0; } + +int +qemuMonitorTextSetCapabilities(qemuMonitorPtr mon) +{ + char *info = NULL; + char *offset; + + if (qemuMonitorCommand(mon, "notify all on", &info) < 0) + return -1; + + if (info && *info) { + VIR_WARN("Could not enable event notifications on monitor %p: %s", + mon, info); + } + + VIR_FREE(info); + + return 0; +} + + int qemuMonitorTextStartCPUs(qemuMonitorPtr mon, virConnectPtr conn) { diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index 9926d34..4e5ac7d 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -31,10 +31,12 @@ # include "hash.h" int qemuMonitorTextIOProcess(qemuMonitorPtr mon, - const char *data, + char *data, size_t len, qemuMonitorMessagePtr msg); +int qemuMonitorTextSetCapabilities(qemuMonitorPtr mon); + int qemuMonitorTextStartCPUs(qemuMonitorPtr mon, virConnectPtr conn); int qemuMonitorTextStopCPUs(qemuMonitorPtr mon); -- 1.7.3.2