2009-09-22 17:48:40 +00:00
|
|
|
/*
|
|
|
|
* qemu_monitor_text.c: interaction with QEMU monitor console
|
|
|
|
*
|
2012-02-17 21:55:35 +00:00
|
|
|
* Copyright (C) 2006-2012 Red Hat, Inc.
|
2009-09-22 17:48:40 +00:00
|
|
|
* Copyright (C) 2006 Daniel P. Berrange
|
|
|
|
*
|
|
|
|
* 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
|
2012-09-20 22:30:55 +00:00
|
|
|
* License along with this library. If not, see
|
2012-07-21 10:06:23 +00:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
2009-09-22 17:48:40 +00:00
|
|
|
*
|
|
|
|
* Author: Daniel P. Berrange <berrange@redhat.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/un.h>
|
|
|
|
#include <poll.h>
|
|
|
|
#include <unistd.h>
|
2009-09-23 13:27:12 +00:00
|
|
|
#include <string.h>
|
2009-09-22 17:48:40 +00:00
|
|
|
|
|
|
|
#include "qemu_monitor_text.h"
|
2010-12-16 15:07:07 +00:00
|
|
|
#include "qemu_command.h"
|
2009-09-23 10:49:14 +00:00
|
|
|
#include "c-ctype.h"
|
2011-03-31 02:26:27 +00:00
|
|
|
#include "c-strcasestr.h"
|
2012-12-12 18:06:53 +00:00
|
|
|
#include "viralloc.h"
|
2012-12-12 17:59:27 +00:00
|
|
|
#include "virlog.h"
|
2009-09-22 17:48:40 +00:00
|
|
|
#include "driver.h"
|
|
|
|
#include "datatypes.h"
|
2012-12-13 18:21:53 +00:00
|
|
|
#include "virerror.h"
|
2012-12-04 12:04:07 +00:00
|
|
|
#include "virbuffer.h"
|
2009-09-22 17:48:40 +00:00
|
|
|
|
2012-04-02 17:24:29 +00:00
|
|
|
#ifdef WITH_DTRACE_PROBES
|
|
|
|
# include "libvirt_qemu_probes.h"
|
|
|
|
#endif
|
|
|
|
|
2009-09-22 17:48:40 +00:00
|
|
|
#define VIR_FROM_THIS VIR_FROM_QEMU
|
|
|
|
|
2009-09-23 16:39:07 +00:00
|
|
|
#define QEMU_CMD_PROMPT "\n(qemu) "
|
|
|
|
#define QEMU_PASSWD_PROMPT "Password: "
|
|
|
|
|
2010-02-01 16:28:27 +00:00
|
|
|
#define DEBUG_IO 0
|
|
|
|
|
2009-09-23 16:39:07 +00:00
|
|
|
/* Return -1 for error, 0 for success */
|
2009-10-09 20:13:06 +00:00
|
|
|
typedef int qemuMonitorExtraPromptHandler(qemuMonitorPtr mon,
|
2009-09-23 16:39:07 +00:00
|
|
|
const char *buf,
|
|
|
|
const char *prompt,
|
|
|
|
void *data);
|
|
|
|
|
|
|
|
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
/* When connecting to a monitor, QEMU will print a greeting like
|
|
|
|
*
|
|
|
|
* QEMU 0.11.0 monitor - type 'help' for more information
|
|
|
|
*
|
|
|
|
* Don't expect the version number bit to be stable :-)
|
2009-09-22 17:48:40 +00:00
|
|
|
*/
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
#define GREETING_PREFIX "QEMU "
|
|
|
|
#define GREETING_POSTFIX "type 'help' for more information\r\n(qemu) "
|
|
|
|
#define BASIC_PROMPT "(qemu) "
|
|
|
|
#define PASSWORD_PROMPT "Password:"
|
|
|
|
#define DISK_ENCRYPTION_PREFIX "("
|
|
|
|
#define DISK_ENCRYPTION_POSTFIX ") is encrypted."
|
|
|
|
#define LINE_ENDING "\r\n"
|
|
|
|
|
|
|
|
int qemuMonitorTextIOProcess(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
|
|
|
const char *data,
|
2010-02-01 16:28:27 +00:00
|
|
|
size_t len ATTRIBUTE_UNUSED,
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
qemuMonitorMessagePtr msg)
|
|
|
|
{
|
|
|
|
int used = 0;
|
2009-09-22 17:48:40 +00:00
|
|
|
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
/* Check for & discard greeting */
|
|
|
|
if (STRPREFIX(data, GREETING_PREFIX)) {
|
|
|
|
const char *offset = strstr(data, GREETING_POSTFIX);
|
2009-09-22 17:48:40 +00:00
|
|
|
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
/* We see the greeting prefix, but not postfix, so pretend we've
|
|
|
|
not consumed anything. We'll restart when more data arrives. */
|
|
|
|
if (!offset) {
|
2010-02-01 16:28:27 +00:00
|
|
|
#if DEBUG_IO
|
2011-05-09 09:24:09 +00:00
|
|
|
VIR_DEBUG("Partial greeting seen, getting out & waiting for more");
|
2010-02-01 16:28:27 +00:00
|
|
|
#endif
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2009-09-22 17:48:40 +00:00
|
|
|
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
used = offset - data + strlen(GREETING_POSTFIX);
|
|
|
|
|
2010-02-01 16:28:27 +00:00
|
|
|
#if DEBUG_IO
|
2011-05-09 09:24:09 +00:00
|
|
|
VIR_DEBUG("Discarded monitor greeting");
|
2010-02-01 16:28:27 +00:00
|
|
|
#endif
|
2009-11-08 21:08:54 +00:00
|
|
|
}
|
2009-09-22 17:48:40 +00:00
|
|
|
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
/* Don't print raw data in debug because its full of control chars */
|
|
|
|
/*VIR_DEBUG("Process data %d byts of data [%s]", len - used, data + used);*/
|
2010-02-01 16:28:27 +00:00
|
|
|
#if DEBUG_IO
|
2009-11-11 10:30:01 +00:00
|
|
|
VIR_DEBUG("Process data %d byts of data", (int)(len - used));
|
2010-02-01 16:28:27 +00:00
|
|
|
#endif
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
|
|
|
|
/* Look for a non-zero reply followed by prompt */
|
|
|
|
if (msg && !msg->finished) {
|
2009-12-08 18:05:02 +00:00
|
|
|
char *start = NULL;
|
|
|
|
char *end = NULL;
|
|
|
|
char *skip;
|
|
|
|
|
|
|
|
/* If we're here, we've already sent the command. We now
|
|
|
|
* strip the trailing '\r' because it makes the matching
|
|
|
|
* code that follows a little easier ie we can just strstr()
|
|
|
|
* for the original command
|
|
|
|
*/
|
|
|
|
if (msg->txLength > 0) {
|
|
|
|
char *tmp;
|
|
|
|
if ((tmp = strchr(msg->txBuffer, '\r'))) {
|
|
|
|
*tmp = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* QEMU echos the command back to us, full of control
|
|
|
|
* character junk that we don't want. We have to skip
|
|
|
|
* over this junk by looking for the first complete
|
|
|
|
* repetition of our command. Then we can look for
|
|
|
|
* the prompt that is supposed to follow
|
|
|
|
*
|
|
|
|
* NB, we can't optimize by immediately looking for
|
|
|
|
* LINE_ENDING, because QEMU 0.10 has bad problems
|
|
|
|
* when initially connecting where it might write a
|
|
|
|
* prompt in the wrong place. So we must not look
|
|
|
|
* for LINE_ENDING, or BASIC_PROMPT until we've
|
|
|
|
* seen our original command echod.
|
|
|
|
*/
|
|
|
|
skip = strstr(data + used, msg->txBuffer);
|
|
|
|
|
|
|
|
/* After the junk we should have a line ending... */
|
|
|
|
if (skip) {
|
|
|
|
start = strstr(skip + strlen(msg->txBuffer), LINE_ENDING);
|
|
|
|
}
|
2009-09-22 17:48:40 +00:00
|
|
|
|
2009-12-08 18:05:02 +00:00
|
|
|
/* ... then our command reply data, following by a (qemu) prompt */
|
|
|
|
if (start) {
|
|
|
|
char *passwd;
|
|
|
|
start += strlen(LINE_ENDING);
|
|
|
|
|
|
|
|
/* We might get a prompt for a password before the (qemu) prompt */
|
|
|
|
passwd = strstr(start, PASSWORD_PROMPT);
|
|
|
|
if (passwd) {
|
2010-02-01 16:28:27 +00:00
|
|
|
#if DEBUG_IO
|
2011-08-17 06:30:02 +00:00
|
|
|
VIR_DEBUG("Seen a password prompt [%s]", data + used);
|
2010-02-01 16:28:27 +00:00
|
|
|
#endif
|
2009-12-08 18:05:02 +00:00
|
|
|
if (msg->passwordHandler) {
|
|
|
|
int i;
|
2011-05-29 12:51:08 +00:00
|
|
|
/* Try and handle the prompt. The handler is required
|
|
|
|
* to report a normal libvirt error */
|
2009-12-08 18:05:02 +00:00
|
|
|
if (msg->passwordHandler(mon, msg,
|
|
|
|
start,
|
|
|
|
passwd - start + strlen(PASSWORD_PROMPT),
|
|
|
|
msg->passwordOpaque) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Blank out the password prompt so we don't re-trigger
|
|
|
|
* if we have to go back to sleep for more I/O */
|
|
|
|
for (i = 0 ; i < strlen(PASSWORD_PROMPT) ; i++)
|
|
|
|
start[i] = ' ';
|
|
|
|
|
|
|
|
/* Handled, so skip forward over password prompt */
|
|
|
|
start = passwd;
|
|
|
|
} else {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Password request seen, but no handler available"));
|
2009-12-08 18:05:02 +00:00
|
|
|
return -1;
|
|
|
|
}
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
}
|
2009-12-08 18:05:02 +00:00
|
|
|
|
|
|
|
end = strstr(start, BASIC_PROMPT);
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
}
|
2009-09-22 17:48:40 +00:00
|
|
|
|
2009-12-08 18:05:02 +00:00
|
|
|
if (start && end) {
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
int want = end - start;
|
|
|
|
/* Annoyingly some commands may not have any reply data
|
|
|
|
* at all upon success, but since we've detected the
|
|
|
|
* BASIC_PROMPT we can reasonably reliably cope */
|
|
|
|
if (want) {
|
|
|
|
if (VIR_REALLOC_N(msg->rxBuffer,
|
2011-05-29 12:51:08 +00:00
|
|
|
msg->rxLength + want + 1) < 0) {
|
|
|
|
virReportOOMError();
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
return -1;
|
2011-05-29 12:51:08 +00:00
|
|
|
}
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
memcpy(msg->rxBuffer + msg->rxLength, start, want);
|
|
|
|
msg->rxLength += want;
|
|
|
|
msg->rxBuffer[msg->rxLength] = '\0';
|
2010-02-01 16:28:27 +00:00
|
|
|
#if DEBUG_IO
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
VIR_DEBUG("Finished %d byte reply [%s]", want, msg->rxBuffer);
|
|
|
|
} else {
|
2011-05-09 09:24:09 +00:00
|
|
|
VIR_DEBUG("Finished 0 byte reply");
|
2010-02-01 16:28:27 +00:00
|
|
|
#endif
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
}
|
2011-10-24 14:31:37 +00:00
|
|
|
PROBE(QEMU_MONITOR_RECV_REPLY,
|
|
|
|
"mon=%p reply=%s",
|
|
|
|
mon, msg->rxBuffer);
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
msg->finished = 1;
|
|
|
|
used += end - (data + used);
|
|
|
|
used += strlen(BASIC_PROMPT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-01 16:28:27 +00:00
|
|
|
#if DEBUG_IO
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
VIR_DEBUG("Total used %d", used);
|
2010-02-01 16:28:27 +00:00
|
|
|
#endif
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
return used;
|
2009-09-22 17:48:40 +00:00
|
|
|
}
|
|
|
|
|
2009-09-23 16:39:07 +00:00
|
|
|
static int
|
2011-03-10 08:37:08 +00:00
|
|
|
qemuMonitorTextCommandWithHandler(qemuMonitorPtr mon,
|
|
|
|
const char *cmd,
|
|
|
|
qemuMonitorPasswordHandler passwordHandler,
|
|
|
|
void *passwordOpaque,
|
|
|
|
int scm_fd,
|
|
|
|
char **reply)
|
|
|
|
{
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
int ret;
|
|
|
|
qemuMonitorMessage msg;
|
2009-09-22 17:48:40 +00:00
|
|
|
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
*reply = NULL;
|
2009-09-22 17:48:40 +00:00
|
|
|
|
2012-03-29 09:52:04 +00:00
|
|
|
memset(&msg, 0, sizeof(msg));
|
2009-09-22 17:48:40 +00:00
|
|
|
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
if (virAsprintf(&msg.txBuffer, "%s\r", cmd) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
msg.txLength = strlen(msg.txBuffer);
|
|
|
|
msg.txFD = scm_fd;
|
|
|
|
msg.passwordHandler = passwordHandler;
|
|
|
|
msg.passwordOpaque = passwordOpaque;
|
2009-09-22 17:48:40 +00:00
|
|
|
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
VIR_DEBUG("Send command '%s' for write with FD %d", cmd, scm_fd);
|
2009-09-22 17:48:40 +00:00
|
|
|
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
ret = qemuMonitorSend(mon, &msg);
|
2009-09-22 17:48:40 +00:00
|
|
|
|
2011-05-29 12:51:08 +00:00
|
|
|
VIR_DEBUG("Receive command reply ret=%d rxLength=%d rxBuffer='%s'",
|
|
|
|
ret, msg.rxLength, msg.rxBuffer);
|
2009-09-22 17:48:40 +00:00
|
|
|
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
/* Just in case buffer had some passwords in */
|
|
|
|
memset(msg.txBuffer, 0, msg.txLength);
|
|
|
|
VIR_FREE(msg.txBuffer);
|
2009-09-22 17:48:40 +00:00
|
|
|
|
2011-05-29 12:51:08 +00:00
|
|
|
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;
|
|
|
|
}
|
2009-09-22 17:48:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
return ret;
|
2009-09-22 17:48:40 +00:00
|
|
|
}
|
|
|
|
|
2011-03-09 20:24:04 +00:00
|
|
|
int
|
|
|
|
qemuMonitorTextCommandWithFd(qemuMonitorPtr mon,
|
|
|
|
const char *cmd,
|
|
|
|
int scm_fd,
|
|
|
|
char **reply)
|
|
|
|
{
|
2011-03-10 08:37:08 +00:00
|
|
|
return qemuMonitorTextCommandWithHandler(mon, cmd, NULL, NULL,
|
|
|
|
scm_fd, reply);
|
2009-09-22 17:48:40 +00:00
|
|
|
}
|
|
|
|
|
2011-08-24 06:39:42 +00:00
|
|
|
/* Check monitor output for evidence that the command was not recognized.
|
|
|
|
* For 'info' commands, qemu returns help text. For other commands, qemu
|
|
|
|
* returns 'unknown command:'.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
qemuMonitorTextCommandNotFound(const char *cmd, const char *reply)
|
|
|
|
{
|
|
|
|
if (STRPREFIX(cmd, "info ")) {
|
|
|
|
if (strstr(reply, "info version"))
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
if (strstr(reply, "unknown command:"))
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2009-09-22 17:48:40 +00:00
|
|
|
|
|
|
|
static int
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
qemuMonitorSendDiskPassphrase(qemuMonitorPtr mon,
|
|
|
|
qemuMonitorMessagePtr msg,
|
|
|
|
const char *data,
|
|
|
|
size_t len ATTRIBUTE_UNUSED,
|
|
|
|
void *opaque)
|
2009-09-22 17:48:40 +00:00
|
|
|
{
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
virConnectPtr conn = opaque;
|
|
|
|
char *path;
|
|
|
|
char *passphrase = NULL;
|
|
|
|
size_t passphrase_len = 0;
|
2009-09-22 17:48:40 +00:00
|
|
|
int res;
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
const char *pathStart;
|
|
|
|
const char *pathEnd;
|
2009-09-22 17:48:40 +00:00
|
|
|
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
/*
|
|
|
|
* For disk passwords:
|
|
|
|
*
|
|
|
|
* ide0-hd0 (/path/to/volume) is encrypted.
|
|
|
|
* Password:
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
pathStart = strstr(data, DISK_ENCRYPTION_PREFIX);
|
|
|
|
pathEnd = strstr(data, DISK_ENCRYPTION_POSTFIX);
|
|
|
|
if (!pathStart || !pathEnd || pathStart >= pathEnd) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Unable to extract disk path from %s"),
|
|
|
|
data);
|
2009-09-22 17:48:40 +00:00
|
|
|
return -1;
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Extra the path */
|
|
|
|
pathStart += strlen(DISK_ENCRYPTION_PREFIX);
|
2011-05-29 12:51:08 +00:00
|
|
|
if (!(path = strndup(pathStart, pathEnd - pathStart))) {
|
|
|
|
virReportOOMError();
|
2009-09-22 17:48:40 +00:00
|
|
|
return -1;
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
}
|
2009-09-22 17:48:40 +00:00
|
|
|
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
/* Fetch the disk password if possible */
|
|
|
|
res = qemuMonitorGetDiskSecret(mon,
|
|
|
|
conn,
|
|
|
|
path,
|
|
|
|
&passphrase,
|
|
|
|
&passphrase_len);
|
2009-09-22 17:48:40 +00:00
|
|
|
VIR_FREE(path);
|
2009-10-09 19:34:24 +00:00
|
|
|
if (res < 0)
|
2009-09-22 17:48:40 +00:00
|
|
|
return -1;
|
|
|
|
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
/* Enlarge transmit buffer to allow for the extra data
|
|
|
|
* to be sent back */
|
|
|
|
if (VIR_REALLOC_N(msg->txBuffer,
|
|
|
|
msg->txLength + passphrase_len + 1 + 1) < 0) {
|
|
|
|
memset(passphrase, 0, passphrase_len);
|
|
|
|
VIR_FREE(passphrase);
|
2011-05-29 12:51:08 +00:00
|
|
|
virReportOOMError();
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Queue the password for sending */
|
|
|
|
memcpy(msg->txBuffer + msg->txLength,
|
|
|
|
passphrase, passphrase_len);
|
|
|
|
msg->txLength += passphrase_len;
|
|
|
|
msg->txBuffer[msg->txLength] = '\r';
|
|
|
|
msg->txLength++;
|
|
|
|
msg->txBuffer[msg->txLength] = '\0';
|
2009-09-22 17:48:40 +00:00
|
|
|
|
|
|
|
memset(passphrase, 0, passphrase_len);
|
|
|
|
VIR_FREE(passphrase);
|
|
|
|
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
return 0;
|
2009-09-22 17:48:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2009-10-09 20:13:06 +00:00
|
|
|
qemuMonitorTextStartCPUs(qemuMonitorPtr mon,
|
|
|
|
virConnectPtr conn) {
|
2009-09-22 17:48:40 +00:00
|
|
|
char *reply;
|
|
|
|
|
2011-03-10 08:37:08 +00:00
|
|
|
if (qemuMonitorTextCommandWithHandler(mon, "cont",
|
|
|
|
qemuMonitorSendDiskPassphrase,
|
|
|
|
conn,
|
|
|
|
-1, &reply) < 0)
|
2009-09-22 17:48:40 +00:00
|
|
|
return -1;
|
2009-09-24 14:37:05 +00:00
|
|
|
|
2009-09-22 17:48:40 +00:00
|
|
|
VIR_FREE(reply);
|
|
|
|
return 0;
|
|
|
|
}
|
2009-09-23 10:49:14 +00:00
|
|
|
|
|
|
|
|
2009-09-23 11:14:53 +00:00
|
|
|
int
|
2009-10-09 20:13:06 +00:00
|
|
|
qemuMonitorTextStopCPUs(qemuMonitorPtr mon) {
|
2009-09-23 11:14:53 +00:00
|
|
|
char *info;
|
2012-09-13 12:54:54 +00:00
|
|
|
int ret;
|
2009-09-23 11:14:53 +00:00
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
ret = qemuMonitorHMPCommand(mon, "stop", &info);
|
2009-09-23 11:14:53 +00:00
|
|
|
VIR_FREE(info);
|
2012-09-13 12:54:54 +00:00
|
|
|
return ret;
|
2009-09-23 11:14:53 +00:00
|
|
|
}
|
|
|
|
|
2009-09-23 11:21:00 +00:00
|
|
|
|
2011-05-05 11:50:25 +00:00
|
|
|
int
|
2011-09-27 09:42:04 +00:00
|
|
|
qemuMonitorTextGetStatus(qemuMonitorPtr mon,
|
|
|
|
bool *running,
|
|
|
|
virDomainPausedReason *reason)
|
2011-05-05 11:50:25 +00:00
|
|
|
{
|
|
|
|
char *reply;
|
|
|
|
int ret = -1;
|
|
|
|
|
2011-09-27 09:42:04 +00:00
|
|
|
if (reason)
|
|
|
|
*reason = VIR_DOMAIN_PAUSED_UNKNOWN;
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, "info status", &reply) < 0)
|
2011-05-05 11:50:25 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (strstr(reply, "running")) {
|
|
|
|
*running = true;
|
|
|
|
} else if (strstr(reply, "paused")) {
|
2011-09-27 09:42:04 +00:00
|
|
|
char *status;
|
|
|
|
|
|
|
|
if ((status = strchr(reply, '('))) {
|
|
|
|
char *end = strchr(status, ')');
|
|
|
|
if (end)
|
|
|
|
*end = '\0';
|
|
|
|
else
|
|
|
|
status = NULL;
|
|
|
|
}
|
|
|
|
if (!status)
|
|
|
|
VIR_DEBUG("info status was missing status details");
|
|
|
|
else if (reason)
|
|
|
|
*reason = qemuMonitorVMStatusToPausedReason(status);
|
2011-05-05 11:50:25 +00:00
|
|
|
*running = false;
|
|
|
|
} else {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
_("unexpected reply from info status: %s"), reply);
|
2011-05-05 11:50:25 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-09 20:13:06 +00:00
|
|
|
int qemuMonitorTextSystemPowerdown(qemuMonitorPtr mon) {
|
2009-09-23 11:21:00 +00:00
|
|
|
char *info;
|
2012-09-13 12:54:54 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = qemuMonitorHMPCommand(mon, "system_powerdown", &info);
|
2009-09-23 11:21:00 +00:00
|
|
|
|
|
|
|
VIR_FREE(info);
|
2012-09-13 12:54:54 +00:00
|
|
|
return ret;
|
2009-09-23 11:21:00 +00:00
|
|
|
}
|
|
|
|
|
2011-09-06 08:18:57 +00:00
|
|
|
int qemuMonitorTextSetLink(qemuMonitorPtr mon, const char *name, enum virDomainNetInterfaceLinkState state) {
|
|
|
|
char *info = NULL;
|
|
|
|
char *cmd = NULL;
|
|
|
|
const char *st_str = NULL;
|
|
|
|
|
|
|
|
/* determine state */
|
|
|
|
if (state == VIR_DOMAIN_NET_INTERFACE_LINK_STATE_DOWN)
|
|
|
|
st_str = "off";
|
|
|
|
else
|
|
|
|
st_str = "on";
|
|
|
|
|
|
|
|
if (virAsprintf(&cmd, "set_link %s %s", name, st_str) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto error;
|
|
|
|
}
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &info) < 0)
|
2011-09-06 08:18:57 +00:00
|
|
|
goto error;
|
|
|
|
|
|
|
|
/* check if set_link command is supported */
|
|
|
|
if (strstr(info, "\nunknown ")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_NO_SUPPORT,
|
|
|
|
"%s",
|
|
|
|
_("\'set_link\' not supported by this qemu"));
|
2011-09-06 08:18:57 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check if qemu didn't reject device name */
|
|
|
|
if (strstr(info, "\nDevice ")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
"%s", _("device name rejected"));
|
2011-09-06 08:18:57 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(info);
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
error:
|
|
|
|
VIR_FREE(info);
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
2009-09-23 11:21:00 +00:00
|
|
|
|
2011-06-15 16:49:58 +00:00
|
|
|
int qemuMonitorTextSystemReset(qemuMonitorPtr mon) {
|
|
|
|
char *info;
|
2012-09-13 12:54:54 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = qemuMonitorHMPCommand(mon, "system_reset", &info);
|
2011-06-15 16:49:58 +00:00
|
|
|
|
|
|
|
VIR_FREE(info);
|
2012-09-13 12:54:54 +00:00
|
|
|
return ret;
|
2011-06-15 16:49:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-09 20:13:06 +00:00
|
|
|
int qemuMonitorTextGetCPUInfo(qemuMonitorPtr mon,
|
|
|
|
int **pids)
|
2009-09-23 10:49:14 +00:00
|
|
|
{
|
|
|
|
char *qemucpus = NULL;
|
|
|
|
char *line;
|
|
|
|
int lastVcpu = -1;
|
|
|
|
pid_t *cpupids = NULL;
|
|
|
|
size_t ncpupids = 0;
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, "info cpus", &qemucpus) < 0)
|
2009-09-23 10:49:14 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is the gross format we're about to parse :-{
|
|
|
|
*
|
|
|
|
* (qemu) info cpus
|
|
|
|
* * CPU #0: pc=0x00000000000f0c4a thread_id=30019
|
|
|
|
* CPU #1: pc=0x00000000fffffff0 thread_id=30020
|
|
|
|
* CPU #2: pc=0x00000000fffffff0 thread_id=30021
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
line = qemucpus;
|
|
|
|
do {
|
|
|
|
char *offset = strchr(line, '#');
|
|
|
|
char *end = NULL;
|
|
|
|
int vcpu = 0, tid = 0;
|
|
|
|
|
|
|
|
/* See if we're all done */
|
|
|
|
if (offset == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Extract VCPU number */
|
|
|
|
if (virStrToLong_i(offset + 1, &end, 10, &vcpu) < 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (end == NULL || *end != ':')
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
/* Extract host Thread ID */
|
|
|
|
if ((offset = strstr(line, "thread_id=")) == NULL)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (virStrToLong_i(offset + strlen("thread_id="), &end, 10, &tid) < 0)
|
|
|
|
goto error;
|
|
|
|
if (end == NULL || !c_isspace(*end))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (vcpu != (lastVcpu + 1))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (VIR_REALLOC_N(cpupids, ncpupids+1) < 0)
|
|
|
|
goto error;
|
|
|
|
|
2011-02-16 23:37:57 +00:00
|
|
|
VIR_DEBUG("vcpu=%d pid=%d", vcpu, tid);
|
2009-09-23 10:49:14 +00:00
|
|
|
cpupids[ncpupids++] = tid;
|
|
|
|
lastVcpu = vcpu;
|
|
|
|
|
|
|
|
/* Skip to next data line */
|
|
|
|
line = strchr(offset, '\r');
|
|
|
|
if (line == NULL)
|
|
|
|
line = strchr(offset, '\n');
|
|
|
|
} while (line != NULL);
|
|
|
|
|
|
|
|
/* Validate we got data for all VCPUs we expected */
|
|
|
|
VIR_FREE(qemucpus);
|
|
|
|
*pids = cpupids;
|
|
|
|
return ncpupids;
|
|
|
|
|
|
|
|
error:
|
|
|
|
VIR_FREE(qemucpus);
|
|
|
|
VIR_FREE(cpupids);
|
|
|
|
|
|
|
|
/* Returning 0 to indicate non-fatal failure, since
|
|
|
|
* older QEMU does not have VCPU<->PID mapping and
|
|
|
|
* we don't want to fail on that
|
|
|
|
*/
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-06-17 14:31:45 +00:00
|
|
|
|
|
|
|
int qemuMonitorTextGetVirtType(qemuMonitorPtr mon,
|
|
|
|
int *virtType)
|
|
|
|
{
|
|
|
|
char *reply = NULL;
|
|
|
|
|
|
|
|
*virtType = VIR_DOMAIN_VIRT_QEMU;
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, "info kvm", &reply) < 0)
|
2011-06-17 14:31:45 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (strstr(reply, "enabled"))
|
|
|
|
*virtType = VIR_DOMAIN_VIRT_KVM;
|
|
|
|
|
|
|
|
VIR_FREE(reply);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-20 12:36:28 +00:00
|
|
|
static int parseMemoryStat(char **text, unsigned int tag,
|
|
|
|
const char *search, virDomainMemoryStatPtr stat)
|
|
|
|
{
|
|
|
|
char *dummy;
|
|
|
|
unsigned long long value;
|
|
|
|
|
2012-10-17 09:23:12 +00:00
|
|
|
if (STRPREFIX(*text, search)) {
|
2009-12-20 12:36:28 +00:00
|
|
|
*text += strlen(search);
|
2012-10-17 09:23:12 +00:00
|
|
|
if (virStrToLong_ull(*text, &dummy, 10, &value)) {
|
|
|
|
VIR_DEBUG("error reading %s: %s", search, *text);
|
2009-12-20 12:36:28 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (tag) {
|
2011-08-19 13:34:59 +00:00
|
|
|
/* Convert megabytes to kilobytes for libvirt */
|
|
|
|
case VIR_DOMAIN_MEMORY_STAT_ACTUAL_BALLOON:
|
|
|
|
value <<= 10;
|
|
|
|
break;
|
|
|
|
/* Convert bytes to kilobytes for libvirt */
|
2009-12-20 12:36:28 +00:00
|
|
|
case VIR_DOMAIN_MEMORY_STAT_SWAP_IN:
|
|
|
|
case VIR_DOMAIN_MEMORY_STAT_SWAP_OUT:
|
|
|
|
case VIR_DOMAIN_MEMORY_STAT_UNUSED:
|
|
|
|
case VIR_DOMAIN_MEMORY_STAT_AVAILABLE:
|
2011-08-19 13:34:59 +00:00
|
|
|
value >>= 10;
|
2009-12-20 12:36:28 +00:00
|
|
|
}
|
|
|
|
stat->tag = tag;
|
|
|
|
stat->val = value;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The reply from the 'info balloon' command may contain additional memory
|
2011-08-19 13:34:59 +00:00
|
|
|
* statistics in the form: 'actual=<val> [,<tag>=<val>]*'
|
2009-12-20 12:36:28 +00:00
|
|
|
*/
|
2011-08-19 13:34:59 +00:00
|
|
|
static int qemuMonitorParseBalloonInfo(char *text,
|
|
|
|
virDomainMemoryStatPtr stats,
|
|
|
|
unsigned int nr_stats)
|
2009-12-20 12:36:28 +00:00
|
|
|
{
|
|
|
|
char *p = text;
|
|
|
|
unsigned int nr_stats_found = 0;
|
|
|
|
|
2011-08-19 13:34:59 +00:00
|
|
|
/* Since "actual=" always comes first in the returned string,
|
|
|
|
* and sometime we only care about the value of "actual", such
|
|
|
|
* as qemuMonitorGetBalloonInfo, we parse it outside of the
|
|
|
|
* loop.
|
|
|
|
*/
|
|
|
|
if (parseMemoryStat(&p, VIR_DOMAIN_MEMORY_STAT_ACTUAL_BALLOON,
|
|
|
|
"actual=", &stats[nr_stats_found]) == 1) {
|
|
|
|
nr_stats_found++;
|
|
|
|
}
|
|
|
|
|
2009-12-20 12:36:28 +00:00
|
|
|
while (*p && nr_stats_found < nr_stats) {
|
|
|
|
if (parseMemoryStat(&p, VIR_DOMAIN_MEMORY_STAT_SWAP_IN,
|
|
|
|
",mem_swapped_in=", &stats[nr_stats_found]) ||
|
|
|
|
parseMemoryStat(&p, VIR_DOMAIN_MEMORY_STAT_SWAP_OUT,
|
|
|
|
",mem_swapped_out=", &stats[nr_stats_found]) ||
|
|
|
|
parseMemoryStat(&p, VIR_DOMAIN_MEMORY_STAT_MAJOR_FAULT,
|
|
|
|
",major_page_faults=", &stats[nr_stats_found]) ||
|
|
|
|
parseMemoryStat(&p, VIR_DOMAIN_MEMORY_STAT_MINOR_FAULT,
|
|
|
|
",minor_page_faults=", &stats[nr_stats_found]) ||
|
|
|
|
parseMemoryStat(&p, VIR_DOMAIN_MEMORY_STAT_UNUSED,
|
|
|
|
",free_mem=", &stats[nr_stats_found]) ||
|
|
|
|
parseMemoryStat(&p, VIR_DOMAIN_MEMORY_STAT_AVAILABLE,
|
2011-08-19 13:34:59 +00:00
|
|
|
",total_mem=", &stats[nr_stats_found]))
|
2009-12-20 12:36:28 +00:00
|
|
|
nr_stats_found++;
|
|
|
|
|
2010-01-21 15:26:35 +00:00
|
|
|
/* Skip to the next label. When *p is ',' the last match attempt
|
|
|
|
* failed so try to match the next ','.
|
|
|
|
*/
|
|
|
|
if (*p == ',')
|
|
|
|
p++;
|
2012-10-17 09:23:12 +00:00
|
|
|
p = strchr(p, ',');
|
2009-12-20 12:36:28 +00:00
|
|
|
if (!p) break;
|
|
|
|
}
|
|
|
|
return nr_stats_found;
|
|
|
|
}
|
2009-09-23 10:49:14 +00:00
|
|
|
|
2009-09-23 11:29:39 +00:00
|
|
|
|
|
|
|
/* The reply from QEMU contains 'ballon: actual=421' where value is in MB */
|
2011-08-19 13:34:59 +00:00
|
|
|
#define BALLOON_PREFIX "balloon: "
|
2009-09-23 11:29:39 +00:00
|
|
|
|
2009-09-23 11:35:10 +00:00
|
|
|
/*
|
|
|
|
* Returns: 0 if balloon not supported, +1 if balloon query worked
|
|
|
|
* or -1 on failure
|
|
|
|
*/
|
2009-10-09 20:13:06 +00:00
|
|
|
int qemuMonitorTextGetBalloonInfo(qemuMonitorPtr mon,
|
2012-03-02 20:27:39 +00:00
|
|
|
unsigned long long *currmem)
|
2009-09-23 11:29:39 +00:00
|
|
|
{
|
|
|
|
char *reply = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
char *offset;
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, "info balloon", &reply) < 0)
|
2009-09-23 11:29:39 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
if ((offset = strstr(reply, BALLOON_PREFIX)) != NULL) {
|
|
|
|
offset += strlen(BALLOON_PREFIX);
|
2011-08-19 13:34:59 +00:00
|
|
|
struct _virDomainMemoryStat stats[1];
|
|
|
|
|
|
|
|
if (qemuMonitorParseBalloonInfo(offset, stats, 1) == 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unexpected balloon information '%s'"), reply);
|
2009-09-23 11:29:39 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2011-08-19 13:34:59 +00:00
|
|
|
|
|
|
|
if (stats[0].tag != VIR_DOMAIN_MEMORY_STAT_ACTUAL_BALLOON) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unexpected balloon information '%s'"), reply);
|
2011-08-19 13:34:59 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
*currmem = stats[0].val;
|
2009-09-23 11:29:39 +00:00
|
|
|
ret = 1;
|
|
|
|
} else {
|
|
|
|
/* We don't raise an error here, since its to be expected that
|
|
|
|
* many QEMU's don't support ballooning
|
|
|
|
*/
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-12-20 12:36:28 +00:00
|
|
|
int qemuMonitorTextGetMemoryStats(qemuMonitorPtr mon,
|
|
|
|
virDomainMemoryStatPtr stats,
|
|
|
|
unsigned int nr_stats)
|
|
|
|
{
|
|
|
|
char *reply = NULL;
|
|
|
|
int ret = 0;
|
|
|
|
char *offset;
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, "info balloon", &reply) < 0)
|
2009-12-20 12:36:28 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
if ((offset = strstr(reply, BALLOON_PREFIX)) != NULL) {
|
2010-03-05 14:25:48 +00:00
|
|
|
offset += strlen(BALLOON_PREFIX);
|
2011-08-19 13:34:59 +00:00
|
|
|
ret = qemuMonitorParseBalloonInfo(offset, stats, nr_stats);
|
2009-12-20 12:36:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-09-23 11:29:39 +00:00
|
|
|
|
2011-09-13 13:49:50 +00:00
|
|
|
int qemuMonitorTextGetBlockInfo(qemuMonitorPtr mon,
|
2012-01-18 21:01:30 +00:00
|
|
|
virHashTablePtr table)
|
2011-09-13 13:49:50 +00:00
|
|
|
{
|
2012-01-30 12:36:46 +00:00
|
|
|
struct qemuDomainDiskInfo *info = NULL;
|
2011-09-13 13:49:50 +00:00
|
|
|
char *reply = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
char *dummy;
|
2012-01-18 21:01:30 +00:00
|
|
|
char *p, *eol;
|
|
|
|
char *dev;
|
2011-09-13 13:49:50 +00:00
|
|
|
int tmp;
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, "info block", &reply) < 0)
|
2011-09-13 13:49:50 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (strstr(reply, "\ninfo ")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
|
|
"%s",
|
|
|
|
_("info block not supported by this qemu"));
|
2011-09-13 13:49:50 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The output looks like this:
|
|
|
|
* drive-ide0-0-0: removable=0 file=<path> ro=0 drv=raw encrypted=0
|
|
|
|
* drive-ide0-1-0: removable=1 locked=0 file=<path> ro=1 drv=raw encrypted=0
|
|
|
|
*/
|
|
|
|
p = reply;
|
|
|
|
|
|
|
|
while (*p) {
|
|
|
|
if (STRPREFIX(p, QEMU_DRIVE_HOST_PREFIX))
|
|
|
|
p += strlen(QEMU_DRIVE_HOST_PREFIX);
|
|
|
|
|
2012-01-18 21:01:30 +00:00
|
|
|
eol = strchr(p, '\n');
|
|
|
|
if (!eol)
|
|
|
|
eol = p + strlen(p) - 1;
|
2011-09-13 13:49:50 +00:00
|
|
|
|
2012-01-18 21:01:30 +00:00
|
|
|
dev = p;
|
|
|
|
p = strchr(p, ':');
|
|
|
|
if (p && p < eol && *(p + 1) == ' ') {
|
|
|
|
if (VIR_ALLOC(info) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2011-09-13 13:49:50 +00:00
|
|
|
|
2012-01-18 21:01:30 +00:00
|
|
|
*p = '\0';
|
|
|
|
p += 2;
|
2011-09-13 13:49:50 +00:00
|
|
|
|
2012-01-18 21:01:30 +00:00
|
|
|
while (p < eol) {
|
2011-09-13 13:49:50 +00:00
|
|
|
if (STRPREFIX(p, "removable=")) {
|
|
|
|
p += strlen("removable=");
|
|
|
|
if (virStrToLong_i(p, &dummy, 10, &tmp) == -1)
|
|
|
|
VIR_DEBUG("error reading removable: %s", p);
|
|
|
|
else
|
2011-10-13 00:27:20 +00:00
|
|
|
info->removable = (tmp != 0);
|
2011-09-13 13:49:50 +00:00
|
|
|
} else if (STRPREFIX(p, "locked=")) {
|
|
|
|
p += strlen("locked=");
|
|
|
|
if (virStrToLong_i(p, &dummy, 10, &tmp) == -1)
|
|
|
|
VIR_DEBUG("error reading locked: %s", p);
|
|
|
|
else
|
2011-10-13 00:27:20 +00:00
|
|
|
info->locked = (tmp != 0);
|
2013-01-25 11:56:48 +00:00
|
|
|
} else if (STRPREFIX(p, "tray-open=")) {
|
|
|
|
p += strlen("tray-open=");
|
2011-09-13 13:49:50 +00:00
|
|
|
if (virStrToLong_i(p, &dummy, 10, &tmp) == -1)
|
2013-01-25 11:56:48 +00:00
|
|
|
VIR_DEBUG("error reading tray-open: %s", p);
|
2011-09-13 13:49:50 +00:00
|
|
|
else
|
2011-10-13 00:27:20 +00:00
|
|
|
info->tray_open = (tmp != 0);
|
2012-01-19 16:58:58 +00:00
|
|
|
} else if (STRPREFIX(p, "io-status=")) {
|
|
|
|
char *end;
|
|
|
|
char c;
|
|
|
|
|
|
|
|
p += strlen("io-status=");
|
|
|
|
end = strchr(p, ' ');
|
|
|
|
if (!end || end > eol)
|
|
|
|
end = eol;
|
|
|
|
|
|
|
|
c = *end;
|
|
|
|
*end = '\0';
|
|
|
|
info->io_status = qemuMonitorBlockIOStatusToError(p);
|
|
|
|
*end = c;
|
|
|
|
if (info->io_status < 0)
|
|
|
|
goto cleanup;
|
2011-09-13 13:49:50 +00:00
|
|
|
} else {
|
|
|
|
/* ignore because we don't parse all options */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* skip to next label */
|
|
|
|
p = strchr(p, ' ');
|
2012-01-18 21:01:30 +00:00
|
|
|
if (!p)
|
|
|
|
break;
|
2011-09-13 13:49:50 +00:00
|
|
|
p++;
|
|
|
|
}
|
|
|
|
|
2012-01-18 21:01:30 +00:00
|
|
|
if (virHashAddEntry(table, dev, info) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
else
|
|
|
|
info = NULL;
|
2011-09-13 13:49:50 +00:00
|
|
|
}
|
|
|
|
|
2012-01-18 21:01:30 +00:00
|
|
|
/* skip to the next line */
|
|
|
|
p = eol + 1;
|
2011-09-13 13:49:50 +00:00
|
|
|
}
|
|
|
|
|
2012-01-18 21:01:30 +00:00
|
|
|
ret = 0;
|
2011-09-13 13:49:50 +00:00
|
|
|
|
|
|
|
cleanup:
|
2012-01-18 21:01:30 +00:00
|
|
|
VIR_FREE(info);
|
2011-09-13 13:49:50 +00:00
|
|
|
VIR_FREE(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-10-09 20:13:06 +00:00
|
|
|
int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon,
|
2011-09-16 12:05:58 +00:00
|
|
|
const char *dev_name,
|
2009-10-09 20:13:06 +00:00
|
|
|
long long *rd_req,
|
|
|
|
long long *rd_bytes,
|
2011-09-05 08:22:17 +00:00
|
|
|
long long *rd_total_times,
|
2009-10-09 20:13:06 +00:00
|
|
|
long long *wr_req,
|
|
|
|
long long *wr_bytes,
|
2011-09-05 08:22:17 +00:00
|
|
|
long long *wr_total_times,
|
|
|
|
long long *flush_req,
|
|
|
|
long long *flush_total_times,
|
2009-10-09 20:13:06 +00:00
|
|
|
long long *errs)
|
2009-09-23 12:51:05 +00:00
|
|
|
{
|
|
|
|
char *info = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
char *dummy;
|
|
|
|
const char *p, *eol;
|
2011-09-16 12:05:58 +00:00
|
|
|
int devnamelen = strlen(dev_name);
|
2009-09-23 12:51:05 +00:00
|
|
|
|
2012-10-17 09:23:12 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, "info blockstats", &info) < 0)
|
2009-09-23 12:51:05 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* If the command isn't supported then qemu prints the supported
|
|
|
|
* info commands, so the output starts "info ". Since this is
|
|
|
|
* unlikely to be the name of a block device, we can use this
|
|
|
|
* to detect if qemu supports the command.
|
|
|
|
*/
|
|
|
|
if (strstr(info, "\ninfo ")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
|
|
"%s",
|
|
|
|
_("'info blockstats' not supported by this qemu"));
|
2009-09-23 12:51:05 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2011-09-05 08:22:17 +00:00
|
|
|
*rd_req = *rd_bytes = -1;
|
|
|
|
*wr_req = *wr_bytes = *errs = -1;
|
|
|
|
|
|
|
|
if (rd_total_times)
|
|
|
|
*rd_total_times = -1;
|
|
|
|
if (wr_total_times)
|
|
|
|
*wr_total_times = -1;
|
|
|
|
if (flush_req)
|
|
|
|
*flush_req = -1;
|
|
|
|
if (flush_total_times)
|
|
|
|
*flush_total_times = -1;
|
2009-09-23 12:51:05 +00:00
|
|
|
|
|
|
|
/* The output format for both qemu & KVM is:
|
|
|
|
* blockdevice: rd_bytes=% wr_bytes=% rd_operations=% wr_operations=%
|
|
|
|
* (repeated for each block device)
|
|
|
|
* where '%' is a 64 bit number.
|
|
|
|
*/
|
|
|
|
p = info;
|
|
|
|
|
|
|
|
while (*p) {
|
2010-02-10 17:46:44 +00:00
|
|
|
/* New QEMU has separate names for host & guest side of the disk
|
|
|
|
* and libvirt gives the host side a 'drive-' prefix. The passed
|
2011-09-16 12:05:58 +00:00
|
|
|
* in dev_name is the guest side though
|
2010-02-10 17:46:44 +00:00
|
|
|
*/
|
|
|
|
if (STRPREFIX(p, QEMU_DRIVE_HOST_PREFIX))
|
|
|
|
p += strlen(QEMU_DRIVE_HOST_PREFIX);
|
|
|
|
|
2012-10-17 09:23:12 +00:00
|
|
|
if (STREQLEN(p, dev_name, devnamelen)
|
2009-09-23 12:51:05 +00:00
|
|
|
&& p[devnamelen] == ':' && p[devnamelen+1] == ' ') {
|
|
|
|
|
2012-10-17 09:23:12 +00:00
|
|
|
eol = strchr(p, '\n');
|
2009-09-23 12:51:05 +00:00
|
|
|
if (!eol)
|
2012-10-17 09:23:12 +00:00
|
|
|
eol = p + strlen(p);
|
2009-09-23 12:51:05 +00:00
|
|
|
|
|
|
|
p += devnamelen+2; /* Skip to first label. */
|
|
|
|
|
|
|
|
while (*p) {
|
2012-10-17 09:23:12 +00:00
|
|
|
if (STRPREFIX(p, "rd_bytes=")) {
|
2011-09-05 08:22:17 +00:00
|
|
|
p += strlen("rd_bytes=");
|
2012-10-17 09:23:12 +00:00
|
|
|
if (virStrToLong_ll(p, &dummy, 10, rd_bytes) == -1)
|
|
|
|
VIR_DEBUG("error reading rd_bytes: %s", p);
|
|
|
|
} else if (STRPREFIX(p, "wr_bytes=")) {
|
2011-09-05 08:22:17 +00:00
|
|
|
p += strlen("wr_bytes=");
|
2012-10-17 09:23:12 +00:00
|
|
|
if (virStrToLong_ll(p, &dummy, 10, wr_bytes) == -1)
|
|
|
|
VIR_DEBUG("error reading wr_bytes: %s", p);
|
|
|
|
} else if (STRPREFIX(p, "rd_operations=")) {
|
2011-09-05 08:22:17 +00:00
|
|
|
p += strlen("rd_operations=");
|
2012-10-17 09:23:12 +00:00
|
|
|
if (virStrToLong_ll(p, &dummy, 10, rd_req) == -1)
|
|
|
|
VIR_DEBUG("error reading rd_req: %s", p);
|
|
|
|
} else if (STRPREFIX(p, "wr_operations=")) {
|
2011-09-05 08:22:17 +00:00
|
|
|
p += strlen("wr_operations=");
|
2012-10-17 09:23:12 +00:00
|
|
|
if (virStrToLong_ll(p, &dummy, 10, wr_req) == -1)
|
|
|
|
VIR_DEBUG("error reading wr_req: %s", p);
|
2011-09-05 08:22:17 +00:00
|
|
|
} else if (rd_total_times &&
|
2012-10-17 09:23:12 +00:00
|
|
|
STRPREFIX(p, "rd_total_time_ns=")) {
|
2012-06-21 07:37:13 +00:00
|
|
|
p += strlen("rd_total_time_ns=");
|
2012-10-17 09:23:12 +00:00
|
|
|
if (virStrToLong_ll(p, &dummy, 10, rd_total_times) == -1)
|
|
|
|
VIR_DEBUG("error reading rd_total_times: %s", p);
|
2011-09-05 08:22:17 +00:00
|
|
|
} else if (wr_total_times &&
|
2012-10-17 09:23:12 +00:00
|
|
|
STRPREFIX(p, "wr_total_time_ns=")) {
|
2012-06-21 07:37:13 +00:00
|
|
|
p += strlen("wr_total_time_ns=");
|
2012-10-17 09:23:12 +00:00
|
|
|
if (virStrToLong_ll(p, &dummy, 10, wr_total_times) == -1)
|
|
|
|
VIR_DEBUG("error reading wr_total_times: %s", p);
|
2011-09-05 08:22:17 +00:00
|
|
|
} else if (flush_req &&
|
2012-10-17 09:23:12 +00:00
|
|
|
STRPREFIX(p, "flush_operations=")) {
|
2011-09-05 08:22:17 +00:00
|
|
|
p += strlen("flush_operations=");
|
2012-10-17 09:23:12 +00:00
|
|
|
if (virStrToLong_ll(p, &dummy, 10, flush_req) == -1)
|
|
|
|
VIR_DEBUG("error reading flush_req: %s", p);
|
2011-09-05 08:22:17 +00:00
|
|
|
} else if (flush_total_times &&
|
2012-10-17 09:23:12 +00:00
|
|
|
STRPREFIX(p, "flush_total_time_ns=")) {
|
2012-06-21 07:37:13 +00:00
|
|
|
p += strlen("flush_total_time_ns=");
|
2012-10-17 09:23:12 +00:00
|
|
|
if (virStrToLong_ll(p, &dummy, 10, flush_total_times) == -1)
|
|
|
|
VIR_DEBUG("error reading flush_total_times: %s", p);
|
2011-09-05 08:22:17 +00:00
|
|
|
} else {
|
2012-10-17 09:23:12 +00:00
|
|
|
VIR_DEBUG("unknown block stat near %s", p);
|
2011-09-05 08:22:17 +00:00
|
|
|
}
|
2009-09-23 12:51:05 +00:00
|
|
|
|
|
|
|
/* Skip to next label. */
|
2012-10-17 09:23:12 +00:00
|
|
|
p = strchr(p, ' ');
|
2009-09-23 12:51:05 +00:00
|
|
|
if (!p || p >= eol) break;
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip to next line. */
|
2012-10-17 09:23:12 +00:00
|
|
|
p = strchr(p, '\n');
|
2009-09-23 12:51:05 +00:00
|
|
|
if (!p) break;
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we reach here then the device was not found. */
|
2012-10-17 09:23:12 +00:00
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("no stats found for device %s"), dev_name);
|
2009-09-23 12:51:05 +00:00
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(info);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-09-05 08:22:17 +00:00
|
|
|
int qemuMonitorTextGetBlockStatsParamsNumber(qemuMonitorPtr mon,
|
|
|
|
int *nparams)
|
|
|
|
{
|
|
|
|
char *info = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
int num = 0;
|
|
|
|
const char *p, *eol;
|
|
|
|
|
2012-10-17 09:23:12 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, "info blockstats", &info) < 0)
|
2011-09-05 08:22:17 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* If the command isn't supported then qemu prints the supported
|
|
|
|
* info commands, so the output starts "info ". Since this is
|
|
|
|
* unlikely to be the name of a block device, we can use this
|
|
|
|
* to detect if qemu supports the command.
|
|
|
|
*/
|
|
|
|
if (strstr(info, "\ninfo ")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
|
|
"%s",
|
|
|
|
_("'info blockstats' not supported by this qemu"));
|
2011-09-05 08:22:17 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The output format for both qemu & KVM is:
|
|
|
|
* blockdevice: rd_bytes=% wr_bytes=% rd_operations=% wr_operations=%
|
|
|
|
* (repeated for each block device)
|
|
|
|
* where '%' is a 64 bit number.
|
|
|
|
*/
|
|
|
|
p = info;
|
|
|
|
|
2012-10-17 09:23:12 +00:00
|
|
|
eol = strchr(p, '\n');
|
2011-09-05 08:22:17 +00:00
|
|
|
if (!eol)
|
2012-10-17 09:23:12 +00:00
|
|
|
eol = p + strlen(p);
|
2011-09-05 08:22:17 +00:00
|
|
|
|
|
|
|
/* Skip the device name and following ":", and spaces (e.g.
|
|
|
|
* "floppy0: ")
|
|
|
|
*/
|
|
|
|
p = strchr(p, ' ');
|
|
|
|
|
2011-10-13 00:19:28 +00:00
|
|
|
while (p && p < eol) {
|
2012-10-17 09:23:12 +00:00
|
|
|
if (STRPREFIX(p, " rd_bytes=") ||
|
|
|
|
STRPREFIX(p, " wr_bytes=") ||
|
|
|
|
STRPREFIX(p, " rd_operations=") ||
|
|
|
|
STRPREFIX(p, " wr_operations=") ||
|
|
|
|
STRPREFIX(p, " rd_total_time_ns=") ||
|
|
|
|
STRPREFIX(p, " wr_total_time_ns=") ||
|
|
|
|
STRPREFIX(p, " flush_operations=") ||
|
|
|
|
STRPREFIX(p, " flush_total_time_ns=")) {
|
2011-10-13 00:19:28 +00:00
|
|
|
num++;
|
|
|
|
} else {
|
2012-10-17 09:23:12 +00:00
|
|
|
VIR_DEBUG("unknown block stat near %s", p);
|
2011-10-13 00:19:28 +00:00
|
|
|
}
|
2011-09-05 08:22:17 +00:00
|
|
|
|
2011-10-13 00:19:28 +00:00
|
|
|
/* Skip to next label. */
|
|
|
|
p = strchr(p + 1, ' ');
|
2011-09-05 08:22:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
*nparams = num;
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(info);
|
|
|
|
return ret;
|
|
|
|
}
|
2009-09-23 12:51:05 +00:00
|
|
|
|
2010-05-14 13:10:01 +00:00
|
|
|
int qemuMonitorTextGetBlockExtent(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
2011-09-16 12:05:58 +00:00
|
|
|
const char *dev_name ATTRIBUTE_UNUSED,
|
2010-05-14 13:10:01 +00:00
|
|
|
unsigned long long *extent ATTRIBUTE_UNUSED)
|
|
|
|
{
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("unable to query block extent with this QEMU"));
|
2010-05-14 13:10:01 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-03-03 14:43:22 +00:00
|
|
|
/* Return 0 on success, -1 on failure, or -2 if not supported. Size
|
|
|
|
* is in bytes. */
|
2011-11-29 07:34:53 +00:00
|
|
|
int qemuMonitorTextBlockResize(qemuMonitorPtr mon,
|
|
|
|
const char *device,
|
|
|
|
unsigned long long size)
|
|
|
|
{
|
|
|
|
char *cmd = NULL;
|
|
|
|
char *reply = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
2012-03-03 14:43:22 +00:00
|
|
|
if (virAsprintf(&cmd, "block_resize %s %lluB", device, size) < 0) {
|
2011-11-29 07:34:53 +00:00
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0)
|
2011-11-29 07:34:53 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (strstr(reply, "unknown command:")) {
|
|
|
|
ret = -2;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
VIR_FREE(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2010-05-14 13:10:01 +00:00
|
|
|
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
static int
|
|
|
|
qemuMonitorSendVNCPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
|
|
|
qemuMonitorMessagePtr msg,
|
|
|
|
const char *data ATTRIBUTE_UNUSED,
|
|
|
|
size_t len ATTRIBUTE_UNUSED,
|
|
|
|
void *opaque)
|
|
|
|
{
|
|
|
|
char *passphrase = opaque;
|
|
|
|
size_t passphrase_len = strlen(passphrase);
|
|
|
|
|
|
|
|
/* Enlarge transmit buffer to allow for the extra data
|
|
|
|
* to be sent back */
|
|
|
|
if (VIR_REALLOC_N(msg->txBuffer,
|
|
|
|
msg->txLength + passphrase_len + 1 + 1) < 0) {
|
2011-05-29 12:51:08 +00:00
|
|
|
virReportOOMError();
|
Fully asynchronous monitor I/O processing
Change the QEMU monitor file handle watch to poll for both
read & write events, as well as EOF. All I/O to/from the
QEMU monitor FD is now done in the event callback thread.
When the QEMU driver needs to send a command, it puts the
data to be sent into a qemuMonitorMessagePtr object instance,
queues it for dispatch, and then goes to sleep on a condition
variable. The event thread sends all the data, and then waits
for the reply to arrive, putting the response / error data
back into the qemuMonitorMessagePtr and notifying the condition
variable.
There is a temporary hack in the disk passphrase callback to
avoid acquiring the domain lock. This avoids a deadlock in
the command processing, since the domain lock is still held
when running monitor commands. The next commit will remove
the locking when running commands & thus allow re-introduction
of locking the disk passphrase callback
* src/qemu/qemu_driver.c: Temporarily don't acquire lock in
disk passphrase callback. To be reverted in next commit
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h: Remove
raw I/O functions, and a generic qemuMonitorSend() for
invoking a command
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
Remove all low level I/O, and use the new qemuMonitorSend()
API. Provide a qemuMonitorTextIOProcess() method for detecting
command/reply/prompt boundaries in the monitor data stream
2009-10-14 17:40:51 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Queue the password for sending */
|
|
|
|
memcpy(msg->txBuffer + msg->txLength,
|
|
|
|
passphrase, passphrase_len);
|
|
|
|
msg->txLength += passphrase_len;
|
|
|
|
msg->txBuffer[msg->txLength] = '\r';
|
|
|
|
msg->txLength++;
|
|
|
|
msg->txBuffer[msg->txLength] = '\0';
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-10-09 20:13:06 +00:00
|
|
|
int qemuMonitorTextSetVNCPassword(qemuMonitorPtr mon,
|
|
|
|
const char *password)
|
2009-09-23 11:06:57 +00:00
|
|
|
{
|
|
|
|
char *info = NULL;
|
2009-09-24 14:37:05 +00:00
|
|
|
|
2011-03-10 08:37:08 +00:00
|
|
|
if (qemuMonitorTextCommandWithHandler(mon, "change vnc password",
|
|
|
|
qemuMonitorSendVNCPassphrase,
|
|
|
|
(char *)password,
|
|
|
|
-1, &info) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("setting VNC password failed"));
|
2009-09-23 11:06:57 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
VIR_FREE(info);
|
|
|
|
return 0;
|
|
|
|
}
|
2009-09-23 11:35:10 +00:00
|
|
|
|
2011-01-10 11:12:32 +00:00
|
|
|
/* Returns -1 on error, -2 if not supported */
|
|
|
|
int qemuMonitorTextSetPassword(qemuMonitorPtr mon,
|
|
|
|
const char *protocol,
|
|
|
|
const char *password,
|
|
|
|
const char *action_if_connected)
|
|
|
|
{
|
|
|
|
char *cmd = NULL;
|
|
|
|
char *reply = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (virAsprintf(&cmd, "set_password %s \"%s\" %s",
|
|
|
|
protocol, password, action_if_connected) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0)
|
2011-01-10 11:12:32 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (strstr(reply, "unknown command:")) {
|
|
|
|
ret = -2;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(reply);
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-01-18 18:37:45 +00:00
|
|
|
/* Returns -1 on error, -2 if not supported */
|
2011-01-10 11:12:32 +00:00
|
|
|
int qemuMonitorTextExpirePassword(qemuMonitorPtr mon,
|
|
|
|
const char *protocol,
|
|
|
|
const char *expire_time)
|
|
|
|
{
|
|
|
|
char *cmd = NULL;
|
|
|
|
char *reply = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (virAsprintf(&cmd, "expire_password %s %s",
|
|
|
|
protocol, expire_time) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0)
|
2011-01-10 11:12:32 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (strstr(reply, "unknown command:")) {
|
2011-01-18 18:37:45 +00:00
|
|
|
ret = -2;
|
2011-01-10 11:12:32 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(reply);
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-09-23 11:35:10 +00:00
|
|
|
/*
|
|
|
|
* Returns: 0 if balloon not supported, +1 if balloon adjust worked
|
|
|
|
* or -1 on failure
|
|
|
|
*/
|
2009-10-09 20:13:06 +00:00
|
|
|
int qemuMonitorTextSetBalloon(qemuMonitorPtr mon,
|
|
|
|
unsigned long newmem)
|
2009-09-23 11:35:10 +00:00
|
|
|
{
|
|
|
|
char *cmd;
|
|
|
|
char *reply = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 'newmem' is in KB, QEMU monitor works in MB, and we all wish
|
|
|
|
* we just worked in bytes with unsigned long long everywhere.
|
|
|
|
*/
|
2011-01-28 21:03:24 +00:00
|
|
|
if (virAsprintf(&cmd, "balloon %lu", VIR_DIV_UP(newmem, 1024)) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-23 11:35:10 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-03-10 08:33:01 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) {
|
2009-09-23 11:35:10 +00:00
|
|
|
VIR_FREE(cmd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
|
|
|
|
/* If the command failed qemu prints: 'unknown command'
|
|
|
|
* No message is printed on success it seems */
|
2010-04-22 15:47:33 +00:00
|
|
|
if (strstr(reply, "unknown command:")) {
|
2009-09-23 11:35:10 +00:00
|
|
|
/* Don't set error - it is expected memory balloon fails on many qemu */
|
|
|
|
ret = 0;
|
|
|
|
} else {
|
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-02-08 16:37:17 +00:00
|
|
|
|
|
|
|
/*
|
2010-04-22 15:39:10 +00:00
|
|
|
* Returns: 0 if CPU hotplug not supported, +1 if CPU hotplug worked
|
2010-02-08 16:37:17 +00:00
|
|
|
* or -1 on failure
|
|
|
|
*/
|
|
|
|
int qemuMonitorTextSetCPU(qemuMonitorPtr mon, int cpu, int online)
|
|
|
|
{
|
|
|
|
char *cmd;
|
|
|
|
char *reply = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
2010-04-22 15:39:10 +00:00
|
|
|
if (virAsprintf(&cmd, "cpu_set %d %s", cpu, online ? "online" : "offline") < 0) {
|
2010-02-08 16:37:17 +00:00
|
|
|
virReportOOMError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-03-10 08:33:01 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) {
|
2010-02-08 16:37:17 +00:00
|
|
|
VIR_FREE(cmd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
|
|
|
|
/* If the command failed qemu prints: 'unknown command'
|
|
|
|
* No message is printed on success it seems */
|
2010-04-22 15:47:33 +00:00
|
|
|
if (strstr(reply, "unknown command:")) {
|
2010-02-08 16:37:17 +00:00
|
|
|
/* Don't set error - it is expected CPU onlining fails on many qemu - caller will handle */
|
|
|
|
ret = 0;
|
|
|
|
} else {
|
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-09 20:13:06 +00:00
|
|
|
int qemuMonitorTextEjectMedia(qemuMonitorPtr mon,
|
2011-09-16 12:05:58 +00:00
|
|
|
const char *dev_name,
|
2010-11-08 17:52:48 +00:00
|
|
|
bool force)
|
2009-09-23 11:51:59 +00:00
|
|
|
{
|
|
|
|
char *cmd = NULL;
|
|
|
|
char *reply = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
2011-09-16 12:05:58 +00:00
|
|
|
if (virAsprintf(&cmd, "eject %s%s", force ? "-f " : "", dev_name) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-23 11:51:59 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0)
|
2009-09-23 11:51:59 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* If the command failed qemu prints:
|
|
|
|
* device not found, device is locked ...
|
|
|
|
* No message is printed on success it seems */
|
2011-03-31 02:26:27 +00:00
|
|
|
if (c_strcasestr(reply, "device ")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
_("could not eject media on %s: %s"), dev_name, reply);
|
2009-09-23 11:51:59 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(reply);
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-09 20:13:06 +00:00
|
|
|
int qemuMonitorTextChangeMedia(qemuMonitorPtr mon,
|
2011-09-16 12:05:58 +00:00
|
|
|
const char *dev_name,
|
2009-11-26 13:48:17 +00:00
|
|
|
const char *newmedia,
|
|
|
|
const char *format ATTRIBUTE_UNUSED)
|
2009-09-23 11:51:59 +00:00
|
|
|
{
|
|
|
|
char *cmd = NULL;
|
|
|
|
char *reply = NULL;
|
|
|
|
char *safepath = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
2009-09-23 16:39:07 +00:00
|
|
|
if (!(safepath = qemuMonitorEscapeArg(newmedia))) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-23 11:51:59 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2011-09-16 12:05:58 +00:00
|
|
|
if (virAsprintf(&cmd, "change %s \"%s\"", dev_name, safepath) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-23 11:51:59 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0)
|
2009-09-23 11:51:59 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* If the command failed qemu prints:
|
|
|
|
* device not found, device is locked ...
|
|
|
|
* No message is printed on success it seems */
|
2011-08-30 14:59:24 +00:00
|
|
|
if (c_strcasestr(reply, "device ")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
_("could not change media on %s: %s"), dev_name, reply);
|
2009-09-23 11:51:59 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2010-04-09 13:07:16 +00:00
|
|
|
/* Could not open message indicates bad filename */
|
2010-04-22 15:47:33 +00:00
|
|
|
if (strstr(reply, "Could not open ")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
_("could not change media on %s: %s"), dev_name, reply);
|
2010-04-09 13:07:16 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2009-09-23 11:51:59 +00:00
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(reply);
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
VIR_FREE(safepath);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-10-09 20:13:06 +00:00
|
|
|
static int qemuMonitorTextSaveMemory(qemuMonitorPtr mon,
|
|
|
|
const char *cmdtype,
|
|
|
|
unsigned long long offset,
|
|
|
|
size_t length,
|
|
|
|
const char *path)
|
2009-09-23 12:33:45 +00:00
|
|
|
{
|
|
|
|
char *cmd = NULL;
|
|
|
|
char *reply = NULL;
|
|
|
|
char *safepath = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
2009-09-23 16:39:07 +00:00
|
|
|
if (!(safepath = qemuMonitorEscapeArg(path))) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-23 12:33:45 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virAsprintf(&cmd, "%s %llu %zi \"%s\"", cmdtype, offset, length, safepath) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-23 12:33:45 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0)
|
2009-09-23 12:33:45 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* XXX what is printed on failure ? */
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
VIR_FREE(reply);
|
|
|
|
VIR_FREE(safepath);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-09 20:13:06 +00:00
|
|
|
int qemuMonitorTextSaveVirtualMemory(qemuMonitorPtr mon,
|
|
|
|
unsigned long long offset,
|
|
|
|
size_t length,
|
|
|
|
const char *path)
|
2009-09-23 12:33:45 +00:00
|
|
|
{
|
2009-10-09 20:13:06 +00:00
|
|
|
return qemuMonitorTextSaveMemory(mon, "memsave", offset, length, path);
|
2009-09-23 12:33:45 +00:00
|
|
|
}
|
|
|
|
|
2009-10-09 20:13:06 +00:00
|
|
|
int qemuMonitorTextSavePhysicalMemory(qemuMonitorPtr mon,
|
|
|
|
unsigned long long offset,
|
|
|
|
size_t length,
|
|
|
|
const char *path)
|
2009-09-23 12:33:45 +00:00
|
|
|
{
|
2009-10-09 20:13:06 +00:00
|
|
|
return qemuMonitorTextSaveMemory(mon, "pmemsave", offset, length, path);
|
2009-09-23 12:33:45 +00:00
|
|
|
}
|
2009-09-23 12:57:56 +00:00
|
|
|
|
|
|
|
|
2009-10-09 20:13:06 +00:00
|
|
|
int qemuMonitorTextSetMigrationSpeed(qemuMonitorPtr mon,
|
|
|
|
unsigned long bandwidth)
|
2009-09-23 12:57:56 +00:00
|
|
|
{
|
|
|
|
char *cmd = NULL;
|
|
|
|
char *info = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (virAsprintf(&cmd, "migrate_set_speed %lum", bandwidth) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-23 12:57:56 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &info) < 0)
|
2009-09-23 12:57:56 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(info);
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
return ret;
|
|
|
|
}
|
2009-09-23 13:27:12 +00:00
|
|
|
|
|
|
|
|
2010-03-17 15:53:14 +00:00
|
|
|
int qemuMonitorTextSetMigrationDowntime(qemuMonitorPtr mon,
|
|
|
|
unsigned long long downtime)
|
|
|
|
{
|
|
|
|
char *cmd = NULL;
|
|
|
|
char *info = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (virAsprintf(&cmd, "migrate_set_downtime %llums", downtime) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &info) < 0)
|
2010-03-17 15:53:14 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(info);
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-09-23 13:27:12 +00:00
|
|
|
#define MIGRATION_PREFIX "Migration status: "
|
|
|
|
#define MIGRATION_TRANSFER_PREFIX "transferred ram: "
|
|
|
|
#define MIGRATION_REMAINING_PREFIX "remaining ram: "
|
|
|
|
#define MIGRATION_TOTAL_PREFIX "total ram: "
|
2011-08-17 06:30:02 +00:00
|
|
|
#define MIGRATION_DISK_TRANSFER_PREFIX "transferred disk: "
|
|
|
|
#define MIGRATION_DISK_REMAINING_PREFIX "remaining disk: "
|
|
|
|
#define MIGRATION_DISK_TOTAL_PREFIX "total disk: "
|
2009-09-23 13:27:12 +00:00
|
|
|
|
2009-10-09 20:13:06 +00:00
|
|
|
int qemuMonitorTextGetMigrationStatus(qemuMonitorPtr mon,
|
|
|
|
int *status,
|
|
|
|
unsigned long long *transferred,
|
|
|
|
unsigned long long *remaining,
|
|
|
|
unsigned long long *total) {
|
2009-09-23 13:27:12 +00:00
|
|
|
char *reply;
|
|
|
|
char *tmp;
|
|
|
|
char *end;
|
2011-08-17 06:30:02 +00:00
|
|
|
unsigned long long disk_transferred = 0;
|
|
|
|
unsigned long long disk_remaining = 0;
|
|
|
|
unsigned long long disk_total = 0;
|
2009-09-23 13:27:12 +00:00
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
*status = QEMU_MONITOR_MIGRATION_STATUS_INACTIVE;
|
|
|
|
*transferred = 0;
|
|
|
|
*remaining = 0;
|
|
|
|
*total = 0;
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, "info migrate", &reply) < 0)
|
2009-09-23 13:27:12 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
if ((tmp = strstr(reply, MIGRATION_PREFIX)) != NULL) {
|
|
|
|
tmp += strlen(MIGRATION_PREFIX);
|
2009-10-01 18:18:28 +00:00
|
|
|
end = strchr(tmp, '\r');
|
2010-03-01 14:34:33 +00:00
|
|
|
if (end == NULL) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unexpected migration status in %s"), reply);
|
2010-03-01 14:34:33 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2009-09-23 13:27:12 +00:00
|
|
|
*end = '\0';
|
|
|
|
|
|
|
|
if ((*status = qemuMonitorMigrationStatusTypeFromString(tmp)) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unexpected migration status in %s"), reply);
|
2009-09-23 13:27:12 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*status == QEMU_MONITOR_MIGRATION_STATUS_ACTIVE) {
|
|
|
|
tmp = end + 1;
|
|
|
|
|
|
|
|
if (!(tmp = strstr(tmp, MIGRATION_TRANSFER_PREFIX)))
|
|
|
|
goto done;
|
|
|
|
tmp += strlen(MIGRATION_TRANSFER_PREFIX);
|
|
|
|
|
2010-03-30 21:07:24 +00:00
|
|
|
if (virStrToLong_ull(tmp, &end, 10, transferred) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot parse migration data transferred "
|
|
|
|
"statistic %s"), tmp);
|
2009-09-23 13:27:12 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
*transferred *= 1024;
|
2010-02-03 16:45:05 +00:00
|
|
|
tmp = end;
|
2009-09-23 13:27:12 +00:00
|
|
|
|
|
|
|
if (!(tmp = strstr(tmp, MIGRATION_REMAINING_PREFIX)))
|
|
|
|
goto done;
|
|
|
|
tmp += strlen(MIGRATION_REMAINING_PREFIX);
|
|
|
|
|
2010-03-30 21:07:24 +00:00
|
|
|
if (virStrToLong_ull(tmp, &end, 10, remaining) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot parse migration data remaining "
|
|
|
|
"statistic %s"), tmp);
|
2009-09-23 13:27:12 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
*remaining *= 1024;
|
2011-08-17 06:30:02 +00:00
|
|
|
tmp = end;
|
2009-09-23 13:27:12 +00:00
|
|
|
|
|
|
|
if (!(tmp = strstr(tmp, MIGRATION_TOTAL_PREFIX)))
|
|
|
|
goto done;
|
|
|
|
tmp += strlen(MIGRATION_TOTAL_PREFIX);
|
|
|
|
|
2010-03-30 21:07:24 +00:00
|
|
|
if (virStrToLong_ull(tmp, &end, 10, total) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot parse migration data total "
|
|
|
|
"statistic %s"), tmp);
|
2009-09-23 13:27:12 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
*total *= 1024;
|
2011-08-17 06:30:02 +00:00
|
|
|
tmp = end;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for Optional Disk Migration status
|
|
|
|
*/
|
|
|
|
if (!(tmp = strstr(tmp, MIGRATION_DISK_TRANSFER_PREFIX)))
|
|
|
|
goto done;
|
|
|
|
tmp += strlen(MIGRATION_DISK_TRANSFER_PREFIX);
|
|
|
|
|
|
|
|
if (virStrToLong_ull(tmp, &end, 10, &disk_transferred) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot parse disk migration data "
|
|
|
|
"transferred statistic %s"), tmp);
|
2011-08-17 06:30:02 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
*transferred += disk_transferred * 1024;
|
|
|
|
tmp = end;
|
|
|
|
|
|
|
|
if (!(tmp = strstr(tmp, MIGRATION_DISK_REMAINING_PREFIX)))
|
|
|
|
goto done;
|
|
|
|
tmp += strlen(MIGRATION_DISK_REMAINING_PREFIX);
|
2009-09-23 13:27:12 +00:00
|
|
|
|
2011-08-17 06:30:02 +00:00
|
|
|
if (virStrToLong_ull(tmp, &end, 10, &disk_remaining) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot parse disk migration data remaining "
|
|
|
|
"statistic %s"), tmp);
|
2011-08-17 06:30:02 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
*remaining += disk_remaining * 1024;
|
|
|
|
tmp = end;
|
|
|
|
|
|
|
|
if (!(tmp = strstr(tmp, MIGRATION_DISK_TOTAL_PREFIX)))
|
|
|
|
goto done;
|
|
|
|
tmp += strlen(MIGRATION_DISK_TOTAL_PREFIX);
|
|
|
|
|
|
|
|
if (virStrToLong_ull(tmp, &end, 10, &disk_total) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot parse disk migration data total "
|
|
|
|
"statistic %s"), tmp);
|
2011-08-17 06:30:02 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
*total += disk_total * 1024;
|
2009-09-23 13:27:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2009-09-23 13:48:49 +00:00
|
|
|
|
|
|
|
|
2011-03-04 18:51:48 +00:00
|
|
|
int qemuMonitorTextMigrate(qemuMonitorPtr mon,
|
|
|
|
unsigned int flags,
|
|
|
|
const char *dest)
|
2009-09-23 13:48:49 +00:00
|
|
|
{
|
|
|
|
char *cmd = NULL;
|
|
|
|
char *info = NULL;
|
|
|
|
int ret = -1;
|
2009-09-23 16:39:07 +00:00
|
|
|
char *safedest = qemuMonitorEscapeArg(dest);
|
2010-05-04 21:36:42 +00:00
|
|
|
virBuffer extra = VIR_BUFFER_INITIALIZER;
|
2010-05-20 19:25:41 +00:00
|
|
|
char *extrastr = NULL;
|
2009-09-23 13:48:49 +00:00
|
|
|
|
2009-09-23 14:23:47 +00:00
|
|
|
if (!safedest) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-23 13:48:49 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2010-06-24 18:58:36 +00:00
|
|
|
if (flags & QEMU_MONITOR_MIGRATE_BACKGROUND)
|
2010-05-04 21:36:42 +00:00
|
|
|
virBufferAddLit(&extra, " -d");
|
2010-06-24 18:58:36 +00:00
|
|
|
if (flags & QEMU_MONITOR_MIGRATE_NON_SHARED_DISK)
|
2010-05-04 21:36:42 +00:00
|
|
|
virBufferAddLit(&extra, " -b");
|
2010-06-24 18:58:36 +00:00
|
|
|
if (flags & QEMU_MONITOR_MIGRATE_NON_SHARED_INC)
|
2010-05-04 21:36:42 +00:00
|
|
|
virBufferAddLit(&extra, " -i");
|
|
|
|
if (virBufferError(&extra)) {
|
|
|
|
virBufferFreeAndReset(&extra);
|
|
|
|
virReportOOMError();
|
2010-05-20 19:25:41 +00:00
|
|
|
goto cleanup;
|
2010-05-04 21:36:42 +00:00
|
|
|
}
|
2010-05-20 19:25:41 +00:00
|
|
|
|
|
|
|
extrastr = virBufferContentAndReset(&extra);
|
|
|
|
if (virAsprintf(&cmd, "migrate %s\"%s\"", extrastr ? extrastr : "",
|
|
|
|
safedest) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-23 14:23:47 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &info) < 0)
|
2009-09-23 13:48:49 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* Now check for "fail" in the output string */
|
|
|
|
if (strstr(info, "fail") != NULL) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
_("migration to '%s' failed: %s"), dest, info);
|
2009-09-23 14:23:47 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
/* If the command isn't supported then qemu prints:
|
|
|
|
* unknown command: migrate" */
|
|
|
|
if (strstr(info, "unknown command:")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
|
|
_("migration to '%s' not supported by this qemu: %s"), dest, info);
|
2009-09-23 13:48:49 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
2010-05-20 19:25:41 +00:00
|
|
|
VIR_FREE(extrastr);
|
2009-09-23 14:23:47 +00:00
|
|
|
VIR_FREE(safedest);
|
2009-09-23 13:48:49 +00:00
|
|
|
VIR_FREE(info);
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-10-09 20:13:06 +00:00
|
|
|
int qemuMonitorTextMigrateCancel(qemuMonitorPtr mon)
|
2009-09-30 12:51:32 +00:00
|
|
|
{
|
|
|
|
char *info = NULL;
|
2012-09-13 12:54:54 +00:00
|
|
|
int ret;
|
2009-09-30 12:51:32 +00:00
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
ret = qemuMonitorHMPCommand(mon, "migrate_cancel", &info);
|
2009-09-30 12:51:32 +00:00
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
VIR_FREE(info);
|
|
|
|
return ret;
|
2009-09-30 12:51:32 +00:00
|
|
|
}
|
2009-09-23 14:38:43 +00:00
|
|
|
|
2011-02-17 13:39:36 +00:00
|
|
|
|
|
|
|
int qemuMonitorTextGraphicsRelocate(qemuMonitorPtr mon,
|
|
|
|
int type,
|
|
|
|
const char *hostname,
|
|
|
|
int port,
|
|
|
|
int tlsPort,
|
|
|
|
const char *tlsSubject)
|
|
|
|
{
|
|
|
|
char *cmd;
|
|
|
|
char *info = NULL;
|
|
|
|
|
|
|
|
if (virAsprintf(&cmd, "client_migrate_info %s %s %d %d %s",
|
|
|
|
type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE ? "spice" : "vnc",
|
|
|
|
hostname, port, tlsPort, tlsSubject ? tlsSubject : "") < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &info) < 0) {
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
VIR_FREE(info);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-09 20:13:06 +00:00
|
|
|
int qemuMonitorTextAddUSBDisk(qemuMonitorPtr mon,
|
|
|
|
const char *path)
|
2009-09-23 14:38:43 +00:00
|
|
|
{
|
|
|
|
char *cmd = NULL;
|
|
|
|
char *safepath;
|
|
|
|
int ret = -1;
|
|
|
|
char *info = NULL;
|
|
|
|
|
2009-09-23 16:39:07 +00:00
|
|
|
safepath = qemuMonitorEscapeArg(path);
|
2009-09-23 14:38:43 +00:00
|
|
|
if (!safepath) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-23 14:38:43 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virAsprintf(&cmd, "usb_add disk:%s", safepath) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-23 14:38:43 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &info) < 0)
|
2009-09-23 14:38:43 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* If the command failed qemu prints:
|
|
|
|
* Could not add ... */
|
|
|
|
if (strstr(info, "Could not add ")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
_("unable to add USB disk %s: %s"), path, info);
|
2009-09-23 14:38:43 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
VIR_FREE(safepath);
|
2010-02-16 12:51:38 +00:00
|
|
|
VIR_FREE(info);
|
2009-09-23 14:38:43 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2009-09-23 15:04:55 +00:00
|
|
|
|
|
|
|
|
2009-10-09 20:13:06 +00:00
|
|
|
static int qemuMonitorTextAddUSBDevice(qemuMonitorPtr mon,
|
|
|
|
const char *addr)
|
2009-09-23 15:04:55 +00:00
|
|
|
{
|
|
|
|
char *cmd;
|
|
|
|
char *reply = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (virAsprintf(&cmd, "usb_add %s", addr) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-23 15:04:55 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0)
|
2009-09-23 15:04:55 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* If the command failed qemu prints:
|
|
|
|
* Could not add ... */
|
|
|
|
if (strstr(reply, "Could not add ")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
"%s", _("adding usb device failed"));
|
2009-09-23 15:04:55 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
VIR_FREE(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-09 20:13:06 +00:00
|
|
|
int qemuMonitorTextAddUSBDeviceExact(qemuMonitorPtr mon,
|
|
|
|
int bus,
|
|
|
|
int dev)
|
2009-09-23 15:04:55 +00:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
char *addr;
|
|
|
|
|
|
|
|
if (virAsprintf(&addr, "host:%.3d.%.3d", bus, dev) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-23 15:04:55 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-10-09 20:13:06 +00:00
|
|
|
ret = qemuMonitorTextAddUSBDevice(mon, addr);
|
2009-09-23 15:04:55 +00:00
|
|
|
|
|
|
|
VIR_FREE(addr);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-10-09 20:13:06 +00:00
|
|
|
int qemuMonitorTextAddUSBDeviceMatch(qemuMonitorPtr mon,
|
|
|
|
int vendor,
|
|
|
|
int product)
|
2009-09-23 15:04:55 +00:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
char *addr;
|
|
|
|
|
|
|
|
if (virAsprintf(&addr, "host:%.4x:%.4x", vendor, product) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-23 15:04:55 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-10-09 20:13:06 +00:00
|
|
|
ret = qemuMonitorTextAddUSBDevice(mon, addr);
|
2009-09-23 15:04:55 +00:00
|
|
|
|
|
|
|
VIR_FREE(addr);
|
|
|
|
return ret;
|
|
|
|
}
|
2009-09-23 15:15:51 +00:00
|
|
|
|
|
|
|
|
|
|
|
static int
|
2009-10-09 20:13:06 +00:00
|
|
|
qemuMonitorTextParsePciAddReply(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
|
|
|
const char *reply,
|
2012-08-16 15:41:06 +00:00
|
|
|
virDevicePCIAddress *addr)
|
2009-09-23 15:15:51 +00:00
|
|
|
{
|
|
|
|
char *s, *e;
|
|
|
|
|
|
|
|
/* If the command succeeds qemu prints:
|
|
|
|
* OK bus 0, slot XXX...
|
|
|
|
* or
|
|
|
|
* OK domain 0, bus 0, slot XXX
|
|
|
|
*/
|
|
|
|
if (!(s = strstr(reply, "OK ")))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
s += 3;
|
|
|
|
|
|
|
|
if (STRPREFIX(s, "domain ")) {
|
|
|
|
s += strlen("domain ");
|
|
|
|
|
2009-12-07 19:07:39 +00:00
|
|
|
if (virStrToLong_ui(s, &e, 10, &addr->domain) == -1) {
|
2010-05-19 10:00:18 +00:00
|
|
|
VIR_WARN("Unable to parse domain number '%s'", s);
|
2009-09-23 15:15:51 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!STRPREFIX(e, ", ")) {
|
2010-05-19 10:00:18 +00:00
|
|
|
VIR_WARN("Expected ', ' parsing pci_add reply '%s'", s);
|
2009-09-23 15:15:51 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
s = e + 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!STRPREFIX(s, "bus ")) {
|
2010-05-19 10:00:18 +00:00
|
|
|
VIR_WARN("Expected 'bus ' parsing pci_add reply '%s'", s);
|
2009-09-23 15:15:51 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
s += strlen("bus ");
|
|
|
|
|
2009-12-07 19:07:39 +00:00
|
|
|
if (virStrToLong_ui(s, &e, 10, &addr->bus) == -1) {
|
2010-05-19 10:00:18 +00:00
|
|
|
VIR_WARN("Unable to parse bus number '%s'", s);
|
2009-09-23 15:15:51 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!STRPREFIX(e, ", ")) {
|
2010-05-19 10:00:18 +00:00
|
|
|
VIR_WARN("Expected ', ' parsing pci_add reply '%s'", s);
|
2009-09-23 15:15:51 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
s = e + 2;
|
|
|
|
|
|
|
|
if (!STRPREFIX(s, "slot ")) {
|
2010-05-19 10:00:18 +00:00
|
|
|
VIR_WARN("Expected 'slot ' parsing pci_add reply '%s'", s);
|
2009-09-23 15:15:51 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
s += strlen("slot ");
|
|
|
|
|
2009-12-07 19:07:39 +00:00
|
|
|
if (virStrToLong_ui(s, &e, 10, &addr->slot) == -1) {
|
2010-05-19 10:00:18 +00:00
|
|
|
VIR_WARN("Unable to parse slot number '%s'", s);
|
2009-09-23 15:15:51 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-09 20:13:06 +00:00
|
|
|
int qemuMonitorTextAddPCIHostDevice(qemuMonitorPtr mon,
|
2012-08-16 15:41:06 +00:00
|
|
|
virDevicePCIAddress *hostAddr,
|
|
|
|
virDevicePCIAddress *guestAddr)
|
2009-09-23 15:15:51 +00:00
|
|
|
{
|
|
|
|
char *cmd;
|
|
|
|
char *reply = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
2009-12-07 19:07:39 +00:00
|
|
|
memset(guestAddr, 0, sizeof(*guestAddr));
|
2009-09-23 15:15:51 +00:00
|
|
|
|
2009-12-07 19:07:39 +00:00
|
|
|
/* XXX hostAddr->domain */
|
2009-09-23 15:15:51 +00:00
|
|
|
if (virAsprintf(&cmd, "pci_add pci_addr=auto host host=%.2x:%.2x.%.1x",
|
2009-12-07 19:07:39 +00:00
|
|
|
hostAddr->bus, hostAddr->slot, hostAddr->function) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-23 15:15:51 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0)
|
2009-09-23 15:15:51 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (strstr(reply, "invalid type: host")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
|
|
_("PCI device assignment is not supported by this version of qemu"));
|
2009-09-23 15:15:51 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2009-12-07 19:07:39 +00:00
|
|
|
if (qemuMonitorTextParsePciAddReply(mon, reply, guestAddr) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
_("parsing pci_add reply failed: %s"), reply);
|
2009-09-23 15:15:51 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
VIR_FREE(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-09-23 15:25:05 +00:00
|
|
|
|
2009-10-09 20:13:06 +00:00
|
|
|
int qemuMonitorTextAddPCIDisk(qemuMonitorPtr mon,
|
|
|
|
const char *path,
|
|
|
|
const char *bus,
|
2012-08-16 15:41:06 +00:00
|
|
|
virDevicePCIAddress *guestAddr)
|
2009-12-07 19:07:39 +00:00
|
|
|
{
|
2009-09-23 15:38:44 +00:00
|
|
|
char *cmd = NULL;
|
|
|
|
char *reply = NULL;
|
|
|
|
char *safe_path = NULL;
|
|
|
|
int tryOldSyntax = 0;
|
|
|
|
int ret = -1;
|
|
|
|
|
2009-09-23 16:39:07 +00:00
|
|
|
safe_path = qemuMonitorEscapeArg(path);
|
2009-09-23 15:38:44 +00:00
|
|
|
if (!safe_path) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-23 15:38:44 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
try_command:
|
|
|
|
if (virAsprintf(&cmd, "pci_add %s storage file=%s,if=%s",
|
|
|
|
(tryOldSyntax ? "0": "pci_addr=auto"), safe_path, bus) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-23 15:38:44 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0)
|
2009-09-23 15:38:44 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2009-12-07 19:07:39 +00:00
|
|
|
if (qemuMonitorTextParsePciAddReply(mon, reply, guestAddr) < 0) {
|
2009-09-23 15:38:44 +00:00
|
|
|
if (!tryOldSyntax && strstr(reply, "invalid char in expression")) {
|
|
|
|
VIR_FREE(reply);
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
tryOldSyntax = 1;
|
|
|
|
goto try_command;
|
|
|
|
}
|
|
|
|
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
_("adding %s disk failed %s: %s"), bus, path, reply);
|
2009-09-23 15:38:44 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(safe_path);
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
VIR_FREE(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-09 20:13:06 +00:00
|
|
|
int qemuMonitorTextAddPCINetwork(qemuMonitorPtr mon,
|
|
|
|
const char *nicstr,
|
2012-08-16 15:41:06 +00:00
|
|
|
virDevicePCIAddress *guestAddr)
|
2009-09-23 16:01:39 +00:00
|
|
|
{
|
|
|
|
char *cmd;
|
|
|
|
char *reply = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (virAsprintf(&cmd, "pci_add pci_addr=auto nic %s", nicstr) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-23 16:01:39 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0)
|
2009-09-23 16:01:39 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2009-12-07 19:07:39 +00:00
|
|
|
if (qemuMonitorTextParsePciAddReply(mon, reply, guestAddr) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
_("parsing pci_add reply failed: %s"), reply);
|
2009-09-23 16:01:39 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(reply);
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-09-23 15:38:44 +00:00
|
|
|
|
2009-10-09 20:13:06 +00:00
|
|
|
int qemuMonitorTextRemovePCIDevice(qemuMonitorPtr mon,
|
2012-08-16 15:41:06 +00:00
|
|
|
virDevicePCIAddress *guestAddr)
|
2009-09-23 15:25:05 +00:00
|
|
|
{
|
|
|
|
char *cmd = NULL;
|
|
|
|
char *reply = NULL;
|
|
|
|
int tryOldSyntax = 0;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
try_command:
|
|
|
|
if (tryOldSyntax) {
|
2009-12-07 19:07:39 +00:00
|
|
|
if (virAsprintf(&cmd, "pci_del 0 %.2x", guestAddr->slot) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-23 15:25:05 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
} else {
|
2009-12-07 19:07:39 +00:00
|
|
|
/* XXX function ? */
|
2009-09-23 15:25:05 +00:00
|
|
|
if (virAsprintf(&cmd, "pci_del pci_addr=%.4x:%.2x:%.2x",
|
2009-12-07 19:07:39 +00:00
|
|
|
guestAddr->domain, guestAddr->bus, guestAddr->slot) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-23 15:25:05 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0)
|
2009-09-23 15:25:05 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* Syntax changed when KVM merged PCI hotplug upstream to QEMU,
|
|
|
|
* so check for an error message from old KVM indicating the
|
|
|
|
* need to try the old syntax */
|
|
|
|
if (!tryOldSyntax &&
|
|
|
|
strstr(reply, "extraneous characters")) {
|
|
|
|
tryOldSyntax = 1;
|
|
|
|
VIR_FREE(reply);
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
goto try_command;
|
|
|
|
}
|
|
|
|
/* If the command fails due to a wrong slot qemu prints: invalid slot,
|
|
|
|
* nothing is printed on success */
|
|
|
|
if (strstr(reply, "invalid slot") ||
|
|
|
|
strstr(reply, "Invalid pci address")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
_("failed to detach PCI device, invalid address %.4x:%.2x:%.2x: %s"),
|
|
|
|
guestAddr->domain, guestAddr->bus, guestAddr->slot, reply);
|
2009-09-23 15:25:05 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
VIR_FREE(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2009-09-23 15:51:10 +00:00
|
|
|
|
|
|
|
|
2009-10-09 20:13:06 +00:00
|
|
|
int qemuMonitorTextSendFileHandle(qemuMonitorPtr mon,
|
|
|
|
const char *fdname,
|
|
|
|
int fd)
|
2009-09-23 15:51:10 +00:00
|
|
|
{
|
|
|
|
char *cmd;
|
|
|
|
char *reply = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (virAsprintf(&cmd, "getfd %s", fdname) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-23 15:51:10 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommandWithFd(mon, cmd, fd, &reply) < 0)
|
2009-09-23 15:51:10 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* If the command isn't supported then qemu prints:
|
|
|
|
* unknown command: getfd" */
|
|
|
|
if (strstr(reply, "unknown command:")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
|
|
_("qemu does not support sending of file handles: %s"),
|
|
|
|
reply);
|
2009-09-23 15:51:10 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2010-04-15 15:40:54 +00:00
|
|
|
if (STRNEQ(reply, "")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unable to send file handle '%s': %s"),
|
2011-03-28 21:14:15 +00:00
|
|
|
fdname, reply);
|
2010-04-15 15:40:54 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2009-09-23 15:51:10 +00:00
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
VIR_FREE(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-09 20:13:06 +00:00
|
|
|
int qemuMonitorTextCloseFileHandle(qemuMonitorPtr mon,
|
|
|
|
const char *fdname)
|
2009-09-23 15:51:10 +00:00
|
|
|
{
|
|
|
|
char *cmd;
|
|
|
|
char *reply = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (virAsprintf(&cmd, "closefd %s", fdname) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-23 15:51:10 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0)
|
2009-09-23 15:51:10 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* If the command isn't supported then qemu prints:
|
|
|
|
* unknown command: getfd" */
|
|
|
|
if (strstr(reply, "unknown command:")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
|
|
_("qemu does not support closing of file handles: %s"),
|
|
|
|
reply);
|
2009-09-23 15:51:10 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
VIR_FREE(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-09-23 16:25:28 +00:00
|
|
|
|
2009-10-09 20:13:06 +00:00
|
|
|
int qemuMonitorTextAddHostNetwork(qemuMonitorPtr mon,
|
|
|
|
const char *netstr)
|
2009-09-23 16:25:28 +00:00
|
|
|
{
|
|
|
|
char *cmd;
|
|
|
|
char *reply = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (virAsprintf(&cmd, "host_net_add %s", netstr) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-23 16:25:28 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0)
|
2009-09-23 16:25:28 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2010-04-15 15:40:54 +00:00
|
|
|
if (STRNEQ(reply, "")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unable to add host net: %s"),
|
|
|
|
reply);
|
2010-04-15 15:40:54 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2009-09-23 16:25:28 +00:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
VIR_FREE(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2009-09-23 16:32:50 +00:00
|
|
|
|
|
|
|
|
2009-10-09 20:13:06 +00:00
|
|
|
int qemuMonitorTextRemoveHostNetwork(qemuMonitorPtr mon,
|
|
|
|
int vlan,
|
|
|
|
const char *netname)
|
2009-09-23 16:32:50 +00:00
|
|
|
{
|
|
|
|
char *cmd;
|
|
|
|
char *reply = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (virAsprintf(&cmd, "host_net_remove %d %s", vlan, netname) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-23 16:32:50 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0)
|
2009-09-23 16:32:50 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* XXX error messages here ? */
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
VIR_FREE(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2009-12-14 09:50:01 +00:00
|
|
|
|
|
|
|
|
2010-04-15 13:52:03 +00:00
|
|
|
int qemuMonitorTextAddNetdev(qemuMonitorPtr mon,
|
|
|
|
const char *netdevstr)
|
|
|
|
{
|
|
|
|
char *cmd;
|
|
|
|
char *reply = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (virAsprintf(&cmd, "netdev_add %s", netdevstr) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0)
|
2010-04-15 13:52:03 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* XXX error messages here ? */
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
VIR_FREE(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorTextRemoveNetdev(qemuMonitorPtr mon,
|
|
|
|
const char *alias)
|
|
|
|
{
|
|
|
|
char *cmd;
|
|
|
|
char *reply = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (virAsprintf(&cmd, "netdev_del %s", alias) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0)
|
2010-04-15 13:52:03 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* XXX error messages here ? */
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
VIR_FREE(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-14 09:50:01 +00:00
|
|
|
/* Parse the output of "info chardev" and return a hash of pty paths.
|
|
|
|
*
|
|
|
|
* Output is:
|
|
|
|
* foo: filename=pty:/dev/pts/7
|
|
|
|
* monitor: filename=stdio
|
|
|
|
* serial0: filename=vc
|
|
|
|
* parallel0: filename=vc
|
|
|
|
*
|
|
|
|
* Non-pty lines are ignored. In the above example, key is 'foo', value is
|
|
|
|
* '/dev/pty/7'. The hash will contain only a single value.
|
|
|
|
*/
|
|
|
|
|
|
|
|
int qemuMonitorTextGetPtyPaths(qemuMonitorPtr mon,
|
|
|
|
virHashTablePtr paths)
|
|
|
|
{
|
|
|
|
char *reply = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, "info chardev", &reply) < 0)
|
build: detect potentential uninitialized variables
Even with -Wuninitialized (which is part of autobuild.sh
--enable-compile-warnings=error), gcc does NOT catch this
use of an uninitialized variable:
{
if (cond)
goto error;
int a = 1;
error:
printf("%d", a);
}
which prints 0 (supposing the stack started life wiped) if
cond was true. Clang will catch it, but we don't use clang
as often. Using gcc -Wjump-misses-init catches it, but also
gives false positives:
{
if (cond)
goto error;
int a = 1;
return a;
error:
return 0;
}
Here, a was never used in the scope of the error block, so
declaring it after goto is technically fine (and clang agrees).
However, given that our HACKING already documents a preference
to C89 decl-before-statement, the false positive warning is
enough of a prod to comply with HACKING.
[Personally, I'd _really_ rather use C99 decl-after-statement
to minimize scope, but until gcc can efficiently and reliably
catch scoping and uninitialized usage bugs, I'll settle with
the compromise of enforcing a coding standard that happens to
reject false positives if it can also detect real bugs.]
* acinclude.m4 (LIBVIRT_COMPILE_WARNINGS): Add -Wjump-misses-init.
* src/util/util.c (__virExec): Adjust offenders.
* src/conf/domain_conf.c (virDomainTimerDefParseXML): Likewise.
* src/remote/remote_driver.c (doRemoteOpen): Likewise.
* src/phyp/phyp_driver.c (phypGetLparNAME, phypGetLparProfile)
(phypGetVIOSFreeSCSIAdapter, phypVolumeGetKey)
(phypGetStoragePoolDevice)
(phypVolumeGetPhysicalVolumeByStoragePool)
(phypVolumeGetPath): Likewise.
* src/vbox/vbox_tmpl.c (vboxNetworkUndefineDestroy)
(vboxNetworkCreate, vboxNetworkDumpXML)
(vboxNetworkDefineCreateXML): Likewise.
* src/xenapi/xenapi_driver.c (getCapsObject)
(xenapiDomainDumpXML): Likewise.
* src/xenapi/xenapi_utils.c (createVMRecordFromXml): Likewise.
* src/security/security_selinux.c (SELinuxGenNewContext):
Likewise.
* src/qemu/qemu_command.c (qemuBuildCommandLine): Likewise.
* src/qemu/qemu_hotplug.c (qemuDomainChangeEjectableMedia):
Likewise.
* src/qemu/qemu_process.c (qemuProcessWaitForMonitor): Likewise.
* src/qemu/qemu_monitor_text.c (qemuMonitorTextGetPtyPaths):
Likewise.
* src/qemu/qemu_driver.c (qemudDomainShutdown)
(qemudDomainBlockStats, qemudDomainMemoryPeek): Likewise.
* src/storage/storage_backend_iscsi.c
(virStorageBackendCreateIfaceIQN): Likewise.
* src/node_device/node_device_udev.c (udevProcessPCI): Likewise.
2011-04-01 15:41:45 +00:00
|
|
|
return -1;
|
2009-12-14 09:50:01 +00:00
|
|
|
|
2010-01-06 16:09:04 +00:00
|
|
|
char *pos; /* The current start of searching */
|
|
|
|
char *next = reply; /* The start of the next line */
|
2009-12-14 09:50:01 +00:00
|
|
|
char *eol; /* The character which ends the current line */
|
2010-01-06 16:09:04 +00:00
|
|
|
char *end = reply + strlen(reply); /* The end of the reply string */
|
|
|
|
|
|
|
|
while (next) {
|
|
|
|
pos = next;
|
2009-12-14 09:50:01 +00:00
|
|
|
|
|
|
|
/* Split the output into lines */
|
|
|
|
eol = memchr(pos, '\n', end - pos);
|
2010-01-06 16:09:04 +00:00
|
|
|
if (eol == NULL) {
|
2009-12-14 09:50:01 +00:00
|
|
|
eol = end;
|
2010-01-06 16:09:04 +00:00
|
|
|
next = NULL;
|
|
|
|
} else {
|
|
|
|
next = eol + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Ignore all whitespace immediately before eol */
|
|
|
|
while (eol > pos && c_isspace(*(eol-1)))
|
|
|
|
eol -= 1;
|
2009-12-14 09:50:01 +00:00
|
|
|
|
|
|
|
/* Look for 'filename=pty:' */
|
|
|
|
#define NEEDLE "filename=pty:"
|
|
|
|
char *needle = memmem(pos, eol - pos, NEEDLE, strlen(NEEDLE));
|
|
|
|
|
|
|
|
/* If it's not there we can ignore this line */
|
|
|
|
if (!needle)
|
2010-01-06 16:09:04 +00:00
|
|
|
continue;
|
2009-12-14 09:50:01 +00:00
|
|
|
|
2012-10-11 16:31:20 +00:00
|
|
|
/* id is everything from the beginning of the line to the ':'
|
2009-12-14 09:50:01 +00:00
|
|
|
* find ':' and turn it into a terminator */
|
|
|
|
char *colon = memchr(pos, ':', needle - pos);
|
|
|
|
if (colon == NULL)
|
2010-01-06 16:09:04 +00:00
|
|
|
continue;
|
2009-12-14 09:50:01 +00:00
|
|
|
*colon = '\0';
|
|
|
|
char *id = pos;
|
|
|
|
|
|
|
|
/* Path is everything after needle to the end of the line */
|
|
|
|
*eol = '\0';
|
2009-12-14 10:05:55 +00:00
|
|
|
char *path = strdup(needle + strlen(NEEDLE));
|
|
|
|
if (path == NULL) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-12-14 10:05:55 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2009-12-14 09:50:01 +00:00
|
|
|
|
2009-12-14 10:05:55 +00:00
|
|
|
if (virHashAddEntry(paths, id, path) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
_("failed to save chardev path '%s'"),
|
|
|
|
path);
|
2009-12-14 10:05:55 +00:00
|
|
|
VIR_FREE(path);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2009-12-14 09:50:01 +00:00
|
|
|
#undef NEEDLE
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
2009-12-07 19:28:05 +00:00
|
|
|
VIR_FREE(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorTextAttachPCIDiskController(qemuMonitorPtr mon,
|
|
|
|
const char *bus,
|
2012-08-16 15:41:06 +00:00
|
|
|
virDevicePCIAddress *guestAddr)
|
2009-12-07 19:28:05 +00:00
|
|
|
{
|
|
|
|
char *cmd = NULL;
|
|
|
|
char *reply = NULL;
|
|
|
|
int tryOldSyntax = 0;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
try_command:
|
|
|
|
if (virAsprintf(&cmd, "pci_add %s storage if=%s",
|
|
|
|
(tryOldSyntax ? "0": "pci_addr=auto"), bus) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-12-07 19:28:05 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0)
|
2009-12-07 19:28:05 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (qemuMonitorTextParsePciAddReply(mon, reply, guestAddr) < 0) {
|
|
|
|
if (!tryOldSyntax && strstr(reply, "invalid char in expression")) {
|
|
|
|
VIR_FREE(reply);
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
tryOldSyntax = 1;
|
|
|
|
goto try_command;
|
|
|
|
}
|
|
|
|
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
_("adding %s disk controller failed: %s"), bus, reply);
|
2009-12-07 19:28:05 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(cmd);
|
2009-12-14 09:50:01 +00:00
|
|
|
VIR_FREE(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
Properly support SCSI drive hotplug
The current SCSI hotplug support attaches a brand new SCSI controller
for every disk. This is broken because the semantics differ from those
used when starting the VM initially. In the latter case, each SCSI
controller is filled before a new one is added.
If the user specifies an high drive index (sdazz) then at initial
startup, many intermediate SCSI controllers may be added with no
drives.
This patch changes SCSI hotplug so that it exactly matches the
behaviour of initial startup. First the SCSI controller number is
determined for the drive to be hotplugged. If any controller upto
and including that controller number is not yet present, it is
attached. Then finally the drive is attached to the last controller.
NB, this breaks SCSI hotunplug, because there is no 'drive_del'
command in current QEMU. Previous SCSI hotunplug was broken in
any case because it was unplugging the entire controller, not
just the drive in question.
A future QEMU will allow proper SCSI hotunplug of a drive.
This patch is derived from work done by Wolfgang Mauerer on disk
controllers.
* src/qemu/qemu_driver.c: Fix SCSI hotplug to add a drive to
the correct controller, instead of just attaching a new
controller.
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
support for 'drive_add' command
2009-12-09 17:57:09 +00:00
|
|
|
|
|
|
|
|
|
|
|
static int
|
2012-10-31 19:03:48 +00:00
|
|
|
qemuParseDriveAddReply(const char *reply,
|
|
|
|
virDomainDeviceDriveAddressPtr addr)
|
Properly support SCSI drive hotplug
The current SCSI hotplug support attaches a brand new SCSI controller
for every disk. This is broken because the semantics differ from those
used when starting the VM initially. In the latter case, each SCSI
controller is filled before a new one is added.
If the user specifies an high drive index (sdazz) then at initial
startup, many intermediate SCSI controllers may be added with no
drives.
This patch changes SCSI hotplug so that it exactly matches the
behaviour of initial startup. First the SCSI controller number is
determined for the drive to be hotplugged. If any controller upto
and including that controller number is not yet present, it is
attached. Then finally the drive is attached to the last controller.
NB, this breaks SCSI hotunplug, because there is no 'drive_del'
command in current QEMU. Previous SCSI hotunplug was broken in
any case because it was unplugging the entire controller, not
just the drive in question.
A future QEMU will allow proper SCSI hotunplug of a drive.
This patch is derived from work done by Wolfgang Mauerer on disk
controllers.
* src/qemu/qemu_driver.c: Fix SCSI hotplug to add a drive to
the correct controller, instead of just attaching a new
controller.
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
support for 'drive_add' command
2009-12-09 17:57:09 +00:00
|
|
|
{
|
|
|
|
char *s, *e;
|
|
|
|
|
|
|
|
/* If the command succeeds qemu prints:
|
|
|
|
* OK bus X, unit Y
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!(s = strstr(reply, "OK ")))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
s += 3;
|
|
|
|
|
|
|
|
if (STRPREFIX(s, "bus ")) {
|
|
|
|
s += strlen("bus ");
|
|
|
|
|
|
|
|
if (virStrToLong_ui(s, &e, 10, &addr->bus) == -1) {
|
2010-05-19 10:00:18 +00:00
|
|
|
VIR_WARN("Unable to parse bus '%s'", s);
|
Properly support SCSI drive hotplug
The current SCSI hotplug support attaches a brand new SCSI controller
for every disk. This is broken because the semantics differ from those
used when starting the VM initially. In the latter case, each SCSI
controller is filled before a new one is added.
If the user specifies an high drive index (sdazz) then at initial
startup, many intermediate SCSI controllers may be added with no
drives.
This patch changes SCSI hotplug so that it exactly matches the
behaviour of initial startup. First the SCSI controller number is
determined for the drive to be hotplugged. If any controller upto
and including that controller number is not yet present, it is
attached. Then finally the drive is attached to the last controller.
NB, this breaks SCSI hotunplug, because there is no 'drive_del'
command in current QEMU. Previous SCSI hotunplug was broken in
any case because it was unplugging the entire controller, not
just the drive in question.
A future QEMU will allow proper SCSI hotunplug of a drive.
This patch is derived from work done by Wolfgang Mauerer on disk
controllers.
* src/qemu/qemu_driver.c: Fix SCSI hotplug to add a drive to
the correct controller, instead of just attaching a new
controller.
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
support for 'drive_add' command
2009-12-09 17:57:09 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!STRPREFIX(e, ", ")) {
|
2010-05-19 10:00:18 +00:00
|
|
|
VIR_WARN("Expected ', ' parsing drive_add reply '%s'", s);
|
Properly support SCSI drive hotplug
The current SCSI hotplug support attaches a brand new SCSI controller
for every disk. This is broken because the semantics differ from those
used when starting the VM initially. In the latter case, each SCSI
controller is filled before a new one is added.
If the user specifies an high drive index (sdazz) then at initial
startup, many intermediate SCSI controllers may be added with no
drives.
This patch changes SCSI hotplug so that it exactly matches the
behaviour of initial startup. First the SCSI controller number is
determined for the drive to be hotplugged. If any controller upto
and including that controller number is not yet present, it is
attached. Then finally the drive is attached to the last controller.
NB, this breaks SCSI hotunplug, because there is no 'drive_del'
command in current QEMU. Previous SCSI hotunplug was broken in
any case because it was unplugging the entire controller, not
just the drive in question.
A future QEMU will allow proper SCSI hotunplug of a drive.
This patch is derived from work done by Wolfgang Mauerer on disk
controllers.
* src/qemu/qemu_driver.c: Fix SCSI hotplug to add a drive to
the correct controller, instead of just attaching a new
controller.
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
support for 'drive_add' command
2009-12-09 17:57:09 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
s = e + 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!STRPREFIX(s, "unit ")) {
|
2010-05-19 10:00:18 +00:00
|
|
|
VIR_WARN("Expected 'unit ' parsing drive_add reply '%s'", s);
|
Properly support SCSI drive hotplug
The current SCSI hotplug support attaches a brand new SCSI controller
for every disk. This is broken because the semantics differ from those
used when starting the VM initially. In the latter case, each SCSI
controller is filled before a new one is added.
If the user specifies an high drive index (sdazz) then at initial
startup, many intermediate SCSI controllers may be added with no
drives.
This patch changes SCSI hotplug so that it exactly matches the
behaviour of initial startup. First the SCSI controller number is
determined for the drive to be hotplugged. If any controller upto
and including that controller number is not yet present, it is
attached. Then finally the drive is attached to the last controller.
NB, this breaks SCSI hotunplug, because there is no 'drive_del'
command in current QEMU. Previous SCSI hotunplug was broken in
any case because it was unplugging the entire controller, not
just the drive in question.
A future QEMU will allow proper SCSI hotunplug of a drive.
This patch is derived from work done by Wolfgang Mauerer on disk
controllers.
* src/qemu/qemu_driver.c: Fix SCSI hotplug to add a drive to
the correct controller, instead of just attaching a new
controller.
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
support for 'drive_add' command
2009-12-09 17:57:09 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
s += strlen("bus ");
|
|
|
|
|
|
|
|
if (virStrToLong_ui(s, &e, 10, &addr->unit) == -1) {
|
2010-05-19 10:00:18 +00:00
|
|
|
VIR_WARN("Unable to parse unit number '%s'", s);
|
Properly support SCSI drive hotplug
The current SCSI hotplug support attaches a brand new SCSI controller
for every disk. This is broken because the semantics differ from those
used when starting the VM initially. In the latter case, each SCSI
controller is filled before a new one is added.
If the user specifies an high drive index (sdazz) then at initial
startup, many intermediate SCSI controllers may be added with no
drives.
This patch changes SCSI hotplug so that it exactly matches the
behaviour of initial startup. First the SCSI controller number is
determined for the drive to be hotplugged. If any controller upto
and including that controller number is not yet present, it is
attached. Then finally the drive is attached to the last controller.
NB, this breaks SCSI hotunplug, because there is no 'drive_del'
command in current QEMU. Previous SCSI hotunplug was broken in
any case because it was unplugging the entire controller, not
just the drive in question.
A future QEMU will allow proper SCSI hotunplug of a drive.
This patch is derived from work done by Wolfgang Mauerer on disk
controllers.
* src/qemu/qemu_driver.c: Fix SCSI hotplug to add a drive to
the correct controller, instead of just attaching a new
controller.
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
support for 'drive_add' command
2009-12-09 17:57:09 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorTextAttachDrive(qemuMonitorPtr mon,
|
|
|
|
const char *drivestr,
|
2012-08-16 15:41:06 +00:00
|
|
|
virDevicePCIAddress *controllerAddr,
|
Properly support SCSI drive hotplug
The current SCSI hotplug support attaches a brand new SCSI controller
for every disk. This is broken because the semantics differ from those
used when starting the VM initially. In the latter case, each SCSI
controller is filled before a new one is added.
If the user specifies an high drive index (sdazz) then at initial
startup, many intermediate SCSI controllers may be added with no
drives.
This patch changes SCSI hotplug so that it exactly matches the
behaviour of initial startup. First the SCSI controller number is
determined for the drive to be hotplugged. If any controller upto
and including that controller number is not yet present, it is
attached. Then finally the drive is attached to the last controller.
NB, this breaks SCSI hotunplug, because there is no 'drive_del'
command in current QEMU. Previous SCSI hotunplug was broken in
any case because it was unplugging the entire controller, not
just the drive in question.
A future QEMU will allow proper SCSI hotunplug of a drive.
This patch is derived from work done by Wolfgang Mauerer on disk
controllers.
* src/qemu/qemu_driver.c: Fix SCSI hotplug to add a drive to
the correct controller, instead of just attaching a new
controller.
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
support for 'drive_add' command
2009-12-09 17:57:09 +00:00
|
|
|
virDomainDeviceDriveAddress *driveAddr)
|
|
|
|
{
|
|
|
|
char *cmd = NULL;
|
|
|
|
char *reply = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
char *safe_str;
|
|
|
|
int tryOldSyntax = 0;
|
|
|
|
|
|
|
|
safe_str = qemuMonitorEscapeArg(drivestr);
|
|
|
|
if (!safe_str) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
Properly support SCSI drive hotplug
The current SCSI hotplug support attaches a brand new SCSI controller
for every disk. This is broken because the semantics differ from those
used when starting the VM initially. In the latter case, each SCSI
controller is filled before a new one is added.
If the user specifies an high drive index (sdazz) then at initial
startup, many intermediate SCSI controllers may be added with no
drives.
This patch changes SCSI hotplug so that it exactly matches the
behaviour of initial startup. First the SCSI controller number is
determined for the drive to be hotplugged. If any controller upto
and including that controller number is not yet present, it is
attached. Then finally the drive is attached to the last controller.
NB, this breaks SCSI hotunplug, because there is no 'drive_del'
command in current QEMU. Previous SCSI hotunplug was broken in
any case because it was unplugging the entire controller, not
just the drive in question.
A future QEMU will allow proper SCSI hotunplug of a drive.
This patch is derived from work done by Wolfgang Mauerer on disk
controllers.
* src/qemu/qemu_driver.c: Fix SCSI hotplug to add a drive to
the correct controller, instead of just attaching a new
controller.
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
support for 'drive_add' command
2009-12-09 17:57:09 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
try_command:
|
2010-11-01 17:09:31 +00:00
|
|
|
if (virAsprintf(&cmd, "drive_add %s%.2x:%.2x:%.2x %s",
|
|
|
|
(tryOldSyntax ? "" : "pci_addr="),
|
|
|
|
controllerAddr->domain, controllerAddr->bus,
|
|
|
|
controllerAddr->slot, safe_str) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
Properly support SCSI drive hotplug
The current SCSI hotplug support attaches a brand new SCSI controller
for every disk. This is broken because the semantics differ from those
used when starting the VM initially. In the latter case, each SCSI
controller is filled before a new one is added.
If the user specifies an high drive index (sdazz) then at initial
startup, many intermediate SCSI controllers may be added with no
drives.
This patch changes SCSI hotplug so that it exactly matches the
behaviour of initial startup. First the SCSI controller number is
determined for the drive to be hotplugged. If any controller upto
and including that controller number is not yet present, it is
attached. Then finally the drive is attached to the last controller.
NB, this breaks SCSI hotunplug, because there is no 'drive_del'
command in current QEMU. Previous SCSI hotunplug was broken in
any case because it was unplugging the entire controller, not
just the drive in question.
A future QEMU will allow proper SCSI hotunplug of a drive.
This patch is derived from work done by Wolfgang Mauerer on disk
controllers.
* src/qemu/qemu_driver.c: Fix SCSI hotplug to add a drive to
the correct controller, instead of just attaching a new
controller.
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
support for 'drive_add' command
2009-12-09 17:57:09 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0)
|
Properly support SCSI drive hotplug
The current SCSI hotplug support attaches a brand new SCSI controller
for every disk. This is broken because the semantics differ from those
used when starting the VM initially. In the latter case, each SCSI
controller is filled before a new one is added.
If the user specifies an high drive index (sdazz) then at initial
startup, many intermediate SCSI controllers may be added with no
drives.
This patch changes SCSI hotplug so that it exactly matches the
behaviour of initial startup. First the SCSI controller number is
determined for the drive to be hotplugged. If any controller upto
and including that controller number is not yet present, it is
attached. Then finally the drive is attached to the last controller.
NB, this breaks SCSI hotunplug, because there is no 'drive_del'
command in current QEMU. Previous SCSI hotunplug was broken in
any case because it was unplugging the entire controller, not
just the drive in question.
A future QEMU will allow proper SCSI hotunplug of a drive.
This patch is derived from work done by Wolfgang Mauerer on disk
controllers.
* src/qemu/qemu_driver.c: Fix SCSI hotplug to add a drive to
the correct controller, instead of just attaching a new
controller.
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
support for 'drive_add' command
2009-12-09 17:57:09 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2010-11-01 17:09:31 +00:00
|
|
|
if (strstr(reply, "unknown command:")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
|
|
|
|
_("drive hotplug is not supported"));
|
2010-11-01 17:09:31 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-10-31 19:03:48 +00:00
|
|
|
if (qemuParseDriveAddReply(reply, driveAddr) < 0) {
|
Properly support SCSI drive hotplug
The current SCSI hotplug support attaches a brand new SCSI controller
for every disk. This is broken because the semantics differ from those
used when starting the VM initially. In the latter case, each SCSI
controller is filled before a new one is added.
If the user specifies an high drive index (sdazz) then at initial
startup, many intermediate SCSI controllers may be added with no
drives.
This patch changes SCSI hotplug so that it exactly matches the
behaviour of initial startup. First the SCSI controller number is
determined for the drive to be hotplugged. If any controller upto
and including that controller number is not yet present, it is
attached. Then finally the drive is attached to the last controller.
NB, this breaks SCSI hotunplug, because there is no 'drive_del'
command in current QEMU. Previous SCSI hotunplug was broken in
any case because it was unplugging the entire controller, not
just the drive in question.
A future QEMU will allow proper SCSI hotunplug of a drive.
This patch is derived from work done by Wolfgang Mauerer on disk
controllers.
* src/qemu/qemu_driver.c: Fix SCSI hotplug to add a drive to
the correct controller, instead of just attaching a new
controller.
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
support for 'drive_add' command
2009-12-09 17:57:09 +00:00
|
|
|
if (!tryOldSyntax && strstr(reply, "invalid char in expression")) {
|
|
|
|
VIR_FREE(reply);
|
2010-01-25 19:19:01 +00:00
|
|
|
VIR_FREE(cmd);
|
Properly support SCSI drive hotplug
The current SCSI hotplug support attaches a brand new SCSI controller
for every disk. This is broken because the semantics differ from those
used when starting the VM initially. In the latter case, each SCSI
controller is filled before a new one is added.
If the user specifies an high drive index (sdazz) then at initial
startup, many intermediate SCSI controllers may be added with no
drives.
This patch changes SCSI hotplug so that it exactly matches the
behaviour of initial startup. First the SCSI controller number is
determined for the drive to be hotplugged. If any controller upto
and including that controller number is not yet present, it is
attached. Then finally the drive is attached to the last controller.
NB, this breaks SCSI hotunplug, because there is no 'drive_del'
command in current QEMU. Previous SCSI hotunplug was broken in
any case because it was unplugging the entire controller, not
just the drive in question.
A future QEMU will allow proper SCSI hotunplug of a drive.
This patch is derived from work done by Wolfgang Mauerer on disk
controllers.
* src/qemu/qemu_driver.c: Fix SCSI hotplug to add a drive to
the correct controller, instead of just attaching a new
controller.
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
support for 'drive_add' command
2009-12-09 17:57:09 +00:00
|
|
|
tryOldSyntax = 1;
|
|
|
|
goto try_command;
|
|
|
|
}
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
_("adding %s disk failed: %s"), drivestr, reply);
|
2010-01-25 19:19:01 +00:00
|
|
|
goto cleanup;
|
Properly support SCSI drive hotplug
The current SCSI hotplug support attaches a brand new SCSI controller
for every disk. This is broken because the semantics differ from those
used when starting the VM initially. In the latter case, each SCSI
controller is filled before a new one is added.
If the user specifies an high drive index (sdazz) then at initial
startup, many intermediate SCSI controllers may be added with no
drives.
This patch changes SCSI hotplug so that it exactly matches the
behaviour of initial startup. First the SCSI controller number is
determined for the drive to be hotplugged. If any controller upto
and including that controller number is not yet present, it is
attached. Then finally the drive is attached to the last controller.
NB, this breaks SCSI hotunplug, because there is no 'drive_del'
command in current QEMU. Previous SCSI hotunplug was broken in
any case because it was unplugging the entire controller, not
just the drive in question.
A future QEMU will allow proper SCSI hotunplug of a drive.
This patch is derived from work done by Wolfgang Mauerer on disk
controllers.
* src/qemu/qemu_driver.c: Fix SCSI hotplug to add a drive to
the correct controller, instead of just attaching a new
controller.
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
support for 'drive_add' command
2009-12-09 17:57:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
VIR_FREE(reply);
|
|
|
|
VIR_FREE(safe_str);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
Detect PCI addresses at QEMU startup
Hotunplug of devices requires that we know their PCI address. Even
hotplug of SCSI drives, required that we know the PCI address of
the SCSI controller to attach the drive to. We can find this out
by running 'info pci' and then correlating the vendor/product IDs
with the devices we booted with.
Although this approach is somewhat fragile, it is the only viable
option with QEMU < 0.12, since there is no way for libvirto set
explicit PCI addresses when creating devices in the first place.
For QEMU > 0.12, this code will not be used.
* src/qemu/qemu_driver.c: Assign all dynamic PCI addresses on
startup of QEMU VM, matching vendor/product IDs
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
API for fetching PCI device address mapping
2009-12-09 21:59:04 +00:00
|
|
|
/*
|
|
|
|
* The format we're after looks like this
|
|
|
|
*
|
|
|
|
* (qemu) info pci
|
|
|
|
* Bus 0, device 0, function 0:
|
|
|
|
* Host bridge: PCI device 8086:1237
|
|
|
|
* id ""
|
|
|
|
* Bus 0, device 1, function 0:
|
|
|
|
* ISA bridge: PCI device 8086:7000
|
|
|
|
* id ""
|
|
|
|
* Bus 0, device 1, function 1:
|
|
|
|
* IDE controller: PCI device 8086:7010
|
|
|
|
* BAR4: I/O at 0xc000 [0xc00f].
|
|
|
|
* id ""
|
|
|
|
* Bus 0, device 1, function 3:
|
|
|
|
* Bridge: PCI device 8086:7113
|
|
|
|
* IRQ 9.
|
|
|
|
* id ""
|
|
|
|
* Bus 0, device 2, function 0:
|
|
|
|
* VGA controller: PCI device 1013:00b8
|
|
|
|
* BAR0: 32 bit prefetchable memory at 0xf0000000 [0xf1ffffff].
|
|
|
|
* BAR1: 32 bit memory at 0xf2000000 [0xf2000fff].
|
|
|
|
* id ""
|
|
|
|
* Bus 0, device 3, function 0:
|
|
|
|
* Ethernet controller: PCI device 8086:100e
|
|
|
|
* IRQ 11.
|
|
|
|
* BAR0: 32 bit memory at 0xf2020000 [0xf203ffff].
|
|
|
|
* BAR1: I/O at 0xc040 [0xc07f].
|
|
|
|
* id ""
|
|
|
|
*
|
|
|
|
* Of this, we're interesting in the vendor/product ID
|
|
|
|
* and the bus/device/function data.
|
|
|
|
*/
|
|
|
|
#define CHECK_END(p) if (!(p)) break;
|
|
|
|
#define SKIP_TO(p, lbl) \
|
|
|
|
(p) = strstr((p), (lbl)); \
|
|
|
|
if (p) \
|
|
|
|
(p) += strlen(lbl);
|
|
|
|
#define GET_INT(p, base, val) \
|
|
|
|
if (virStrToLong_ui((p), &(p), (base), &(val)) < 0) { \
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED, \
|
|
|
|
_("cannot parse value for %s"), #val); \
|
Detect PCI addresses at QEMU startup
Hotunplug of devices requires that we know their PCI address. Even
hotplug of SCSI drives, required that we know the PCI address of
the SCSI controller to attach the drive to. We can find this out
by running 'info pci' and then correlating the vendor/product IDs
with the devices we booted with.
Although this approach is somewhat fragile, it is the only viable
option with QEMU < 0.12, since there is no way for libvirto set
explicit PCI addresses when creating devices in the first place.
For QEMU > 0.12, this code will not be used.
* src/qemu/qemu_driver.c: Assign all dynamic PCI addresses on
startup of QEMU VM, matching vendor/product IDs
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
API for fetching PCI device address mapping
2009-12-09 21:59:04 +00:00
|
|
|
break; \
|
|
|
|
}
|
|
|
|
#define SKIP_SPACE(p) \
|
|
|
|
while (*(p) == ' ') (p)++;
|
|
|
|
|
|
|
|
int qemuMonitorTextGetAllPCIAddresses(qemuMonitorPtr mon,
|
|
|
|
qemuMonitorPCIAddress **retaddrs)
|
|
|
|
{
|
|
|
|
char *reply;
|
|
|
|
qemuMonitorPCIAddress *addrs = NULL;
|
|
|
|
int naddrs = 0;
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
*retaddrs = NULL;
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, "info pci", &reply) < 0)
|
Detect PCI addresses at QEMU startup
Hotunplug of devices requires that we know their PCI address. Even
hotplug of SCSI drives, required that we know the PCI address of
the SCSI controller to attach the drive to. We can find this out
by running 'info pci' and then correlating the vendor/product IDs
with the devices we booted with.
Although this approach is somewhat fragile, it is the only viable
option with QEMU < 0.12, since there is no way for libvirto set
explicit PCI addresses when creating devices in the first place.
For QEMU > 0.12, this code will not be used.
* src/qemu/qemu_driver.c: Assign all dynamic PCI addresses on
startup of QEMU VM, matching vendor/product IDs
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
API for fetching PCI device address mapping
2009-12-09 21:59:04 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
p = reply;
|
|
|
|
|
|
|
|
|
|
|
|
while (p) {
|
|
|
|
unsigned int bus, slot, func, vendor, product;
|
|
|
|
|
|
|
|
SKIP_TO(p, " Bus");
|
|
|
|
CHECK_END(p);
|
|
|
|
SKIP_SPACE(p);
|
|
|
|
GET_INT(p, 10, bus);
|
|
|
|
CHECK_END(p);
|
|
|
|
|
|
|
|
SKIP_TO(p, ", device");
|
|
|
|
CHECK_END(p);
|
|
|
|
SKIP_SPACE(p);
|
|
|
|
GET_INT(p, 10, slot);
|
|
|
|
CHECK_END(p);
|
|
|
|
|
|
|
|
SKIP_TO(p, ", function");
|
|
|
|
CHECK_END(p);
|
|
|
|
SKIP_SPACE(p);
|
|
|
|
GET_INT(p, 10, func);
|
|
|
|
CHECK_END(p);
|
|
|
|
|
|
|
|
SKIP_TO(p, "PCI device");
|
|
|
|
CHECK_END(p);
|
|
|
|
SKIP_SPACE(p);
|
|
|
|
GET_INT(p, 16, vendor);
|
|
|
|
CHECK_END(p);
|
|
|
|
|
|
|
|
if (*p != ':')
|
|
|
|
break;
|
|
|
|
p++;
|
|
|
|
GET_INT(p, 16, product);
|
|
|
|
|
|
|
|
if (VIR_REALLOC_N(addrs, naddrs+1) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
Detect PCI addresses at QEMU startup
Hotunplug of devices requires that we know their PCI address. Even
hotplug of SCSI drives, required that we know the PCI address of
the SCSI controller to attach the drive to. We can find this out
by running 'info pci' and then correlating the vendor/product IDs
with the devices we booted with.
Although this approach is somewhat fragile, it is the only viable
option with QEMU < 0.12, since there is no way for libvirto set
explicit PCI addresses when creating devices in the first place.
For QEMU > 0.12, this code will not be used.
* src/qemu/qemu_driver.c: Assign all dynamic PCI addresses on
startup of QEMU VM, matching vendor/product IDs
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
API for fetching PCI device address mapping
2009-12-09 21:59:04 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
addrs[naddrs].addr.domain = 0;
|
|
|
|
addrs[naddrs].addr.bus = bus;
|
|
|
|
addrs[naddrs].addr.slot = slot;
|
|
|
|
addrs[naddrs].addr.function = func;
|
|
|
|
addrs[naddrs].vendor = vendor;
|
|
|
|
addrs[naddrs].product = product;
|
|
|
|
naddrs++;
|
|
|
|
|
|
|
|
VIR_DEBUG("Got dev %d:%d:%d %x:%x", bus, slot, func, vendor, product);
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(reply);
|
|
|
|
|
|
|
|
*retaddrs = addrs;
|
|
|
|
|
|
|
|
return naddrs;
|
|
|
|
|
|
|
|
error:
|
|
|
|
VIR_FREE(addrs);
|
|
|
|
VIR_FREE(reply);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#undef GET_INT
|
|
|
|
#undef SKIP_SPACE
|
|
|
|
#undef CHECK_END
|
|
|
|
#undef SKIP_TO
|
2010-01-26 15:34:46 +00:00
|
|
|
|
|
|
|
|
2010-03-02 08:40:51 +00:00
|
|
|
int qemuMonitorTextDelDevice(qemuMonitorPtr mon,
|
2010-04-14 14:36:42 +00:00
|
|
|
const char *devalias)
|
2010-03-02 08:40:51 +00:00
|
|
|
{
|
|
|
|
char *cmd = NULL;
|
|
|
|
char *reply = NULL;
|
|
|
|
char *safedev;
|
|
|
|
int ret = -1;
|
|
|
|
|
2010-04-14 14:36:42 +00:00
|
|
|
if (!(safedev = qemuMonitorEscapeArg(devalias))) {
|
2010-03-02 08:40:51 +00:00
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virAsprintf(&cmd, "device_del %s", safedev) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2011-02-16 23:37:57 +00:00
|
|
|
VIR_DEBUG("TextDelDevice devalias=%s", devalias);
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0)
|
2010-03-02 08:40:51 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (STRNEQ(reply, "")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
_("detaching %s device failed: %s"), devalias, reply);
|
2010-03-02 08:40:51 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
VIR_FREE(reply);
|
|
|
|
VIR_FREE(safedev);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-01-26 15:34:46 +00:00
|
|
|
int qemuMonitorTextAddDevice(qemuMonitorPtr mon,
|
|
|
|
const char *devicestr)
|
|
|
|
{
|
|
|
|
char *cmd = NULL;
|
|
|
|
char *reply = NULL;
|
|
|
|
char *safedev;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (!(safedev = qemuMonitorEscapeArg(devicestr))) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2010-01-26 15:34:46 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virAsprintf(&cmd, "device_add %s", safedev) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2010-01-26 15:34:46 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0)
|
2010-01-26 15:34:46 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2011-03-24 08:56:40 +00:00
|
|
|
/* If the host device is hotpluged first time, qemu will output
|
|
|
|
* husb: using %s file-system with %s if the command succeeds.
|
|
|
|
*/
|
|
|
|
if (STRPREFIX(reply, "husb: using")) {
|
|
|
|
ret = 0;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Otherwise, if the command succeeds, no output is sent. So
|
2010-05-28 11:18:09 +00:00
|
|
|
* any non-empty string shows an error */
|
|
|
|
if (STRNEQ(reply, "")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
_("adding %s device failed: %s"), devicestr, reply);
|
2010-01-26 15:34:46 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
VIR_FREE(reply);
|
2010-02-16 07:22:34 +00:00
|
|
|
VIR_FREE(safedev);
|
2010-01-26 15:34:46 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorTextAddDrive(qemuMonitorPtr mon,
|
|
|
|
const char *drivestr)
|
|
|
|
{
|
|
|
|
char *cmd = NULL;
|
|
|
|
char *reply = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
char *safe_str;
|
|
|
|
|
|
|
|
safe_str = qemuMonitorEscapeArg(drivestr);
|
|
|
|
if (!safe_str) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2010-01-26 15:34:46 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 'dummy' here is just a placeholder since there is no PCI
|
|
|
|
* address required when attaching drives to a controller */
|
2010-11-01 17:09:31 +00:00
|
|
|
if (virAsprintf(&cmd, "drive_add dummy %s", safe_str) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2010-01-26 15:34:46 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0)
|
2010-01-26 15:34:46 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2010-11-01 17:09:31 +00:00
|
|
|
if (strstr(reply, "unknown command:")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
|
|
|
|
_("drive hotplug is not supported"));
|
2010-11-01 17:09:31 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2011-02-17 03:32:16 +00:00
|
|
|
if (strstr(reply, "could not open disk image")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
|
|
|
|
_("open disk image file failed"));
|
2011-02-17 03:32:16 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2010-01-26 15:34:46 +00:00
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
VIR_FREE(reply);
|
|
|
|
VIR_FREE(safe_str);
|
|
|
|
return ret;
|
|
|
|
}
|
2010-02-11 14:28:16 +00:00
|
|
|
|
2010-12-08 21:30:12 +00:00
|
|
|
/* Attempts to remove a host drive.
|
|
|
|
* Returns 1 if unsupported, 0 if ok, and -1 on other failure */
|
|
|
|
int qemuMonitorTextDriveDel(qemuMonitorPtr mon,
|
|
|
|
const char *drivestr)
|
2010-10-22 14:14:22 +00:00
|
|
|
{
|
|
|
|
char *cmd = NULL;
|
|
|
|
char *reply = NULL;
|
|
|
|
char *safedev;
|
|
|
|
int ret = -1;
|
2011-02-16 23:37:57 +00:00
|
|
|
VIR_DEBUG("TextDriveDel drivestr=%s", drivestr);
|
2010-10-22 14:14:22 +00:00
|
|
|
|
|
|
|
if (!(safedev = qemuMonitorEscapeArg(drivestr))) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2010-12-08 21:30:12 +00:00
|
|
|
if (virAsprintf(&cmd, "drive_del %s", safedev) < 0) {
|
2010-10-22 14:14:22 +00:00
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0)
|
2010-10-22 14:14:22 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (strstr(reply, "unknown command:")) {
|
2011-05-09 09:24:09 +00:00
|
|
|
VIR_ERROR(_("deleting drive is not supported. "
|
2010-10-22 14:14:22 +00:00
|
|
|
"This may leak data if disk is reassigned"));
|
|
|
|
ret = 1;
|
|
|
|
goto cleanup;
|
2010-12-08 21:30:12 +00:00
|
|
|
|
|
|
|
/* (qemu) drive_del wark
|
|
|
|
* Device 'wark' not found */
|
|
|
|
} else if (STRPREFIX(reply, "Device '") && (strstr(reply, "not found"))) {
|
|
|
|
/* NB: device not found errors mean the drive was auto-deleted and we
|
|
|
|
* ignore the error */
|
2010-10-22 14:14:22 +00:00
|
|
|
} else if (STRNEQ(reply, "")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
_("deleting %s drive failed: %s"), drivestr, reply);
|
2010-10-22 14:14:22 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
VIR_FREE(reply);
|
|
|
|
VIR_FREE(safedev);
|
|
|
|
return ret;
|
|
|
|
}
|
2010-02-11 14:28:16 +00:00
|
|
|
|
|
|
|
int qemuMonitorTextSetDrivePassphrase(qemuMonitorPtr mon,
|
|
|
|
const char *alias,
|
|
|
|
const char *passphrase)
|
|
|
|
{
|
|
|
|
char *cmd = NULL;
|
|
|
|
char *reply = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
char *safe_str;
|
|
|
|
|
|
|
|
safe_str = qemuMonitorEscapeArg(passphrase);
|
|
|
|
if (!safe_str) {
|
|
|
|
virReportOOMError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2010-11-01 17:09:31 +00:00
|
|
|
if (virAsprintf(&cmd, "block_passwd %s%s \"%s\"",
|
|
|
|
QEMU_DRIVE_HOST_PREFIX, alias, safe_str) < 0) {
|
2010-02-11 14:28:16 +00:00
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0)
|
2010-02-11 14:28:16 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2010-04-22 15:47:33 +00:00
|
|
|
if (strstr(reply, "unknown command:")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
|
|
|
|
_("setting disk password is not supported"));
|
2010-02-11 14:28:16 +00:00
|
|
|
goto cleanup;
|
|
|
|
} else if (strstr(reply, "The entered password is invalid")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
|
|
|
|
_("the disk password is incorrect"));
|
2010-02-11 14:28:16 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
VIR_FREE(reply);
|
|
|
|
VIR_FREE(safe_str);
|
|
|
|
return ret;
|
|
|
|
}
|
2010-04-02 14:10:37 +00:00
|
|
|
|
|
|
|
int qemuMonitorTextCreateSnapshot(qemuMonitorPtr mon, const char *name)
|
|
|
|
{
|
2011-03-09 20:47:06 +00:00
|
|
|
char *cmd = NULL;
|
2010-04-02 14:10:37 +00:00
|
|
|
char *reply = NULL;
|
|
|
|
int ret = -1;
|
2011-03-09 20:47:06 +00:00
|
|
|
char *safename;
|
2010-04-02 14:10:37 +00:00
|
|
|
|
2011-03-09 20:47:06 +00:00
|
|
|
if (!(safename = qemuMonitorEscapeArg(name)) ||
|
|
|
|
virAsprintf(&cmd, "savevm \"%s\"", safename) < 0) {
|
2010-04-02 14:10:37 +00:00
|
|
|
virReportOOMError();
|
2011-03-09 20:47:06 +00:00
|
|
|
goto cleanup;
|
2010-04-02 14:10:37 +00:00
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply))
|
2010-04-02 14:10:37 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (strstr(reply, "Error while creating snapshot") != NULL) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
_("Failed to take snapshot: %s"), reply);
|
2010-04-02 14:10:37 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
else if (strstr(reply, "No block device can accept snapshots") != NULL) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
|
|
_("this domain does not have a device to take snapshots"));
|
2010-04-02 14:10:37 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
else if (strstr(reply, "Could not open VM state file") != NULL) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED, "%s", reply);
|
2010-04-02 14:10:37 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
else if (strstr(reply, "Error") != NULL
|
|
|
|
&& strstr(reply, "while writing VM") != NULL) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED, "%s", reply);
|
2010-04-02 14:10:37 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
2011-03-09 20:47:06 +00:00
|
|
|
VIR_FREE(safename);
|
2010-04-02 14:10:37 +00:00
|
|
|
VIR_FREE(cmd);
|
|
|
|
VIR_FREE(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int qemuMonitorTextLoadSnapshot(qemuMonitorPtr mon, const char *name)
|
|
|
|
{
|
2011-03-09 20:47:06 +00:00
|
|
|
char *cmd = NULL;
|
2010-04-02 14:10:37 +00:00
|
|
|
char *reply = NULL;
|
|
|
|
int ret = -1;
|
2011-03-09 20:47:06 +00:00
|
|
|
char *safename;
|
2010-04-02 14:10:37 +00:00
|
|
|
|
2011-03-09 20:47:06 +00:00
|
|
|
if (!(safename = qemuMonitorEscapeArg(name)) ||
|
|
|
|
virAsprintf(&cmd, "loadvm \"%s\"", safename) < 0) {
|
2010-04-02 14:10:37 +00:00
|
|
|
virReportOOMError();
|
2011-03-09 20:47:06 +00:00
|
|
|
goto cleanup;
|
2010-04-02 14:10:37 +00:00
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply))
|
2010-04-02 14:10:37 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (strstr(reply, "No block device supports snapshots") != NULL) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
|
|
_("this domain does not have a device to load snapshots"));
|
2010-04-02 14:10:37 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
else if (strstr(reply, "Could not find snapshot") != NULL) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
|
|
_("the snapshot '%s' does not exist, and was not loaded"),
|
|
|
|
name);
|
2010-04-02 14:10:37 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
else if (strstr(reply, "Snapshots not supported on device") != NULL) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s", reply);
|
2010-04-02 14:10:37 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
else if (strstr(reply, "Could not open VM state file") != NULL) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED, "%s", reply);
|
2010-04-02 14:10:37 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
else if (strstr(reply, "Error") != NULL
|
|
|
|
&& strstr(reply, "while loading VM state") != NULL) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED, "%s", reply);
|
2010-04-02 14:10:37 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
else if (strstr(reply, "Error") != NULL
|
|
|
|
&& strstr(reply, "while activating snapshot on") != NULL) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED, "%s", reply);
|
2010-04-02 14:10:37 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
2011-03-09 20:47:06 +00:00
|
|
|
VIR_FREE(safename);
|
2010-04-02 14:10:37 +00:00
|
|
|
VIR_FREE(cmd);
|
|
|
|
VIR_FREE(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int qemuMonitorTextDeleteSnapshot(qemuMonitorPtr mon, const char *name)
|
|
|
|
{
|
2011-03-09 20:47:06 +00:00
|
|
|
char *cmd = NULL;
|
2010-04-02 14:10:37 +00:00
|
|
|
char *reply = NULL;
|
|
|
|
int ret = -1;
|
2011-03-09 20:47:06 +00:00
|
|
|
char *safename;
|
2010-04-02 14:10:37 +00:00
|
|
|
|
2011-03-09 20:47:06 +00:00
|
|
|
if (!(safename = qemuMonitorEscapeArg(name)) ||
|
|
|
|
virAsprintf(&cmd, "delvm \"%s\"", safename) < 0) {
|
2010-04-02 14:10:37 +00:00
|
|
|
virReportOOMError();
|
2011-03-09 20:47:06 +00:00
|
|
|
goto cleanup;
|
2010-04-02 14:10:37 +00:00
|
|
|
}
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply))
|
2010-04-02 14:10:37 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (strstr(reply, "No block device supports snapshots") != NULL) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
|
|
|
_("this domain does not have a device to delete snapshots"));
|
2010-04-02 14:10:37 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
else if (strstr(reply, "Snapshots not supported on device") != NULL) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID, "%s", reply);
|
2010-04-02 14:10:37 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
else if (strstr(reply, "Error") != NULL
|
|
|
|
&& strstr(reply, "while deleting snapshot") != NULL) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED, "%s", reply);
|
2010-04-02 14:10:37 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
2011-03-09 20:47:06 +00:00
|
|
|
VIR_FREE(safename);
|
2010-04-02 14:10:37 +00:00
|
|
|
VIR_FREE(cmd);
|
|
|
|
VIR_FREE(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2010-04-17 02:12:45 +00:00
|
|
|
|
2011-08-15 23:25:54 +00:00
|
|
|
|
2010-04-17 02:12:45 +00:00
|
|
|
int qemuMonitorTextArbitraryCommand(qemuMonitorPtr mon, const char *cmd,
|
|
|
|
char **reply)
|
|
|
|
{
|
|
|
|
char *safecmd = NULL;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!(safecmd = qemuMonitorEscapeArg(cmd))) {
|
|
|
|
virReportOOMError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-03-10 08:33:01 +00:00
|
|
|
ret = qemuMonitorHMPCommand(mon, safecmd, reply);
|
2010-04-17 02:12:45 +00:00
|
|
|
|
|
|
|
VIR_FREE(safecmd);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2011-05-10 08:26:06 +00:00
|
|
|
|
|
|
|
int qemuMonitorTextInjectNMI(qemuMonitorPtr mon)
|
|
|
|
{
|
|
|
|
char *reply = NULL;
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, "inject-nmi", &reply) < 0)
|
|
|
|
return -1;
|
2011-05-10 08:26:06 +00:00
|
|
|
|
|
|
|
if (strstr(reply, "unknown command") != NULL) {
|
|
|
|
VIR_FREE(reply);
|
|
|
|
|
|
|
|
/* fallback to 'nmi' if qemu has not supported "inject-nmi" yet. */
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, "nmi 0", &reply) < 0)
|
|
|
|
return -1;
|
2011-05-10 08:26:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(reply);
|
|
|
|
return 0;
|
|
|
|
}
|
2011-04-01 06:23:58 +00:00
|
|
|
|
2011-07-21 07:55:56 +00:00
|
|
|
int qemuMonitorTextSendKey(qemuMonitorPtr mon,
|
|
|
|
unsigned int holdtime,
|
|
|
|
unsigned int *keycodes,
|
|
|
|
unsigned int nkeycodes)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
|
|
|
char *cmd, *reply = NULL;
|
2011-08-25 16:48:25 +00:00
|
|
|
int ret = -1;
|
2011-07-21 07:55:56 +00:00
|
|
|
|
|
|
|
if (nkeycodes > VIR_DOMAIN_SEND_KEY_MAX_KEYS || nkeycodes == 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
virBufferAddLit(&buf, "sendkey ");
|
|
|
|
for (i = 0; i < nkeycodes; i++) {
|
|
|
|
if (keycodes[i] > 0xffff) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
_("keycode %d is invalid: 0x%X"),
|
|
|
|
i, keycodes[i]);
|
2011-07-21 07:55:56 +00:00
|
|
|
virBufferFreeAndReset(&buf);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i)
|
|
|
|
virBufferAddChar(&buf, '-');
|
|
|
|
virBufferAsprintf(&buf, "0x%02X", keycodes[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (holdtime)
|
|
|
|
virBufferAsprintf(&buf, " %u", holdtime);
|
|
|
|
|
|
|
|
if (virBufferError(&buf)) {
|
|
|
|
virReportOOMError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd = virBufferContentAndReset(&buf);
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0)
|
2011-08-25 16:48:25 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (STRNEQ(reply, "")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
_("failed to send key '%s'"), reply);
|
2011-08-25 16:48:25 +00:00
|
|
|
goto cleanup;
|
2011-07-21 07:55:56 +00:00
|
|
|
}
|
|
|
|
|
2011-08-25 16:48:25 +00:00
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
2011-07-21 07:55:56 +00:00
|
|
|
VIR_FREE(cmd);
|
|
|
|
VIR_FREE(reply);
|
2011-08-25 16:48:25 +00:00
|
|
|
return ret;
|
2011-07-21 07:55:56 +00:00
|
|
|
}
|
|
|
|
|
2011-04-01 06:23:58 +00:00
|
|
|
/* Returns -1 on error, -2 if not supported */
|
|
|
|
int qemuMonitorTextScreendump(qemuMonitorPtr mon, const char *file)
|
|
|
|
{
|
|
|
|
char *cmd = NULL;
|
|
|
|
char *reply = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (virAsprintf(&cmd, "screendump %s", file) < 0){
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0)
|
2011-04-01 06:23:58 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (strstr(reply, "unknown command:")) {
|
|
|
|
ret = -2;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(reply);
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
return ret;
|
|
|
|
}
|
2011-07-22 05:39:37 +00:00
|
|
|
|
2011-10-21 08:00:13 +00:00
|
|
|
|
|
|
|
int qemuMonitorTextOpenGraphics(qemuMonitorPtr mon,
|
|
|
|
const char *protocol,
|
|
|
|
const char *fdname,
|
|
|
|
bool skipauth)
|
|
|
|
{
|
|
|
|
char *cmd = NULL;
|
|
|
|
char *reply = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (virAsprintf(&cmd, "add_client %s %s %d", protocol, fdname, skipauth ? 0 : 1) < 0){
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0)
|
2011-10-21 08:00:13 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (STRNEQ(reply, ""))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(reply);
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
return ret;
|
|
|
|
}
|
2011-11-15 09:02:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorTextSetBlockIoThrottle(qemuMonitorPtr mon,
|
|
|
|
const char *device,
|
|
|
|
virDomainBlockIoTuneInfoPtr info)
|
|
|
|
{
|
|
|
|
char *cmd = NULL;
|
|
|
|
char *result = NULL;
|
2012-09-13 12:54:54 +00:00
|
|
|
int ret = -1;
|
2011-11-15 09:02:45 +00:00
|
|
|
const char *cmd_name = NULL;
|
|
|
|
|
|
|
|
/* For the not specified fields, 0 by default */
|
|
|
|
cmd_name = "block_set_io_throttle";
|
2012-09-13 12:54:54 +00:00
|
|
|
if (virAsprintf(&cmd, "%s %s %llu %llu %llu %llu %llu %llu", cmd_name,
|
|
|
|
device, info->total_bytes_sec, info->read_bytes_sec,
|
|
|
|
info->write_bytes_sec, info->total_iops_sec,
|
|
|
|
info->read_iops_sec, info->write_iops_sec) < 0) {
|
2011-11-15 09:02:45 +00:00
|
|
|
virReportOOMError();
|
2012-09-13 12:54:54 +00:00
|
|
|
goto cleanup;
|
2011-11-15 09:02:45 +00:00
|
|
|
}
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd, &result) < 0)
|
2011-11-15 09:02:45 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (qemuMonitorTextCommandNotFound(cmd_name, result)) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
|
|
_("Command '%s' is not found"), cmd_name);
|
2011-11-15 09:02:45 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2012-09-13 12:54:54 +00:00
|
|
|
ret = 0;
|
2011-11-15 09:02:45 +00:00
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(cmd);
|
|
|
|
VIR_FREE(result);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuMonitorTextParseBlockIoThrottle(const char *result,
|
|
|
|
const char *device,
|
|
|
|
virDomainBlockIoTuneInfoPtr reply)
|
|
|
|
{
|
|
|
|
char *dummy = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
const char *p, *eol;
|
|
|
|
int devnamelen = strlen(device);
|
|
|
|
|
|
|
|
p = result;
|
|
|
|
|
|
|
|
while (*p) {
|
|
|
|
if (STREQLEN(p, device, devnamelen) &&
|
|
|
|
p[devnamelen] == ':' && p[devnamelen+1] == ' ') {
|
|
|
|
|
|
|
|
eol = strchr(p, '\n');
|
|
|
|
if (!eol)
|
|
|
|
eol = p + strlen(p);
|
|
|
|
|
|
|
|
p += devnamelen + 2; /* Skip to first label. */
|
|
|
|
|
|
|
|
while (*p) {
|
|
|
|
if (STRPREFIX(p, "bps=")) {
|
|
|
|
p += strlen("bps=");
|
|
|
|
if (virStrToLong_ull(p, &dummy, 10, &reply->total_bytes_sec) == -1)
|
|
|
|
VIR_DEBUG("error reading total_bytes_sec: %s", p);
|
|
|
|
} else if (STRPREFIX(p, "bps_rd=")) {
|
|
|
|
p += strlen("bps_rd=");
|
|
|
|
if (virStrToLong_ull(p, &dummy, 10, &reply->read_bytes_sec) == -1)
|
|
|
|
VIR_DEBUG("error reading read_bytes_sec: %s", p);
|
|
|
|
} else if (STRPREFIX(p, "bps_wr=")) {
|
|
|
|
p += strlen("bps_wr=");
|
|
|
|
if (virStrToLong_ull(p, &dummy, 10, &reply->write_bytes_sec) == -1)
|
|
|
|
VIR_DEBUG("error reading write_bytes_sec: %s", p);
|
|
|
|
} else if (STRPREFIX(p, "iops=")) {
|
|
|
|
p += strlen("iops=");
|
|
|
|
if (virStrToLong_ull(p, &dummy, 10, &reply->total_iops_sec) == -1)
|
|
|
|
VIR_DEBUG("error reading total_iops_sec: %s", p);
|
|
|
|
} else if (STRPREFIX(p, "iops_rd=")) {
|
|
|
|
p += strlen("iops_rd=");
|
|
|
|
if (virStrToLong_ull(p, &dummy, 10, &reply->read_iops_sec) == -1)
|
|
|
|
VIR_DEBUG("error reading read_iops_sec: %s", p);
|
|
|
|
} else if (STRPREFIX(p, "iops_wr=")) {
|
|
|
|
p += strlen("iops_wr=");
|
|
|
|
if (virStrToLong_ull(p, &dummy, 10, &reply->write_iops_sec) == -1)
|
|
|
|
VIR_DEBUG("error reading write_iops_sec: %s", p);
|
|
|
|
} else {
|
|
|
|
VIR_DEBUG(" unknown block info %s", p);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip to next label. */
|
2012-10-17 09:23:12 +00:00
|
|
|
p = strchr(p, ' ');
|
2011-11-15 09:02:45 +00:00
|
|
|
if (!p || p >= eol)
|
|
|
|
break;
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip to next line. */
|
2012-10-17 09:23:12 +00:00
|
|
|
p = strchr(p, '\n');
|
2011-11-15 09:02:45 +00:00
|
|
|
if (!p)
|
|
|
|
break;
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("No info for device '%s'"), device);
|
2011-11-15 09:02:45 +00:00
|
|
|
|
|
|
|
cleanup:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int qemuMonitorTextGetBlockIoThrottle(qemuMonitorPtr mon,
|
|
|
|
const char *device,
|
|
|
|
virDomainBlockIoTuneInfoPtr reply)
|
|
|
|
{
|
|
|
|
char *result = NULL;
|
2012-09-13 12:54:54 +00:00
|
|
|
int ret = -1;
|
2011-11-15 09:02:45 +00:00
|
|
|
const char *cmd_name = "info block";
|
|
|
|
|
2012-09-13 12:54:54 +00:00
|
|
|
if (qemuMonitorHMPCommand(mon, cmd_name, &result) < 0)
|
2011-11-15 09:02:45 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (qemuMonitorTextCommandNotFound(cmd_name, result)) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
|
|
_("Command '%s' is not found"), cmd_name);
|
2011-11-15 09:02:45 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = qemuMonitorTextParseBlockIoThrottle(result, device, reply);
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(result);
|
|
|
|
return ret;
|
|
|
|
}
|