mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-23 19:49:43 +00:00
Merge remote-tracking branch 'qmp/queue/qmp' into staging
* qmp/queue/qmp: block: live snapshot documentation tweaks input: index_from_key(): drop unused code qmp: qmp_send_key(): accept key codes in hex input: qmp_send_key(): simplify hmp: dump-guest-memory: hardcode protocol argument to "file:" qmp: dump-guest-memory: don't spin if non-blocking fd would block qmp: dump-guest-memory: improve schema doc (again) qapi: convert add_client monitor: add Error * argument to monitor_get_fd pci-assign: use monitor_handle_fd_param qapi: add "unix" to the set of reserved words qapi: do not protect enum values from namespace pollution Add qemu-ga-client script Support settimeout in QEMUMonitorProtocol Make negotiation optional in QEMUMonitorProtocol
This commit is contained in:
commit
97f3461555
299
QMP/qemu-ga-client
Executable file
299
QMP/qemu-ga-client
Executable file
@ -0,0 +1,299 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# QEMU Guest Agent Client
|
||||
#
|
||||
# Copyright (C) 2012 Ryota Ozaki <ozaki.ryota@gmail.com>
|
||||
#
|
||||
# This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
# the COPYING file in the top-level directory.
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# Start QEMU with:
|
||||
#
|
||||
# # qemu [...] -chardev socket,path=/tmp/qga.sock,server,nowait,id=qga0 \
|
||||
# -device virtio-serial -device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0
|
||||
#
|
||||
# Run the script:
|
||||
#
|
||||
# $ qemu-ga-client --address=/tmp/qga.sock <command> [args...]
|
||||
#
|
||||
# or
|
||||
#
|
||||
# $ export QGA_CLIENT_ADDRESS=/tmp/qga.sock
|
||||
# $ qemu-ga-client <command> [args...]
|
||||
#
|
||||
# For example:
|
||||
#
|
||||
# $ qemu-ga-client cat /etc/resolv.conf
|
||||
# # Generated by NetworkManager
|
||||
# nameserver 10.0.2.3
|
||||
# $ qemu-ga-client fsfreeze status
|
||||
# thawed
|
||||
# $ qemu-ga-client fsfreeze freeze
|
||||
# 2 filesystems frozen
|
||||
#
|
||||
# See also: http://wiki.qemu.org/Features/QAPI/GuestAgent
|
||||
#
|
||||
|
||||
import base64
|
||||
import random
|
||||
|
||||
import qmp
|
||||
|
||||
|
||||
class QemuGuestAgent(qmp.QEMUMonitorProtocol):
|
||||
def __getattr__(self, name):
|
||||
def wrapper(**kwds):
|
||||
return self.command('guest-' + name.replace('_', '-'), **kwds)
|
||||
return wrapper
|
||||
|
||||
|
||||
class QemuGuestAgentClient:
|
||||
error = QemuGuestAgent.error
|
||||
|
||||
def __init__(self, address):
|
||||
self.qga = QemuGuestAgent(address)
|
||||
self.qga.connect(negotiate=False)
|
||||
|
||||
def sync(self, timeout=3):
|
||||
# Avoid being blocked forever
|
||||
if not self.ping(timeout):
|
||||
raise EnvironmentError('Agent seems not alive')
|
||||
uid = random.randint(0, (1 << 32) - 1)
|
||||
while True:
|
||||
ret = self.qga.sync(id=uid)
|
||||
if isinstance(ret, int) and int(ret) == uid:
|
||||
break
|
||||
|
||||
def __file_read_all(self, handle):
|
||||
eof = False
|
||||
data = ''
|
||||
while not eof:
|
||||
ret = self.qga.file_read(handle=handle, count=1024)
|
||||
_data = base64.b64decode(ret['buf-b64'])
|
||||
data += _data
|
||||
eof = ret['eof']
|
||||
return data
|
||||
|
||||
def read(self, path):
|
||||
handle = self.qga.file_open(path=path)
|
||||
try:
|
||||
data = self.__file_read_all(handle)
|
||||
finally:
|
||||
self.qga.file_close(handle=handle)
|
||||
return data
|
||||
|
||||
def info(self):
|
||||
info = self.qga.info()
|
||||
|
||||
msgs = []
|
||||
msgs.append('version: ' + info['version'])
|
||||
msgs.append('supported_commands:')
|
||||
enabled = [c['name'] for c in info['supported_commands'] if c['enabled']]
|
||||
msgs.append('\tenabled: ' + ', '.join(enabled))
|
||||
disabled = [c['name'] for c in info['supported_commands'] if not c['enabled']]
|
||||
msgs.append('\tdisabled: ' + ', '.join(disabled))
|
||||
|
||||
return '\n'.join(msgs)
|
||||
|
||||
def __gen_ipv4_netmask(self, prefixlen):
|
||||
mask = int('1' * prefixlen + '0' * (32 - prefixlen), 2)
|
||||
return '.'.join([str(mask >> 24),
|
||||
str((mask >> 16) & 0xff),
|
||||
str((mask >> 8) & 0xff),
|
||||
str(mask & 0xff)])
|
||||
|
||||
def ifconfig(self):
|
||||
nifs = self.qga.network_get_interfaces()
|
||||
|
||||
msgs = []
|
||||
for nif in nifs:
|
||||
msgs.append(nif['name'] + ':')
|
||||
if 'ip-addresses' in nif:
|
||||
for ipaddr in nif['ip-addresses']:
|
||||
if ipaddr['ip-address-type'] == 'ipv4':
|
||||
addr = ipaddr['ip-address']
|
||||
mask = self.__gen_ipv4_netmask(int(ipaddr['prefix']))
|
||||
msgs.append("\tinet %s netmask %s" % (addr, mask))
|
||||
elif ipaddr['ip-address-type'] == 'ipv6':
|
||||
addr = ipaddr['ip-address']
|
||||
prefix = ipaddr['prefix']
|
||||
msgs.append("\tinet6 %s prefixlen %s" % (addr, prefix))
|
||||
if nif['hardware-address'] != '00:00:00:00:00:00':
|
||||
msgs.append("\tether " + nif['hardware-address'])
|
||||
|
||||
return '\n'.join(msgs)
|
||||
|
||||
def ping(self, timeout):
|
||||
self.qga.settimeout(timeout)
|
||||
try:
|
||||
self.qga.ping()
|
||||
except self.qga.timeout:
|
||||
return False
|
||||
return True
|
||||
|
||||
def fsfreeze(self, cmd):
|
||||
if cmd not in ['status', 'freeze', 'thaw']:
|
||||
raise StandardError('Invalid command: ' + cmd)
|
||||
|
||||
return getattr(self.qga, 'fsfreeze' + '_' + cmd)()
|
||||
|
||||
def fstrim(self, minimum=0):
|
||||
return getattr(self.qga, 'fstrim')(minimum=minimum)
|
||||
|
||||
def suspend(self, mode):
|
||||
if mode not in ['disk', 'ram', 'hybrid']:
|
||||
raise StandardError('Invalid mode: ' + mode)
|
||||
|
||||
try:
|
||||
getattr(self.qga, 'suspend' + '_' + mode)()
|
||||
# On error exception will raise
|
||||
except self.qga.timeout:
|
||||
# On success command will timed out
|
||||
return
|
||||
|
||||
def shutdown(self, mode='powerdown'):
|
||||
if mode not in ['powerdown', 'halt', 'reboot']:
|
||||
raise StandardError('Invalid mode: ' + mode)
|
||||
|
||||
try:
|
||||
self.qga.shutdown(mode=mode)
|
||||
except self.qga.timeout:
|
||||
return
|
||||
|
||||
|
||||
def _cmd_cat(client, args):
|
||||
if len(args) != 1:
|
||||
print('Invalid argument')
|
||||
print('Usage: cat <file>')
|
||||
sys.exit(1)
|
||||
print(client.read(args[0]))
|
||||
|
||||
|
||||
def _cmd_fsfreeze(client, args):
|
||||
usage = 'Usage: fsfreeze status|freeze|thaw'
|
||||
if len(args) != 1:
|
||||
print('Invalid argument')
|
||||
print(usage)
|
||||
sys.exit(1)
|
||||
if args[0] not in ['status', 'freeze', 'thaw']:
|
||||
print('Invalid command: ' + args[0])
|
||||
print(usage)
|
||||
sys.exit(1)
|
||||
cmd = args[0]
|
||||
ret = client.fsfreeze(cmd)
|
||||
if cmd == 'status':
|
||||
print(ret)
|
||||
elif cmd == 'freeze':
|
||||
print("%d filesystems frozen" % ret)
|
||||
else:
|
||||
print("%d filesystems thawed" % ret)
|
||||
|
||||
|
||||
def _cmd_fstrim(client, args):
|
||||
if len(args) == 0:
|
||||
minimum = 0
|
||||
else:
|
||||
minimum = int(args[0])
|
||||
print(client.fstrim(minimum))
|
||||
|
||||
|
||||
def _cmd_ifconfig(client, args):
|
||||
print(client.ifconfig())
|
||||
|
||||
|
||||
def _cmd_info(client, args):
|
||||
print(client.info())
|
||||
|
||||
|
||||
def _cmd_ping(client, args):
|
||||
if len(args) == 0:
|
||||
timeout = 3
|
||||
else:
|
||||
timeout = float(args[0])
|
||||
alive = client.ping(timeout)
|
||||
if not alive:
|
||||
print("Not responded in %s sec" % args[0])
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def _cmd_suspend(client, args):
|
||||
usage = 'Usage: suspend disk|ram|hybrid'
|
||||
if len(args) != 1:
|
||||
print('Less argument')
|
||||
print(usage)
|
||||
sys.exit(1)
|
||||
if args[0] not in ['disk', 'ram', 'hybrid']:
|
||||
print('Invalid command: ' + args[0])
|
||||
print(usage)
|
||||
sys.exit(1)
|
||||
client.suspend(args[0])
|
||||
|
||||
|
||||
def _cmd_shutdown(client, args):
|
||||
client.shutdown()
|
||||
_cmd_powerdown = _cmd_shutdown
|
||||
|
||||
|
||||
def _cmd_halt(client, args):
|
||||
client.shutdown('halt')
|
||||
|
||||
|
||||
def _cmd_reboot(client, args):
|
||||
client.shutdown('reboot')
|
||||
|
||||
|
||||
commands = [m.replace('_cmd_', '') for m in dir() if '_cmd_' in m]
|
||||
|
||||
|
||||
def main(address, cmd, args):
|
||||
if not os.path.exists(address):
|
||||
print('%s not found' % address)
|
||||
sys.exit(1)
|
||||
|
||||
if cmd not in commands:
|
||||
print('Invalid command: ' + cmd)
|
||||
print('Available commands: ' + ', '.join(commands))
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
client = QemuGuestAgentClient(address)
|
||||
except QemuGuestAgent.error, e:
|
||||
import errno
|
||||
|
||||
print(e)
|
||||
if e.errno == errno.ECONNREFUSED:
|
||||
print('Hint: qemu is not running?')
|
||||
sys.exit(1)
|
||||
|
||||
if cmd != 'ping':
|
||||
client.sync()
|
||||
|
||||
globals()['_cmd_' + cmd](client, args)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
import os
|
||||
import optparse
|
||||
|
||||
address = os.environ['QGA_CLIENT_ADDRESS'] if 'QGA_CLIENT_ADDRESS' in os.environ else None
|
||||
|
||||
usage = "%prog [--address=<unix_path>|<ipv4_address>] <command> [args...]\n"
|
||||
usage += '<command>: ' + ', '.join(commands)
|
||||
parser = optparse.OptionParser(usage=usage)
|
||||
parser.add_option('--address', action='store', type='string',
|
||||
default=address, help='Specify a ip:port pair or a unix socket path')
|
||||
options, args = parser.parse_args()
|
||||
|
||||
address = options.address
|
||||
if address is None:
|
||||
parser.error('address is not specified')
|
||||
sys.exit(1)
|
||||
|
||||
if len(args) == 0:
|
||||
parser.error('Less argument')
|
||||
sys.exit(1)
|
||||
|
||||
main(address, args[0], args[1:])
|
12
QMP/qmp.py
12
QMP/qmp.py
@ -49,7 +49,6 @@ class QEMUMonitorProtocol:
|
||||
return socket.socket(family, socket.SOCK_STREAM)
|
||||
|
||||
def __negotiate_capabilities(self):
|
||||
self.__sockfile = self.__sock.makefile()
|
||||
greeting = self.__json_read()
|
||||
if greeting is None or not greeting.has_key('QMP'):
|
||||
raise QMPConnectError
|
||||
@ -73,7 +72,7 @@ class QEMUMonitorProtocol:
|
||||
|
||||
error = socket.error
|
||||
|
||||
def connect(self):
|
||||
def connect(self, negotiate=True):
|
||||
"""
|
||||
Connect to the QMP Monitor and perform capabilities negotiation.
|
||||
|
||||
@ -83,7 +82,9 @@ class QEMUMonitorProtocol:
|
||||
@raise QMPCapabilitiesError if fails to negotiate capabilities
|
||||
"""
|
||||
self.__sock.connect(self.__address)
|
||||
return self.__negotiate_capabilities()
|
||||
self.__sockfile = self.__sock.makefile()
|
||||
if negotiate:
|
||||
return self.__negotiate_capabilities()
|
||||
|
||||
def accept(self):
|
||||
"""
|
||||
@ -161,3 +162,8 @@ class QEMUMonitorProtocol:
|
||||
def close(self):
|
||||
self.__sock.close()
|
||||
self.__sockfile.close()
|
||||
|
||||
timeout = socket.timeout
|
||||
|
||||
def settimeout(self, timeout):
|
||||
self.__sock.settimeout(timeout)
|
||||
|
18
dump.c
18
dump.c
@ -100,18 +100,11 @@ static void dump_error(DumpState *s, const char *reason)
|
||||
static int fd_write_vmcore(void *buf, size_t size, void *opaque)
|
||||
{
|
||||
DumpState *s = opaque;
|
||||
int fd = s->fd;
|
||||
size_t writen_size;
|
||||
size_t written_size;
|
||||
|
||||
/* The fd may be passed from user, and it can be non-blocked */
|
||||
while (size) {
|
||||
writen_size = qemu_write_full(fd, buf, size);
|
||||
if (writen_size != size && errno != EAGAIN) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf += writen_size;
|
||||
size -= writen_size;
|
||||
written_size = qemu_write_full(s->fd, buf, size);
|
||||
if (written_size != size) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -836,9 +829,8 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
|
||||
|
||||
#if !defined(WIN32)
|
||||
if (strstart(file, "fd:", &p)) {
|
||||
fd = monitor_get_fd(cur_mon, p);
|
||||
fd = monitor_get_fd(cur_mon, p, errp);
|
||||
if (fd == -1) {
|
||||
error_set(errp, QERR_FD_NOT_FOUND, p);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -914,12 +914,11 @@ ETEXI
|
||||
#if defined(CONFIG_HAVE_CORE_DUMP)
|
||||
{
|
||||
.name = "dump-guest-memory",
|
||||
.args_type = "paging:-p,protocol:s,begin:i?,length:i?",
|
||||
.params = "[-p] protocol [begin] [length]",
|
||||
.args_type = "paging:-p,filename:F,begin:i?,length:i?",
|
||||
.params = "[-p] filename [begin] [length]",
|
||||
.help = "dump guest memory to file"
|
||||
"\n\t\t\t begin(optional): the starting physical address"
|
||||
"\n\t\t\t length(optional): the memory size, in bytes",
|
||||
.user_print = monitor_user_noop,
|
||||
.mhandler.cmd = hmp_dump_guest_memory,
|
||||
},
|
||||
|
||||
@ -929,8 +928,7 @@ STEXI
|
||||
@findex dump-guest-memory
|
||||
Dump guest memory to @var{protocol}. The file can be processed with crash or
|
||||
gdb.
|
||||
protocol: destination file(started with "file:") or destination file
|
||||
descriptor (started with "fd:")
|
||||
filename: dump file name
|
||||
paging: do paging to get guest's memory mapping
|
||||
begin: the starting physical address. It's optional, and should be
|
||||
specified with length together.
|
||||
|
51
hmp.c
51
hmp.c
@ -1042,11 +1042,12 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
Error *errp = NULL;
|
||||
int paging = qdict_get_try_bool(qdict, "paging", 0);
|
||||
const char *file = qdict_get_str(qdict, "protocol");
|
||||
const char *file = qdict_get_str(qdict, "filename");
|
||||
bool has_begin = qdict_haskey(qdict, "begin");
|
||||
bool has_length = qdict_haskey(qdict, "length");
|
||||
int64_t begin = 0;
|
||||
int64_t length = 0;
|
||||
char *prot;
|
||||
|
||||
if (has_begin) {
|
||||
begin = qdict_get_int(qdict, "begin");
|
||||
@ -1055,9 +1056,12 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict)
|
||||
length = qdict_get_int(qdict, "length");
|
||||
}
|
||||
|
||||
qmp_dump_guest_memory(paging, file, has_begin, begin, has_length, length,
|
||||
prot = g_strconcat("file:", file, NULL);
|
||||
|
||||
qmp_dump_guest_memory(paging, prot, has_begin, begin, has_length, length,
|
||||
&errp);
|
||||
hmp_handle_error(mon, &errp);
|
||||
g_free(prot);
|
||||
}
|
||||
|
||||
void hmp_netdev_add(Monitor *mon, const QDict *qdict)
|
||||
@ -1109,13 +1113,13 @@ void hmp_closefd(Monitor *mon, const QDict *qdict)
|
||||
void hmp_send_key(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
const char *keys = qdict_get_str(qdict, "keys");
|
||||
QKeyCodeList *keylist, *head = NULL, *tmp = NULL;
|
||||
KeyValueList *keylist, *head = NULL, *tmp = NULL;
|
||||
int has_hold_time = qdict_haskey(qdict, "hold-time");
|
||||
int hold_time = qdict_get_try_int(qdict, "hold-time", -1);
|
||||
Error *err = NULL;
|
||||
char keyname_buf[16];
|
||||
char *separator;
|
||||
int keyname_len, idx;
|
||||
int keyname_len;
|
||||
|
||||
while (1) {
|
||||
separator = strchr(keys, '-');
|
||||
@ -1129,15 +1133,8 @@ void hmp_send_key(Monitor *mon, const QDict *qdict)
|
||||
}
|
||||
keyname_buf[keyname_len] = 0;
|
||||
|
||||
idx = index_from_key(keyname_buf);
|
||||
if (idx == Q_KEY_CODE_MAX) {
|
||||
monitor_printf(mon, "invalid parameter: %s\n", keyname_buf);
|
||||
break;
|
||||
}
|
||||
|
||||
keylist = g_malloc0(sizeof(*keylist));
|
||||
keylist->value = idx;
|
||||
keylist->next = NULL;
|
||||
keylist->value = g_malloc0(sizeof(*keylist->value));
|
||||
|
||||
if (!head) {
|
||||
head = keylist;
|
||||
@ -1147,17 +1144,39 @@ void hmp_send_key(Monitor *mon, const QDict *qdict)
|
||||
}
|
||||
tmp = keylist;
|
||||
|
||||
if (strstart(keyname_buf, "0x", NULL)) {
|
||||
char *endp;
|
||||
int value = strtoul(keyname_buf, &endp, 0);
|
||||
if (*endp != '\0') {
|
||||
goto err_out;
|
||||
}
|
||||
keylist->value->kind = KEY_VALUE_KIND_NUMBER;
|
||||
keylist->value->number = value;
|
||||
} else {
|
||||
int idx = index_from_key(keyname_buf);
|
||||
if (idx == Q_KEY_CODE_MAX) {
|
||||
goto err_out;
|
||||
}
|
||||
keylist->value->kind = KEY_VALUE_KIND_QCODE;
|
||||
keylist->value->qcode = idx;
|
||||
}
|
||||
|
||||
if (!separator) {
|
||||
break;
|
||||
}
|
||||
keys = separator + 1;
|
||||
}
|
||||
|
||||
if (idx != Q_KEY_CODE_MAX) {
|
||||
qmp_send_key(head, has_hold_time, hold_time, &err);
|
||||
}
|
||||
qmp_send_key(head, has_hold_time, hold_time, &err);
|
||||
hmp_handle_error(mon, &err);
|
||||
qapi_free_QKeyCodeList(head);
|
||||
|
||||
out:
|
||||
qapi_free_KeyValueList(head);
|
||||
return;
|
||||
|
||||
err_out:
|
||||
monitor_printf(mon, "invalid parameter: %s\n", keyname_buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
void hmp_screen_dump(Monitor *mon, const QDict *qdict)
|
||||
|
@ -579,15 +579,9 @@ static int get_real_device(AssignedDevice *pci_dev, uint16_t r_seg,
|
||||
snprintf(name, sizeof(name), "%sconfig", dir);
|
||||
|
||||
if (pci_dev->configfd_name && *pci_dev->configfd_name) {
|
||||
if (qemu_isdigit(pci_dev->configfd_name[0])) {
|
||||
dev->config_fd = strtol(pci_dev->configfd_name, NULL, 0);
|
||||
} else {
|
||||
dev->config_fd = monitor_get_fd(cur_mon, pci_dev->configfd_name);
|
||||
if (dev->config_fd < 0) {
|
||||
error_report("%s: (%s) unkown", __func__,
|
||||
pci_dev->configfd_name);
|
||||
return 1;
|
||||
}
|
||||
dev->config_fd = monitor_handle_fd_param(cur_mon, pci_dev->configfd_name);
|
||||
if (dev->config_fd < 0) {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
dev->config_fd = open(name, O_RDWR);
|
||||
|
77
input.c
77
input.c
@ -186,8 +186,7 @@ static const int key_defs[] = {
|
||||
|
||||
int index_from_key(const char *key)
|
||||
{
|
||||
int i, keycode;
|
||||
char *endp;
|
||||
int i;
|
||||
|
||||
for (i = 0; QKeyCode_lookup[i] != NULL; i++) {
|
||||
if (!strcmp(key, QKeyCode_lookup[i])) {
|
||||
@ -195,17 +194,6 @@ int index_from_key(const char *key)
|
||||
}
|
||||
}
|
||||
|
||||
if (strstart(key, "0x", NULL)) {
|
||||
keycode = strtoul(key, &endp, 0);
|
||||
if (*endp == '\0' && keycode >= 0x01 && keycode <= 0xff) {
|
||||
for (i = 0; i < Q_KEY_CODE_MAX; i++) {
|
||||
if (keycode == key_defs[i]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Return Q_KEY_CODE_MAX if the key is invalid */
|
||||
return i;
|
||||
}
|
||||
@ -224,30 +212,46 @@ int index_from_keycode(int code)
|
||||
return i;
|
||||
}
|
||||
|
||||
static QKeyCodeList *keycodes;
|
||||
static int *keycodes;
|
||||
static int keycodes_size;
|
||||
static QEMUTimer *key_timer;
|
||||
|
||||
static int keycode_from_keyvalue(const KeyValue *value)
|
||||
{
|
||||
if (value->kind == KEY_VALUE_KIND_QCODE) {
|
||||
return key_defs[value->qcode];
|
||||
} else {
|
||||
assert(value->kind == KEY_VALUE_KIND_NUMBER);
|
||||
return value->number;
|
||||
}
|
||||
}
|
||||
|
||||
static void free_keycodes(void)
|
||||
{
|
||||
g_free(keycodes);
|
||||
keycodes = NULL;
|
||||
keycodes_size = 0;
|
||||
}
|
||||
|
||||
static void release_keys(void *opaque)
|
||||
{
|
||||
int keycode;
|
||||
QKeyCodeList *p;
|
||||
int i;
|
||||
|
||||
for (p = keycodes; p != NULL; p = p->next) {
|
||||
keycode = key_defs[p->value];
|
||||
if (keycode & 0x80) {
|
||||
for (i = 0; i < keycodes_size; i++) {
|
||||
if (keycodes[i] & 0x80) {
|
||||
kbd_put_keycode(0xe0);
|
||||
}
|
||||
kbd_put_keycode(keycode | 0x80);
|
||||
kbd_put_keycode(keycodes[i]| 0x80);
|
||||
}
|
||||
qapi_free_QKeyCodeList(keycodes);
|
||||
keycodes = NULL;
|
||||
|
||||
free_keycodes();
|
||||
}
|
||||
|
||||
void qmp_send_key(QKeyCodeList *keys, bool has_hold_time, int64_t hold_time,
|
||||
void qmp_send_key(KeyValueList *keys, bool has_hold_time, int64_t hold_time,
|
||||
Error **errp)
|
||||
{
|
||||
int keycode;
|
||||
QKeyCodeList *p, *keylist, *head = NULL, *tmp = NULL;
|
||||
KeyValueList *p;
|
||||
|
||||
if (!key_timer) {
|
||||
key_timer = qemu_new_timer_ns(vm_clock, release_keys, NULL);
|
||||
@ -257,31 +261,28 @@ void qmp_send_key(QKeyCodeList *keys, bool has_hold_time, int64_t hold_time,
|
||||
qemu_del_timer(key_timer);
|
||||
release_keys(NULL);
|
||||
}
|
||||
|
||||
if (!has_hold_time) {
|
||||
hold_time = 100;
|
||||
}
|
||||
|
||||
for (p = keys; p != NULL; p = p->next) {
|
||||
keylist = g_malloc0(sizeof(*keylist));
|
||||
keylist->value = p->value;
|
||||
keylist->next = NULL;
|
||||
|
||||
if (!head) {
|
||||
head = keylist;
|
||||
}
|
||||
if (tmp) {
|
||||
tmp->next = keylist;
|
||||
}
|
||||
tmp = keylist;
|
||||
|
||||
/* key down events */
|
||||
keycode = key_defs[p->value];
|
||||
keycode = keycode_from_keyvalue(p->value);
|
||||
if (keycode < 0x01 || keycode > 0xff) {
|
||||
error_setg(errp, "invalid hex keycode 0x%x\n", keycode);
|
||||
free_keycodes();
|
||||
return;
|
||||
}
|
||||
|
||||
if (keycode & 0x80) {
|
||||
kbd_put_keycode(0xe0);
|
||||
}
|
||||
kbd_put_keycode(keycode & 0x7f);
|
||||
|
||||
keycodes = g_realloc(keycodes, sizeof(int) * (keycodes_size + 1));
|
||||
keycodes[keycodes_size++] = keycode;
|
||||
}
|
||||
keycodes = head;
|
||||
|
||||
/* delayed key up events */
|
||||
qemu_mod_timer(key_timer, qemu_get_clock_ns(vm_clock) +
|
||||
|
@ -75,7 +75,7 @@ static int fd_close(MigrationState *s)
|
||||
|
||||
int fd_start_outgoing_migration(MigrationState *s, const char *fdname)
|
||||
{
|
||||
s->fd = monitor_get_fd(cur_mon, fdname);
|
||||
s->fd = monitor_get_fd(cur_mon, fdname, NULL);
|
||||
if (s->fd == -1) {
|
||||
DPRINTF("fd_migration: invalid file descriptor identifier\n");
|
||||
goto err_after_get_fd;
|
||||
|
48
monitor.c
48
monitor.c
@ -944,45 +944,6 @@ static void do_trace_print_events(Monitor *mon)
|
||||
trace_print_events((FILE *)mon, &monitor_fprintf);
|
||||
}
|
||||
|
||||
static int add_graphics_client(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
||||
{
|
||||
const char *protocol = qdict_get_str(qdict, "protocol");
|
||||
const char *fdname = qdict_get_str(qdict, "fdname");
|
||||
CharDriverState *s;
|
||||
|
||||
if (strcmp(protocol, "spice") == 0) {
|
||||
int fd = monitor_get_fd(mon, fdname);
|
||||
int skipauth = qdict_get_try_bool(qdict, "skipauth", 0);
|
||||
int tls = qdict_get_try_bool(qdict, "tls", 0);
|
||||
if (!using_spice) {
|
||||
/* correct one? spice isn't a device ,,, */
|
||||
qerror_report(QERR_DEVICE_NOT_ACTIVE, "spice");
|
||||
return -1;
|
||||
}
|
||||
if (qemu_spice_display_add_client(fd, skipauth, tls) < 0) {
|
||||
close(fd);
|
||||
}
|
||||
return 0;
|
||||
#ifdef CONFIG_VNC
|
||||
} else if (strcmp(protocol, "vnc") == 0) {
|
||||
int fd = monitor_get_fd(mon, fdname);
|
||||
int skipauth = qdict_get_try_bool(qdict, "skipauth", 0);
|
||||
vnc_display_add_client(NULL, fd, skipauth);
|
||||
return 0;
|
||||
#endif
|
||||
} else if ((s = qemu_chr_find(protocol)) != NULL) {
|
||||
int fd = monitor_get_fd(mon, fdname);
|
||||
if (qemu_chr_add_client(s, fd) < 0) {
|
||||
qerror_report(QERR_ADD_CLIENT_FAILED);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
qerror_report(QERR_INVALID_PARAMETER, "protocol");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int client_migrate_info(Monitor *mon, const QDict *qdict,
|
||||
MonitorCompletion cb, void *opaque)
|
||||
{
|
||||
@ -2118,7 +2079,7 @@ static void do_loadvm(Monitor *mon, const QDict *qdict)
|
||||
}
|
||||
}
|
||||
|
||||
int monitor_get_fd(Monitor *mon, const char *fdname)
|
||||
int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp)
|
||||
{
|
||||
mon_fd_t *monfd;
|
||||
|
||||
@ -2139,6 +2100,7 @@ int monitor_get_fd(Monitor *mon, const char *fdname)
|
||||
return fd;
|
||||
}
|
||||
|
||||
error_setg(errp, "File descriptor named '%s' has not been found", fdname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -2410,12 +2372,14 @@ int monitor_fdset_dup_fd_remove(int dup_fd)
|
||||
int monitor_handle_fd_param(Monitor *mon, const char *fdname)
|
||||
{
|
||||
int fd;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (!qemu_isdigit(fdname[0]) && mon) {
|
||||
|
||||
fd = monitor_get_fd(mon, fdname);
|
||||
fd = monitor_get_fd(mon, fdname, &local_err);
|
||||
if (fd == -1) {
|
||||
error_report("No file descriptor named %s found", fdname);
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
|
@ -66,7 +66,7 @@ int monitor_read_block_device_key(Monitor *mon, const char *device,
|
||||
BlockDriverCompletionFunc *completion_cb,
|
||||
void *opaque);
|
||||
|
||||
int monitor_get_fd(Monitor *mon, const char *fdname);
|
||||
int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp);
|
||||
int monitor_handle_fd_param(Monitor *mon, const char *fdname);
|
||||
|
||||
void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
|
||||
|
@ -32,6 +32,31 @@
|
||||
'DeviceNotActive', 'DeviceNotFound', 'KVMMissingCap',
|
||||
'MigrationExpected' ] }
|
||||
|
||||
##
|
||||
# @add_client
|
||||
#
|
||||
# Allow client connections for VNC, Spice and socket based
|
||||
# character devices to be passed in to QEMU via SCM_RIGHTS.
|
||||
#
|
||||
# @protocol: protocol name. Valid names are "vnc", "spice" or the
|
||||
# name of a character device (eg. from -chardev id=XXXX)
|
||||
#
|
||||
# @fdname: file descriptor name previously passed via 'getfd' command
|
||||
#
|
||||
# @skipauth: #optional whether to skip authentication. Only applies
|
||||
# to "vnc" and "spice" protocols
|
||||
#
|
||||
# @tls: #optional whether to perform TLS. Only applies to the "spice"
|
||||
# protocol
|
||||
#
|
||||
# Returns: nothing on success.
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'command': 'add_client',
|
||||
'data': { 'protocol': 'str', 'fdname': 'str', '*skipauth': 'bool',
|
||||
'*tls': 'bool' } }
|
||||
|
||||
##
|
||||
# @NameInfo:
|
||||
#
|
||||
@ -1374,7 +1399,7 @@
|
||||
# @format: #optional the format of the snapshot image, default is 'qcow2'.
|
||||
#
|
||||
# @mode: #optional whether and how QEMU should create a new image, default is
|
||||
# 'absolute-paths'.
|
||||
# 'absolute-paths'.
|
||||
##
|
||||
{ 'type': 'BlockdevSnapshot',
|
||||
'data': { 'device': 'str', 'snapshot-file': 'str', '*format': 'str',
|
||||
@ -1428,7 +1453,7 @@
|
||||
# @format: #optional the format of the snapshot image, default is 'qcow2'.
|
||||
#
|
||||
# @mode: #optional whether and how QEMU should create a new image, default is
|
||||
# 'absolute-paths'.
|
||||
# 'absolute-paths'.
|
||||
#
|
||||
# Returns: nothing on success
|
||||
# If @device is not a valid block device, DeviceNotFound
|
||||
@ -1982,26 +2007,33 @@
|
||||
# supported on i386 and x86_64.
|
||||
#
|
||||
# @paging: if true, do paging to get guest's memory mapping. This allows
|
||||
# using gdb to process the core file. However, setting @paging to false
|
||||
# may be desirable because of two reasons:
|
||||
# using gdb to process the core file.
|
||||
#
|
||||
# 1. The guest may be in a catastrophic state or can have corrupted
|
||||
# memory, which cannot be trusted
|
||||
# 2. The guest can be in real-mode even if paging is enabled. For example,
|
||||
# the guest uses ACPI to sleep, and ACPI sleep state goes in real-mode
|
||||
# IMPORTANT: this option can make QEMU allocate several gigabytes
|
||||
# of RAM. This can happen for a large guest, or a
|
||||
# malicious guest pretending to be large.
|
||||
#
|
||||
# Also, paging=true has the following limitations:
|
||||
#
|
||||
# 1. The guest may be in a catastrophic state or can have corrupted
|
||||
# memory, which cannot be trusted
|
||||
# 2. The guest can be in real-mode even if paging is enabled. For
|
||||
# example, the guest uses ACPI to sleep, and ACPI sleep state
|
||||
# goes in real-mode
|
||||
#
|
||||
# @protocol: the filename or file descriptor of the vmcore. The supported
|
||||
# protocols are:
|
||||
# protocols are:
|
||||
#
|
||||
# 1. file: the protocol starts with "file:", and the following string is
|
||||
# the file's path.
|
||||
# 2. fd: the protocol starts with "fd:", and the following string is the
|
||||
# fd's name.
|
||||
# 1. file: the protocol starts with "file:", and the following
|
||||
# string is the file's path.
|
||||
# 2. fd: the protocol starts with "fd:", and the following string
|
||||
# is the fd's name.
|
||||
#
|
||||
# @begin: #optional if specified, the starting physical address.
|
||||
#
|
||||
# @length: #optional if specified, the memory size, in bytes. If you don't
|
||||
# want to dump all guest's memory, please specify the start @begin and @length
|
||||
# want to dump all guest's memory, please specify the start @begin
|
||||
# and @length
|
||||
#
|
||||
# Returns: nothing on success
|
||||
#
|
||||
@ -2010,6 +2042,7 @@
|
||||
{ 'command': 'dump-guest-memory',
|
||||
'data': { 'paging': 'bool', 'protocol': 'str', '*begin': 'int',
|
||||
'*length': 'int' } }
|
||||
|
||||
##
|
||||
# @netdev_add:
|
||||
#
|
||||
@ -2587,13 +2620,27 @@
|
||||
'props', 'undo', 'front', 'copy', 'open', 'paste', 'find', 'cut',
|
||||
'lf', 'help', 'meta_l', 'meta_r', 'compose' ] }
|
||||
|
||||
##
|
||||
# @KeyValue
|
||||
#
|
||||
# Represents a keyboard key.
|
||||
#
|
||||
# Since: 1.3.0
|
||||
##
|
||||
{ 'union': 'KeyValue',
|
||||
'data': {
|
||||
'number': 'int',
|
||||
'qcode': 'QKeyCode' } }
|
||||
|
||||
##
|
||||
# @send-key:
|
||||
#
|
||||
# Send keys to guest.
|
||||
#
|
||||
# @keys: key sequence. 'keys' is the name of the key. Use a JSON array to
|
||||
# press several keys simultaneously.
|
||||
# @keys: An array of @KeyValue elements. All @KeyValues in this array are
|
||||
# simultaneously sent to the guest. A @KeyValue.number value is sent
|
||||
# directly to the guest, while @KeyValue.qcode must be a valid
|
||||
# @QKeyCode value
|
||||
#
|
||||
# @hold-time: #optional time to delay key up events, milliseconds. Defaults
|
||||
# to 100
|
||||
@ -2605,7 +2652,7 @@
|
||||
#
|
||||
##
|
||||
{ 'command': 'send-key',
|
||||
'data': { 'keys': ['QKeyCode'], '*hold-time': 'int' } }
|
||||
'data': { 'keys': ['KeyValue'], '*hold-time': 'int' } }
|
||||
|
||||
##
|
||||
# @screendump:
|
||||
|
@ -1231,10 +1231,7 @@ EQMP
|
||||
{
|
||||
.name = "add_client",
|
||||
.args_type = "protocol:s,fdname:s,skipauth:b?,tls:b?",
|
||||
.params = "protocol fdname skipauth tls",
|
||||
.help = "add a graphics client",
|
||||
.user_print = monitor_user_noop,
|
||||
.mhandler.cmd_new = add_graphics_client,
|
||||
.mhandler.cmd_new = qmp_marshal_input_add_client,
|
||||
},
|
||||
|
||||
SQMP
|
||||
|
43
qmp.c
43
qmp.c
@ -479,3 +479,46 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
|
||||
return arch_query_cpu_definitions(errp);
|
||||
}
|
||||
|
||||
void qmp_add_client(const char *protocol, const char *fdname,
|
||||
bool has_skipauth, bool skipauth, bool has_tls, bool tls,
|
||||
Error **errp)
|
||||
{
|
||||
CharDriverState *s;
|
||||
int fd;
|
||||
|
||||
fd = monitor_get_fd(cur_mon, fdname, errp);
|
||||
if (fd < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(protocol, "spice") == 0) {
|
||||
if (!using_spice) {
|
||||
error_set(errp, QERR_DEVICE_NOT_ACTIVE, "spice");
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
skipauth = has_skipauth ? skipauth : false;
|
||||
tls = has_tls ? tls : false;
|
||||
if (qemu_spice_display_add_client(fd, skipauth, tls) < 0) {
|
||||
error_setg(errp, "spice failed to add client");
|
||||
close(fd);
|
||||
}
|
||||
return;
|
||||
#ifdef CONFIG_VNC
|
||||
} else if (strcmp(protocol, "vnc") == 0) {
|
||||
skipauth = has_skipauth ? skipauth : false;
|
||||
vnc_display_add_client(NULL, fd, skipauth);
|
||||
return;
|
||||
#endif
|
||||
} else if ((s = qemu_chr_find(protocol)) != NULL) {
|
||||
if (qemu_chr_add_client(s, fd) < 0) {
|
||||
error_setg(errp, "failed to add client");
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
error_setg(errp, "protocol '%s' is invalid", protocol);
|
||||
close(fd);
|
||||
}
|
||||
|
@ -91,9 +91,9 @@ const char *%(name)s_lookup[] = {
|
||||
|
||||
def generate_enum_name(name):
|
||||
if name.isupper():
|
||||
return c_fun(name)
|
||||
return c_fun(name, False)
|
||||
new_name = ''
|
||||
for c in c_fun(name):
|
||||
for c in c_fun(name, False):
|
||||
if c.isupper():
|
||||
new_name += '_'
|
||||
new_name += c
|
||||
|
@ -173,7 +173,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **
|
||||
break;
|
||||
''',
|
||||
abbrev = de_camel_case(name).upper(),
|
||||
enum = c_fun(de_camel_case(key)).upper(),
|
||||
enum = c_fun(de_camel_case(key),False).upper(),
|
||||
c_type=members[key],
|
||||
c_name=c_fun(key))
|
||||
|
||||
|
@ -141,7 +141,7 @@ def camel_case(name):
|
||||
new_name += ch.lower()
|
||||
return new_name
|
||||
|
||||
def c_var(name):
|
||||
def c_var(name, protect=True):
|
||||
# ANSI X3J11/88-090, 3.1.1
|
||||
c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
|
||||
'default', 'do', 'double', 'else', 'enum', 'extern', 'float',
|
||||
@ -156,12 +156,14 @@ def c_var(name):
|
||||
# GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
|
||||
# excluding _.*
|
||||
gcc_words = set(['asm', 'typeof'])
|
||||
if name in c89_words | c99_words | c11_words | gcc_words:
|
||||
# namespace pollution:
|
||||
polluted_words = set(['unix'])
|
||||
if protect and (name in c89_words | c99_words | c11_words | gcc_words | polluted_words):
|
||||
return "q_" + name
|
||||
return name.replace('-', '_').lstrip("*")
|
||||
|
||||
def c_fun(name):
|
||||
return c_var(name).replace('.', '_')
|
||||
def c_fun(name, protect=True):
|
||||
return c_var(name, protect).replace('.', '_')
|
||||
|
||||
def c_list_type(name):
|
||||
return '%sList' % name
|
||||
|
Loading…
Reference in New Issue
Block a user