/* If anything went wrong, this will be fed back
* the next monitor msg */
- int lastErrno;
+ virError lastError;
+
+ int nextSerial;
unsigned json: 1;
unsigned json_hmp: 1;
}
+/* This method processes data that has been received
+ * from the monitor. Looking for async events and
+ * replies/errors.
+ */
static int
qemuMonitorIOProcess(qemuMonitorPtr mon)
{
mon->buffer, mon->bufferOffset,
msg);
- if (len < 0) {
- mon->lastErrno = errno;
+ if (len < 0)
return -1;
- }
if (len < mon->bufferOffset) {
memmove(mon->buffer, mon->buffer + len, mon->bufferOffset - len);
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;
if (!mon->msg || mon->msg->txOffset == mon->msg->txLength)
return 0;
+ if (mon->msg->txFD != -1 && !mon->hasSendFD) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Monitor does not support sending of file descriptors"));
+ return -1;
+ }
+
if (mon->msg->txFD == -1)
done = write(mon->fd,
mon->msg->txBuffer + mon->msg->txOffset,
if (errno == EAGAIN)
return 0;
- mon->lastErrno = errno;
+ virReportSystemError(errno, "%s",
+ _("Unable to write to monitor"));
return -1;
}
mon->msg->txOffset += done;
if (avail < 1024) {
if (VIR_REALLOC_N(mon->buffer,
mon->bufferLength + 1024) < 0) {
- errno = ENOMEM;
+ virReportOOMError();
return -1;
}
mon->bufferLength += 1024;
if (got < 0) {
if (errno == EAGAIN)
break;
- mon->lastErrno = errno;
+ virReportSystemError(errno, "%s",
+ _("Unable to read from monitor"));
ret = -1;
break;
}
VIR_EVENT_HANDLE_HANGUP |
VIR_EVENT_HANDLE_ERROR;
- if (!mon->lastErrno) {
+ if (mon->lastError.code == VIR_ERR_OK) {
events |= VIR_EVENT_HANDLE_READABLE;
if (mon->msg && mon->msg->txOffset < mon->msg->txLength)
#endif
if (mon->fd != fd || mon->watch != watch) {
- VIR_ERROR(_("event from unexpected fd %d!=%d / watch %d!=%d"), mon->fd, fd, mon->watch, watch);
+ if (events & VIR_EVENT_HANDLE_HANGUP)
+ eof = true;
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("event from unexpected fd %d!=%d / watch %d!=%d"),
+ mon->fd, fd, mon->watch, watch);
+ error = true;
+ } else if (mon->lastError.code != VIR_ERR_OK) {
+ if (events & VIR_EVENT_HANDLE_HANGUP)
+ eof = true;
error = true;
} else {
- if (!mon->lastErrno &&
- events & VIR_EVENT_HANDLE_WRITABLE) {
- int done = qemuMonitorIOWrite(mon);
- if (done < 0)
- error = 1;
+ if (events & VIR_EVENT_HANDLE_WRITABLE) {
+ if (qemuMonitorIOWrite(mon) < 0)
+ error = true;
events &= ~VIR_EVENT_HANDLE_WRITABLE;
}
- if (!mon->lastErrno &&
+
+ if (!error &&
events & VIR_EVENT_HANDLE_READABLE) {
int got = qemuMonitorIORead(mon);
- if (got < 0)
+ events &= ~VIR_EVENT_HANDLE_READABLE;
+ if (got < 0) {
error = true;
- /* Ignore hangup/error events if we read some data, to
- * give time for that data to be consumed */
- if (got > 0) {
+ } else if (got == 0) {
+ eof = true;
+ } else {
+ /* Ignore hangup/error events if we read some data, to
+ * give time for that data to be consumed */
events = 0;
if (qemuMonitorIOProcess(mon) < 0)
error = true;
- } else
- events &= ~VIR_EVENT_HANDLE_READABLE;
+ }
}
- /* If IO process resulted in an error & we have a message,
- * then wakeup that waiter */
- if (mon->lastErrno && mon->msg && !mon->msg->finished) {
- mon->msg->lastErrno = mon->lastErrno;
- mon->msg->finished = 1;
- virCondSignal(&mon->notify);
+ if (!error &&
+ events & VIR_EVENT_HANDLE_HANGUP) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("End of file from monitor"));
+ eof = 1;
+ events &= ~VIR_EVENT_HANDLE_HANGUP;
}
- qemuMonitorUpdateWatch(mon);
-
- if (events & (VIR_EVENT_HANDLE_HANGUP |
- VIR_EVENT_HANDLE_ERROR)) {
- /* If IO process resulted in EOF & we have a message,
- * then wakeup that waiter */
- if (mon->msg && !mon->msg->finished) {
- mon->msg->finished = 1;
- mon->msg->lastErrno = EIO;
- virCondSignal(&mon->notify);
- }
- eof = 1;
- } else if (events) {
- VIR_ERROR(_("unhandled fd event %d for monitor fd %d"),
- events, mon->fd);
+ if (!error && !eof &&
+ events & VIR_EVENT_HANDLE_ERROR) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Error while waiting for monitor"));
+ error = 1;
+ }
+ if (!error && events) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unhandled event %d for monitor fd %d"),
+ events, mon->fd);
error = 1;
}
}
- if (eof || error)
- mon->lastErrno = EIO;
+ if (error || eof) {
+ if (mon->lastError.code != VIR_ERR_OK) {
+ /* Already have an error, so clear any new error */
+ virResetLastError();
+ } else {
+ virErrorPtr err = virGetLastError();
+ if (!err)
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Error while processing monitor IO"));
+ virCopyLastError(&mon->lastError);
+ virResetLastError();
+ }
+
+ VIR_DEBUG("Error on monitor %s", NULLSTR(mon->lastError.message));
+ /* If IO process resulted in an error & we have a message,
+ * then wakeup that waiter */
+ if (mon->msg && !mon->msg->finished) {
+ mon->msg->finished = 1;
+ virCondSignal(&mon->notify);
+ }
+ }
qemuMonitorUpdateWatch(mon);
}
+char *qemuMonitorNextCommandID(qemuMonitorPtr mon)
+{
+ char *id;
+
+ if (virAsprintf(&id, "libvirt-%d", ++mon->nextSerial) < 0) {
+ virReportOOMError();
+ return NULL;
+ }
+ return id;
+}
+
+
int qemuMonitorSend(qemuMonitorPtr mon,
qemuMonitorMessagePtr msg)
{
int ret = -1;
/* Check whether qemu quited unexpectedly */
- if (mon->lastErrno) {
- msg->lastErrno = mon->lastErrno;
+ if (mon->lastError.code != VIR_ERR_OK) {
+ VIR_DEBUG("Attempt to send command while error is set %s",
+ NULLSTR(mon->lastError.message));
+ virSetError(&mon->lastError);
return -1;
}
qemuMonitorUpdateWatch(mon);
while (!mon->msg->finished) {
- if (virCondWait(&mon->notify, &mon->lock) < 0)
+ if (virCondWait(&mon->notify, &mon->lock) < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Unable to wait on monitor condition"));
goto cleanup;
+ }
}
- if (mon->lastErrno == 0)
- ret = 0;
+ if (mon->lastError.code != VIR_ERR_OK) {
+ VIR_DEBUG("Send command resulted in error %s",
+ NULLSTR(mon->lastError.message));
+ virSetError(&mon->lastError);
+ goto cleanup;
+ }
+
+ ret = 0;
cleanup:
mon->msg = NULL;
VIR_DEBUG("Line [%s]", line);
- if (!(obj = virJSONValueFromString(line))) {
- VIR_DEBUG("Parsing JSON string failed");
- errno = EINVAL;
+ if (!(obj = virJSONValueFromString(line)))
goto cleanup;
- }
if (obj->type != VIR_JSON_TYPE_OBJECT) {
- VIR_DEBUG("Parsed JSON string isn't an object");
- errno = EINVAL;
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Parsed JSON reply '%s' isn't an object"), line);
+ goto cleanup;
}
if (virJSONValueObjectHasKey(obj, "QMP") == 1) {
- VIR_DEBUG("Got QMP capabilities data");
ret = 0;
- goto cleanup;
- }
-
- if (virJSONValueObjectHasKey(obj, "event") == 1) {
+ virJSONValueFree(obj);
+ } else if (virJSONValueObjectHasKey(obj, "event") == 1) {
ret = qemuMonitorJSONIOProcessEvent(mon, obj);
- goto cleanup;
- }
-
- if (msg) {
- if (!(msg->rxBuffer = strdup(line))) {
- errno = ENOMEM;
- goto cleanup;
+ } else if (virJSONValueObjectHasKey(obj, "error") == 1 ||
+ virJSONValueObjectHasKey(obj, "return") == 1) {
+ if (msg) {
+ msg->rxObject = obj;
+ msg->finished = 1;
+ ret = 0;
+ } else {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unexpected JSON reply '%s'"), line);
}
- msg->rxLength = strlen(line);
- msg->finished = 1;
} else {
- VIR_DEBUG("Ignoring unexpected JSON message [%s]", line);
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unknown JSON reply '%s'"), line);
}
- ret = 0;
-
cleanup:
- virJSONValueFree(obj);
+ if (ret < 0)
+ virJSONValueFree(obj);
return ret;
}
int got = nl - (data + used);
char *line = strndup(data + used, got);
if (!line) {
- errno = ENOMEM;
+ virReportOOMError();
return -1;
}
used += got + strlen(LINE_ENDING);
int ret = -1;
qemuMonitorMessage msg;
char *cmdstr = NULL;
+ char *id = NULL;
+ virJSONValuePtr exe;
*reply = NULL;
memset(&msg, 0, sizeof msg);
+ exe = virJSONValueObjectGet(cmd, "execute");
+ if (exe) {
+ if (!(id = qemuMonitorNextCommandID(mon)))
+ goto cleanup;
+ if (virJSONValueObjectAppendString(cmd, "id", id) < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Unable to append command 'id' string"));
+ goto cleanup;
+ }
+ }
+
if (!(cmdstr = virJSONValueToString(cmd))) {
virReportOOMError();
goto cleanup;
ret = qemuMonitorSend(mon, &msg);
- VIR_DEBUG("Receive command reply ret=%d errno=%d %d bytes '%s'",
- ret, msg.lastErrno, msg.rxLength, msg.rxBuffer);
+ VIR_DEBUG("Receive command reply ret=%d rxObject=%p",
+ ret, msg.rxObject);
- /* If we got ret==0, but not reply data something rather bad
- * went wrong, so lets fake an EIO error */
- if (!msg.rxBuffer && ret == 0) {
- msg.lastErrno = EIO;
- ret = -1;
- }
-
if (ret == 0) {
- if (!((*reply) = virJSONValueFromString(msg.rxBuffer))) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("cannot parse JSON doc '%s'"), msg.rxBuffer);
- goto cleanup;
+ if (!msg.rxObject) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Missing monitor reply object"));
+ ret = -1;
+ } else {
+ *reply = msg.rxObject;
}
}
- if (ret < 0)
- virReportSystemError(msg.lastErrno,
- _("cannot send monitor command '%s'"), cmdstr);
-
cleanup:
+ VIR_FREE(id);
VIR_FREE(cmdstr);
VIR_FREE(msg.txBuffer);
- VIR_FREE(msg.rxBuffer);
return ret;
}
#endif
if (msg->passwordHandler) {
int i;
- /* Try and handle the prompt */
+ /* Try and handle the prompt. The handler is required
+ * to report a normal libvirt error */
if (msg->passwordHandler(mon, msg,
start,
passwd - start + strlen(PASSWORD_PROMPT),
/* Handled, so skip forward over password prompt */
start = passwd;
} else {
- errno = EACCES;
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Password request seen, but no handler available"));
return -1;
}
}
* BASIC_PROMPT we can reasonably reliably cope */
if (want) {
if (VIR_REALLOC_N(msg->rxBuffer,
- msg->rxLength + want + 1) < 0)
+ msg->rxLength + want + 1) < 0) {
+ virReportOOMError();
return -1;
+ }
memcpy(msg->rxBuffer + msg->rxLength, start, want);
msg->rxLength += want;
msg->rxBuffer[msg->rxLength] = '\0';
ret = qemuMonitorSend(mon, &msg);
- VIR_DEBUG("Receive command reply ret=%d errno=%d %d bytes '%s'",
- ret, msg.lastErrno, msg.rxLength, msg.rxBuffer);
+ VIR_DEBUG("Receive command reply ret=%d rxLength=%d rxBuffer='%s'",
+ ret, msg.rxLength, msg.rxBuffer);
/* Just in case buffer had some passwords in */
memset(msg.txBuffer, 0, msg.txLength);
VIR_FREE(msg.txBuffer);
- /* To make life safer for callers, already ensure there's at least an empty string */
- if (msg.rxBuffer) {
- *reply = msg.rxBuffer;
- } else {
- *reply = strdup("");
- if (!*reply) {
- virReportOOMError();
- return -1;
+ if (ret >= 0) {
+ /* To make life safer for callers, already ensure there's at least an empty string */
+ if (msg.rxBuffer) {
+ *reply = msg.rxBuffer;
+ } else {
+ *reply = strdup("");
+ if (!*reply) {
+ virReportOOMError();
+ return -1;
+ }
}
}
- if (ret < 0) {
- virReportSystemError(msg.lastErrno,
- _("cannot send monitor command '%s'"), cmd);
- VIR_FREE(*reply);
- }
-
return ret;
}
pathStart = strstr(data, DISK_ENCRYPTION_PREFIX);
pathEnd = strstr(data, DISK_ENCRYPTION_POSTFIX);
if (!pathStart || !pathEnd || pathStart >= pathEnd) {
- errno = -EINVAL;
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to extract disk path from %s"),
+ data);
return -1;
}
/* Extra the path */
pathStart += strlen(DISK_ENCRYPTION_PREFIX);
- path = strndup(pathStart, pathEnd - pathStart);
- if (!path) {
- errno = ENOMEM;
+ if (!(path = strndup(pathStart, pathEnd - pathStart))) {
+ virReportOOMError();
return -1;
}
msg->txLength + passphrase_len + 1 + 1) < 0) {
memset(passphrase, 0, passphrase_len);
VIR_FREE(passphrase);
- errno = ENOMEM;
+ virReportOOMError();
return -1;
}
* to be sent back */
if (VIR_REALLOC_N(msg->txBuffer,
msg->txLength + passphrase_len + 1 + 1) < 0) {
- errno = ENOMEM;
+ virReportOOMError();
return -1;
}