/** * qemuAgentSend: * @mon: Monitor * @msg: Message * @seconds: number of seconds to wait for the result, it can be either * -2, -1, 0 or positive. * * Send @msg to agent @mon. If @seconds is equal to * VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK(-2), this function will block forever * waiting for the result. The value of * VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT(-1) means use default timeout value * and VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWAIT(0) makes this this function return * immediately without waiting. Any positive value means the number of seconds * to wait for the result. * * Returns: 0 on success, * -2 on timeout, * -1 otherwise */ static int qemuAgentSend(qemuAgentPtr mon, qemuAgentMessagePtr msg, int seconds) { int ret = -1; unsigned long long then = 0; /* Check whether qemu quit unexpectedly */ 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; } if (seconds > VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) { unsigned long long now; if (virTimeMillisNow(&now) < 0) return -1; if (seconds == VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT) seconds = QEMU_AGENT_WAIT_TIME; then = now + seconds * 1000ull; } mon->msg = msg; qemuAgentUpdateWatch(mon); while (!mon->msg->finished) { if ((then && virCondWaitUntil(&mon->notify, &mon->parent.lock, then) < 0) || (!then && virCondWait(&mon->notify, &mon->parent.lock) < 0)) { if (errno == ETIMEDOUT) { virReportError(VIR_ERR_AGENT_UNRESPONSIVE, "%s", _("Guest agent not available for now")); ret = -2; } else { virReportSystemError(errno, "%s", _("Unable to wait on agent monitor " "condition")); } goto cleanup; } } 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; qemuAgentUpdateWatch(mon); return ret; }
/** * qemuAgentSend: * @mon: Monitor * @msg: Message * @timeout: use timeout? * * Send @msg to agent @mon. * Wait max QEMU_AGENT_WAIT_TIME for agent * to reply. * * Returns: 0 on success, * -2 on timeout, * -1 otherwise */ static int qemuAgentSend(qemuAgentPtr mon, qemuAgentMessagePtr msg, bool timeout) { int ret = -1; unsigned long long now, then = 0; /* Check whether qemu quit unexpectedly */ 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; } if (timeout) { if (virTimeMillisNow(&now) < 0) return -1; then = now + QEMU_AGENT_WAIT_TIME; } mon->msg = msg; qemuAgentUpdateWatch(mon); while (!mon->msg->finished) { if ((timeout && virCondWaitUntil(&mon->notify, &mon->lock, then) < 0) || (!timeout && virCondWait(&mon->notify, &mon->lock) < 0)) { if (errno == ETIMEDOUT) { qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Guest agent not available for now")); ret = -2; } else { qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Unable to wait on monitor condition")); } goto cleanup; } } 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; qemuAgentUpdateWatch(mon); return ret; }
static void qemuAgentIO(int watch, int fd, int events, void *opaque) { qemuAgentPtr mon = opaque; bool error = false; bool eof = false; virObjectRef(mon); /* lock access to the monitor and protect fd */ qemuAgentLock(mon); #if DEBUG_IO VIR_DEBUG("Agent %p I/O on watch %d fd %d events %d", mon, watch, fd, events); #endif if (mon->fd != fd || mon->watch != watch) { if (events & (VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR)) eof = true; virReportError(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 | VIR_EVENT_HANDLE_ERROR)) eof = true; error = true; } else { if (events & VIR_EVENT_HANDLE_WRITABLE) { if (mon->connectPending) { if (qemuAgentIOConnect(mon) < 0) error = true; } else { if (qemuAgentIOWrite(mon) < 0) error = true; } events &= ~VIR_EVENT_HANDLE_WRITABLE; } if (!error && events & VIR_EVENT_HANDLE_READABLE) { int got = qemuAgentIORead(mon); events &= ~VIR_EVENT_HANDLE_READABLE; if (got < 0) { error = true; } 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 (qemuAgentIOProcess(mon) < 0) error = true; } } if (!error && events & VIR_EVENT_HANDLE_HANGUP) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("End of file from monitor")); eof = 1; events &= ~VIR_EVENT_HANDLE_HANGUP; } if (!error && !eof && events & VIR_EVENT_HANDLE_ERROR) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid file descriptor while waiting for monitor")); eof = 1; events &= ~VIR_EVENT_HANDLE_ERROR; } if (!error && events) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unhandled event %d for monitor fd %d"), events, mon->fd); error = 1; } } 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) virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("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); } } qemuAgentUpdateWatch(mon); /* We have to unlock to avoid deadlock against command thread, * but is this safe ? I think it is, because the callback * will try to acquire the virDomainObjPtr mutex next */ if (eof) { void (*eofNotify)(qemuAgentPtr, virDomainObjPtr) = mon->cb->eofNotify; virDomainObjPtr vm = mon->vm; /* Make sure anyone waiting wakes up now */ virCondSignal(&mon->notify); qemuAgentUnlock(mon); virObjectUnref(mon); VIR_DEBUG("Triggering EOF callback"); (eofNotify)(mon, vm); } else if (error) { void (*errorNotify)(qemuAgentPtr, virDomainObjPtr) = mon->cb->errorNotify; virDomainObjPtr vm = mon->vm; /* Make sure anyone waiting wakes up now */ virCondSignal(&mon->notify); qemuAgentUnlock(mon); virObjectUnref(mon); VIR_DEBUG("Triggering error callback"); (errorNotify)(mon, vm); } else { qemuAgentUnlock(mon); virObjectUnref(mon); } }