qemu/monitor.c

4741 lines
129 KiB
C
Raw Normal View History

/*
* QEMU monitor
*
* Copyright (c) 2003-2004 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include <dirent.h>
#include "cpu.h"
#include "hw/hw.h"
#include "monitor/qdev.h"
#include "hw/usb.h"
#include "hw/pci/pci.h"
#include "sysemu/watchdog.h"
#include "hw/loader.h"
#include "exec/gdbstub.h"
#include "net/net.h"
#include "net/slirp.h"
#include "chardev/char-fe.h"
#include "chardev/char-io.h"
#include "chardev/char-mux.h"
#include "ui/qemu-spice.h"
#include "sysemu/numa.h"
#include "monitor/monitor.h"
#include "qemu/config-file.h"
#include "qemu/readline.h"
#include "ui/console.h"
#include "ui/input.h"
#include "sysemu/blockdev.h"
#include "sysemu/block-backend.h"
#include "audio/audio.h"
#include "disas/disas.h"
#include "sysemu/balloon.h"
#include "qemu/timer.h"
#include "sysemu/hw_accel.h"
#include "qemu/acl.h"
#include "sysemu/tpm.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qerror.h"
#include "qapi/qmp/qnum.h"
#include "qapi/qmp/qstring.h"
#include "qapi/qmp/qjson.h"
#include "qapi/qmp/json-streamer.h"
#include "qapi/qmp/json-parser.h"
#include "qapi/qmp/qlist.h"
#include "qom/object_interfaces.h"
#include "trace-root.h"
#include "trace/control.h"
#include "monitor/hmp-target.h"
#ifdef CONFIG_TRACE_SIMPLE
#include "trace/simple.h"
#endif
#include "exec/memory.h"
#include "exec/exec-all.h"
#include "qemu/log.h"
#include "qemu/option.h"
#include "hmp.h"
#include "qemu/thread.h"
#include "block/qapi.h"
#include "qapi/qapi-commands.h"
#include "qapi/qapi-events.h"
#include "qapi/error.h"
#include "qapi/qmp-event.h"
#include "qapi/qapi-introspect.h"
#include "sysemu/qtest.h"
#include "sysemu/cpus.h"
#include "sysemu/iothread.h"
#include "qemu/cutils.h"
#if defined(TARGET_S390X)
#include "hw/s390x/storage-keys.h"
#include "hw/s390x/storage-attributes.h"
#endif
/*
* Supported types:
*
* 'F' filename
* 'B' block device name
* 's' string (accept optional quote)
* 'S' it just appends the rest of the string (accept optional quote)
* 'O' option string of the form NAME=VALUE,...
* parsed according to QemuOptsList given by its name
* Example: 'device:O' uses qemu_device_opts.
* Restriction: only lists with empty desc are supported
* TODO lift the restriction
* 'i' 32 bit integer
* 'l' target long (32 or 64 bit)
* 'M' Non-negative target long (32 or 64 bit), in user mode the
* value is multiplied by 2^20 (think Mebibyte)
* 'o' octets (aka bytes)
* user mode accepts an optional E, e, P, p, T, t, G, g, M, m,
* K, k suffix, which multiplies the value by 2^60 for suffixes E
* and e, 2^50 for suffixes P and p, 2^40 for suffixes T and t,
* 2^30 for suffixes G and g, 2^20 for M and m, 2^10 for K and k
* 'T' double
* user mode accepts an optional ms, us, ns suffix,
* which divides the value by 1e3, 1e6, 1e9, respectively
* '/' optional gdb-like print format (like "/10x")
*
* '?' optional type (for all types, except '/')
* '.' other form of optional type (for 'i' and 'l')
* 'b' boolean
* user mode accepts "on" or "off"
* '-' optional parameter (eg. '-f')
*
*/
typedef struct mon_cmd_t {
const char *name;
const char *args_type;
const char *params;
const char *help;
void (*cmd)(Monitor *mon, const QDict *qdict);
/* @sub_table is a list of 2nd level of commands. If it does not exist,
* cmd should be used. If it exists, sub_table[?].cmd should be
* used, and cmd of 1st level plays the role of help function.
*/
struct mon_cmd_t *sub_table;
void (*command_completion)(ReadLineState *rs, int nb_args, const char *str);
} mon_cmd_t;
/* file descriptors passed via SCM_RIGHTS */
typedef struct mon_fd_t mon_fd_t;
struct mon_fd_t {
char *name;
int fd;
QLIST_ENTRY(mon_fd_t) next;
};
/* file descriptor associated with a file descriptor set */
typedef struct MonFdsetFd MonFdsetFd;
struct MonFdsetFd {
int fd;
bool removed;
char *opaque;
QLIST_ENTRY(MonFdsetFd) next;
};
/* file descriptor set containing fds passed via SCM_RIGHTS */
typedef struct MonFdset MonFdset;
struct MonFdset {
int64_t id;
QLIST_HEAD(, MonFdsetFd) fds;
QLIST_HEAD(, MonFdsetFd) dup_fds;
QLIST_ENTRY(MonFdset) next;
};
typedef struct {
JSONMessageParser parser;
/*
* When a client connects, we're in capabilities negotiation mode.
* When command qmp_capabilities succeeds, we go into command
* mode.
*/
QmpCommandList *commands;
bool qmp_caps[QMP_CAPABILITY__MAX];
/*
* Protects qmp request/response queue. Please take monitor_lock
* first when used together.
*/
QemuMutex qmp_queue_lock;
/* Input queue that holds all the parsed QMP requests */
GQueue *qmp_requests;
/* Output queue contains all the QMP responses in order */
GQueue *qmp_responses;
} MonitorQMP;
/*
* To prevent flooding clients, events can be throttled. The
* throttling is calculated globally, rather than per-Monitor
* instance.
*/
typedef struct MonitorQAPIEventState {
QAPIEvent event; /* Throttling state for this event type and... */
QDict *data; /* ... data, see qapi_event_throttle_equal() */
QEMUTimer *timer; /* Timer for handling delayed events */
QDict *qdict; /* Delayed event (if any) */
} MonitorQAPIEventState;
typedef struct {
int64_t rate; /* Minimum time (in ns) between two events */
} MonitorQAPIEventConf;
struct Monitor {
CharBackend chr;
int reset_seen;
int flags;
int suspend_cnt; /* Needs to be accessed atomically */
bool skip_flush;
bool use_io_thr;
/* We can't access guest memory when holding the lock */
QemuMutex out_lock;
QString *outbuf;
guint out_watch;
/* Read under either BQL or out_lock, written with BQL+out_lock. */
int mux_out;
ReadLineState *rs;
MonitorQMP qmp;
gchar *mon_cpu_path;
BlockCompletionFunc *password_completion_cb;
void *password_opaque;
mon_cmd_t *cmd_table;
QLIST_HEAD(,mon_fd_t) fds;
QTAILQ_ENTRY(Monitor) entry;
};
/* Let's add monitor global variables to this struct. */
static struct {
IOThread *mon_iothread;
/* Bottom half to dispatch the requests received from IO thread */
QEMUBH *qmp_dispatcher_bh;
/* Bottom half to deliver the responses back to clients */
QEMUBH *qmp_respond_bh;
} mon_global;
struct QMPRequest {
/* Owner of the request */
Monitor *mon;
/* "id" field of the request */
QObject *id;
/* Request object to be handled */
QObject *req;
/*
* Whether we need to resume the monitor afterward. This flag is
* used to emulate the old QMP server behavior that the current
* command must be completed before execution of the next one.
*/
bool need_resume;
};
typedef struct QMPRequest QMPRequest;
/* QMP checker flags */
#define QMP_ACCEPT_UNKNOWNS 1
/* Protects mon_list, monitor_event_state. */
static QemuMutex monitor_lock;
static QTAILQ_HEAD(mon_list, Monitor) mon_list;
static QLIST_HEAD(mon_fdsets, MonFdset) mon_fdsets;
static int mon_refcount;
static mon_cmd_t mon_cmds[];
static mon_cmd_t info_cmds[];
QmpCommandList qmp_commands, qmp_cap_negotiation_commands;
monitor: Separate "default monitor" and "current monitor" cleanly Commits 376253ec..731b0364 introduced global variable cur_mon, which points to the "default monitor" (if any), except during execution of monitor_read() or monitor_control_read() it points to the monitor from which we're reading instead (the "current monitor"). Monitor command handlers run within monitor_read() or monitor_control_read(). Default monitor and current monitor are really separate things, and squashing them together is confusing and error-prone. For instance, usb_host_scan() can run both in "info usbhost" and periodically via usb_host_auto_check(). It prints to cur_mon, which is what we want in the former case: the monitor executing "info usbhost". But since that's the default monitor in the latter case, it periodically spams the default monitor there. A few places use cur_mon to log stuff to the default monitor. If we ever log something while cur_mon points to current monitor instead of default monitor, the log temporarily "jumps" to another monitor. Whether that can or cannot happen isn't always obvious. Maybe logging to the default monitor (which may not even exist) is a bad idea, and we should log to stderr or a logfile instead. But that's outside the scope of this commit. Change cur_mon to point to the current monitor. Create new default_mon to point to the default monitor. Update users of cur_mon accordingly. This fixes the periodical spamming of the default monitor by usb_host_scan(). It also stops "log jumping", should that problem exist.
2010-02-18 13:41:55 +03:00
Monitor *cur_mon;
static QEMUClockType event_clock_type = QEMU_CLOCK_REALTIME;
static void monitor_command_cb(void *opaque, const char *cmdline,
void *readline_opaque);
/**
* Is @mon a QMP monitor?
*/
static inline bool monitor_is_qmp(const Monitor *mon)
{
return (mon->flags & MONITOR_USE_CONTROL);
}
/**
* Whether @mon is using readline? Note: not all HMP monitors use
* readline, e.g., gdbserver has a non-interactive HMP monitor, so
* readline is not used there.
*/
static inline bool monitor_uses_readline(const Monitor *mon)
{
return mon->flags & MONITOR_USE_READLINE;
}
static inline bool monitor_is_hmp_non_interactive(const Monitor *mon)
{
return !monitor_is_qmp(mon) && !monitor_uses_readline(mon);
}
/**
* Is the current monitor, if any, a QMP monitor?
*/
bool monitor_cur_is_qmp(void)
2010-02-11 19:05:43 +03:00
{
return cur_mon && monitor_is_qmp(cur_mon);
2010-02-11 19:05:43 +03:00
}
void monitor_read_command(Monitor *mon, int show_prompt)
{
if (!mon->rs)
return;
readline_start(mon->rs, "(qemu) ", 0, monitor_command_cb, NULL);
if (show_prompt)
readline_show_prompt(mon->rs);
}
int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func,
void *opaque)
{
if (mon->rs) {
readline_start(mon->rs, "Password: ", 1, readline_func, opaque);
/* prompt is printed on return from the command handler */
return 0;
} else {
monitor_printf(mon, "terminal does not support password prompting\n");
return -ENOTTY;
}
}
static void qmp_request_free(QMPRequest *req)
{
qobject_decref(req->id);
qobject_decref(req->req);
g_free(req);
}
/* Must with the mon->qmp.qmp_queue_lock held */
static void monitor_qmp_cleanup_req_queue_locked(Monitor *mon)
{
while (!g_queue_is_empty(mon->qmp.qmp_requests)) {
qmp_request_free(g_queue_pop_head(mon->qmp.qmp_requests));
}
}
/* Must with the mon->qmp.qmp_queue_lock held */
static void monitor_qmp_cleanup_resp_queue_locked(Monitor *mon)
{
while (!g_queue_is_empty(mon->qmp.qmp_responses)) {
qobject_decref(g_queue_pop_head(mon->qmp.qmp_responses));
}
}
static void monitor_qmp_cleanup_queues(Monitor *mon)
{
qemu_mutex_lock(&mon->qmp.qmp_queue_lock);
monitor_qmp_cleanup_req_queue_locked(mon);
monitor_qmp_cleanup_resp_queue_locked(mon);
qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
}
static void monitor_flush_locked(Monitor *mon);
static gboolean monitor_unblocked(GIOChannel *chan, GIOCondition cond,
void *opaque)
{
Monitor *mon = opaque;
qemu_mutex_lock(&mon->out_lock);
mon->out_watch = 0;
monitor_flush_locked(mon);
qemu_mutex_unlock(&mon->out_lock);
return FALSE;
}
/* Called with mon->out_lock held. */
static void monitor_flush_locked(Monitor *mon)
{
int rc;
size_t len;
const char *buf;
if (mon->skip_flush) {
return;
}
buf = qstring_get_str(mon->outbuf);
len = qstring_get_length(mon->outbuf);
if (len && !mon->mux_out) {
rc = qemu_chr_fe_write(&mon->chr, (const uint8_t *) buf, len);
if ((rc < 0 && errno != EAGAIN) || (rc == len)) {
/* all flushed or error */
QDECREF(mon->outbuf);
mon->outbuf = qstring_new();
return;
}
if (rc > 0) {
/* partial write */
QString *tmp = qstring_from_str(buf + rc);
QDECREF(mon->outbuf);
mon->outbuf = tmp;
}
if (mon->out_watch == 0) {
mon->out_watch =
qemu_chr_fe_add_watch(&mon->chr, G_IO_OUT | G_IO_HUP,
monitor_unblocked, mon);
}
}
}
void monitor_flush(Monitor *mon)
{
qemu_mutex_lock(&mon->out_lock);
monitor_flush_locked(mon);
qemu_mutex_unlock(&mon->out_lock);
}
/* flush at every end of line */
static void monitor_puts(Monitor *mon, const char *str)
{
char c;
qemu_mutex_lock(&mon->out_lock);
for(;;) {
c = *str++;
if (c == '\0')
break;
if (c == '\n') {
qstring_append_chr(mon->outbuf, '\r');
}
qstring_append_chr(mon->outbuf, c);
if (c == '\n') {
monitor_flush_locked(mon);
}
}
qemu_mutex_unlock(&mon->out_lock);
}
void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
{
char *buf;
if (!mon)
return;
if (monitor_is_qmp(mon)) {
return;
}
buf = g_strdup_vprintf(fmt, ap);
monitor_puts(mon, buf);
g_free(buf);
}
void monitor_printf(Monitor *mon, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
monitor_vprintf(mon, fmt, ap);
va_end(ap);
}
int monitor_fprintf(FILE *stream, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
monitor_vprintf((Monitor *)stream, fmt, ap);
va_end(ap);
return 0;
}
static void monitor_json_emitter_raw(Monitor *mon,
QObject *data)
{
QString *json;
json = mon->flags & MONITOR_USE_PRETTY ? qobject_to_json_pretty(data) :
qobject_to_json(data);
assert(json != NULL);
qstring_append_chr(json, '\n');
monitor_puts(mon, qstring_get_str(json));
QDECREF(json);
}
static void monitor_json_emitter(Monitor *mon, QObject *data)
{
if (mon->use_io_thr) {
/*
* If using IO thread, we need to queue the item so that IO
* thread will do the rest for us. Take refcount so that
* caller won't free the data (which will be finally freed in
* responder thread).
*/
qobject_incref(data);
qemu_mutex_lock(&mon->qmp.qmp_queue_lock);
g_queue_push_tail(mon->qmp.qmp_responses, (void *)data);
qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
qemu_bh_schedule(mon_global.qmp_respond_bh);
} else {
/*
* If not using monitor IO thread, then we are in main thread.
* Do the emission right away.
*/
monitor_json_emitter_raw(mon, data);
}
}
struct QMPResponse {
Monitor *mon;
QObject *data;
};
typedef struct QMPResponse QMPResponse;
/*
* Return one QMPResponse. The response is only valid if
* response.data is not NULL.
*/
static QMPResponse monitor_qmp_response_pop_one(void)
{
Monitor *mon;
QObject *data = NULL;
qemu_mutex_lock(&monitor_lock);
QTAILQ_FOREACH(mon, &mon_list, entry) {
qemu_mutex_lock(&mon->qmp.qmp_queue_lock);
data = g_queue_pop_head(mon->qmp.qmp_responses);
qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
if (data) {
break;
}
}
qemu_mutex_unlock(&monitor_lock);
return (QMPResponse) { .mon = mon, .data = data };
}
static void monitor_qmp_bh_responder(void *opaque)
{
QMPResponse response;
while (true) {
response = monitor_qmp_response_pop_one();
if (!response.data) {
break;
}
monitor_json_emitter_raw(response.mon, response.data);
qobject_decref(response.data);
}
}
static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = {
/* Limit guest-triggerable events to 1 per second */
[QAPI_EVENT_RTC_CHANGE] = { 1000 * SCALE_MS },
[QAPI_EVENT_WATCHDOG] = { 1000 * SCALE_MS },
[QAPI_EVENT_BALLOON_CHANGE] = { 1000 * SCALE_MS },
[QAPI_EVENT_QUORUM_REPORT_BAD] = { 1000 * SCALE_MS },
[QAPI_EVENT_QUORUM_FAILURE] = { 1000 * SCALE_MS },
[QAPI_EVENT_VSERPORT_CHANGE] = { 1000 * SCALE_MS },
};
GHashTable *monitor_qapi_event_state;
/*
* Emits the event to every monitor instance, @event is only used for trace
* Called with monitor_lock held.
*/
static void monitor_qapi_event_emit(QAPIEvent event, QDict *qdict)
{
Monitor *mon;
trace_monitor_protocol_event_emit(event, qdict);
QTAILQ_FOREACH(mon, &mon_list, entry) {
if (monitor_is_qmp(mon)
&& mon->qmp.commands != &qmp_cap_negotiation_commands) {
monitor_json_emitter(mon, QOBJECT(qdict));
}
}
}
static void monitor_qapi_event_handler(void *opaque);
/*
* Queue a new event for emission to Monitor instances,
* applying any rate limiting if required.
*/
static void
monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp)
{
MonitorQAPIEventConf *evconf;
MonitorQAPIEventState *evstate;
assert(event < QAPI_EVENT__MAX);
evconf = &monitor_qapi_event_conf[event];
trace_monitor_protocol_event_queue(event, qdict, evconf->rate);
qemu_mutex_lock(&monitor_lock);
if (!evconf->rate) {
/* Unthrottled event */
monitor_qapi_event_emit(event, qdict);
} else {
QDict *data = qobject_to(QDict, qdict_get(qdict, "data"));
MonitorQAPIEventState key = { .event = event, .data = data };
evstate = g_hash_table_lookup(monitor_qapi_event_state, &key);
assert(!evstate || timer_pending(evstate->timer));
if (evstate) {
/*
* Timer is pending for (at least) evconf->rate ns after
* last send. Store event for sending when timer fires,
* replacing a prior stored event if any.
*/
QDECREF(evstate->qdict);
evstate->qdict = qdict;
QINCREF(evstate->qdict);
} else {
/*
* Last send was (at least) evconf->rate ns ago.
* Send immediately, and arm the timer to call
* monitor_qapi_event_handler() in evconf->rate ns. Any
* events arriving before then will be delayed until then.
*/
int64_t now = qemu_clock_get_ns(event_clock_type);
monitor_qapi_event_emit(event, qdict);
evstate = g_new(MonitorQAPIEventState, 1);
evstate->event = event;
evstate->data = data;
QINCREF(evstate->data);
evstate->qdict = NULL;
evstate->timer = timer_new_ns(event_clock_type,
monitor_qapi_event_handler,
evstate);
g_hash_table_add(monitor_qapi_event_state, evstate);
timer_mod_ns(evstate->timer, now + evconf->rate);
}
}
qemu_mutex_unlock(&monitor_lock);
}
/*
* This function runs evconf->rate ns after sending a throttled
* event.
* If another event has since been stored, send it.
*/
static void monitor_qapi_event_handler(void *opaque)
{
MonitorQAPIEventState *evstate = opaque;
MonitorQAPIEventConf *evconf = &monitor_qapi_event_conf[evstate->event];
trace_monitor_protocol_event_handler(evstate->event, evstate->qdict);
qemu_mutex_lock(&monitor_lock);
if (evstate->qdict) {
int64_t now = qemu_clock_get_ns(event_clock_type);
monitor_qapi_event_emit(evstate->event, evstate->qdict);
QDECREF(evstate->qdict);
evstate->qdict = NULL;
timer_mod_ns(evstate->timer, now + evconf->rate);
} else {
g_hash_table_remove(monitor_qapi_event_state, evstate);
QDECREF(evstate->data);
timer_free(evstate->timer);
g_free(evstate);
}
qemu_mutex_unlock(&monitor_lock);
}
static unsigned int qapi_event_throttle_hash(const void *key)
{
const MonitorQAPIEventState *evstate = key;
unsigned int hash = evstate->event * 255;
if (evstate->event == QAPI_EVENT_VSERPORT_CHANGE) {
hash += g_str_hash(qdict_get_str(evstate->data, "id"));
}
if (evstate->event == QAPI_EVENT_QUORUM_REPORT_BAD) {
hash += g_str_hash(qdict_get_str(evstate->data, "node-name"));
}
return hash;
}
static gboolean qapi_event_throttle_equal(const void *a, const void *b)
{
const MonitorQAPIEventState *eva = a;
const MonitorQAPIEventState *evb = b;
if (eva->event != evb->event) {
return FALSE;
}
if (eva->event == QAPI_EVENT_VSERPORT_CHANGE) {
return !strcmp(qdict_get_str(eva->data, "id"),
qdict_get_str(evb->data, "id"));
}
if (eva->event == QAPI_EVENT_QUORUM_REPORT_BAD) {
return !strcmp(qdict_get_str(eva->data, "node-name"),
qdict_get_str(evb->data, "node-name"));
}
return TRUE;
}
static void monitor_qapi_event_init(void)
{
if (qtest_enabled()) {
event_clock_type = QEMU_CLOCK_VIRTUAL;
}
monitor_qapi_event_state = g_hash_table_new(qapi_event_throttle_hash,
qapi_event_throttle_equal);
qmp_event_set_func_emit(monitor_qapi_event_queue);
}
static void handle_hmp_command(Monitor *mon, const char *cmdline);
static void monitor_data_init(Monitor *mon, bool skip_flush,
bool use_io_thr)
{
memset(mon, 0, sizeof(Monitor));
qemu_mutex_init(&mon->out_lock);
qemu_mutex_init(&mon->qmp.qmp_queue_lock);
mon->outbuf = qstring_new();
/* Use *mon_cmds by default. */
mon->cmd_table = mon_cmds;
mon->skip_flush = skip_flush;
mon->use_io_thr = use_io_thr;
mon->qmp.qmp_requests = g_queue_new();
mon->qmp.qmp_responses = g_queue_new();
}
static void monitor_data_destroy(Monitor *mon)
{
g_free(mon->mon_cpu_path);
qemu_chr_fe_deinit(&mon->chr, false);
monitor: fix crash when leaving qemu with spice audio Since aa5cb7f5e, the chardevs are being cleaned up when leaving qemu. However, the monitor has still references to them, which may lead to crashes when running atexit() and trying to send monitor events: #0 0x00007fffdb18f6f5 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54 #1 0x00007fffdb1912fa in __GI_abort () at abort.c:89 #2 0x0000555555c263e7 in error_exit (err=22, msg=0x555555d47980 <__func__.13537> "qemu_mutex_lock") at util/qemu-thread-posix.c:39 #3 0x0000555555c26488 in qemu_mutex_lock (mutex=0x5555567a2420) at util/qemu-thread-posix.c:66 #4 0x00005555558c52db in qemu_chr_fe_write (s=0x5555567a2420, buf=0x55555740dc40 "{\"timestamp\": {\"seconds\": 1470041716, \"microseconds\": 989699}, \"event\": \"SPICE_DISCONNECTED\", \"data\": {\"server\": {\"port\": \"5900\", \"family\": \"ipv4\", \"host\": \"127.0.0.1\"}, \"client\": {\"port\": \"40272\", \"f"..., len=240) at qemu-char.c:280 #5 0x0000555555787cad in monitor_flush_locked (mon=0x5555567bd9e0) at /home/elmarco/src/qemu/monitor.c:311 #6 0x0000555555787e46 in monitor_puts (mon=0x5555567bd9e0, str=0x5555567a44ef "") at /home/elmarco/src/qemu/monitor.c:353 #7 0x00005555557880fe in monitor_json_emitter (mon=0x5555567bd9e0, data=0x5555567c73a0) at /home/elmarco/src/qemu/monitor.c:401 #8 0x00005555557882d2 in monitor_qapi_event_emit (event=QAPI_EVENT_SPICE_DISCONNECTED, qdict=0x5555567c73a0) at /home/elmarco/src/qemu/monitor.c:472 #9 0x000055555578838f in monitor_qapi_event_queue (event=QAPI_EVENT_SPICE_DISCONNECTED, qdict=0x5555567c73a0, errp=0x7fffffffca88) at /home/elmarco/src/qemu/monitor.c:497 #10 0x0000555555c15541 in qapi_event_send_spice_disconnected (server=0x5555571139d0, client=0x5555570d0db0, errp=0x5555566c0428 <error_abort>) at qapi-event.c:1038 #11 0x0000555555b11bc6 in channel_event (event=3, info=0x5555570d6c00) at ui/spice-core.c:248 #12 0x00007fffdcc9983a in adapter_channel_event (event=3, info=0x5555570d6c00) at reds.c:120 #13 0x00007fffdcc99a25 in reds_handle_channel_event (reds=0x5555567a9d60, event=3, info=0x5555570d6c00) at reds.c:324 #14 0x00007fffdcc7d4c4 in main_dispatcher_self_handle_channel_event (self=0x5555567b28b0, event=3, info=0x5555570d6c00) at main-dispatcher.c:175 #15 0x00007fffdcc7d5b1 in main_dispatcher_channel_event (self=0x5555567b28b0, event=3, info=0x5555570d6c00) at main-dispatcher.c:194 #16 0x00007fffdcca7674 in reds_stream_push_channel_event (s=0x5555570d9910, event=3) at reds-stream.c:354 #17 0x00007fffdcca749b in reds_stream_free (s=0x5555570d9910) at reds-stream.c:323 #18 0x00007fffdccb5dad in snd_disconnect_channel (channel=0x5555576a89a0) at sound.c:229 #19 0x00007fffdccb9e57 in snd_detach_common (worker=0x555557739720) at sound.c:1589 #20 0x00007fffdccb9f0e in snd_detach_playback (sin=0x5555569fe3f8) at sound.c:1602 #21 0x00007fffdcca3373 in spice_server_remove_interface (sin=0x5555569fe3f8) at reds.c:3387 #22 0x00005555558ff6e2 in line_out_fini (hw=0x5555569fe370) at audio/spiceaudio.c:152 #23 0x00005555558f909e in audio_atexit () at audio/audio.c:1754 #24 0x00007fffdb1941e8 in __run_exit_handlers (status=0, listp=0x7fffdb5175d8 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true) at exit.c:82 #25 0x00007fffdb194235 in __GI_exit (status=<optimized out>) at exit.c:104 #26 0x00007fffdb17b738 in __libc_start_main (main=0x5555558d7874 <main>, argc=67, argv=0x7fffffffcf48, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffcf38) at ../csu/libc-start.c:323 Add a monitor_cleanup() functions to remove all the monitors before cleaning up the chardev. Note that we are "losing" some events that used to be sent during atexit(). Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-Id: <20160801112343.29082-2-marcandre.lureau@redhat.com> Reviewed-by: Paolo Bonzini <pbonzini@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-08-01 14:23:42 +03:00
if (monitor_is_qmp(mon)) {
json_message_parser_destroy(&mon->qmp.parser);
}
readline: add a free function Fixes leaks such as: Direct leak of 2 byte(s) in 1 object(s) allocated from: #0 0x7eff58beb850 in malloc (/lib64/libasan.so.4+0xde850) #1 0x7eff57942f0c in g_malloc ../glib/gmem.c:94 #2 0x7eff579431cf in g_malloc_n ../glib/gmem.c:331 #3 0x7eff5795f6eb in g_strdup ../glib/gstrfuncs.c:363 #4 0x55db720f1d46 in readline_hist_add /home/elmarco/src/qq/util/readline.c:258 #5 0x55db720f2d34 in readline_handle_byte /home/elmarco/src/qq/util/readline.c:387 #6 0x55db71539d00 in monitor_read /home/elmarco/src/qq/monitor.c:3896 #7 0x55db71f9be35 in qemu_chr_be_write_impl /home/elmarco/src/qq/chardev/char.c:167 #8 0x55db71f9bed3 in qemu_chr_be_write /home/elmarco/src/qq/chardev/char.c:179 #9 0x55db71fa013c in fd_chr_read /home/elmarco/src/qq/chardev/char-fd.c:66 #10 0x55db71fe18a8 in qio_channel_fd_source_dispatch /home/elmarco/src/qq/io/channel-watch.c:84 #11 0x7eff5793a90b in g_main_dispatch ../glib/gmain.c:3182 #12 0x7eff5793b7ac in g_main_context_dispatch ../glib/gmain.c:3847 #13 0x55db720af3bd in glib_pollfds_poll /home/elmarco/src/qq/util/main-loop.c:214 #14 0x55db720af505 in os_host_main_loop_wait /home/elmarco/src/qq/util/main-loop.c:261 #15 0x55db720af6d6 in main_loop_wait /home/elmarco/src/qq/util/main-loop.c:515 #16 0x55db7184e0de in main_loop /home/elmarco/src/qq/vl.c:1995 #17 0x55db7185e956 in main /home/elmarco/src/qq/vl.c:4914 #18 0x7eff4ea17039 in __libc_start_main (/lib64/libc.so.6+0x21039) (while at it, use g_new0(ReadLineState), it's a bit easier to read) Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Message-Id: <20180104160523.22995-11-marcandre.lureau@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2018-01-04 19:05:15 +03:00
readline_free(mon->rs);
QDECREF(mon->outbuf);
qemu_mutex_destroy(&mon->out_lock);
qemu_mutex_destroy(&mon->qmp.qmp_queue_lock);
monitor_qmp_cleanup_req_queue_locked(mon);
monitor_qmp_cleanup_resp_queue_locked(mon);
g_queue_free(mon->qmp.qmp_requests);
g_queue_free(mon->qmp.qmp_responses);
}
char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index,
int64_t cpu_index, Error **errp)
{
char *output = NULL;
Monitor *old_mon, hmp;
monitor_data_init(&hmp, true, false);
old_mon = cur_mon;
cur_mon = &hmp;
if (has_cpu_index) {
int ret = monitor_set_cpu(cpu_index);
if (ret < 0) {
cur_mon = old_mon;
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index",
"a CPU number");
goto out;
}
}
handle_hmp_command(&hmp, command_line);
cur_mon = old_mon;
qemu_mutex_lock(&hmp.out_lock);
if (qstring_get_length(hmp.outbuf) > 0) {
output = g_strdup(qstring_get_str(hmp.outbuf));
} else {
output = g_strdup("");
}
qemu_mutex_unlock(&hmp.out_lock);
out:
monitor_data_destroy(&hmp);
return output;
}
static int compare_cmd(const char *name, const char *list)
{
const char *p, *pstart;
int len;
len = strlen(name);
p = list;
for(;;) {
pstart = p;
p = strchr(p, '|');
if (!p)
p = pstart + strlen(pstart);
if ((p - pstart) == len && !memcmp(pstart, name, len))
return 1;
if (*p == '\0')
break;
p++;
}
return 0;
}
static int get_str(char *buf, int buf_size, const char **pp)
{
const char *p;
char *q;
int c;
q = buf;
p = *pp;
while (qemu_isspace(*p)) {
p++;
}
if (*p == '\0') {
fail:
*q = '\0';
*pp = p;
return -1;
}
if (*p == '\"') {
p++;
while (*p != '\0' && *p != '\"') {
if (*p == '\\') {
p++;
c = *p++;
switch (c) {
case 'n':
c = '\n';
break;
case 'r':
c = '\r';
break;
case '\\':
case '\'':
case '\"':
break;
default:
printf("unsupported escape code: '\\%c'\n", c);
goto fail;
}
if ((q - buf) < buf_size - 1) {
*q++ = c;
}
} else {
if ((q - buf) < buf_size - 1) {
*q++ = *p;
}
p++;
}
}
if (*p != '\"') {
printf("unterminated string\n");
goto fail;
}
p++;
} else {
while (*p != '\0' && !qemu_isspace(*p)) {
if ((q - buf) < buf_size - 1) {
*q++ = *p;
}
p++;
}
}
*q = '\0';
*pp = p;
return 0;
}
#define MAX_ARGS 16
static void free_cmdline_args(char **args, int nb_args)
{
int i;
assert(nb_args <= MAX_ARGS);
for (i = 0; i < nb_args; i++) {
g_free(args[i]);
}
}
/*
* Parse the command line to get valid args.
* @cmdline: command line to be parsed.
* @pnb_args: location to store the number of args, must NOT be NULL.
* @args: location to store the args, which should be freed by caller, must
* NOT be NULL.
*
* Returns 0 on success, negative on failure.
*
* NOTE: this parser is an approximate form of the real command parser. Number
* of args have a limit of MAX_ARGS. If cmdline contains more, it will
* return with failure.
*/
static int parse_cmdline(const char *cmdline,
int *pnb_args, char **args)
{
const char *p;
int nb_args, ret;
char buf[1024];
p = cmdline;
nb_args = 0;
for (;;) {
while (qemu_isspace(*p)) {
p++;
}
if (*p == '\0') {
break;
}
if (nb_args >= MAX_ARGS) {
goto fail;
}
ret = get_str(buf, sizeof(buf), &p);
if (ret < 0) {
goto fail;
}
args[nb_args] = g_strdup(buf);
nb_args++;
}
*pnb_args = nb_args;
return 0;
fail:
free_cmdline_args(args, nb_args);
return -1;
}
static void help_cmd_dump_one(Monitor *mon,
const mon_cmd_t *cmd,
char **prefix_args,
int prefix_args_nb)
{
int i;
for (i = 0; i < prefix_args_nb; i++) {
monitor_printf(mon, "%s ", prefix_args[i]);
}
monitor_printf(mon, "%s %s -- %s\n", cmd->name, cmd->params, cmd->help);
}
/* @args[@arg_index] is the valid command need to find in @cmds */
static void help_cmd_dump(Monitor *mon, const mon_cmd_t *cmds,
char **args, int nb_args, int arg_index)
{
const mon_cmd_t *cmd;
/* No valid arg need to compare with, dump all in *cmds */
if (arg_index >= nb_args) {
for (cmd = cmds; cmd->name != NULL; cmd++) {
help_cmd_dump_one(mon, cmd, args, arg_index);
}
return;
}
/* Find one entry to dump */
for (cmd = cmds; cmd->name != NULL; cmd++) {
if (compare_cmd(args[arg_index], cmd->name)) {
if (cmd->sub_table) {
/* continue with next arg */
help_cmd_dump(mon, cmd->sub_table,
args, nb_args, arg_index + 1);
} else {
help_cmd_dump_one(mon, cmd, args, arg_index);
}
break;
}
}
}
static void help_cmd(Monitor *mon, const char *name)
{
char *args[MAX_ARGS];
int nb_args = 0;
/* 1. parse user input */
if (name) {
/* special case for log, directly dump and return */
if (!strcmp(name, "log")) {
const QEMULogItem *item;
monitor_printf(mon, "Log items (comma separated):\n");
monitor_printf(mon, "%-10s %s\n", "none", "remove all logs");
for (item = qemu_log_items; item->mask != 0; item++) {
monitor_printf(mon, "%-10s %s\n", item->name, item->help);
}
return;
}
if (parse_cmdline(name, &nb_args, args) < 0) {
return;
}
}
/* 2. dump the contents according to parsed args */
help_cmd_dump(mon, mon->cmd_table, args, nb_args, 0);
free_cmdline_args(args, nb_args);
}
static void do_help_cmd(Monitor *mon, const QDict *qdict)
{
help_cmd(mon, qdict_get_try_str(qdict, "name"));
}
static void hmp_trace_event(Monitor *mon, const QDict *qdict)
{
const char *tp_name = qdict_get_str(qdict, "name");
bool new_state = qdict_get_bool(qdict, "option");
bool has_vcpu = qdict_haskey(qdict, "vcpu");
int vcpu = qdict_get_try_int(qdict, "vcpu", 0);
Error *local_err = NULL;
if (vcpu < 0) {
monitor_printf(mon, "argument vcpu must be positive");
return;
}
qmp_trace_event_set_state(tp_name, new_state, true, true, has_vcpu, vcpu, &local_err);
if (local_err) {
error_report_err(local_err);
}
}
trace: Add trace-file command to open/close/flush trace file This patch adds the trace-file command: trace-file [on|off|flush] Open, close, or flush the trace file. If no argument is given, the status of the trace file is displayed. The trace file is turned on by default but is only written out when the trace buffer becomes full. The flush operation can be used to force write out at any time. Turning off the trace file does not change the state of trace events; tracing will continue to the trace buffer. When the trace file is off, use "info trace" to display the contents of the trace buffer in memory. Signed-off-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> This commit also contains the trace-file sub-command from the following commit: commit 5ce8d1a957afae2c52ad748944ce72848ccf57bd Author: Prerna Saxena <prerna@linux.vnet.ibm.com> Date: Wed Aug 4 16:23:54 2010 +0530 trace: Add options to specify trace file name at startup and runtime This patch adds an optional command line switch '-trace' to specify the filename to write traces to, when qemu starts. Eg, If compiled with the 'simple' trace backend, [temp@system]$ qemu -trace FILENAME IMAGE Allows the binary traces to be written to FILENAME instead of the option set at config-time. Also, this adds monitor sub-command 'set' to trace-file commands to dynamically change trace log file at runtime. Eg, (qemu)trace-file set FILENAME This allows one to set trace outputs to FILENAME from the default specified at startup. Signed-off-by: Prerna Saxena <prerna@linux.vnet.ibm.com> Signed-off-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
2010-07-13 12:26:33 +04:00
#ifdef CONFIG_TRACE_SIMPLE
static void hmp_trace_file(Monitor *mon, const QDict *qdict)
trace: Add trace-file command to open/close/flush trace file This patch adds the trace-file command: trace-file [on|off|flush] Open, close, or flush the trace file. If no argument is given, the status of the trace file is displayed. The trace file is turned on by default but is only written out when the trace buffer becomes full. The flush operation can be used to force write out at any time. Turning off the trace file does not change the state of trace events; tracing will continue to the trace buffer. When the trace file is off, use "info trace" to display the contents of the trace buffer in memory. Signed-off-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> This commit also contains the trace-file sub-command from the following commit: commit 5ce8d1a957afae2c52ad748944ce72848ccf57bd Author: Prerna Saxena <prerna@linux.vnet.ibm.com> Date: Wed Aug 4 16:23:54 2010 +0530 trace: Add options to specify trace file name at startup and runtime This patch adds an optional command line switch '-trace' to specify the filename to write traces to, when qemu starts. Eg, If compiled with the 'simple' trace backend, [temp@system]$ qemu -trace FILENAME IMAGE Allows the binary traces to be written to FILENAME instead of the option set at config-time. Also, this adds monitor sub-command 'set' to trace-file commands to dynamically change trace log file at runtime. Eg, (qemu)trace-file set FILENAME This allows one to set trace outputs to FILENAME from the default specified at startup. Signed-off-by: Prerna Saxena <prerna@linux.vnet.ibm.com> Signed-off-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
2010-07-13 12:26:33 +04:00
{
const char *op = qdict_get_try_str(qdict, "op");
const char *arg = qdict_get_try_str(qdict, "arg");
if (!op) {
st_print_trace_file_status((FILE *)mon, &monitor_fprintf);
} else if (!strcmp(op, "on")) {
st_set_trace_file_enabled(true);
} else if (!strcmp(op, "off")) {
st_set_trace_file_enabled(false);
} else if (!strcmp(op, "flush")) {
st_flush_trace_buffer();
} else if (!strcmp(op, "set")) {
if (arg) {
st_set_trace_file(arg);
}
} else {
monitor_printf(mon, "unexpected argument \"%s\"\n", op);
help_cmd(mon, "trace-file");
}
}
#endif
static void hmp_info_help(Monitor *mon, const QDict *qdict)
{
help_cmd(mon, "info");
}
static void query_commands_cb(QmpCommand *cmd, void *opaque)
{
CommandInfoList *info, **list = opaque;
if (!cmd->enabled) {
return;
}
info = g_malloc0(sizeof(*info));
info->value = g_malloc0(sizeof(*info->value));
info->value->name = g_strdup(cmd->name);
info->next = *list;
*list = info;
}
CommandInfoList *qmp_query_commands(Error **errp)
{
CommandInfoList *list = NULL;
qmp_for_each_command(cur_mon->qmp.commands, query_commands_cb, &list);
return list;
}
EventInfoList *qmp_query_events(Error **errp)
{
EventInfoList *info, *ev_list = NULL;
QAPIEvent e;
for (e = 0 ; e < QAPI_EVENT__MAX ; e++) {
const char *event_name = QAPIEvent_str(e);
assert(event_name != NULL);
info = g_malloc0(sizeof(*info));
info->value = g_malloc0(sizeof(*info->value));
info->value->name = g_strdup(event_name);
info->next = ev_list;
ev_list = info;
}
return ev_list;
}
qapi: New QMP command query-qmp-schema for QMP introspection qapi/introspect.json defines the introspection schema. It's designed for QMP introspection, but should do for similar uses, such as QGA. The introspection schema does not reflect all the rules and restrictions that apply to QAPI schemata. A valid QAPI schema has an introspection value conforming to the introspection schema, but the converse is not true. Introspection lowers away a number of schema details, and makes implicit things explicit: * The built-in types are declared with their JSON type. All integer types are mapped to 'int', because how many bits we use internally is an implementation detail. It could be pressed into external interface service as very approximate range information, but that's a bad idea. If we need range information, we better do it properly. * Implicit type definitions are made explicit, and given auto-generated names: - Array types, named by appending "List" to the name of their element type, like in generated C. - The enumeration types implicitly defined by simple union types, named by appending "Kind" to the name of their simple union type, like in generated C. - Types that don't occur in generated C. Their names start with ':' so they don't clash with the user's names. * All type references are by name. * The struct and union types are generalized into an object type. * Base types are flattened. * Commands take a single argument and return a single result. Dictionary argument or list result is an implicit type definition. The empty object type is used when a command takes no arguments or produces no results. The argument is always of object type, but the introspection schema doesn't reflect that. The 'gen': false directive is omitted as implementation detail. The 'success-response' directive is omitted as well for now, even though it's not an implementation detail, because it's not used by QMP. * Events carry a single data value. Implicit type definition and empty object type use, just like for commands. The value is of object type, but the introspection schema doesn't reflect that. * Types not used by commands or events are omitted. Indirect use counts as use. * Optional members have a default, which can only be null right now Instead of a mandatory "optional" flag, we have an optional default. No default means mandatory, default null means optional without default value. Non-null is available for optional with default (possible future extension). * Clients should *not* look up types by name, because type names are not ABI. Look up the command or event you're interested in, then follow the references. TODO Should we hide the type names to eliminate the temptation? New generator scripts/qapi-introspect.py computes an introspection value for its input, and generates a C variable holding it. It can generate awfully long lines. Marked TODO. A new test-qmp-input-visitor test case feeds its result for both tests/qapi-schema/qapi-schema-test.json and qapi-schema.json to a QmpInputVisitor to verify it actually conforms to the schema. New QMP command query-qmp-schema takes its return value from that variable. Its reply is some 85KiBytes for me right now. If this turns out to be too much, we have a couple of options: * We can use shorter names in the JSON. Not the QMP style. * Optionally return the sub-schema for commands and events given as arguments. Right now qmp_query_schema() sends the string literal computed by qmp-introspect.py. To compute sub-schema at run time, we'd have to duplicate parts of qapi-introspect.py in C. Unattractive. * Let clients cache the output of query-qmp-schema. It changes only on QEMU upgrades, i.e. rarely. Provide a command query-qmp-schema-hash. Clients can have a cache indexed by hash, and re-query the schema only when they don't have it cached. Even simpler: put the hash in the QMP greeting. Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com>
2015-09-16 14:06:28 +03:00
/*
* Minor hack: generated marshalling suppressed for this command
* ('gen': false in the schema) so we can parse the JSON string
* directly into QObject instead of first parsing it with
* visit_type_SchemaInfoList() into a SchemaInfoList, then marshal it
* to QObject with generated output marshallers, every time. Instead,
* we do it in test-qobject-input-visitor.c, just to make sure
* qapi-gen.py's output actually conforms to the schema.
qapi: New QMP command query-qmp-schema for QMP introspection qapi/introspect.json defines the introspection schema. It's designed for QMP introspection, but should do for similar uses, such as QGA. The introspection schema does not reflect all the rules and restrictions that apply to QAPI schemata. A valid QAPI schema has an introspection value conforming to the introspection schema, but the converse is not true. Introspection lowers away a number of schema details, and makes implicit things explicit: * The built-in types are declared with their JSON type. All integer types are mapped to 'int', because how many bits we use internally is an implementation detail. It could be pressed into external interface service as very approximate range information, but that's a bad idea. If we need range information, we better do it properly. * Implicit type definitions are made explicit, and given auto-generated names: - Array types, named by appending "List" to the name of their element type, like in generated C. - The enumeration types implicitly defined by simple union types, named by appending "Kind" to the name of their simple union type, like in generated C. - Types that don't occur in generated C. Their names start with ':' so they don't clash with the user's names. * All type references are by name. * The struct and union types are generalized into an object type. * Base types are flattened. * Commands take a single argument and return a single result. Dictionary argument or list result is an implicit type definition. The empty object type is used when a command takes no arguments or produces no results. The argument is always of object type, but the introspection schema doesn't reflect that. The 'gen': false directive is omitted as implementation detail. The 'success-response' directive is omitted as well for now, even though it's not an implementation detail, because it's not used by QMP. * Events carry a single data value. Implicit type definition and empty object type use, just like for commands. The value is of object type, but the introspection schema doesn't reflect that. * Types not used by commands or events are omitted. Indirect use counts as use. * Optional members have a default, which can only be null right now Instead of a mandatory "optional" flag, we have an optional default. No default means mandatory, default null means optional without default value. Non-null is available for optional with default (possible future extension). * Clients should *not* look up types by name, because type names are not ABI. Look up the command or event you're interested in, then follow the references. TODO Should we hide the type names to eliminate the temptation? New generator scripts/qapi-introspect.py computes an introspection value for its input, and generates a C variable holding it. It can generate awfully long lines. Marked TODO. A new test-qmp-input-visitor test case feeds its result for both tests/qapi-schema/qapi-schema-test.json and qapi-schema.json to a QmpInputVisitor to verify it actually conforms to the schema. New QMP command query-qmp-schema takes its return value from that variable. Its reply is some 85KiBytes for me right now. If this turns out to be too much, we have a couple of options: * We can use shorter names in the JSON. Not the QMP style. * Optionally return the sub-schema for commands and events given as arguments. Right now qmp_query_schema() sends the string literal computed by qmp-introspect.py. To compute sub-schema at run time, we'd have to duplicate parts of qapi-introspect.py in C. Unattractive. * Let clients cache the output of query-qmp-schema. It changes only on QEMU upgrades, i.e. rarely. Provide a command query-qmp-schema-hash. Clients can have a cache indexed by hash, and re-query the schema only when they don't have it cached. Even simpler: put the hash in the QMP greeting. Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com>
2015-09-16 14:06:28 +03:00
*/
static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
Error **errp)
{
*ret_data = qobject_from_qlit(&qmp_schema_qlit);
qapi: New QMP command query-qmp-schema for QMP introspection qapi/introspect.json defines the introspection schema. It's designed for QMP introspection, but should do for similar uses, such as QGA. The introspection schema does not reflect all the rules and restrictions that apply to QAPI schemata. A valid QAPI schema has an introspection value conforming to the introspection schema, but the converse is not true. Introspection lowers away a number of schema details, and makes implicit things explicit: * The built-in types are declared with their JSON type. All integer types are mapped to 'int', because how many bits we use internally is an implementation detail. It could be pressed into external interface service as very approximate range information, but that's a bad idea. If we need range information, we better do it properly. * Implicit type definitions are made explicit, and given auto-generated names: - Array types, named by appending "List" to the name of their element type, like in generated C. - The enumeration types implicitly defined by simple union types, named by appending "Kind" to the name of their simple union type, like in generated C. - Types that don't occur in generated C. Their names start with ':' so they don't clash with the user's names. * All type references are by name. * The struct and union types are generalized into an object type. * Base types are flattened. * Commands take a single argument and return a single result. Dictionary argument or list result is an implicit type definition. The empty object type is used when a command takes no arguments or produces no results. The argument is always of object type, but the introspection schema doesn't reflect that. The 'gen': false directive is omitted as implementation detail. The 'success-response' directive is omitted as well for now, even though it's not an implementation detail, because it's not used by QMP. * Events carry a single data value. Implicit type definition and empty object type use, just like for commands. The value is of object type, but the introspection schema doesn't reflect that. * Types not used by commands or events are omitted. Indirect use counts as use. * Optional members have a default, which can only be null right now Instead of a mandatory "optional" flag, we have an optional default. No default means mandatory, default null means optional without default value. Non-null is available for optional with default (possible future extension). * Clients should *not* look up types by name, because type names are not ABI. Look up the command or event you're interested in, then follow the references. TODO Should we hide the type names to eliminate the temptation? New generator scripts/qapi-introspect.py computes an introspection value for its input, and generates a C variable holding it. It can generate awfully long lines. Marked TODO. A new test-qmp-input-visitor test case feeds its result for both tests/qapi-schema/qapi-schema-test.json and qapi-schema.json to a QmpInputVisitor to verify it actually conforms to the schema. New QMP command query-qmp-schema takes its return value from that variable. Its reply is some 85KiBytes for me right now. If this turns out to be too much, we have a couple of options: * We can use shorter names in the JSON. Not the QMP style. * Optionally return the sub-schema for commands and events given as arguments. Right now qmp_query_schema() sends the string literal computed by qmp-introspect.py. To compute sub-schema at run time, we'd have to duplicate parts of qapi-introspect.py in C. Unattractive. * Let clients cache the output of query-qmp-schema. It changes only on QEMU upgrades, i.e. rarely. Provide a command query-qmp-schema-hash. Clients can have a cache indexed by hash, and re-query the schema only when they don't have it cached. Even simpler: put the hash in the QMP greeting. Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com>
2015-09-16 14:06:28 +03:00
}
/*
* We used to define commands in qmp-commands.hx in addition to the
* QAPI schema. This permitted defining some of them only in certain
* configurations. query-commands has always reflected that (good,
* because it lets QMP clients figure out what's actually available),
* while query-qmp-schema never did (not so good). This function is a
* hack to keep the configuration-specific commands defined exactly as
* before, even though qmp-commands.hx is gone.
*
* FIXME Educate the QAPI schema on configuration-specific commands,
* and drop this hack.
*/
static void qmp_unregister_commands_hack(void)
{
#ifndef CONFIG_SPICE
qmp_unregister_command(&qmp_commands, "query-spice");
#endif
#ifndef CONFIG_REPLICATION
qmp_unregister_command(&qmp_commands, "xen-set-replication");
qmp_unregister_command(&qmp_commands, "query-xen-replication-status");
qmp_unregister_command(&qmp_commands, "xen-colo-do-checkpoint");
#endif
#ifndef TARGET_I386
qmp_unregister_command(&qmp_commands, "rtc-reset-reinjection");
qmp_unregister_command(&qmp_commands, "query-sev");
qmp_unregister_command(&qmp_commands, "query-sev-launch-measure");
qmp_unregister_command(&qmp_commands, "query-sev-capabilities");
#endif
#ifndef TARGET_S390X
qmp_unregister_command(&qmp_commands, "dump-skeys");
#endif
#ifndef TARGET_ARM
qmp_unregister_command(&qmp_commands, "query-gic-capabilities");
#endif
#if !defined(TARGET_S390X) && !defined(TARGET_I386)
qmp_unregister_command(&qmp_commands, "query-cpu-model-expansion");
#endif
#if !defined(TARGET_S390X)
qmp_unregister_command(&qmp_commands, "query-cpu-model-baseline");
qmp_unregister_command(&qmp_commands, "query-cpu-model-comparison");
#endif
#if !defined(TARGET_PPC) && !defined(TARGET_ARM) && !defined(TARGET_I386) \
&& !defined(TARGET_S390X)
qmp_unregister_command(&qmp_commands, "query-cpu-definitions");
#endif
}
static void monitor_init_qmp_commands(void)
{
/*
* Two command lists:
* - qmp_commands contains all QMP commands
* - qmp_cap_negotiation_commands contains just
* "qmp_capabilities", to enforce capability negotiation
*/
qmp_init_marshal(&qmp_commands);
qmp: Dumb down how we run QMP command registration The way we get QMP commands registered is high tech: * qapi-commands.py generates qmp_init_marshal() that does the actual work * it also generates the magic to register it as a MODULE_INIT_QAPI function, so it runs when someone calls module_call_init(MODULE_INIT_QAPI) * main() calls module_call_init() QEMU needs to register a few non-qapified commands. Same high tech works: monitor.c has its own qmp_init_marshal() along with the magic to make it run in module_call_init(MODULE_INIT_QAPI). QEMU also needs to unregister commands that are not wanted in this build's configuration (commit 5032a16). Simple enough: qmp_unregister_commands_hack(). The difficulty is to make it run after the generated qmp_init_marshal(). We can't simply run it in monitor.c's qmp_init_marshal(), because the order in which the registered functions run is indeterminate. So qmp_init_marshal() registers qmp_unregister_commands_hack() separately. Since registering *appends* to the list of registered functions, this will make it run after all the functions that have been registered already. I suspect it takes a long and expensive computer science education to not find this silly. Dumb it down as follows: * Drop MODULE_INIT_QAPI entirely * Give the generated qmp_init_marshal() external linkage. * Call it instead of module_call_init(MODULE_INIT_QAPI) * Except in QEMU proper, call new monitor_init_qmp_commands() that in turn calls the generated qmp_init_marshal(), registers the additional commands and unregisters the unwanted ones. Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Message-Id: <1488544368-30622-5-git-send-email-armbru@redhat.com>
2017-03-03 15:32:24 +03:00
qmp_register_command(&qmp_commands, "query-qmp-schema",
qmp_query_qmp_schema,
QCO_NO_OPTIONS);
qmp_register_command(&qmp_commands, "device_add", qmp_device_add,
QCO_NO_OPTIONS);
qmp_register_command(&qmp_commands, "netdev_add", qmp_netdev_add,
QCO_NO_OPTIONS);
qmp: Dumb down how we run QMP command registration The way we get QMP commands registered is high tech: * qapi-commands.py generates qmp_init_marshal() that does the actual work * it also generates the magic to register it as a MODULE_INIT_QAPI function, so it runs when someone calls module_call_init(MODULE_INIT_QAPI) * main() calls module_call_init() QEMU needs to register a few non-qapified commands. Same high tech works: monitor.c has its own qmp_init_marshal() along with the magic to make it run in module_call_init(MODULE_INIT_QAPI). QEMU also needs to unregister commands that are not wanted in this build's configuration (commit 5032a16). Simple enough: qmp_unregister_commands_hack(). The difficulty is to make it run after the generated qmp_init_marshal(). We can't simply run it in monitor.c's qmp_init_marshal(), because the order in which the registered functions run is indeterminate. So qmp_init_marshal() registers qmp_unregister_commands_hack() separately. Since registering *appends* to the list of registered functions, this will make it run after all the functions that have been registered already. I suspect it takes a long and expensive computer science education to not find this silly. Dumb it down as follows: * Drop MODULE_INIT_QAPI entirely * Give the generated qmp_init_marshal() external linkage. * Call it instead of module_call_init(MODULE_INIT_QAPI) * Except in QEMU proper, call new monitor_init_qmp_commands() that in turn calls the generated qmp_init_marshal(), registers the additional commands and unregisters the unwanted ones. Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Message-Id: <1488544368-30622-5-git-send-email-armbru@redhat.com>
2017-03-03 15:32:24 +03:00
qmp_unregister_commands_hack();
QTAILQ_INIT(&qmp_cap_negotiation_commands);
qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities",
qmp_marshal_qmp_capabilities, QCO_NO_OPTIONS);
}
static bool qmp_cap_enabled(Monitor *mon, QMPCapability cap)
{
return mon->qmp.qmp_caps[cap];
}
static bool qmp_oob_enabled(Monitor *mon)
{
return qmp_cap_enabled(mon, QMP_CAPABILITY_OOB);
}
static void qmp_caps_check(Monitor *mon, QMPCapabilityList *list,
Error **errp)
{
for (; list; list = list->next) {
assert(list->value < QMP_CAPABILITY__MAX);
switch (list->value) {
case QMP_CAPABILITY_OOB:
if (!mon->use_io_thr) {
/*
* Out-Of-Band only works with monitors that are
* running on dedicated IOThread.
*/
error_setg(errp, "This monitor does not support "
"Out-Of-Band (OOB)");
return;
}
break;
default:
break;
}
}
}
/* This function should only be called after capabilities are checked. */
static void qmp_caps_apply(Monitor *mon, QMPCapabilityList *list)
{
for (; list; list = list->next) {
mon->qmp.qmp_caps[list->value] = true;
}
}
/*
* Return true if check successful, or false otherwise. When false is
* returned, detailed error will be in errp if provided.
*/
static bool qmp_cmd_oob_check(Monitor *mon, QDict *req, Error **errp)
{
const char *command;
QmpCommand *cmd;
command = qdict_get_try_str(req, "execute");
if (!command) {
error_setg(errp, "Command field 'execute' missing");
return false;
}
cmd = qmp_find_command(mon->qmp.commands, command);
if (!cmd) {
if (mon->qmp.commands == &qmp_cap_negotiation_commands) {
error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
"Expecting capabilities negotiation "
"with 'qmp_capabilities'");
} else {
error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
"The command %s has not been found", command);
}
return false;
}
if (qmp_is_oob(req)) {
if (!qmp_oob_enabled(mon)) {
error_setg(errp, "Please enable Out-Of-Band first "
"for the session during capabilities negotiation");
return false;
}
if (!(cmd->options & QCO_ALLOW_OOB)) {
error_setg(errp, "The command %s does not support OOB",
command);
return false;
}
}
return true;
}
void qmp_qmp_capabilities(bool has_enable, QMPCapabilityList *enable,
Error **errp)
{
Error *local_err = NULL;
if (cur_mon->qmp.commands == &qmp_commands) {
error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
"Capabilities negotiation is already complete, command "
"ignored");
return;
}
/* Enable QMP capabilities provided by the client if applicable. */
if (has_enable) {
qmp_caps_check(cur_mon, enable, &local_err);
if (local_err) {
/*
* Failed check on any of the capabilities will fail the
* entire command (and thus not apply any of the other
* capabilities that were also requested).
*/
error_propagate(errp, local_err);
return;
}
qmp_caps_apply(cur_mon, enable);
}
cur_mon->qmp.commands = &qmp_commands;
}
/* set the current CPU defined by the user */
int monitor_set_cpu(int cpu_index)
{
CPUState *cpu;
cpu = qemu_get_cpu(cpu_index);
if (cpu == NULL) {
return -1;
}
g_free(cur_mon->mon_cpu_path);
cur_mon->mon_cpu_path = object_get_canonical_path(OBJECT(cpu));
return 0;
}
static CPUState *mon_get_cpu_sync(bool synchronize)
{
CPUState *cpu;
if (cur_mon->mon_cpu_path) {
cpu = (CPUState *) object_resolve_path_type(cur_mon->mon_cpu_path,
TYPE_CPU, NULL);
if (!cpu) {
g_free(cur_mon->mon_cpu_path);
cur_mon->mon_cpu_path = NULL;
}
}
if (!cur_mon->mon_cpu_path) {
if (!first_cpu) {
return NULL;
}
monitor_set_cpu(first_cpu->cpu_index);
cpu = first_cpu;
}
if (synchronize) {
cpu_synchronize_state(cpu);
}
return cpu;
}
CPUState *mon_get_cpu(void)
{
return mon_get_cpu_sync(true);
}
CPUArchState *mon_get_cpu_env(void)
{
CPUState *cs = mon_get_cpu();
return cs ? cs->env_ptr : NULL;
}
int monitor_get_cpu_index(void)
{
CPUState *cs = mon_get_cpu_sync(false);
return cs ? cs->cpu_index : UNASSIGNED_CPU_INDEX;
}
static void hmp_info_registers(Monitor *mon, const QDict *qdict)
{
bool all_cpus = qdict_get_try_bool(qdict, "cpustate_all", false);
CPUState *cs;
if (all_cpus) {
CPU_FOREACH(cs) {
monitor_printf(mon, "\nCPU#%d\n", cs->cpu_index);
cpu_dump_state(cs, (FILE *)mon, monitor_fprintf, CPU_DUMP_FPU);
}
} else {
cs = mon_get_cpu();
if (!cs) {
monitor_printf(mon, "No CPU available\n");
return;
}
cpu_dump_state(cs, (FILE *)mon, monitor_fprintf, CPU_DUMP_FPU);
}
}
#ifdef CONFIG_TCG
static void hmp_info_jit(Monitor *mon, const QDict *qdict)
{
if (!tcg_enabled()) {
error_report("JIT information is only available with accel=tcg");
return;
}
dump_exec_info((FILE *)mon, monitor_fprintf);
dump_drift_info((FILE *)mon, monitor_fprintf);
}
static void hmp_info_opcount(Monitor *mon, const QDict *qdict)
{
dump_opcount_info((FILE *)mon, monitor_fprintf);
}
#endif
static void hmp_info_history(Monitor *mon, const QDict *qdict)
{
int i;
const char *str;
if (!mon->rs)
return;
i = 0;
for(;;) {
str = readline_get_history(mon->rs, i);
if (!str)
break;
monitor_printf(mon, "%d: '%s'\n", i, str);
i++;
}
}
static void hmp_info_cpustats(Monitor *mon, const QDict *qdict)
Great PowerPC emulation code resynchronisation and improvments: - Add status file to make regression tracking easier - Move all micro-operations helpers definitions into a separate header: should never be seen outside of op.c - Update copyrights - Add new / missing PowerPC CPU definitions - Add definitions for PowerPC BookE - Add support for PowerPC 6xx/7xx software driven TLBs Allow use of PowerPC 603 as an example - Add preliminary code for POWER, POWER2, PowerPC 403, 405, 440, 601, 602 and BookE support - Avoid compiling priviledged only resources support for user-mode emulation - Remove unused helpers / micro-ops / dead code - Add instructions usage statistics dump: useful to figure which instructions need strong optimizations. - Micro-operation fixes: * add missing RETURN in some micro-ops * fix prototypes * use softfloat routines for all floating-point operations * fix tlbie instruction * move some huge micro-operations into helpers - emulation fixes: * fix inverted opcodes for fcmpo / fcmpu * condition register update is always to be done after the whole instruction has completed * add missing NIP updates when calling helpers that may generate an exception - optimizations and improvments: * optimize very often used instructions (li, mr, rlwixx...) * remove specific micro-ops for rarely used instructions * add routines for addresses computations to avoid bugs due to multiple different implementations * fix TB linking: do not reset T0 at the end of every TB. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2473 c046a42c-6fe2-441c-8c8c-71466251a162
2007-03-07 11:32:30 +03:00
{
CPUState *cs = mon_get_cpu();
if (!cs) {
monitor_printf(mon, "No CPU available\n");
return;
}
cpu_dump_statistics(cs, (FILE *)mon, &monitor_fprintf, 0);
Great PowerPC emulation code resynchronisation and improvments: - Add status file to make regression tracking easier - Move all micro-operations helpers definitions into a separate header: should never be seen outside of op.c - Update copyrights - Add new / missing PowerPC CPU definitions - Add definitions for PowerPC BookE - Add support for PowerPC 6xx/7xx software driven TLBs Allow use of PowerPC 603 as an example - Add preliminary code for POWER, POWER2, PowerPC 403, 405, 440, 601, 602 and BookE support - Avoid compiling priviledged only resources support for user-mode emulation - Remove unused helpers / micro-ops / dead code - Add instructions usage statistics dump: useful to figure which instructions need strong optimizations. - Micro-operation fixes: * add missing RETURN in some micro-ops * fix prototypes * use softfloat routines for all floating-point operations * fix tlbie instruction * move some huge micro-operations into helpers - emulation fixes: * fix inverted opcodes for fcmpo / fcmpu * condition register update is always to be done after the whole instruction has completed * add missing NIP updates when calling helpers that may generate an exception - optimizations and improvments: * optimize very often used instructions (li, mr, rlwixx...) * remove specific micro-ops for rarely used instructions * add routines for addresses computations to avoid bugs due to multiple different implementations * fix TB linking: do not reset T0 at the end of every TB. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2473 c046a42c-6fe2-441c-8c8c-71466251a162
2007-03-07 11:32:30 +03:00
}
static void hmp_info_trace_events(Monitor *mon, const QDict *qdict)
{
const char *name = qdict_get_try_str(qdict, "name");
bool has_vcpu = qdict_haskey(qdict, "vcpu");
int vcpu = qdict_get_try_int(qdict, "vcpu", 0);
TraceEventInfoList *events;
TraceEventInfoList *elem;
Error *local_err = NULL;
if (name == NULL) {
name = "*";
}
if (vcpu < 0) {
monitor_printf(mon, "argument vcpu must be positive");
return;
}
events = qmp_trace_event_get_state(name, has_vcpu, vcpu, &local_err);
if (local_err) {
error_report_err(local_err);
return;
}
for (elem = events; elem != NULL; elem = elem->next) {
monitor_printf(mon, "%s : state %u\n",
elem->value->name,
elem->value->state == TRACE_EVENT_STATE_ENABLED ? 1 : 0);
}
qapi_free_TraceEventInfoList(events);
}
void qmp_client_migrate_info(const char *protocol, const char *hostname,
bool has_port, int64_t port,
bool has_tls_port, int64_t tls_port,
bool has_cert_subject, const char *cert_subject,
Error **errp)
{
if (strcmp(protocol, "spice") == 0) {
if (!qemu_using_spice(errp)) {
return;
}
if (!has_port && !has_tls_port) {
error_setg(errp, QERR_MISSING_PARAMETER, "port/tls-port");
return;
}
if (qemu_spice_migrate_info(hostname,
has_port ? port : -1,
has_tls_port ? tls_port : -1,
cert_subject)) {
error_setg(errp, QERR_UNDEFINED_ERROR);
return;
}
return;
}
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "protocol", "spice");
}
static void hmp_logfile(Monitor *mon, const QDict *qdict)
{
Error *err = NULL;
qemu_set_log_filename(qdict_get_str(qdict, "filename"), &err);
if (err) {
error_report_err(err);
}
}
static void hmp_log(Monitor *mon, const QDict *qdict)
{
int mask;
const char *items = qdict_get_str(qdict, "items");
if (!strcmp(items, "none")) {
mask = 0;
} else {
mask = qemu_str_to_log_mask(items);
if (!mask) {
help_cmd(mon, "log");
return;
}
}
qemu_set_log(mask);
}
static void hmp_singlestep(Monitor *mon, const QDict *qdict)
{
const char *option = qdict_get_try_str(qdict, "option");
if (!option || !strcmp(option, "on")) {
singlestep = 1;
} else if (!strcmp(option, "off")) {
singlestep = 0;
} else {
monitor_printf(mon, "unexpected option %s\n", option);
}
}
static void hmp_gdbserver(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_try_str(qdict, "device");
if (!device)
device = "tcp::" DEFAULT_GDBSTUB_PORT;
if (gdbserver_start(device) < 0) {
monitor_printf(mon, "Could not open gdbserver on device '%s'\n",
device);
} else if (strcmp(device, "none") == 0) {
monitor_printf(mon, "Disabled gdbserver\n");
} else {
monitor_printf(mon, "Waiting for gdb connection on device '%s'\n",
device);
}
}
static void hmp_watchdog_action(Monitor *mon, const QDict *qdict)
{
const char *action = qdict_get_str(qdict, "action");
if (select_watchdog_action(action) == -1) {
monitor_printf(mon, "Unknown watchdog action '%s'\n", action);
}
}
static void monitor_printc(Monitor *mon, int c)
{
monitor_printf(mon, "'");
switch(c) {
case '\'':
monitor_printf(mon, "\\'");
break;
case '\\':
monitor_printf(mon, "\\\\");
break;
case '\n':
monitor_printf(mon, "\\n");
break;
case '\r':
monitor_printf(mon, "\\r");
break;
default:
if (c >= 32 && c <= 126) {
monitor_printf(mon, "%c", c);
} else {
monitor_printf(mon, "\\x%02x", c);
}
break;
}
monitor_printf(mon, "'");
}
static void memory_dump(Monitor *mon, int count, int format, int wsize,
hwaddr addr, int is_physical)
{
int l, line_size, i, max_digits, len;
uint8_t buf[16];
uint64_t v;
CPUState *cs = mon_get_cpu();
if (!cs && (format == 'i' || !is_physical)) {
monitor_printf(mon, "Can not dump without CPU\n");
return;
}
if (format == 'i') {
monitor_disas(mon, cs, addr, count, is_physical);
return;
}
len = wsize * count;
if (wsize == 1)
line_size = 8;
else
line_size = 16;
max_digits = 0;
switch(format) {
case 'o':
max_digits = DIV_ROUND_UP(wsize * 8, 3);
break;
default:
case 'x':
max_digits = (wsize * 8) / 4;
break;
case 'u':
case 'd':
max_digits = DIV_ROUND_UP(wsize * 8 * 10, 33);
break;
case 'c':
wsize = 1;
break;
}
while (len > 0) {
if (is_physical)
monitor_printf(mon, TARGET_FMT_plx ":", addr);
else
monitor_printf(mon, TARGET_FMT_lx ":", (target_ulong)addr);
l = len;
if (l > line_size)
l = line_size;
if (is_physical) {
cpu_physical_memory_read(addr, buf, l);
} else {
if (cpu_memory_rw_debug(cs, addr, buf, l, 0) < 0) {
monitor_printf(mon, " Cannot access memory\n");
break;
}
}
i = 0;
while (i < l) {
switch(wsize) {
default:
case 1:
v = ldub_p(buf + i);
break;
case 2:
v = lduw_p(buf + i);
break;
case 4:
v = (uint32_t)ldl_p(buf + i);
break;
case 8:
v = ldq_p(buf + i);
break;
}
monitor_printf(mon, " ");
switch(format) {
case 'o':
monitor_printf(mon, "%#*" PRIo64, max_digits, v);
break;
case 'x':
monitor_printf(mon, "0x%0*" PRIx64, max_digits, v);
break;
case 'u':
monitor_printf(mon, "%*" PRIu64, max_digits, v);
break;
case 'd':
monitor_printf(mon, "%*" PRId64, max_digits, v);
break;
case 'c':
monitor_printc(mon, v);
break;
}
i += wsize;
}
monitor_printf(mon, "\n");
addr += l;
len -= l;
}
}
static void hmp_memory_dump(Monitor *mon, const QDict *qdict)
{
int count = qdict_get_int(qdict, "count");
int format = qdict_get_int(qdict, "format");
int size = qdict_get_int(qdict, "size");
target_long addr = qdict_get_int(qdict, "addr");
memory_dump(mon, count, format, size, addr, 0);
}
static void hmp_physical_memory_dump(Monitor *mon, const QDict *qdict)
{
int count = qdict_get_int(qdict, "count");
int format = qdict_get_int(qdict, "format");
int size = qdict_get_int(qdict, "size");
hwaddr addr = qdict_get_int(qdict, "addr");
memory_dump(mon, count, format, size, addr, 1);
}
static void *gpa2hva(MemoryRegion **p_mr, hwaddr addr, Error **errp)
{
MemoryRegionSection mrs = memory_region_find(get_system_memory(),
addr, 1);
if (!mrs.mr) {
error_setg(errp, "No memory is mapped at address 0x%" HWADDR_PRIx, addr);
return NULL;
}
if (!memory_region_is_ram(mrs.mr) && !memory_region_is_romd(mrs.mr)) {
error_setg(errp, "Memory at address 0x%" HWADDR_PRIx "is not RAM", addr);
memory_region_unref(mrs.mr);
return NULL;
}
*p_mr = mrs.mr;
return qemu_map_ram_ptr(mrs.mr->ram_block, mrs.offset_within_region);
}
static void hmp_gpa2hva(Monitor *mon, const QDict *qdict)
{
hwaddr addr = qdict_get_int(qdict, "addr");
Error *local_err = NULL;
MemoryRegion *mr = NULL;
void *ptr;
ptr = gpa2hva(&mr, addr, &local_err);
if (local_err) {
error_report_err(local_err);
return;
}
monitor_printf(mon, "Host virtual address for 0x%" HWADDR_PRIx
" (%s) is %p\n",
addr, mr->name, ptr);
memory_region_unref(mr);
}
#ifdef CONFIG_LINUX
static uint64_t vtop(void *ptr, Error **errp)
{
uint64_t pinfo;
uint64_t ret = -1;
uintptr_t addr = (uintptr_t) ptr;
uintptr_t pagesize = getpagesize();
off_t offset = addr / pagesize * sizeof(pinfo);
int fd;
fd = open("/proc/self/pagemap", O_RDONLY);
if (fd == -1) {
error_setg_errno(errp, errno, "Cannot open /proc/self/pagemap");
return -1;
}
/* Force copy-on-write if necessary. */
atomic_add((uint8_t *)ptr, 0);
if (pread(fd, &pinfo, sizeof(pinfo), offset) != sizeof(pinfo)) {
error_setg_errno(errp, errno, "Cannot read pagemap");
goto out;
}
if ((pinfo & (1ull << 63)) == 0) {
error_setg(errp, "Page not present");
goto out;
}
ret = ((pinfo & 0x007fffffffffffffull) * pagesize) | (addr & (pagesize - 1));
out:
close(fd);
return ret;
}
static void hmp_gpa2hpa(Monitor *mon, const QDict *qdict)
{
hwaddr addr = qdict_get_int(qdict, "addr");
Error *local_err = NULL;
MemoryRegion *mr = NULL;
void *ptr;
uint64_t physaddr;
ptr = gpa2hva(&mr, addr, &local_err);
if (local_err) {
error_report_err(local_err);
return;
}
physaddr = vtop(ptr, &local_err);
if (local_err) {
error_report_err(local_err);
} else {
monitor_printf(mon, "Host physical address for 0x%" HWADDR_PRIx
" (%s) is 0x%" PRIx64 "\n",
addr, mr->name, (uint64_t) physaddr);
}
memory_region_unref(mr);
}
#endif
static void do_print(Monitor *mon, const QDict *qdict)
{
int format = qdict_get_int(qdict, "format");
hwaddr val = qdict_get_int(qdict, "val");
switch(format) {
case 'o':
monitor_printf(mon, "%#" HWADDR_PRIo, val);
break;
case 'x':
monitor_printf(mon, "%#" HWADDR_PRIx, val);
break;
case 'u':
monitor_printf(mon, "%" HWADDR_PRIu, val);
break;
default:
case 'd':
monitor_printf(mon, "%" HWADDR_PRId, val);
break;
case 'c':
monitor_printc(mon, val);
break;
}
monitor_printf(mon, "\n");
}
static void hmp_sum(Monitor *mon, const QDict *qdict)
{
uint32_t addr;
uint16_t sum;
uint32_t start = qdict_get_int(qdict, "start");
uint32_t size = qdict_get_int(qdict, "size");
sum = 0;
for(addr = start; addr < (start + size); addr++) {
Switch non-CPU callers from ld/st*_phys to address_space_ld/st* Switch all the uses of ld/st*_phys to address_space_ld/st*, except for those cases where the address space is the CPU's (ie cs->as). This was done with the following script which generates a Coccinelle patch. A few over-80-columns lines in the result were rewrapped by hand where Coccinelle failed to do the wrapping automatically, as well as one location where it didn't put a line-continuation '\' when wrapping lines on a change made to a match inside a macro definition. ===begin=== #!/bin/sh -e # Usage: # ./ldst-phys.spatch.sh > ldst-phys.spatch # spatch -sp_file ldst-phys.spatch -dir . | sed -e '/^+/s/\t/ /g' > out.patch # patch -p1 < out.patch for FN in ub uw_le uw_be l_le l_be q_le q_be uw l q; do cat <<EOF @ cpu_matches_ld_${FN} @ expression E1,E2; identifier as; @@ ld${FN}_phys(E1->as,E2) @ other_matches_ld_${FN} depends on !cpu_matches_ld_${FN} @ expression E1,E2; @@ -ld${FN}_phys(E1,E2) +address_space_ld${FN}(E1,E2, MEMTXATTRS_UNSPECIFIED, NULL) EOF done for FN in b w_le w_be l_le l_be q_le q_be w l q; do cat <<EOF @ cpu_matches_st_${FN} @ expression E1,E2,E3; identifier as; @@ st${FN}_phys(E1->as,E2,E3) @ other_matches_st_${FN} depends on !cpu_matches_st_${FN} @ expression E1,E2,E3; @@ -st${FN}_phys(E1,E2,E3) +address_space_st${FN}(E1,E2,E3, MEMTXATTRS_UNSPECIFIED, NULL) EOF done ===endit=== Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
2015-04-26 18:49:24 +03:00
uint8_t val = address_space_ldub(&address_space_memory, addr,
MEMTXATTRS_UNSPECIFIED, NULL);
/* BSD sum algorithm ('sum' Unix command) */
sum = (sum >> 1) | (sum << 15);
sum += val;
}
monitor_printf(mon, "%05d\n", sum);
}
static int mouse_button_state;
static void hmp_mouse_move(Monitor *mon, const QDict *qdict)
{
int dx, dy, dz, button;
const char *dx_str = qdict_get_str(qdict, "dx_str");
const char *dy_str = qdict_get_str(qdict, "dy_str");
const char *dz_str = qdict_get_try_str(qdict, "dz_str");
dx = strtol(dx_str, NULL, 0);
dy = strtol(dy_str, NULL, 0);
qemu_input_queue_rel(NULL, INPUT_AXIS_X, dx);
qemu_input_queue_rel(NULL, INPUT_AXIS_Y, dy);
if (dz_str) {
dz = strtol(dz_str, NULL, 0);
if (dz != 0) {
button = (dz > 0) ? INPUT_BUTTON_WHEEL_UP : INPUT_BUTTON_WHEEL_DOWN;
qemu_input_queue_btn(NULL, button, true);
qemu_input_event_sync();
qemu_input_queue_btn(NULL, button, false);
}
}
qemu_input_event_sync();
}
static void hmp_mouse_button(Monitor *mon, const QDict *qdict)
{
static uint32_t bmap[INPUT_BUTTON__MAX] = {
[INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON,
[INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON,
[INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON,
};
int button_state = qdict_get_int(qdict, "button_state");
if (mouse_button_state == button_state) {
return;
}
qemu_input_update_buttons(NULL, bmap, mouse_button_state, button_state);
qemu_input_event_sync();
mouse_button_state = button_state;
}
static void hmp_ioport_read(Monitor *mon, const QDict *qdict)
{
int size = qdict_get_int(qdict, "size");
int addr = qdict_get_int(qdict, "addr");
int has_index = qdict_haskey(qdict, "index");
uint32_t val;
int suffix;
if (has_index) {
int index = qdict_get_int(qdict, "index");
cpu_outb(addr & IOPORTS_MASK, index & 0xff);
addr++;
}
addr &= 0xffff;
switch(size) {
default:
case 1:
val = cpu_inb(addr);
suffix = 'b';
break;
case 2:
val = cpu_inw(addr);
suffix = 'w';
break;
case 4:
val = cpu_inl(addr);
suffix = 'l';
break;
}
monitor_printf(mon, "port%c[0x%04x] = %#0*x\n",
suffix, addr, size * 2, val);
}
static void hmp_ioport_write(Monitor *mon, const QDict *qdict)
{
int size = qdict_get_int(qdict, "size");
int addr = qdict_get_int(qdict, "addr");
int val = qdict_get_int(qdict, "val");
addr &= IOPORTS_MASK;
switch (size) {
default:
case 1:
cpu_outb(addr, val);
break;
case 2:
cpu_outw(addr, val);
break;
case 4:
cpu_outl(addr, val);
break;
}
}
static void hmp_boot_set(Monitor *mon, const QDict *qdict)
{
Error *local_err = NULL;
const char *bootdevice = qdict_get_str(qdict, "bootdevice");
qemu_boot_set(bootdevice, &local_err);
if (local_err) {
error: Use error_report_err() instead of monitor_printf() Both error_report_err() and monitor_printf() print to the same destination when monitor_printf() is used correctly, i.e. within an HMP monitor. Elsewhere, monitor_printf() does nothing, while error_report_err() reports to stderr. Most changed functions are HMP command handlers. These should only run within an HMP monitor. The one exception is bdrv_password_cb(), which should also only run within an HMP monitor. Four command handlers prefix the error message with the command name: balloon, migrate_set_capability, migrate_set_parameter, migrate. Pointless, drop. Unlike monitor_printf(), error_report_err() uses the error whole instead of just its message obtained with error_get_pretty(). This avoids suppressing its hint (see commit 50b7b00). Example: (qemu) device_add ivshmem,id=666 Parameter 'id' expects an identifier Identifiers consist of letters, digits, '-', '.', '_', starting with a letter. Try "help device_add" for more information The "Identifiers consist of..." line is new with this patch. Coccinelle semantic patch: @@ expression M, E; @@ - monitor_printf(M, "%s\n", error_get_pretty(E)); - error_free(E); + error_report_err(E); @r1@ expression M, E; format F; position p; @@ - monitor_printf(M, "...%@F@\n", error_get_pretty(E));@p - error_free(E); + error_report_err(E); @script:python@ p << r1.p; @@ print "%s:%s:%s: prefix dropped" % (p[0].file, p[0].line, p[0].column) Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Message-Id: <1450452927-8346-4-git-send-email-armbru@redhat.com>
2015-12-18 18:35:06 +03:00
error_report_err(local_err);
} else {
monitor_printf(mon, "boot device list now set to %s\n", bootdevice);
}
}
static void hmp_info_mtree(Monitor *mon, const QDict *qdict)
{
bool flatview = qdict_get_try_bool(qdict, "flatview", false);
bool dispatch_tree = qdict_get_try_bool(qdict, "dispatch_tree", false);
mtree_info((fprintf_function)monitor_printf, mon, flatview, dispatch_tree);
}
static void hmp_info_numa(Monitor *mon, const QDict *qdict)
{
int i;
NumaNodeMem *node_mem;
CpuInfoList *cpu_list, *cpu;
cpu_list = qmp_query_cpus(&error_abort);
node_mem = g_new0(NumaNodeMem, nb_numa_nodes);
query_numa_node_mem(node_mem);
monitor_printf(mon, "%d nodes\n", nb_numa_nodes);
for (i = 0; i < nb_numa_nodes; i++) {
monitor_printf(mon, "node %d cpus:", i);
for (cpu = cpu_list; cpu; cpu = cpu->next) {
if (cpu->value->has_props && cpu->value->props->has_node_id &&
cpu->value->props->node_id == i) {
monitor_printf(mon, " %" PRIi64, cpu->value->CPU);
}
}
monitor_printf(mon, "\n");
monitor_printf(mon, "node %d size: %" PRId64 " MB\n", i,
node_mem[i].node_mem >> 20);
monitor_printf(mon, "node %d plugged: %" PRId64 " MB\n", i,
node_mem[i].node_plugged_mem >> 20);
}
qapi_free_CpuInfoList(cpu_list);
g_free(node_mem);
}
#ifdef CONFIG_PROFILER
int64_t tcg_time;
int64_t dev_time;
static void hmp_info_profile(Monitor *mon, const QDict *qdict)
{
monitor_printf(mon, "async time %" PRId64 " (%0.3f)\n",
dev_time, dev_time / (double)NANOSECONDS_PER_SECOND);
monitor_printf(mon, "qemu time %" PRId64 " (%0.3f)\n",
tcg_time, tcg_time / (double)NANOSECONDS_PER_SECOND);
tcg_time = 0;
dev_time = 0;
}
#else
static void hmp_info_profile(Monitor *mon, const QDict *qdict)
{
monitor_printf(mon, "Internal profiler not compiled\n");
}
#endif
/* Capture support */
static QLIST_HEAD (capture_list_head, CaptureState) capture_head;
static void hmp_info_capture(Monitor *mon, const QDict *qdict)
{
int i;
CaptureState *s;
for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) {
monitor_printf(mon, "[%d]: ", i);
s->ops.info (s->opaque);
}
}
static void hmp_stopcapture(Monitor *mon, const QDict *qdict)
{
int i;
int n = qdict_get_int(qdict, "n");
CaptureState *s;
for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) {
if (i == n) {
s->ops.destroy (s->opaque);
QLIST_REMOVE (s, entries);
g_free (s);
return;
}
}
}
static void hmp_wavcapture(Monitor *mon, const QDict *qdict)
{
const char *path = qdict_get_str(qdict, "path");
int has_freq = qdict_haskey(qdict, "freq");
int freq = qdict_get_try_int(qdict, "freq", -1);
int has_bits = qdict_haskey(qdict, "bits");
int bits = qdict_get_try_int(qdict, "bits", -1);
int has_channels = qdict_haskey(qdict, "nchannels");
int nchannels = qdict_get_try_int(qdict, "nchannels", -1);
CaptureState *s;
s = g_malloc0 (sizeof (*s));
freq = has_freq ? freq : 44100;
bits = has_bits ? bits : 16;
nchannels = has_channels ? nchannels : 2;
if (wav_start_capture (s, path, freq, bits, nchannels)) {
monitor_printf(mon, "Failed to add wave capture\n");
g_free (s);
return;
}
QLIST_INSERT_HEAD (&capture_head, s, entries);
}
static qemu_acl *find_acl(Monitor *mon, const char *name)
Support ACLs for controlling VNC access ("Daniel P. Berrange") This patch introduces a generic internal API for access control lists to be used by network servers in QEMU. It adds support for checking these ACL in the VNC server, in two places. The first ACL is for the SASL authentication mechanism, checking the SASL username. This ACL is called 'vnc.username'. The second is for the TLS authentication mechanism, when x509 client certificates are turned on, checking against the Distinguished Name of the client. This ACL is called 'vnc.x509dname' The internal API provides for an ACL with the following characteristics - A unique name, eg vnc.username, and vnc.x509dname. - A default policy, allow or deny - An ordered series of match rules, with allow or deny policy If none of the match rules apply, then the default policy is used. There is a monitor API to manipulate the ACLs, which I'll describe via examples (qemu) acl show vnc.username policy: allow (qemu) acl policy vnc.username denya acl: policy set to 'deny' (qemu) acl allow vnc.username fred acl: added rule at position 1 (qemu) acl allow vnc.username bob acl: added rule at position 2 (qemu) acl allow vnc.username joe 1 acl: added rule at position 1 (qemu) acl show vnc.username policy: deny 0: allow fred 1: allow joe 2: allow bob (qemu) acl show vnc.x509dname policy: allow (qemu) acl policy vnc.x509dname deny acl: policy set to 'deny' (qemu) acl allow vnc.x509dname C=GB,O=ACME,L=London,CN=* acl: added rule at position 1 (qemu) acl allow vnc.x509dname C=GB,O=ACME,L=Boston,CN=bob acl: added rule at position 2 (qemu) acl show vnc.x509dname policy: deny 0: allow C=GB,O=ACME,L=London,CN=* 1: allow C=GB,O=ACME,L=Boston,CN=bob By default the VNC server will not use any ACLs, allowing access to the server if the user successfully authenticates. To enable use of ACLs to restrict user access, the ',acl' flag should be given when starting QEMU. The initial ACL activated will be a 'deny all' policy and should be customized using monitor commands. eg enable SASL auth and ACLs qemu .... -vnc localhost:1,sasl,acl The next patch will provide a way to load a pre-defined ACL when starting up Makefile | 6 + b/acl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ b/acl.h | 74 ++++++++++++++++++++++ configure | 18 +++++ monitor.c | 95 ++++++++++++++++++++++++++++ qemu-doc.texi | 49 ++++++++++++++ vnc-auth-sasl.c | 16 +++- vnc-auth-sasl.h | 7 ++ vnc-tls.c | 19 +++++ vnc-tls.h | 3 vnc.c | 21 ++++++ vnc.h | 3 12 files changed, 491 insertions(+), 5 deletions(-) Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6726 c046a42c-6fe2-441c-8c8c-71466251a162
2009-03-06 23:27:37 +03:00
{
qemu_acl *acl = qemu_acl_find(name);
Support ACLs for controlling VNC access ("Daniel P. Berrange") This patch introduces a generic internal API for access control lists to be used by network servers in QEMU. It adds support for checking these ACL in the VNC server, in two places. The first ACL is for the SASL authentication mechanism, checking the SASL username. This ACL is called 'vnc.username'. The second is for the TLS authentication mechanism, when x509 client certificates are turned on, checking against the Distinguished Name of the client. This ACL is called 'vnc.x509dname' The internal API provides for an ACL with the following characteristics - A unique name, eg vnc.username, and vnc.x509dname. - A default policy, allow or deny - An ordered series of match rules, with allow or deny policy If none of the match rules apply, then the default policy is used. There is a monitor API to manipulate the ACLs, which I'll describe via examples (qemu) acl show vnc.username policy: allow (qemu) acl policy vnc.username denya acl: policy set to 'deny' (qemu) acl allow vnc.username fred acl: added rule at position 1 (qemu) acl allow vnc.username bob acl: added rule at position 2 (qemu) acl allow vnc.username joe 1 acl: added rule at position 1 (qemu) acl show vnc.username policy: deny 0: allow fred 1: allow joe 2: allow bob (qemu) acl show vnc.x509dname policy: allow (qemu) acl policy vnc.x509dname deny acl: policy set to 'deny' (qemu) acl allow vnc.x509dname C=GB,O=ACME,L=London,CN=* acl: added rule at position 1 (qemu) acl allow vnc.x509dname C=GB,O=ACME,L=Boston,CN=bob acl: added rule at position 2 (qemu) acl show vnc.x509dname policy: deny 0: allow C=GB,O=ACME,L=London,CN=* 1: allow C=GB,O=ACME,L=Boston,CN=bob By default the VNC server will not use any ACLs, allowing access to the server if the user successfully authenticates. To enable use of ACLs to restrict user access, the ',acl' flag should be given when starting QEMU. The initial ACL activated will be a 'deny all' policy and should be customized using monitor commands. eg enable SASL auth and ACLs qemu .... -vnc localhost:1,sasl,acl The next patch will provide a way to load a pre-defined ACL when starting up Makefile | 6 + b/acl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ b/acl.h | 74 ++++++++++++++++++++++ configure | 18 +++++ monitor.c | 95 ++++++++++++++++++++++++++++ qemu-doc.texi | 49 ++++++++++++++ vnc-auth-sasl.c | 16 +++- vnc-auth-sasl.h | 7 ++ vnc-tls.c | 19 +++++ vnc-tls.h | 3 vnc.c | 21 ++++++ vnc.h | 3 12 files changed, 491 insertions(+), 5 deletions(-) Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6726 c046a42c-6fe2-441c-8c8c-71466251a162
2009-03-06 23:27:37 +03:00
if (!acl) {
monitor_printf(mon, "acl: unknown list '%s'\n", name);
Support ACLs for controlling VNC access ("Daniel P. Berrange") This patch introduces a generic internal API for access control lists to be used by network servers in QEMU. It adds support for checking these ACL in the VNC server, in two places. The first ACL is for the SASL authentication mechanism, checking the SASL username. This ACL is called 'vnc.username'. The second is for the TLS authentication mechanism, when x509 client certificates are turned on, checking against the Distinguished Name of the client. This ACL is called 'vnc.x509dname' The internal API provides for an ACL with the following characteristics - A unique name, eg vnc.username, and vnc.x509dname. - A default policy, allow or deny - An ordered series of match rules, with allow or deny policy If none of the match rules apply, then the default policy is used. There is a monitor API to manipulate the ACLs, which I'll describe via examples (qemu) acl show vnc.username policy: allow (qemu) acl policy vnc.username denya acl: policy set to 'deny' (qemu) acl allow vnc.username fred acl: added rule at position 1 (qemu) acl allow vnc.username bob acl: added rule at position 2 (qemu) acl allow vnc.username joe 1 acl: added rule at position 1 (qemu) acl show vnc.username policy: deny 0: allow fred 1: allow joe 2: allow bob (qemu) acl show vnc.x509dname policy: allow (qemu) acl policy vnc.x509dname deny acl: policy set to 'deny' (qemu) acl allow vnc.x509dname C=GB,O=ACME,L=London,CN=* acl: added rule at position 1 (qemu) acl allow vnc.x509dname C=GB,O=ACME,L=Boston,CN=bob acl: added rule at position 2 (qemu) acl show vnc.x509dname policy: deny 0: allow C=GB,O=ACME,L=London,CN=* 1: allow C=GB,O=ACME,L=Boston,CN=bob By default the VNC server will not use any ACLs, allowing access to the server if the user successfully authenticates. To enable use of ACLs to restrict user access, the ',acl' flag should be given when starting QEMU. The initial ACL activated will be a 'deny all' policy and should be customized using monitor commands. eg enable SASL auth and ACLs qemu .... -vnc localhost:1,sasl,acl The next patch will provide a way to load a pre-defined ACL when starting up Makefile | 6 + b/acl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ b/acl.h | 74 ++++++++++++++++++++++ configure | 18 +++++ monitor.c | 95 ++++++++++++++++++++++++++++ qemu-doc.texi | 49 ++++++++++++++ vnc-auth-sasl.c | 16 +++- vnc-auth-sasl.h | 7 ++ vnc-tls.c | 19 +++++ vnc-tls.h | 3 vnc.c | 21 ++++++ vnc.h | 3 12 files changed, 491 insertions(+), 5 deletions(-) Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6726 c046a42c-6fe2-441c-8c8c-71466251a162
2009-03-06 23:27:37 +03:00
}
return acl;
}
static void hmp_acl_show(Monitor *mon, const QDict *qdict)
{
const char *aclname = qdict_get_str(qdict, "aclname");
qemu_acl *acl = find_acl(mon, aclname);
qemu_acl_entry *entry;
int i = 0;
Support ACLs for controlling VNC access ("Daniel P. Berrange") This patch introduces a generic internal API for access control lists to be used by network servers in QEMU. It adds support for checking these ACL in the VNC server, in two places. The first ACL is for the SASL authentication mechanism, checking the SASL username. This ACL is called 'vnc.username'. The second is for the TLS authentication mechanism, when x509 client certificates are turned on, checking against the Distinguished Name of the client. This ACL is called 'vnc.x509dname' The internal API provides for an ACL with the following characteristics - A unique name, eg vnc.username, and vnc.x509dname. - A default policy, allow or deny - An ordered series of match rules, with allow or deny policy If none of the match rules apply, then the default policy is used. There is a monitor API to manipulate the ACLs, which I'll describe via examples (qemu) acl show vnc.username policy: allow (qemu) acl policy vnc.username denya acl: policy set to 'deny' (qemu) acl allow vnc.username fred acl: added rule at position 1 (qemu) acl allow vnc.username bob acl: added rule at position 2 (qemu) acl allow vnc.username joe 1 acl: added rule at position 1 (qemu) acl show vnc.username policy: deny 0: allow fred 1: allow joe 2: allow bob (qemu) acl show vnc.x509dname policy: allow (qemu) acl policy vnc.x509dname deny acl: policy set to 'deny' (qemu) acl allow vnc.x509dname C=GB,O=ACME,L=London,CN=* acl: added rule at position 1 (qemu) acl allow vnc.x509dname C=GB,O=ACME,L=Boston,CN=bob acl: added rule at position 2 (qemu) acl show vnc.x509dname policy: deny 0: allow C=GB,O=ACME,L=London,CN=* 1: allow C=GB,O=ACME,L=Boston,CN=bob By default the VNC server will not use any ACLs, allowing access to the server if the user successfully authenticates. To enable use of ACLs to restrict user access, the ',acl' flag should be given when starting QEMU. The initial ACL activated will be a 'deny all' policy and should be customized using monitor commands. eg enable SASL auth and ACLs qemu .... -vnc localhost:1,sasl,acl The next patch will provide a way to load a pre-defined ACL when starting up Makefile | 6 + b/acl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ b/acl.h | 74 ++++++++++++++++++++++ configure | 18 +++++ monitor.c | 95 ++++++++++++++++++++++++++++ qemu-doc.texi | 49 ++++++++++++++ vnc-auth-sasl.c | 16 +++- vnc-auth-sasl.h | 7 ++ vnc-tls.c | 19 +++++ vnc-tls.h | 3 vnc.c | 21 ++++++ vnc.h | 3 12 files changed, 491 insertions(+), 5 deletions(-) Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6726 c046a42c-6fe2-441c-8c8c-71466251a162
2009-03-06 23:27:37 +03:00
if (acl) {
monitor_printf(mon, "policy: %s\n",
Support ACLs for controlling VNC access ("Daniel P. Berrange") This patch introduces a generic internal API for access control lists to be used by network servers in QEMU. It adds support for checking these ACL in the VNC server, in two places. The first ACL is for the SASL authentication mechanism, checking the SASL username. This ACL is called 'vnc.username'. The second is for the TLS authentication mechanism, when x509 client certificates are turned on, checking against the Distinguished Name of the client. This ACL is called 'vnc.x509dname' The internal API provides for an ACL with the following characteristics - A unique name, eg vnc.username, and vnc.x509dname. - A default policy, allow or deny - An ordered series of match rules, with allow or deny policy If none of the match rules apply, then the default policy is used. There is a monitor API to manipulate the ACLs, which I'll describe via examples (qemu) acl show vnc.username policy: allow (qemu) acl policy vnc.username denya acl: policy set to 'deny' (qemu) acl allow vnc.username fred acl: added rule at position 1 (qemu) acl allow vnc.username bob acl: added rule at position 2 (qemu) acl allow vnc.username joe 1 acl: added rule at position 1 (qemu) acl show vnc.username policy: deny 0: allow fred 1: allow joe 2: allow bob (qemu) acl show vnc.x509dname policy: allow (qemu) acl policy vnc.x509dname deny acl: policy set to 'deny' (qemu) acl allow vnc.x509dname C=GB,O=ACME,L=London,CN=* acl: added rule at position 1 (qemu) acl allow vnc.x509dname C=GB,O=ACME,L=Boston,CN=bob acl: added rule at position 2 (qemu) acl show vnc.x509dname policy: deny 0: allow C=GB,O=ACME,L=London,CN=* 1: allow C=GB,O=ACME,L=Boston,CN=bob By default the VNC server will not use any ACLs, allowing access to the server if the user successfully authenticates. To enable use of ACLs to restrict user access, the ',acl' flag should be given when starting QEMU. The initial ACL activated will be a 'deny all' policy and should be customized using monitor commands. eg enable SASL auth and ACLs qemu .... -vnc localhost:1,sasl,acl The next patch will provide a way to load a pre-defined ACL when starting up Makefile | 6 + b/acl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ b/acl.h | 74 ++++++++++++++++++++++ configure | 18 +++++ monitor.c | 95 ++++++++++++++++++++++++++++ qemu-doc.texi | 49 ++++++++++++++ vnc-auth-sasl.c | 16 +++- vnc-auth-sasl.h | 7 ++ vnc-tls.c | 19 +++++ vnc-tls.h | 3 vnc.c | 21 ++++++ vnc.h | 3 12 files changed, 491 insertions(+), 5 deletions(-) Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6726 c046a42c-6fe2-441c-8c8c-71466251a162
2009-03-06 23:27:37 +03:00
acl->defaultDeny ? "deny" : "allow");
QTAILQ_FOREACH(entry, &acl->entries, next) {
i++;
monitor_printf(mon, "%d: %s %s\n", i,
entry->deny ? "deny" : "allow", entry->match);
}
}
}
static void hmp_acl_reset(Monitor *mon, const QDict *qdict)
{
const char *aclname = qdict_get_str(qdict, "aclname");
qemu_acl *acl = find_acl(mon, aclname);
if (acl) {
qemu_acl_reset(acl);
monitor_printf(mon, "acl: removed all rules\n");
}
}
static void hmp_acl_policy(Monitor *mon, const QDict *qdict)
{
const char *aclname = qdict_get_str(qdict, "aclname");
const char *policy = qdict_get_str(qdict, "policy");
qemu_acl *acl = find_acl(mon, aclname);
if (acl) {
if (strcmp(policy, "allow") == 0) {
acl->defaultDeny = 0;
monitor_printf(mon, "acl: policy set to 'allow'\n");
} else if (strcmp(policy, "deny") == 0) {
acl->defaultDeny = 1;
monitor_printf(mon, "acl: policy set to 'deny'\n");
} else {
monitor_printf(mon, "acl: unknown policy '%s', "
"expected 'deny' or 'allow'\n", policy);
}
}
}
static void hmp_acl_add(Monitor *mon, const QDict *qdict)
{
const char *aclname = qdict_get_str(qdict, "aclname");
const char *match = qdict_get_str(qdict, "match");
const char *policy = qdict_get_str(qdict, "policy");
int has_index = qdict_haskey(qdict, "index");
int index = qdict_get_try_int(qdict, "index", -1);
qemu_acl *acl = find_acl(mon, aclname);
int deny, ret;
if (acl) {
if (strcmp(policy, "allow") == 0) {
deny = 0;
} else if (strcmp(policy, "deny") == 0) {
deny = 1;
} else {
monitor_printf(mon, "acl: unknown policy '%s', "
"expected 'deny' or 'allow'\n", policy);
return;
}
if (has_index)
ret = qemu_acl_insert(acl, deny, match, index);
else
ret = qemu_acl_append(acl, deny, match);
if (ret < 0)
monitor_printf(mon, "acl: unable to add acl entry\n");
else
monitor_printf(mon, "acl: added rule at position %d\n", ret);
}
}
static void hmp_acl_remove(Monitor *mon, const QDict *qdict)
{
const char *aclname = qdict_get_str(qdict, "aclname");
const char *match = qdict_get_str(qdict, "match");
qemu_acl *acl = find_acl(mon, aclname);
int ret;
if (acl) {
ret = qemu_acl_remove(acl, match);
if (ret < 0)
monitor_printf(mon, "acl: no matching acl entry\n");
else
monitor_printf(mon, "acl: removed rule at position %d\n", ret);
Support ACLs for controlling VNC access ("Daniel P. Berrange") This patch introduces a generic internal API for access control lists to be used by network servers in QEMU. It adds support for checking these ACL in the VNC server, in two places. The first ACL is for the SASL authentication mechanism, checking the SASL username. This ACL is called 'vnc.username'. The second is for the TLS authentication mechanism, when x509 client certificates are turned on, checking against the Distinguished Name of the client. This ACL is called 'vnc.x509dname' The internal API provides for an ACL with the following characteristics - A unique name, eg vnc.username, and vnc.x509dname. - A default policy, allow or deny - An ordered series of match rules, with allow or deny policy If none of the match rules apply, then the default policy is used. There is a monitor API to manipulate the ACLs, which I'll describe via examples (qemu) acl show vnc.username policy: allow (qemu) acl policy vnc.username denya acl: policy set to 'deny' (qemu) acl allow vnc.username fred acl: added rule at position 1 (qemu) acl allow vnc.username bob acl: added rule at position 2 (qemu) acl allow vnc.username joe 1 acl: added rule at position 1 (qemu) acl show vnc.username policy: deny 0: allow fred 1: allow joe 2: allow bob (qemu) acl show vnc.x509dname policy: allow (qemu) acl policy vnc.x509dname deny acl: policy set to 'deny' (qemu) acl allow vnc.x509dname C=GB,O=ACME,L=London,CN=* acl: added rule at position 1 (qemu) acl allow vnc.x509dname C=GB,O=ACME,L=Boston,CN=bob acl: added rule at position 2 (qemu) acl show vnc.x509dname policy: deny 0: allow C=GB,O=ACME,L=London,CN=* 1: allow C=GB,O=ACME,L=Boston,CN=bob By default the VNC server will not use any ACLs, allowing access to the server if the user successfully authenticates. To enable use of ACLs to restrict user access, the ',acl' flag should be given when starting QEMU. The initial ACL activated will be a 'deny all' policy and should be customized using monitor commands. eg enable SASL auth and ACLs qemu .... -vnc localhost:1,sasl,acl The next patch will provide a way to load a pre-defined ACL when starting up Makefile | 6 + b/acl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ b/acl.h | 74 ++++++++++++++++++++++ configure | 18 +++++ monitor.c | 95 ++++++++++++++++++++++++++++ qemu-doc.texi | 49 ++++++++++++++ vnc-auth-sasl.c | 16 +++- vnc-auth-sasl.h | 7 ++ vnc-tls.c | 19 +++++ vnc-tls.h | 3 vnc.c | 21 ++++++ vnc.h | 3 12 files changed, 491 insertions(+), 5 deletions(-) Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6726 c046a42c-6fe2-441c-8c8c-71466251a162
2009-03-06 23:27:37 +03:00
}
}
void qmp_getfd(const char *fdname, Error **errp)
{
mon_fd_t *monfd;
int fd;
fd = qemu_chr_fe_get_msgfd(&cur_mon->chr);
if (fd == -1) {
error_setg(errp, QERR_FD_NOT_SUPPLIED);
return;
}
if (qemu_isdigit(fdname[0])) {
close(fd);
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "fdname",
"a name not starting with a digit");
return;
}
QLIST_FOREACH(monfd, &cur_mon->fds, next) {
if (strcmp(monfd->name, fdname) != 0) {
continue;
}
close(monfd->fd);
monfd->fd = fd;
return;
}
monfd = g_malloc0(sizeof(mon_fd_t));
monfd->name = g_strdup(fdname);
monfd->fd = fd;
QLIST_INSERT_HEAD(&cur_mon->fds, monfd, next);
}
void qmp_closefd(const char *fdname, Error **errp)
{
mon_fd_t *monfd;
QLIST_FOREACH(monfd, &cur_mon->fds, next) {
if (strcmp(monfd->name, fdname) != 0) {
continue;
}
QLIST_REMOVE(monfd, next);
close(monfd->fd);
g_free(monfd->name);
g_free(monfd);
return;
}
error_setg(errp, QERR_FD_NOT_FOUND, fdname);
}
int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp)
{
mon_fd_t *monfd;
QLIST_FOREACH(monfd, &mon->fds, next) {
int fd;
if (strcmp(monfd->name, fdname) != 0) {
continue;
}
fd = monfd->fd;
/* caller takes ownership of fd */
QLIST_REMOVE(monfd, next);
g_free(monfd->name);
g_free(monfd);
return fd;
}
error_setg(errp, "File descriptor named '%s' has not been found", fdname);
return -1;
}
static void monitor_fdset_cleanup(MonFdset *mon_fdset)
{
MonFdsetFd *mon_fdset_fd;
MonFdsetFd *mon_fdset_fd_next;
QLIST_FOREACH_SAFE(mon_fdset_fd, &mon_fdset->fds, next, mon_fdset_fd_next) {
if ((mon_fdset_fd->removed ||
(QLIST_EMPTY(&mon_fdset->dup_fds) && mon_refcount == 0)) &&
runstate_is_running()) {
close(mon_fdset_fd->fd);
g_free(mon_fdset_fd->opaque);
QLIST_REMOVE(mon_fdset_fd, next);
g_free(mon_fdset_fd);
}
}
if (QLIST_EMPTY(&mon_fdset->fds) && QLIST_EMPTY(&mon_fdset->dup_fds)) {
QLIST_REMOVE(mon_fdset, next);
g_free(mon_fdset);
}
}
static void monitor_fdsets_cleanup(void)
{
MonFdset *mon_fdset;
MonFdset *mon_fdset_next;
QLIST_FOREACH_SAFE(mon_fdset, &mon_fdsets, next, mon_fdset_next) {
monitor_fdset_cleanup(mon_fdset);
}
}
AddfdInfo *qmp_add_fd(bool has_fdset_id, int64_t fdset_id, bool has_opaque,
const char *opaque, Error **errp)
{
int fd;
Monitor *mon = cur_mon;
AddfdInfo *fdinfo;
fd = qemu_chr_fe_get_msgfd(&mon->chr);
if (fd == -1) {
error_setg(errp, QERR_FD_NOT_SUPPLIED);
goto error;
}
fdinfo = monitor_fdset_add_fd(fd, has_fdset_id, fdset_id,
has_opaque, opaque, errp);
if (fdinfo) {
return fdinfo;
}
error:
if (fd != -1) {
close(fd);
}
return NULL;
}
void qmp_remove_fd(int64_t fdset_id, bool has_fd, int64_t fd, Error **errp)
{
MonFdset *mon_fdset;
MonFdsetFd *mon_fdset_fd;
char fd_str[60];
QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
if (mon_fdset->id != fdset_id) {
continue;
}
QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) {
if (has_fd) {
if (mon_fdset_fd->fd != fd) {
continue;
}
mon_fdset_fd->removed = true;
break;
} else {
mon_fdset_fd->removed = true;
}
}
if (has_fd && !mon_fdset_fd) {
goto error;
}
monitor_fdset_cleanup(mon_fdset);
return;
}
error:
if (has_fd) {
snprintf(fd_str, sizeof(fd_str), "fdset-id:%" PRId64 ", fd:%" PRId64,
fdset_id, fd);
} else {
snprintf(fd_str, sizeof(fd_str), "fdset-id:%" PRId64, fdset_id);
}
error_setg(errp, QERR_FD_NOT_FOUND, fd_str);
}
FdsetInfoList *qmp_query_fdsets(Error **errp)
{
MonFdset *mon_fdset;
MonFdsetFd *mon_fdset_fd;
FdsetInfoList *fdset_list = NULL;
QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
FdsetInfoList *fdset_info = g_malloc0(sizeof(*fdset_info));
FdsetFdInfoList *fdsetfd_list = NULL;
fdset_info->value = g_malloc0(sizeof(*fdset_info->value));
fdset_info->value->fdset_id = mon_fdset->id;
QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) {
FdsetFdInfoList *fdsetfd_info;
fdsetfd_info = g_malloc0(sizeof(*fdsetfd_info));
fdsetfd_info->value = g_malloc0(sizeof(*fdsetfd_info->value));
fdsetfd_info->value->fd = mon_fdset_fd->fd;
if (mon_fdset_fd->opaque) {
fdsetfd_info->value->has_opaque = true;
fdsetfd_info->value->opaque = g_strdup(mon_fdset_fd->opaque);
} else {
fdsetfd_info->value->has_opaque = false;
}
fdsetfd_info->next = fdsetfd_list;
fdsetfd_list = fdsetfd_info;
}
fdset_info->value->fds = fdsetfd_list;
fdset_info->next = fdset_list;
fdset_list = fdset_info;
}
return fdset_list;
}
AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id,
bool has_opaque, const char *opaque,
Error **errp)
{
MonFdset *mon_fdset = NULL;
MonFdsetFd *mon_fdset_fd;
AddfdInfo *fdinfo;
if (has_fdset_id) {
QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
/* Break if match found or match impossible due to ordering by ID */
if (fdset_id <= mon_fdset->id) {
if (fdset_id < mon_fdset->id) {
mon_fdset = NULL;
}
break;
}
}
}
if (mon_fdset == NULL) {
int64_t fdset_id_prev = -1;
MonFdset *mon_fdset_cur = QLIST_FIRST(&mon_fdsets);
if (has_fdset_id) {
if (fdset_id < 0) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "fdset-id",
"a non-negative value");
return NULL;
}
/* Use specified fdset ID */
QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
mon_fdset_cur = mon_fdset;
if (fdset_id < mon_fdset_cur->id) {
break;
}
}
} else {
/* Use first available fdset ID */
QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
mon_fdset_cur = mon_fdset;
if (fdset_id_prev == mon_fdset_cur->id - 1) {
fdset_id_prev = mon_fdset_cur->id;
continue;
}
break;
}
}
mon_fdset = g_malloc0(sizeof(*mon_fdset));
if (has_fdset_id) {
mon_fdset->id = fdset_id;
} else {
mon_fdset->id = fdset_id_prev + 1;
}
/* The fdset list is ordered by fdset ID */
if (!mon_fdset_cur) {
QLIST_INSERT_HEAD(&mon_fdsets, mon_fdset, next);
} else if (mon_fdset->id < mon_fdset_cur->id) {
QLIST_INSERT_BEFORE(mon_fdset_cur, mon_fdset, next);
} else {
QLIST_INSERT_AFTER(mon_fdset_cur, mon_fdset, next);
}
}
mon_fdset_fd = g_malloc0(sizeof(*mon_fdset_fd));
mon_fdset_fd->fd = fd;
mon_fdset_fd->removed = false;
if (has_opaque) {
mon_fdset_fd->opaque = g_strdup(opaque);
}
QLIST_INSERT_HEAD(&mon_fdset->fds, mon_fdset_fd, next);
fdinfo = g_malloc0(sizeof(*fdinfo));
fdinfo->fdset_id = mon_fdset->id;
fdinfo->fd = mon_fdset_fd->fd;
return fdinfo;
}
int monitor_fdset_get_fd(int64_t fdset_id, int flags)
{
#ifndef _WIN32
MonFdset *mon_fdset;
MonFdsetFd *mon_fdset_fd;
int mon_fd_flags;
QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
if (mon_fdset->id != fdset_id) {
continue;
}
QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) {
mon_fd_flags = fcntl(mon_fdset_fd->fd, F_GETFL);
if (mon_fd_flags == -1) {
return -1;
}
if ((flags & O_ACCMODE) == (mon_fd_flags & O_ACCMODE)) {
return mon_fdset_fd->fd;
}
}
errno = EACCES;
return -1;
}
#endif
errno = ENOENT;
return -1;
}
int monitor_fdset_dup_fd_add(int64_t fdset_id, int dup_fd)
{
MonFdset *mon_fdset;
MonFdsetFd *mon_fdset_fd_dup;
QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
if (mon_fdset->id != fdset_id) {
continue;
}
QLIST_FOREACH(mon_fdset_fd_dup, &mon_fdset->dup_fds, next) {
if (mon_fdset_fd_dup->fd == dup_fd) {
return -1;
}
}
mon_fdset_fd_dup = g_malloc0(sizeof(*mon_fdset_fd_dup));
mon_fdset_fd_dup->fd = dup_fd;
QLIST_INSERT_HEAD(&mon_fdset->dup_fds, mon_fdset_fd_dup, next);
return 0;
}
return -1;
}
static int monitor_fdset_dup_fd_find_remove(int dup_fd, bool remove)
{
MonFdset *mon_fdset;
MonFdsetFd *mon_fdset_fd_dup;
QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
QLIST_FOREACH(mon_fdset_fd_dup, &mon_fdset->dup_fds, next) {
if (mon_fdset_fd_dup->fd == dup_fd) {
if (remove) {
QLIST_REMOVE(mon_fdset_fd_dup, next);
if (QLIST_EMPTY(&mon_fdset->dup_fds)) {
monitor_fdset_cleanup(mon_fdset);
}
return -1;
} else {
return mon_fdset->id;
}
}
}
}
return -1;
}
int monitor_fdset_dup_fd_find(int dup_fd)
{
return monitor_fdset_dup_fd_find_remove(dup_fd, false);
}
void monitor_fdset_dup_fd_remove(int dup_fd)
{
monitor_fdset_dup_fd_find_remove(dup_fd, true);
}
int monitor_fd_param(Monitor *mon, const char *fdname, Error **errp)
{
int fd;
Error *local_err = NULL;
if (!qemu_isdigit(fdname[0]) && mon) {
fd = monitor_get_fd(mon, fdname, &local_err);
} else {
fd = qemu_parse_fd(fdname);
if (fd == -1) {
error_setg(&local_err, "Invalid file descriptor number '%s'",
fdname);
}
}
if (local_err) {
error_propagate(errp, local_err);
assert(fd == -1);
} else {
assert(fd != -1);
}
return fd;
}
/* Please update hmp-commands.hx when adding or changing commands */
static mon_cmd_t info_cmds[] = {
#include "hmp-commands-info.h"
{ NULL, NULL, },
};
/* mon_cmds and info_cmds would be sorted at runtime */
static mon_cmd_t mon_cmds[] = {
#include "hmp-commands.h"
{ NULL, NULL, },
};
/*******************************************************************/
static const char *pch;
Replace all setjmp()/longjmp() with sigsetjmp()/siglongjmp() The setjmp() function doesn't specify whether signal masks are saved and restored; on Linux they are not, but on BSD (including MacOSX) they are. We want to have consistent behaviour across platforms, so we should always use "don't save/restore signal mask" (this is also generally going to be faster). This also works around a bug in MacOSX where the signal-restoration on longjmp() affects the signal mask for a completely different thread, not just the mask for the thread which did the longjmp. The most visible effect of this was that ctrl-C was ignored on MacOSX because the CPU thread did a longjmp which resulted in its signal mask being applied to every thread, so that all threads had SIGINT and SIGTERM blocked. The POSIX-sanctioned portable way to do a jump without affecting signal masks is to siglongjmp() to a sigjmp_buf which was created by calling sigsetjmp() with a zero savemask parameter, so change all uses of setjmp()/longjmp() accordingly. [Technically POSIX allows sigsetjmp(buf, 0) to save the signal mask; however the following siglongjmp() must not restore the signal mask, so the pair can be effectively considered as "sigjmp/longjmp which don't touch the mask".] For Windows we provide a trivial sigsetjmp/siglongjmp in terms of setjmp/longjmp -- this is OK because no user will ever pass a non-zero savemask. The setjmp() uses in tests/tcg/test-i386.c and tests/tcg/linux-test.c are left untouched because these are self-contained singlethreaded test programs intended to be run under QEMU's Linux emulation, so they have neither the portability nor the multithreading issues to deal with. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <rth@twiddle.net> Tested-by: Stefan Weil <sw@weilnetz.de> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
2013-02-20 19:21:09 +04:00
static sigjmp_buf expr_env;
static void GCC_FMT_ATTR(2, 3) QEMU_NORETURN
expr_error(Monitor *mon, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
monitor_vprintf(mon, fmt, ap);
monitor_printf(mon, "\n");
va_end(ap);
Replace all setjmp()/longjmp() with sigsetjmp()/siglongjmp() The setjmp() function doesn't specify whether signal masks are saved and restored; on Linux they are not, but on BSD (including MacOSX) they are. We want to have consistent behaviour across platforms, so we should always use "don't save/restore signal mask" (this is also generally going to be faster). This also works around a bug in MacOSX where the signal-restoration on longjmp() affects the signal mask for a completely different thread, not just the mask for the thread which did the longjmp. The most visible effect of this was that ctrl-C was ignored on MacOSX because the CPU thread did a longjmp which resulted in its signal mask being applied to every thread, so that all threads had SIGINT and SIGTERM blocked. The POSIX-sanctioned portable way to do a jump without affecting signal masks is to siglongjmp() to a sigjmp_buf which was created by calling sigsetjmp() with a zero savemask parameter, so change all uses of setjmp()/longjmp() accordingly. [Technically POSIX allows sigsetjmp(buf, 0) to save the signal mask; however the following siglongjmp() must not restore the signal mask, so the pair can be effectively considered as "sigjmp/longjmp which don't touch the mask".] For Windows we provide a trivial sigsetjmp/siglongjmp in terms of setjmp/longjmp -- this is OK because no user will ever pass a non-zero savemask. The setjmp() uses in tests/tcg/test-i386.c and tests/tcg/linux-test.c are left untouched because these are self-contained singlethreaded test programs intended to be run under QEMU's Linux emulation, so they have neither the portability nor the multithreading issues to deal with. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <rth@twiddle.net> Tested-by: Stefan Weil <sw@weilnetz.de> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
2013-02-20 19:21:09 +04:00
siglongjmp(expr_env, 1);
}
/* return 0 if OK, -1 if not found */
static int get_monitor_def(target_long *pval, const char *name)
{
const MonitorDef *md = target_monitor_defs();
CPUState *cs = mon_get_cpu();
void *ptr;
uint64_t tmp = 0;
int ret;
if (cs == NULL || md == NULL) {
return -1;
}
for(; md->name != NULL; md++) {
if (compare_cmd(name, md->name)) {
if (md->get_value) {
*pval = md->get_value(md, md->offset);
} else {
CPUArchState *env = mon_get_cpu_env();
ptr = (uint8_t *)env + md->offset;
switch(md->type) {
case MD_I32:
*pval = *(int32_t *)ptr;
break;
case MD_TLONG:
*pval = *(target_long *)ptr;
break;
default:
*pval = 0;
break;
}
}
return 0;
}
}
ret = target_get_monitor_def(cs, name, &tmp);
if (!ret) {
*pval = (target_long) tmp;
}
return ret;
}
static void next(void)
{
if (*pch != '\0') {
pch++;
while (qemu_isspace(*pch))
pch++;
}
}
static int64_t expr_sum(Monitor *mon);
static int64_t expr_unary(Monitor *mon)
{
int64_t n;
char *p;
int ret;
switch(*pch) {
case '+':
next();
n = expr_unary(mon);
break;
case '-':
next();
n = -expr_unary(mon);
break;
case '~':
next();
n = ~expr_unary(mon);
break;
case '(':
next();
n = expr_sum(mon);
if (*pch != ')') {
expr_error(mon, "')' expected");
}
next();
break;
case '\'':
pch++;
if (*pch == '\0')
expr_error(mon, "character constant expected");
n = *pch;
pch++;
if (*pch != '\'')
expr_error(mon, "missing terminating \' character");
next();
break;
case '$':
{
char buf[128], *q;
target_long reg=0;
pch++;
q = buf;
while ((*pch >= 'a' && *pch <= 'z') ||
(*pch >= 'A' && *pch <= 'Z') ||
(*pch >= '0' && *pch <= '9') ||
*pch == '_' || *pch == '.') {
if ((q - buf) < sizeof(buf) - 1)
*q++ = *pch;
pch++;
}
while (qemu_isspace(*pch))
pch++;
*q = 0;
ret = get_monitor_def(&reg, buf);
if (ret < 0)
expr_error(mon, "unknown register");
n = reg;
}
break;
case '\0':
expr_error(mon, "unexpected end of expression");
n = 0;
break;
default:
errno = 0;
n = strtoull(pch, &p, 0);
if (errno == ERANGE) {
expr_error(mon, "number too large");
}
if (pch == p) {
expr_error(mon, "invalid char '%c' in expression", *p);
}
pch = p;
while (qemu_isspace(*pch))
pch++;
break;
}
return n;
}
static int64_t expr_prod(Monitor *mon)
{
int64_t val, val2;
int op;
val = expr_unary(mon);
for(;;) {
op = *pch;
if (op != '*' && op != '/' && op != '%')
break;
next();
val2 = expr_unary(mon);
switch(op) {
default:
case '*':
val *= val2;
break;
case '/':
case '%':
if (val2 == 0)
expr_error(mon, "division by zero");
if (op == '/')
val /= val2;
else
val %= val2;
break;
}
}
return val;
}
static int64_t expr_logic(Monitor *mon)
{
int64_t val, val2;
int op;
val = expr_prod(mon);
for(;;) {
op = *pch;
if (op != '&' && op != '|' && op != '^')
break;
next();
val2 = expr_prod(mon);
switch(op) {
default:
case '&':
val &= val2;
break;
case '|':
val |= val2;
break;
case '^':
val ^= val2;
break;
}
}
return val;
}
static int64_t expr_sum(Monitor *mon)
{
int64_t val, val2;
int op;
val = expr_logic(mon);
for(;;) {
op = *pch;
if (op != '+' && op != '-')
break;
next();
val2 = expr_logic(mon);
if (op == '+')
val += val2;
else
val -= val2;
}
return val;
}
static int get_expr(Monitor *mon, int64_t *pval, const char **pp)
{
pch = *pp;
Replace all setjmp()/longjmp() with sigsetjmp()/siglongjmp() The setjmp() function doesn't specify whether signal masks are saved and restored; on Linux they are not, but on BSD (including MacOSX) they are. We want to have consistent behaviour across platforms, so we should always use "don't save/restore signal mask" (this is also generally going to be faster). This also works around a bug in MacOSX where the signal-restoration on longjmp() affects the signal mask for a completely different thread, not just the mask for the thread which did the longjmp. The most visible effect of this was that ctrl-C was ignored on MacOSX because the CPU thread did a longjmp which resulted in its signal mask being applied to every thread, so that all threads had SIGINT and SIGTERM blocked. The POSIX-sanctioned portable way to do a jump without affecting signal masks is to siglongjmp() to a sigjmp_buf which was created by calling sigsetjmp() with a zero savemask parameter, so change all uses of setjmp()/longjmp() accordingly. [Technically POSIX allows sigsetjmp(buf, 0) to save the signal mask; however the following siglongjmp() must not restore the signal mask, so the pair can be effectively considered as "sigjmp/longjmp which don't touch the mask".] For Windows we provide a trivial sigsetjmp/siglongjmp in terms of setjmp/longjmp -- this is OK because no user will ever pass a non-zero savemask. The setjmp() uses in tests/tcg/test-i386.c and tests/tcg/linux-test.c are left untouched because these are self-contained singlethreaded test programs intended to be run under QEMU's Linux emulation, so they have neither the portability nor the multithreading issues to deal with. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <rth@twiddle.net> Tested-by: Stefan Weil <sw@weilnetz.de> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
2013-02-20 19:21:09 +04:00
if (sigsetjmp(expr_env, 0)) {
*pp = pch;
return -1;
}
while (qemu_isspace(*pch))
pch++;
*pval = expr_sum(mon);
*pp = pch;
return 0;
}
static int get_double(Monitor *mon, double *pval, const char **pp)
{
const char *p = *pp;
char *tailp;
double d;
d = strtod(p, &tailp);
if (tailp == p) {
monitor_printf(mon, "Number expected\n");
return -1;
}
if (d != d || d - d != 0) {
/* NaN or infinity */
monitor_printf(mon, "Bad number\n");
return -1;
}
*pval = d;
*pp = tailp;
return 0;
}
/*
* Store the command-name in cmdname, and return a pointer to
* the remaining of the command string.
*/
static const char *get_command_name(const char *cmdline,
char *cmdname, size_t nlen)
{
size_t len;
const char *p, *pstart;
p = cmdline;
while (qemu_isspace(*p))
p++;
if (*p == '\0')
return NULL;
pstart = p;
while (*p != '\0' && *p != '/' && !qemu_isspace(*p))
p++;
len = p - pstart;
if (len > nlen - 1)
len = nlen - 1;
memcpy(cmdname, pstart, len);
cmdname[len] = '\0';
return p;
}
/**
* Read key of 'type' into 'key' and return the current
* 'type' pointer.
*/
static char *key_get_info(const char *type, char **key)
{
size_t len;
char *p, *str;
if (*type == ',')
type++;
p = strchr(type, ':');
if (!p) {
*key = NULL;
return NULL;
}
len = p - type;
str = g_malloc(len + 1);
memcpy(str, type, len);
str[len] = '\0';
*key = str;
return ++p;
}
static int default_fmt_format = 'x';
static int default_fmt_size = 4;
static int is_valid_option(const char *c, const char *typestr)
{
char option[3];
option[0] = '-';
option[1] = *c;
option[2] = '\0';
typestr = strstr(typestr, option);
return (typestr != NULL);
}
static const mon_cmd_t *search_dispatch_table(const mon_cmd_t *disp_table,
const char *cmdname)
{
const mon_cmd_t *cmd;
for (cmd = disp_table; cmd->name != NULL; cmd++) {
if (compare_cmd(cmdname, cmd->name)) {
return cmd;
}
}
return NULL;
}
/*
* Parse command name from @cmdp according to command table @table.
* If blank, return NULL.
* Else, if no valid command can be found, report to @mon, and return
* NULL.
* Else, change @cmdp to point right behind the name, and return its
* command table entry.
* Do not assume the return value points into @table! It doesn't when
* the command is found in a sub-command table.
*/
static const mon_cmd_t *monitor_parse_command(Monitor *mon,
const char *cmdp_start,
const char **cmdp,
mon_cmd_t *table)
{
const char *p;
const mon_cmd_t *cmd;
char cmdname[256];
/* extract the command name */
p = get_command_name(*cmdp, cmdname, sizeof(cmdname));
if (!p)
return NULL;
cmd = search_dispatch_table(table, cmdname);
if (!cmd) {
monitor_printf(mon, "unknown command: '%.*s'\n",
(int)(p - cmdp_start), cmdp_start);
return NULL;
}
/* filter out following useless space */
while (qemu_isspace(*p)) {
p++;
}
*cmdp = p;
/* search sub command */
if (cmd->sub_table != NULL && *p != '\0') {
return monitor_parse_command(mon, cmdp_start, cmdp, cmd->sub_table);
}
return cmd;
}
/*
* Parse arguments for @cmd.
* If it can't be parsed, report to @mon, and return NULL.
* Else, insert command arguments into a QDict, and return it.
* Note: On success, caller has to free the QDict structure.
*/
static QDict *monitor_parse_arguments(Monitor *mon,
const char **endp,
const mon_cmd_t *cmd)
{
const char *typestr;
char *key;
int c;
const char *p = *endp;
char buf[1024];
QDict *qdict = qdict_new();
/* parse the parameters */
typestr = cmd->args_type;
for(;;) {
typestr = key_get_info(typestr, &key);
if (!typestr)
break;
c = *typestr;
typestr++;
switch(c) {
case 'F':
case 'B':
case 's':
{
int ret;
while (qemu_isspace(*p))
p++;
if (*typestr == '?') {
typestr++;
if (*p == '\0') {
/* no optional string: NULL argument */
break;
}
}
ret = get_str(buf, sizeof(buf), &p);
if (ret < 0) {
switch(c) {
case 'F':
monitor_printf(mon, "%s: filename expected\n",
cmd->name);
break;
case 'B':
monitor_printf(mon, "%s: block device name expected\n",
cmd->name);
break;
default:
monitor_printf(mon, "%s: string expected\n", cmd->name);
break;
}
goto fail;
}
qdict_put_str(qdict, key, buf);
}
break;
case 'O':
{
QemuOptsList *opts_list;
QemuOpts *opts;
opts_list = qemu_find_opts(key);
if (!opts_list || opts_list->desc->name) {
goto bad_type;
}
while (qemu_isspace(*p)) {
p++;
}
if (!*p)
break;
if (get_str(buf, sizeof(buf), &p) < 0) {
goto fail;
}
QemuOpts: Wean off qerror_report_err() qerror_report_err() is a transitional interface to help with converting existing monitor commands to QMP. It should not be used elsewhere. The only remaining user in qemu-option.c is qemu_opts_parse(). Is it used in QMP context? If not, we can simply replace qerror_report_err() by error_report_err(). The uses in qemu-img.c, qemu-io.c, qemu-nbd.c and under tests/ are clearly not in QMP context. The uses in vl.c aren't either, because the only QMP command handlers there are qmp_query_status() and qmp_query_machines(), and they don't call it. Remaining uses: * drive_def(): Command line -drive and such, HMP drive_add and pci_add * hmp_chardev_add(): HMP chardev-add * monitor_parse_command(): HMP core * tmp_config_parse(): Command line -tpmdev * net_host_device_add(): HMP host_net_add * net_client_parse(): Command line -net and -netdev * qemu_global_option(): Command line -global * vnc_parse_func(): Command line -display, -vnc, default display, HMP change, QMP change. Bummer. * qemu_pci_hot_add_nic(): HMP pci_add * usb_net_init(): Command line -usbdevice, HMP usb_add Propagate errors through qemu_opts_parse(). Create a convenience function qemu_opts_parse_noisily() that passes errors to error_report_err(). Switch all non-QMP users outside tests to it. That leaves vnc_parse_func(). Propagate errors through it. Since I'm touching it anyway, rename it to vnc_parse(). Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Reviewed-by: Luiz Capitulino <lcapitulino@redhat.com>
2015-02-13 14:50:26 +03:00
opts = qemu_opts_parse_noisily(opts_list, buf, true);
if (!opts) {
goto fail;
}
qemu_opts_to_qdict(opts, qdict);
qemu_opts_del(opts);
}
break;
case '/':
{
int count, format, size;
while (qemu_isspace(*p))
p++;
if (*p == '/') {
/* format found */
p++;
count = 1;
if (qemu_isdigit(*p)) {
count = 0;
while (qemu_isdigit(*p)) {
count = count * 10 + (*p - '0');
p++;
}
}
size = -1;
format = -1;
for(;;) {
switch(*p) {
case 'o':
case 'd':
case 'u':
case 'x':
case 'i':
case 'c':
format = *p++;
break;
case 'b':
size = 1;
p++;
break;
case 'h':
size = 2;
p++;
break;
case 'w':
size = 4;
p++;
break;
case 'g':
case 'L':
size = 8;
p++;
break;
default:
goto next;
}
}
next:
if (*p != '\0' && !qemu_isspace(*p)) {
monitor_printf(mon, "invalid char in format: '%c'\n",
*p);
goto fail;
}
if (format < 0)
format = default_fmt_format;
if (format != 'i') {
/* for 'i', not specifying a size gives -1 as size */
if (size < 0)
size = default_fmt_size;
default_fmt_size = size;
}
default_fmt_format = format;
} else {
count = 1;
format = default_fmt_format;
if (format != 'i') {
size = default_fmt_size;
} else {
size = -1;
}
}
qdict_put_int(qdict, "count", count);
qdict_put_int(qdict, "format", format);
qdict_put_int(qdict, "size", size);
}
break;
case 'i':
case 'l':
case 'M':
{
int64_t val;
while (qemu_isspace(*p))
p++;
if (*typestr == '?' || *typestr == '.') {
if (*typestr == '?') {
if (*p == '\0') {
typestr++;
break;
}
} else {
if (*p == '.') {
p++;
while (qemu_isspace(*p))
p++;
} else {
typestr++;
break;
}
}
typestr++;
}
if (get_expr(mon, &val, &p))
goto fail;
/* Check if 'i' is greater than 32-bit */
if ((c == 'i') && ((val >> 32) & 0xffffffff)) {
monitor_printf(mon, "\'%s\' has failed: ", cmd->name);
monitor_printf(mon, "integer is for 32-bit values\n");
goto fail;
} else if (c == 'M') {
if (val < 0) {
monitor_printf(mon, "enter a positive value\n");
goto fail;
}
val <<= 20;
}
qdict_put_int(qdict, key, val);
}
break;
case 'o':
{
int ret;
uint64_t val;
char *end;
while (qemu_isspace(*p)) {
p++;
}
if (*typestr == '?') {
typestr++;
if (*p == '\0') {
break;
}
}
ret = qemu_strtosz_MiB(p, &end, &val);
if (ret < 0 || val > INT64_MAX) {
monitor_printf(mon, "invalid size\n");
goto fail;
}
qdict_put_int(qdict, key, val);
p = end;
}
break;
case 'T':
{
double val;
while (qemu_isspace(*p))
p++;
if (*typestr == '?') {
typestr++;
if (*p == '\0') {
break;
}
}
if (get_double(mon, &val, &p) < 0) {
goto fail;
}
if (p[0] && p[1] == 's') {
switch (*p) {
case 'm':
val /= 1e3; p += 2; break;
case 'u':
val /= 1e6; p += 2; break;
case 'n':
val /= 1e9; p += 2; break;
}
}
if (*p && !qemu_isspace(*p)) {
monitor_printf(mon, "Unknown unit suffix\n");
goto fail;
}
qdict_put(qdict, key, qnum_from_double(val));
}
break;
case 'b':
{
const char *beg;
bool val;
while (qemu_isspace(*p)) {
p++;
}
beg = p;
while (qemu_isgraph(*p)) {
p++;
}
if (p - beg == 2 && !memcmp(beg, "on", p - beg)) {
val = true;
} else if (p - beg == 3 && !memcmp(beg, "off", p - beg)) {
val = false;
} else {
monitor_printf(mon, "Expected 'on' or 'off'\n");
goto fail;
}
qdict_put_bool(qdict, key, val);
}
break;
case '-':
{
const char *tmp = p;
int skip_key = 0;
/* option */
c = *typestr++;
if (c == '\0')
goto bad_type;
while (qemu_isspace(*p))
p++;
if (*p == '-') {
p++;
if(c != *p) {
if(!is_valid_option(p, typestr)) {
monitor_printf(mon, "%s: unsupported option -%c\n",
cmd->name, *p);
goto fail;
} else {
skip_key = 1;
}
}
if(skip_key) {
p = tmp;
} else {
/* has option */
p++;
qdict_put_bool(qdict, key, true);
}
}
}
break;
case 'S':
{
/* package all remaining string */
int len;
while (qemu_isspace(*p)) {
p++;
}
if (*typestr == '?') {
typestr++;
if (*p == '\0') {
/* no remaining string: NULL argument */
break;
}
}
len = strlen(p);
if (len <= 0) {
monitor_printf(mon, "%s: string expected\n",
cmd->name);
goto fail;
}
qdict_put_str(qdict, key, p);
p += len;
}
break;
default:
bad_type:
monitor_printf(mon, "%s: unknown type '%c'\n", cmd->name, c);
goto fail;
}
g_free(key);
key = NULL;
}
/* check that all arguments were parsed */
while (qemu_isspace(*p))
p++;
if (*p != '\0') {
monitor_printf(mon, "%s: extraneous characters at the end of line\n",
cmd->name);
goto fail;
}
return qdict;
fail:
QDECREF(qdict);
g_free(key);
return NULL;
}
static void handle_hmp_command(Monitor *mon, const char *cmdline)
{
QDict *qdict;
const mon_cmd_t *cmd;
trace_handle_hmp_command(mon, cmdline);
cmd = monitor_parse_command(mon, cmdline, &cmdline, mon->cmd_table);
if (!cmd) {
return;
}
qdict = monitor_parse_arguments(mon, &cmdline, cmd);
if (!qdict) {
monitor_printf(mon, "Try \"help %s\" for more information\n",
cmd->name);
return;
}
cmd->cmd(mon, qdict);
QDECREF(qdict);
}
static void cmd_completion(Monitor *mon, const char *name, const char *list)
{
const char *p, *pstart;
char cmd[128];
int len;
p = list;
for(;;) {
pstart = p;
p = strchr(p, '|');
if (!p)
p = pstart + strlen(pstart);
len = p - pstart;
if (len > sizeof(cmd) - 2)
len = sizeof(cmd) - 2;
memcpy(cmd, pstart, len);
cmd[len] = '\0';
if (name[0] == '\0' || !strncmp(name, cmd, strlen(name))) {
readline_add_completion(mon->rs, cmd);
}
if (*p == '\0')
break;
p++;
}
}
static void file_completion(Monitor *mon, const char *input)
{
DIR *ffs;
struct dirent *d;
char path[1024];
char file[1024], file_prefix[1024];
int input_path_len;
const char *p;
p = strrchr(input, '/');
if (!p) {
input_path_len = 0;
pstrcpy(file_prefix, sizeof(file_prefix), input);
pstrcpy(path, sizeof(path), ".");
} else {
input_path_len = p - input + 1;
memcpy(path, input, input_path_len);
if (input_path_len > sizeof(path) - 1)
input_path_len = sizeof(path) - 1;
path[input_path_len] = '\0';
pstrcpy(file_prefix, sizeof(file_prefix), p + 1);
}
ffs = opendir(path);
if (!ffs)
return;
for(;;) {
struct stat sb;
d = readdir(ffs);
if (!d)
break;
if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) {
continue;
}
if (strstart(d->d_name, file_prefix, NULL)) {
memcpy(file, input, input_path_len);
if (input_path_len < sizeof(file))
pstrcpy(file + input_path_len, sizeof(file) - input_path_len,
d->d_name);
/* stat the file to find out if it's a directory.
* In that case add a slash to speed up typing long paths
*/
if (stat(file, &sb) == 0 && S_ISDIR(sb.st_mode)) {
pstrcat(file, sizeof(file), "/");
}
readline_add_completion(mon->rs, file);
}
}
closedir(ffs);
}
static const char *next_arg_type(const char *typestr)
{
const char *p = strchr(typestr, ':');
return (p != NULL ? ++p : typestr);
}
static void add_completion_option(ReadLineState *rs, const char *str,
const char *option)
{
if (!str || !option) {
return;
}
if (!strncmp(option, str, strlen(str))) {
readline_add_completion(rs, option);
}
}
void chardev_add_completion(ReadLineState *rs, int nb_args, const char *str)
{
size_t len;
ChardevBackendInfoList *list, *start;
if (nb_args != 2) {
return;
}
len = strlen(str);
readline_set_completion_index(rs, len);
start = list = qmp_query_chardev_backends(NULL);
while (list) {
const char *chr_name = list->value->name;
if (!strncmp(chr_name, str, len)) {
readline_add_completion(rs, chr_name);
}
list = list->next;
}
qapi_free_ChardevBackendInfoList(start);
}
void netdev_add_completion(ReadLineState *rs, int nb_args, const char *str)
{
size_t len;
int i;
if (nb_args != 2) {
return;
}
len = strlen(str);
readline_set_completion_index(rs, len);
for (i = 0; i < NET_CLIENT_DRIVER__MAX; i++) {
add_completion_option(rs, str, NetClientDriver_str(i));
}
}
void device_add_completion(ReadLineState *rs, int nb_args, const char *str)
{
GSList *list, *elt;
size_t len;
if (nb_args != 2) {
return;
}
len = strlen(str);
readline_set_completion_index(rs, len);
list = elt = object_class_get_list(TYPE_DEVICE, false);
while (elt) {
const char *name;
DeviceClass *dc = OBJECT_CLASS_CHECK(DeviceClass, elt->data,
TYPE_DEVICE);
name = object_class_get_name(OBJECT_CLASS(dc));
qdev: Replace cannot_instantiate_with_device_add_yet with !user_creatable cannot_instantiate_with_device_add_yet was introduced by commit efec3dd631d94160288392721a5f9c39e50fb2bc to replace no_user. It was supposed to be a temporary measure. When it was introduced, we had 54 cannot_instantiate_with_device_add_yet=true lines in the code. Today (3 years later) this number has not shrunk: we now have 57 cannot_instantiate_with_device_add_yet=true lines. I think it is safe to say it is not a temporary measure, and we won't see the flag go away soon. Instead of a long field name that misleads people to believe it is temporary, replace it a shorter and less misleading field: user_creatable. Except for code comments, changes were generated using the following Coccinelle patch: @@ expression DC; @@ ( -DC->cannot_instantiate_with_device_add_yet = false; +DC->user_creatable = true; | -DC->cannot_instantiate_with_device_add_yet = true; +DC->user_creatable = false; ) @@ typedef ObjectClass; expression dc; identifier class, data; @@ static void device_class_init(ObjectClass *class, void *data) { ... dc->hotpluggable = true; +dc->user_creatable = true; ... } @@ @@ struct DeviceClass { ... -bool cannot_instantiate_with_device_add_yet; +bool user_creatable; ... } @@ expression DC; @@ ( -!DC->cannot_instantiate_with_device_add_yet +DC->user_creatable | -DC->cannot_instantiate_with_device_add_yet +!DC->user_creatable ) Cc: Alistair Francis <alistair.francis@xilinx.com> Cc: Laszlo Ersek <lersek@redhat.com> Cc: Marcel Apfelbaum <marcel@redhat.com> Cc: Markus Armbruster <armbru@redhat.com> Cc: Peter Maydell <peter.maydell@linaro.org> Cc: Thomas Huth <thuth@redhat.com> Acked-by: Alistair Francis <alistair.francis@xilinx.com> Reviewed-by: Thomas Huth <thuth@redhat.com> Reviewed-by: Marcel Apfelbaum <marcel@redhat.com> Acked-by: Marcel Apfelbaum <marcel@redhat.com> Signed-off-by: Eduardo Habkost <ehabkost@redhat.com> Message-Id: <20170503203604.31462-2-ehabkost@redhat.com> [ehabkost: kept "TODO remove once we're there" comment] Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
2017-05-03 23:35:44 +03:00
if (dc->user_creatable
&& !strncmp(name, str, len)) {
readline_add_completion(rs, name);
}
elt = elt->next;
}
g_slist_free(list);
}
void object_add_completion(ReadLineState *rs, int nb_args, const char *str)
{
GSList *list, *elt;
size_t len;
if (nb_args != 2) {
return;
}
len = strlen(str);
readline_set_completion_index(rs, len);
list = elt = object_class_get_list(TYPE_USER_CREATABLE, false);
while (elt) {
const char *name;
name = object_class_get_name(OBJECT_CLASS(elt->data));
if (!strncmp(name, str, len) && strcmp(name, TYPE_USER_CREATABLE)) {
readline_add_completion(rs, name);
}
elt = elt->next;
}
g_slist_free(list);
}
static void peripheral_device_del_completion(ReadLineState *rs,
const char *str, size_t len)
{
Object *peripheral = container_get(qdev_get_machine(), "/peripheral");
GSList *list, *item;
list = qdev_build_hotpluggable_device_list(peripheral);
if (!list) {
return;
}
for (item = list; item; item = g_slist_next(item)) {
DeviceState *dev = item->data;
if (dev->id && !strncmp(str, dev->id, len)) {
readline_add_completion(rs, dev->id);
}
}
g_slist_free(list);
}
void chardev_remove_completion(ReadLineState *rs, int nb_args, const char *str)
{
size_t len;
ChardevInfoList *list, *start;
if (nb_args != 2) {
return;
}
len = strlen(str);
readline_set_completion_index(rs, len);
start = list = qmp_query_chardev(NULL);
while (list) {
ChardevInfo *chr = list->value;
if (!strncmp(chr->label, str, len)) {
readline_add_completion(rs, chr->label);
}
list = list->next;
}
qapi_free_ChardevInfoList(start);
}
static void ringbuf_completion(ReadLineState *rs, const char *str)
{
size_t len;
ChardevInfoList *list, *start;
len = strlen(str);
readline_set_completion_index(rs, len);
start = list = qmp_query_chardev(NULL);
while (list) {
ChardevInfo *chr_info = list->value;
if (!strncmp(chr_info->label, str, len)) {
Chardev *chr = qemu_chr_find(chr_info->label);
if (chr && CHARDEV_IS_RINGBUF(chr)) {
readline_add_completion(rs, chr_info->label);
}
}
list = list->next;
}
qapi_free_ChardevInfoList(start);
}
void ringbuf_write_completion(ReadLineState *rs, int nb_args, const char *str)
{
if (nb_args != 2) {
return;
}
ringbuf_completion(rs, str);
}
void device_del_completion(ReadLineState *rs, int nb_args, const char *str)
{
size_t len;
if (nb_args != 2) {
return;
}
len = strlen(str);
readline_set_completion_index(rs, len);
peripheral_device_del_completion(rs, str, len);
}
void object_del_completion(ReadLineState *rs, int nb_args, const char *str)
{
ObjectPropertyInfoList *list, *start;
size_t len;
if (nb_args != 2) {
return;
}
len = strlen(str);
readline_set_completion_index(rs, len);
start = list = qmp_qom_list("/objects", NULL);
while (list) {
ObjectPropertyInfo *info = list->value;
if (!strncmp(info->type, "child<", 5)
&& !strncmp(info->name, str, len)) {
readline_add_completion(rs, info->name);
}
list = list->next;
}
qapi_free_ObjectPropertyInfoList(start);
}
void sendkey_completion(ReadLineState *rs, int nb_args, const char *str)
{
int i;
char *sep;
size_t len;
if (nb_args != 2) {
return;
}
sep = strrchr(str, '-');
if (sep) {
str = sep + 1;
}
len = strlen(str);
readline_set_completion_index(rs, len);
for (i = 0; i < Q_KEY_CODE__MAX; i++) {
if (!strncmp(str, QKeyCode_str(i), len)) {
readline_add_completion(rs, QKeyCode_str(i));
}
}
}
void set_link_completion(ReadLineState *rs, int nb_args, const char *str)
{
size_t len;
len = strlen(str);
readline_set_completion_index(rs, len);
if (nb_args == 2) {
NetClientState *ncs[MAX_QUEUE_NUM];
int count, i;
count = qemu_find_net_clients_except(NULL, ncs,
qapi: Change Netdev into a flat union This is a mostly-mechanical conversion that creates a new flat union 'Netdev' QAPI type that covers all the branches of the former 'NetClientOptions' simple union, where the branches are now listed in a new 'NetClientDriver' enum rather than generated from the simple union. The existence of a flat union has no change to the command line syntax accepted for new code, and will make it possible for a future patch to switch the QMP command to parse a boxed union for no change to valid QMP; but it does have some ripple effect on the C code when dealing with the new types. While making the conversion, note that the 'NetLegacy' type remains unchanged: it applies only to legacy command line options, and will not be ported to QMP, so it should remain a wrapper around a simple union; to avoid confusion, the type named 'NetClientOptions' is now gone, and we introduce 'NetLegacyOptions' in its place. Then, in the C code, we convert from NetLegacy to Netdev as soon as possible, so that the bulk of the net stack only has to deal with one QAPI type, not two. Note that since the old legacy code always rejected 'hubport', we can just omit that branch from the new 'NetLegacyOptions' simple union. Based on an idea originally by Zoltán Kővágó <DirtY.iCE.hu@gmail.com>: Message-Id: <01a527fbf1a5de880091f98cf011616a78adeeee.1441627176.git.DirtY.iCE.hu@gmail.com> although the sed script in that patch no longer applies due to other changes in the tree since then, and I also did some manual cleanups (such as fixing whitespace to keep checkpatch happy). Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <1468468228-27827-13-git-send-email-eblake@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> [Fixup from Eric squashed in] Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-07-14 06:50:23 +03:00
NET_CLIENT_DRIVER_NONE,
MAX_QUEUE_NUM);
for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) {
const char *name = ncs[i]->name;
if (!strncmp(str, name, len)) {
readline_add_completion(rs, name);
}
}
} else if (nb_args == 3) {
add_completion_option(rs, str, "on");
add_completion_option(rs, str, "off");
}
}
void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str)
{
int len, count, i;
NetClientState *ncs[MAX_QUEUE_NUM];
if (nb_args != 2) {
return;
}
len = strlen(str);
readline_set_completion_index(rs, len);
qapi: Change Netdev into a flat union This is a mostly-mechanical conversion that creates a new flat union 'Netdev' QAPI type that covers all the branches of the former 'NetClientOptions' simple union, where the branches are now listed in a new 'NetClientDriver' enum rather than generated from the simple union. The existence of a flat union has no change to the command line syntax accepted for new code, and will make it possible for a future patch to switch the QMP command to parse a boxed union for no change to valid QMP; but it does have some ripple effect on the C code when dealing with the new types. While making the conversion, note that the 'NetLegacy' type remains unchanged: it applies only to legacy command line options, and will not be ported to QMP, so it should remain a wrapper around a simple union; to avoid confusion, the type named 'NetClientOptions' is now gone, and we introduce 'NetLegacyOptions' in its place. Then, in the C code, we convert from NetLegacy to Netdev as soon as possible, so that the bulk of the net stack only has to deal with one QAPI type, not two. Note that since the old legacy code always rejected 'hubport', we can just omit that branch from the new 'NetLegacyOptions' simple union. Based on an idea originally by Zoltán Kővágó <DirtY.iCE.hu@gmail.com>: Message-Id: <01a527fbf1a5de880091f98cf011616a78adeeee.1441627176.git.DirtY.iCE.hu@gmail.com> although the sed script in that patch no longer applies due to other changes in the tree since then, and I also did some manual cleanups (such as fixing whitespace to keep checkpatch happy). Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <1468468228-27827-13-git-send-email-eblake@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> [Fixup from Eric squashed in] Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-07-14 06:50:23 +03:00
count = qemu_find_net_clients_except(NULL, ncs, NET_CLIENT_DRIVER_NIC,
MAX_QUEUE_NUM);
for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) {
QemuOpts *opts;
const char *name = ncs[i]->name;
if (strncmp(str, name, len)) {
continue;
}
opts = qemu_opts_find(qemu_find_opts_err("netdev", NULL), name);
if (opts) {
readline_add_completion(rs, name);
}
}
}
void info_trace_events_completion(ReadLineState *rs, int nb_args, const char *str)
{
size_t len;
len = strlen(str);
readline_set_completion_index(rs, len);
if (nb_args == 2) {
TraceEventIter iter;
TraceEvent *ev;
char *pattern = g_strdup_printf("%s*", str);
trace_event_iter_init(&iter, pattern);
while ((ev = trace_event_iter_next(&iter)) != NULL) {
readline_add_completion(rs, trace_event_get_name(ev));
}
g_free(pattern);
}
}
void trace_event_completion(ReadLineState *rs, int nb_args, const char *str)
{
size_t len;
len = strlen(str);
readline_set_completion_index(rs, len);
if (nb_args == 2) {
TraceEventIter iter;
TraceEvent *ev;
char *pattern = g_strdup_printf("%s*", str);
trace_event_iter_init(&iter, pattern);
while ((ev = trace_event_iter_next(&iter)) != NULL) {
readline_add_completion(rs, trace_event_get_name(ev));
}
g_free(pattern);
} else if (nb_args == 3) {
add_completion_option(rs, str, "on");
add_completion_option(rs, str, "off");
}
}
void watchdog_action_completion(ReadLineState *rs, int nb_args, const char *str)
{
int i;
if (nb_args != 2) {
return;
}
readline_set_completion_index(rs, strlen(str));
for (i = 0; i < WATCHDOG_ACTION__MAX; i++) {
add_completion_option(rs, str, WatchdogAction_str(i));
}
}
void migrate_set_capability_completion(ReadLineState *rs, int nb_args,
const char *str)
{
size_t len;
len = strlen(str);
readline_set_completion_index(rs, len);
if (nb_args == 2) {
int i;
for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) {
const char *name = MigrationCapability_str(i);
if (!strncmp(str, name, len)) {
readline_add_completion(rs, name);
}
}
} else if (nb_args == 3) {
add_completion_option(rs, str, "on");
add_completion_option(rs, str, "off");
}
}
void migrate_set_parameter_completion(ReadLineState *rs, int nb_args,
const char *str)
{
size_t len;
len = strlen(str);
readline_set_completion_index(rs, len);
if (nb_args == 2) {
int i;
for (i = 0; i < MIGRATION_PARAMETER__MAX; i++) {
const char *name = MigrationParameter_str(i);
if (!strncmp(str, name, len)) {
readline_add_completion(rs, name);
}
}
}
}
static void vm_completion(ReadLineState *rs, const char *str)
{
size_t len;
BlockDriverState *bs;
BdrvNextIterator it;
len = strlen(str);
readline_set_completion_index(rs, len);
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
SnapshotInfoList *snapshots, *snapshot;
AioContext *ctx = bdrv_get_aio_context(bs);
bool ok = false;
aio_context_acquire(ctx);
if (bdrv_can_snapshot(bs)) {
ok = bdrv_query_snapshot_info_list(bs, &snapshots, NULL) == 0;
}
aio_context_release(ctx);
if (!ok) {
continue;
}
snapshot = snapshots;
while (snapshot) {
char *completion = snapshot->value->name;
if (!strncmp(str, completion, len)) {
readline_add_completion(rs, completion);
}
completion = snapshot->value->id;
if (!strncmp(str, completion, len)) {
readline_add_completion(rs, completion);
}
snapshot = snapshot->next;
}
qapi_free_SnapshotInfoList(snapshots);
}
}
void delvm_completion(ReadLineState *rs, int nb_args, const char *str)
{
if (nb_args == 2) {
vm_completion(rs, str);
}
}
void loadvm_completion(ReadLineState *rs, int nb_args, const char *str)
{
if (nb_args == 2) {
vm_completion(rs, str);
}
}
static void monitor_find_completion_by_table(Monitor *mon,
const mon_cmd_t *cmd_table,
char **args,
int nb_args)
{
const char *cmdname;
int i;
const char *ptype, *old_ptype, *str, *name;
const mon_cmd_t *cmd;
BlockBackend *blk = NULL;
if (nb_args <= 1) {
/* command completion */
if (nb_args == 0)
cmdname = "";
else
cmdname = args[0];
readline_set_completion_index(mon->rs, strlen(cmdname));
for (cmd = cmd_table; cmd->name != NULL; cmd++) {
cmd_completion(mon, cmdname, cmd->name);
}
} else {
/* find the command */
for (cmd = cmd_table; cmd->name != NULL; cmd++) {
if (compare_cmd(args[0], cmd->name)) {
break;
}
}
if (!cmd->name) {
return;
}
if (cmd->sub_table) {
/* do the job again */
monitor_find_completion_by_table(mon, cmd->sub_table,
&args[1], nb_args - 1);
return;
}
if (cmd->command_completion) {
cmd->command_completion(mon->rs, nb_args, args[nb_args - 1]);
return;
}
ptype = next_arg_type(cmd->args_type);
for(i = 0; i < nb_args - 2; i++) {
if (*ptype != '\0') {
ptype = next_arg_type(ptype);
while (*ptype == '?')
ptype = next_arg_type(ptype);
}
}
str = args[nb_args - 1];
old_ptype = NULL;
while (*ptype == '-' && old_ptype != ptype) {
old_ptype = ptype;
ptype = next_arg_type(ptype);
}
switch(*ptype) {
case 'F':
/* file completion */
readline_set_completion_index(mon->rs, strlen(str));
file_completion(mon, str);
break;
case 'B':
/* block device name completion */
readline_set_completion_index(mon->rs, strlen(str));
while ((blk = blk_next(blk)) != NULL) {
name = blk_name(blk);
if (str[0] == '\0' ||
!strncmp(name, str, strlen(str))) {
readline_add_completion(mon->rs, name);
}
}
break;
case 's':
case 'S':
if (!strcmp(cmd->name, "help|?")) {
monitor_find_completion_by_table(mon, cmd_table,
&args[1], nb_args - 1);
}
break;
default:
break;
}
}
}
static void monitor_find_completion(void *opaque,
const char *cmdline)
{
Monitor *mon = opaque;
char *args[MAX_ARGS];
int nb_args, len;
/* 1. parse the cmdline */
if (parse_cmdline(cmdline, &nb_args, args) < 0) {
return;
}
/* if the line ends with a space, it means we want to complete the
next arg */
len = strlen(cmdline);
if (len > 0 && qemu_isspace(cmdline[len - 1])) {
if (nb_args >= MAX_ARGS) {
goto cleanup;
}
args[nb_args++] = g_strdup("");
}
/* 2. auto complete according to args */
monitor_find_completion_by_table(mon, mon->cmd_table, args, nb_args);
cleanup:
free_cmdline_args(args, nb_args);
}
static int monitor_can_read(void *opaque)
{
Monitor *mon = opaque;
return !atomic_mb_read(&mon->suspend_cnt);
}
/*
* 1. This function takes ownership of rsp, err, and id.
* 2. rsp, err, and id may be NULL.
* 3. If err != NULL then rsp must be NULL.
*/
static void monitor_qmp_respond(Monitor *mon, QObject *rsp,
Error *err, QObject *id)
{
QDict *qdict = NULL;
if (err) {
assert(!rsp);
qdict = qdict_new();
qdict_put_obj(qdict, "error", qmp_build_error_object(err));
error_free(err);
rsp = QOBJECT(qdict);
}
if (rsp) {
if (id) {
/* This is for the qdict below. */
qobject_incref(id);
qdict_put_obj(qobject_to(QDict, rsp), "id", id);
}
monitor_json_emitter(mon, rsp);
}
qobject_decref(id);
qobject_decref(rsp);
}
/*
* Dispatch one single QMP request. The function will free the req_obj
* and objects inside it before return.
*/
static void monitor_qmp_dispatch_one(QMPRequest *req_obj)
{
Monitor *mon, *old_mon;
QObject *req, *rsp = NULL, *id;
bool need_resume;
req = req_obj->req;
mon = req_obj->mon;
id = req_obj->id;
need_resume = req_obj->need_resume;
g_free(req_obj);
if (trace_event_get_state_backends(TRACE_HANDLE_QMP_COMMAND)) {
QString *req_json = qobject_to_json(req);
trace_handle_qmp_command(mon, qstring_get_str(req_json));
QDECREF(req_json);
}
old_mon = cur_mon;
cur_mon = mon;
rsp = qmp_dispatch(mon->qmp.commands, req);
cur_mon = old_mon;
/* Respond if necessary */
monitor_qmp_respond(mon, rsp, NULL, id);
/* This pairs with the monitor_suspend() in handle_qmp_command(). */
if (need_resume) {
monitor_resume(mon);
}
qobject_decref(req);
}
/*
* Pop one QMP request from monitor queues, return NULL if not found.
* We are using round-robin fashion to pop the request, to avoid
* processing commands only on a very busy monitor. To achieve that,
* when we process one request on a specific monitor, we put that
* monitor to the end of mon_list queue.
*/
static QMPRequest *monitor_qmp_requests_pop_one(void)
{
QMPRequest *req_obj = NULL;
Monitor *mon;
qemu_mutex_lock(&monitor_lock);
QTAILQ_FOREACH(mon, &mon_list, entry) {
qemu_mutex_lock(&mon->qmp.qmp_queue_lock);
req_obj = g_queue_pop_head(mon->qmp.qmp_requests);
qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
if (req_obj) {
break;
}
}
if (req_obj) {
/*
* We found one request on the monitor. Degrade this monitor's
* priority to lowest by re-inserting it to end of queue.
*/
QTAILQ_REMOVE(&mon_list, mon, entry);
QTAILQ_INSERT_TAIL(&mon_list, mon, entry);
}
qemu_mutex_unlock(&monitor_lock);
return req_obj;
}
static void monitor_qmp_bh_dispatcher(void *data)
{
QMPRequest *req_obj = monitor_qmp_requests_pop_one();
if (req_obj) {
trace_monitor_qmp_cmd_in_band(qobject_get_try_str(req_obj->id) ?: "");
monitor_qmp_dispatch_one(req_obj);
/* Reschedule instead of looping so the main loop stays responsive */
qemu_bh_schedule(mon_global.qmp_dispatcher_bh);
}
}
#define QMP_REQ_QUEUE_LEN_MAX (8)
static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
{
QObject *req, *id = NULL;
QDict *qdict = NULL;
MonitorQMP *mon_qmp = container_of(parser, MonitorQMP, parser);
Monitor *mon = container_of(mon_qmp, Monitor, qmp);
Error *err = NULL;
QMPRequest *req_obj;
req = json_parser_parse_err(tokens, NULL, &err);
if (!req && !err) {
/* json_parser_parse_err() sucks: can fail without setting @err */
error_setg(&err, QERR_JSON_PARSING);
}
if (err) {
goto err;
}
/* Check against the request in general layout */
qdict = qmp_dispatch_check_obj(req, &err);
if (!qdict) {
goto err;
}
/* Check against OOB specific */
if (!qmp_cmd_oob_check(mon, qdict, &err)) {
goto err;
}
id = qdict_get(qdict, "id");
/* When OOB is enabled, the "id" field is mandatory. */
if (qmp_oob_enabled(mon) && !id) {
error_setg(&err, "Out-Of-Band capability requires that "
"every command contains an 'id' field");
goto err;
}
qobject_incref(id);
qdict_del(qdict, "id");
req_obj = g_new0(QMPRequest, 1);
req_obj->mon = mon;
req_obj->id = id;
req_obj->req = req;
req_obj->need_resume = false;
if (qmp_is_oob(qdict)) {
/* Out-Of-Band (OOB) requests are executed directly in parser. */
trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(req_obj->id)
?: "");
monitor_qmp_dispatch_one(req_obj);
return;
}
/* Protect qmp_requests and fetching its length. */
qemu_mutex_lock(&mon->qmp.qmp_queue_lock);
/*
* If OOB is not enabled on the current monitor, we'll emulate the
* old behavior that we won't process the current monitor any more
* until it has responded. This helps make sure that as long as
* OOB is not enabled, the server will never drop any command.
*/
if (!qmp_oob_enabled(mon)) {
monitor_suspend(mon);
req_obj->need_resume = true;
} else {
/* Drop the request if queue is full. */
if (mon->qmp.qmp_requests->length >= QMP_REQ_QUEUE_LEN_MAX) {
qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
qapi_event_send_command_dropped(id,
COMMAND_DROP_REASON_QUEUE_FULL,
&error_abort);
qmp_request_free(req_obj);
return;
}
}
/*
* Put the request to the end of queue so that requests will be
* handled in time order. Ownership for req_obj, req, id,
* etc. will be delivered to the handler side.
*/
g_queue_push_tail(mon->qmp.qmp_requests, req_obj);
qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
/* Kick the dispatcher routine */
qemu_bh_schedule(mon_global.qmp_dispatcher_bh);
return;
err:
monitor_qmp_respond(mon, NULL, err, NULL);
qobject_decref(req);
}
static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size)
{
Monitor *mon = opaque;
json_message_parser_feed(&mon->qmp.parser, (const char *) buf, size);
}
static void monitor_read(void *opaque, const uint8_t *buf, int size)
{
Monitor *old_mon = cur_mon;
int i;
cur_mon = opaque;
if (cur_mon->rs) {
for (i = 0; i < size; i++)
readline_handle_byte(cur_mon->rs, buf[i]);
} else {
if (size == 0 || buf[size - 1] != 0)
monitor_printf(cur_mon, "corrupted command\n");
else
handle_hmp_command(cur_mon, (char *)buf);
}
cur_mon = old_mon;
}
static void monitor_command_cb(void *opaque, const char *cmdline,
void *readline_opaque)
{
Monitor *mon = opaque;
monitor_suspend(mon);
handle_hmp_command(mon, cmdline);
monitor_resume(mon);
}
int monitor_suspend(Monitor *mon)
{
if (monitor_is_hmp_non_interactive(mon)) {
return -ENOTTY;
}
atomic_inc(&mon->suspend_cnt);
if (monitor_is_qmp(mon)) {
/*
* Kick iothread to make sure this takes effect. It'll be
* evaluated again in prepare() of the watch object.
*/
aio_notify(iothread_get_aio_context(mon_global.mon_iothread));
}
trace_monitor_suspend(mon, 1);
return 0;
}
void monitor_resume(Monitor *mon)
{
if (monitor_is_hmp_non_interactive(mon)) {
return;
}
if (atomic_dec_fetch(&mon->suspend_cnt) == 0) {
if (monitor_is_qmp(mon)) {
/*
* For QMP monitors that are running in IOThread, let's
* kick the thread in case it's sleeping.
*/
if (mon->use_io_thr) {
aio_notify(iothread_get_aio_context(mon_global.mon_iothread));
}
} else {
assert(mon->rs);
readline_show_prompt(mon->rs);
}
}
trace_monitor_suspend(mon, -1);
}
static QObject *get_qmp_greeting(Monitor *mon)
{
QList *cap_list = qlist_new();
QObject *ver = NULL;
QMPCapability cap;
qmp_marshal_query_version(NULL, &ver, NULL);
for (cap = 0; cap < QMP_CAPABILITY__MAX; cap++) {
if (!mon->use_io_thr && cap == QMP_CAPABILITY_OOB) {
/* Monitors that are not using IOThread won't support OOB */
continue;
}
qlist_append_str(cap_list, QMPCapability_str(cap));
}
return qobject_from_jsonf("{'QMP': {'version': %p, 'capabilities': %p}}",
ver, cap_list);
}
static void monitor_qmp_caps_reset(Monitor *mon)
{
memset(mon->qmp.qmp_caps, 0, sizeof(mon->qmp.qmp_caps));
}
static void monitor_qmp_event(void *opaque, int event)
{
QObject *data;
Monitor *mon = opaque;
switch (event) {
case CHR_EVENT_OPENED:
mon->qmp.commands = &qmp_cap_negotiation_commands;
monitor_qmp_caps_reset(mon);
data = get_qmp_greeting(mon);
monitor_json_emitter(mon, data);
qobject_decref(data);
mon_refcount++;
break;
case CHR_EVENT_CLOSED:
monitor_qmp_cleanup_queues(mon);
json_message_parser_destroy(&mon->qmp.parser);
json_message_parser_init(&mon->qmp.parser, handle_qmp_command);
mon_refcount--;
monitor_fdsets_cleanup();
break;
}
}
static void monitor_event(void *opaque, int event)
{
Monitor *mon = opaque;
switch (event) {
case CHR_EVENT_MUX_IN:
qemu_mutex_lock(&mon->out_lock);
mon->mux_out = 0;
qemu_mutex_unlock(&mon->out_lock);
if (mon->reset_seen) {
readline_restart(mon->rs);
monitor_resume(mon);
monitor_flush(mon);
} else {
atomic_mb_set(&mon->suspend_cnt, 0);
}
break;
case CHR_EVENT_MUX_OUT:
if (mon->reset_seen) {
if (atomic_mb_read(&mon->suspend_cnt) == 0) {
monitor_printf(mon, "\n");
}
monitor_flush(mon);
monitor_suspend(mon);
} else {
atomic_inc(&mon->suspend_cnt);
}
qemu_mutex_lock(&mon->out_lock);
mon->mux_out = 1;
qemu_mutex_unlock(&mon->out_lock);
break;
case CHR_EVENT_OPENED:
monitor_printf(mon, "QEMU %s monitor - type 'help' for more "
"information\n", QEMU_VERSION);
if (!mon->mux_out) {
readline_restart(mon->rs);
readline_show_prompt(mon->rs);
}
mon->reset_seen = 1;
mon_refcount++;
break;
case CHR_EVENT_CLOSED:
mon_refcount--;
monitor_fdsets_cleanup();
break;
}
}
static int
compare_mon_cmd(const void *a, const void *b)
{
return strcmp(((const mon_cmd_t *)a)->name,
((const mon_cmd_t *)b)->name);
}
static void sortcmdlist(void)
{
int array_num;
int elem_size = sizeof(mon_cmd_t);
array_num = sizeof(mon_cmds)/elem_size-1;
qsort((void *)mon_cmds, array_num, elem_size, compare_mon_cmd);
array_num = sizeof(info_cmds)/elem_size-1;
qsort((void *)info_cmds, array_num, elem_size, compare_mon_cmd);
}
static GMainContext *monitor_get_io_context(void)
{
return iothread_get_g_main_context(mon_global.mon_iothread);
}
static AioContext *monitor_get_aio_context(void)
{
return iothread_get_aio_context(mon_global.mon_iothread);
}
static void monitor_iothread_init(void)
{
mon_global.mon_iothread = iothread_create("mon_iothread",
&error_abort);
/*
* This MUST be on main loop thread since we have commands that
* have assumption to be run on main loop thread. It would be
* nice that one day we can remove this assumption in the future.
*/
monitor: bind dispatch bh to iohandler context Eric Auger reported the problem days ago that OOB broke ARM when running with libvirt: http://lists.gnu.org/archive/html/qemu-devel/2018-03/msg06231.html The problem was that the monitor dispatcher bottom half was bound to qemu_aio_context now, which could be polled unexpectedly in block code. We should keep the dispatchers run in iohandler_ctx just like what we did before the Out-Of-Band series (chardev uses qio, and qio binds everything with iohandler_ctx). If without this change, QMP dispatcher might be run even before reaching main loop in block IO path, for example, in a stack like (the ARM case, "cont" command handler run even during machine init phase): #0 qmp_cont () #1 0x00000000006bd210 in qmp_marshal_cont () #2 0x0000000000ac05c4 in do_qmp_dispatch () #3 0x0000000000ac07a0 in qmp_dispatch () #4 0x0000000000472d60 in monitor_qmp_dispatch_one () #5 0x000000000047302c in monitor_qmp_bh_dispatcher () #6 0x0000000000acf374 in aio_bh_call () #7 0x0000000000acf428 in aio_bh_poll () #8 0x0000000000ad5110 in aio_poll () #9 0x0000000000a08ab8 in blk_prw () #10 0x0000000000a091c4 in blk_pread () #11 0x0000000000734f94 in pflash_cfi01_realize () #12 0x000000000075a3a4 in device_set_realized () #13 0x00000000009a26cc in property_set_bool () #14 0x00000000009a0a40 in object_property_set () #15 0x00000000009a3a08 in object_property_set_qobject () #16 0x00000000009a0c8c in object_property_set_bool () #17 0x0000000000758f94 in qdev_init_nofail () #18 0x000000000058e190 in create_one_flash () #19 0x000000000058e2f4 in create_flash () #20 0x00000000005902f0 in machvirt_init () #21 0x00000000007635cc in machine_run_board_init () #22 0x00000000006b135c in main () Actually the problem is more severe than that. After we switched to the qemu AIO handler it means the monitor dispatcher code can even be called with nested aio_poll(), then it can be an explicit aio_poll() inside another main loop aio_poll() which could be racy too; breaking code like TPM and 9p that use nested event loops. Switch to use the iohandler_ctx for monitor dispatchers. My sincere thanks to Eric Auger who offered great help during both debugging and verifying the problem. The ARM test was carried out by applying this patch upon QEMU 2.12.0-rc0 and problem is gone after the patch. A quick test of mine shows that after this patch applied we can pass all raw iotests even with OOB on by default. CC: Eric Blake <eblake@redhat.com> CC: Markus Armbruster <armbru@redhat.com> CC: Stefan Hajnoczi <stefanha@redhat.com> CC: Fam Zheng <famz@redhat.com> Reported-by: Eric Auger <eric.auger@redhat.com> Tested-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Peter Xu <peterx@redhat.com> Message-Id: <20180410044942.17059-1-peterx@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Signed-off-by: Eric Blake <eblake@redhat.com>
2018-04-10 07:49:42 +03:00
mon_global.qmp_dispatcher_bh = aio_bh_new(iohandler_get_aio_context(),
monitor_qmp_bh_dispatcher,
NULL);
/*
* Unlike the dispatcher BH, this must be run on the monitor IO
* thread, so that monitors that are using IO thread will make
* sure read/write operations are all done on the IO thread.
*/
mon_global.qmp_respond_bh = aio_bh_new(monitor_get_aio_context(),
monitor_qmp_bh_responder,
NULL);
}
void monitor_init_globals(void)
{
monitor_init_qmp_commands();
monitor_qapi_event_init();
sortcmdlist();
qemu_mutex_init(&monitor_lock);
monitor_iothread_init();
}
/* These functions just adapt the readline interface in a typesafe way. We
* could cast function pointers but that discards compiler checks.
*/
static void GCC_FMT_ATTR(2, 3) monitor_readline_printf(void *opaque,
const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
monitor_vprintf(opaque, fmt, ap);
va_end(ap);
}
static void monitor_readline_flush(void *opaque)
{
monitor_flush(opaque);
}
/*
* Print to current monitor if we have one, else to stderr.
* TODO should return int, so callers can calculate width, but that
* requires surgery to monitor_vprintf(). Left for another day.
*/
void error_vprintf(const char *fmt, va_list ap)
{
if (cur_mon && !monitor_cur_is_qmp()) {
monitor_vprintf(cur_mon, fmt, ap);
} else {
vfprintf(stderr, fmt, ap);
}
}
void error_vprintf_unless_qmp(const char *fmt, va_list ap)
{
if (cur_mon && !monitor_cur_is_qmp()) {
monitor_vprintf(cur_mon, fmt, ap);
} else if (!cur_mon) {
vfprintf(stderr, fmt, ap);
}
}
static void monitor_list_append(Monitor *mon)
{
qemu_mutex_lock(&monitor_lock);
QTAILQ_INSERT_HEAD(&mon_list, mon, entry);
qemu_mutex_unlock(&monitor_lock);
}
static void monitor_qmp_setup_handlers_bh(void *opaque)
{
Monitor *mon = opaque;
GMainContext *context;
if (mon->use_io_thr) {
/*
* When use_io_thr is set, we use the global shared dedicated
* IO thread for this monitor to handle input/output.
*/
context = monitor_get_io_context();
/* We should have inited globals before reaching here. */
assert(context);
} else {
/* The default main loop, which is the main thread */
context = NULL;
}
qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, monitor_qmp_read,
monitor_qmp_event, NULL, mon, context, true);
monitor_list_append(mon);
}
void monitor_init(Chardev *chr, int flags)
{
Monitor *mon = g_malloc(sizeof(*mon));
bool use_readline = flags & MONITOR_USE_READLINE;
bool use_oob = flags & MONITOR_USE_OOB;
if (use_oob) {
if (CHARDEV_IS_MUX(chr)) {
error_report("Monitor Out-Of-Band is not supported with "
"MUX typed chardev backend");
exit(1);
}
if (use_readline) {
error_report("Monitor Out-Of-band is only supported by QMP");
exit(1);
}
}
monitor_data_init(mon, false, use_oob);
qemu_chr_fe_init(&mon->chr, chr, &error_abort);
mon->flags = flags;
if (use_readline) {
mon->rs = readline_init(monitor_readline_printf,
monitor_readline_flush,
mon,
monitor_find_completion);
monitor_read_command(mon, 0);
}
if (monitor_is_qmp(mon)) {
qemu_chr_fe_set_echo(&mon->chr, true);
json_message_parser_init(&mon->qmp.parser, handle_qmp_command);
if (mon->use_io_thr) {
/*
* Make sure the old iowatch is gone. It's possible when
* e.g. the chardev is in client mode, with wait=on.
*/
remove_fd_in_watch(chr);
/*
* We can't call qemu_chr_fe_set_handlers() directly here
* since during the procedure the chardev will be active
* and running in monitor iothread, while we'll still do
* something before returning from it, which is a possible
* race too. To avoid that, we just create a BH to setup
* the handlers.
*/
aio_bh_schedule_oneshot(monitor_get_aio_context(),
monitor_qmp_setup_handlers_bh, mon);
/* We'll add this to mon_list in the BH when setup done */
return;
} else {
qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read,
monitor_qmp_read, monitor_qmp_event,
NULL, mon, NULL, true);
}
} else {
qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, monitor_read,
monitor_event, NULL, mon, NULL, true);
}
monitor_list_append(mon);
}
monitor: fix crash when leaving qemu with spice audio Since aa5cb7f5e, the chardevs are being cleaned up when leaving qemu. However, the monitor has still references to them, which may lead to crashes when running atexit() and trying to send monitor events: #0 0x00007fffdb18f6f5 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54 #1 0x00007fffdb1912fa in __GI_abort () at abort.c:89 #2 0x0000555555c263e7 in error_exit (err=22, msg=0x555555d47980 <__func__.13537> "qemu_mutex_lock") at util/qemu-thread-posix.c:39 #3 0x0000555555c26488 in qemu_mutex_lock (mutex=0x5555567a2420) at util/qemu-thread-posix.c:66 #4 0x00005555558c52db in qemu_chr_fe_write (s=0x5555567a2420, buf=0x55555740dc40 "{\"timestamp\": {\"seconds\": 1470041716, \"microseconds\": 989699}, \"event\": \"SPICE_DISCONNECTED\", \"data\": {\"server\": {\"port\": \"5900\", \"family\": \"ipv4\", \"host\": \"127.0.0.1\"}, \"client\": {\"port\": \"40272\", \"f"..., len=240) at qemu-char.c:280 #5 0x0000555555787cad in monitor_flush_locked (mon=0x5555567bd9e0) at /home/elmarco/src/qemu/monitor.c:311 #6 0x0000555555787e46 in monitor_puts (mon=0x5555567bd9e0, str=0x5555567a44ef "") at /home/elmarco/src/qemu/monitor.c:353 #7 0x00005555557880fe in monitor_json_emitter (mon=0x5555567bd9e0, data=0x5555567c73a0) at /home/elmarco/src/qemu/monitor.c:401 #8 0x00005555557882d2 in monitor_qapi_event_emit (event=QAPI_EVENT_SPICE_DISCONNECTED, qdict=0x5555567c73a0) at /home/elmarco/src/qemu/monitor.c:472 #9 0x000055555578838f in monitor_qapi_event_queue (event=QAPI_EVENT_SPICE_DISCONNECTED, qdict=0x5555567c73a0, errp=0x7fffffffca88) at /home/elmarco/src/qemu/monitor.c:497 #10 0x0000555555c15541 in qapi_event_send_spice_disconnected (server=0x5555571139d0, client=0x5555570d0db0, errp=0x5555566c0428 <error_abort>) at qapi-event.c:1038 #11 0x0000555555b11bc6 in channel_event (event=3, info=0x5555570d6c00) at ui/spice-core.c:248 #12 0x00007fffdcc9983a in adapter_channel_event (event=3, info=0x5555570d6c00) at reds.c:120 #13 0x00007fffdcc99a25 in reds_handle_channel_event (reds=0x5555567a9d60, event=3, info=0x5555570d6c00) at reds.c:324 #14 0x00007fffdcc7d4c4 in main_dispatcher_self_handle_channel_event (self=0x5555567b28b0, event=3, info=0x5555570d6c00) at main-dispatcher.c:175 #15 0x00007fffdcc7d5b1 in main_dispatcher_channel_event (self=0x5555567b28b0, event=3, info=0x5555570d6c00) at main-dispatcher.c:194 #16 0x00007fffdcca7674 in reds_stream_push_channel_event (s=0x5555570d9910, event=3) at reds-stream.c:354 #17 0x00007fffdcca749b in reds_stream_free (s=0x5555570d9910) at reds-stream.c:323 #18 0x00007fffdccb5dad in snd_disconnect_channel (channel=0x5555576a89a0) at sound.c:229 #19 0x00007fffdccb9e57 in snd_detach_common (worker=0x555557739720) at sound.c:1589 #20 0x00007fffdccb9f0e in snd_detach_playback (sin=0x5555569fe3f8) at sound.c:1602 #21 0x00007fffdcca3373 in spice_server_remove_interface (sin=0x5555569fe3f8) at reds.c:3387 #22 0x00005555558ff6e2 in line_out_fini (hw=0x5555569fe370) at audio/spiceaudio.c:152 #23 0x00005555558f909e in audio_atexit () at audio/audio.c:1754 #24 0x00007fffdb1941e8 in __run_exit_handlers (status=0, listp=0x7fffdb5175d8 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true) at exit.c:82 #25 0x00007fffdb194235 in __GI_exit (status=<optimized out>) at exit.c:104 #26 0x00007fffdb17b738 in __libc_start_main (main=0x5555558d7874 <main>, argc=67, argv=0x7fffffffcf48, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffcf38) at ../csu/libc-start.c:323 Add a monitor_cleanup() functions to remove all the monitors before cleaning up the chardev. Note that we are "losing" some events that used to be sent during atexit(). Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-Id: <20160801112343.29082-2-marcandre.lureau@redhat.com> Reviewed-by: Paolo Bonzini <pbonzini@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-08-01 14:23:42 +03:00
void monitor_cleanup(void)
{
Monitor *mon, *next;
/*
* We need to explicitly stop the iothread (but not destroy it),
* cleanup the monitor resources, then destroy the iothread since
* we need to unregister from chardev below in
* monitor_data_destroy(), and chardev is not thread-safe yet
*/
iothread_stop(mon_global.mon_iothread);
/*
* After we have IOThread to send responses, it's possible that
* when we stop the IOThread there are still replies queued in the
* responder queue. Flush all of them. Note that even after this
* flush it's still possible that out buffer is not flushed.
* It'll be done in below monitor_flush() as the last resort.
*/
monitor_qmp_bh_responder(NULL);
monitor: fix crash when leaving qemu with spice audio Since aa5cb7f5e, the chardevs are being cleaned up when leaving qemu. However, the monitor has still references to them, which may lead to crashes when running atexit() and trying to send monitor events: #0 0x00007fffdb18f6f5 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54 #1 0x00007fffdb1912fa in __GI_abort () at abort.c:89 #2 0x0000555555c263e7 in error_exit (err=22, msg=0x555555d47980 <__func__.13537> "qemu_mutex_lock") at util/qemu-thread-posix.c:39 #3 0x0000555555c26488 in qemu_mutex_lock (mutex=0x5555567a2420) at util/qemu-thread-posix.c:66 #4 0x00005555558c52db in qemu_chr_fe_write (s=0x5555567a2420, buf=0x55555740dc40 "{\"timestamp\": {\"seconds\": 1470041716, \"microseconds\": 989699}, \"event\": \"SPICE_DISCONNECTED\", \"data\": {\"server\": {\"port\": \"5900\", \"family\": \"ipv4\", \"host\": \"127.0.0.1\"}, \"client\": {\"port\": \"40272\", \"f"..., len=240) at qemu-char.c:280 #5 0x0000555555787cad in monitor_flush_locked (mon=0x5555567bd9e0) at /home/elmarco/src/qemu/monitor.c:311 #6 0x0000555555787e46 in monitor_puts (mon=0x5555567bd9e0, str=0x5555567a44ef "") at /home/elmarco/src/qemu/monitor.c:353 #7 0x00005555557880fe in monitor_json_emitter (mon=0x5555567bd9e0, data=0x5555567c73a0) at /home/elmarco/src/qemu/monitor.c:401 #8 0x00005555557882d2 in monitor_qapi_event_emit (event=QAPI_EVENT_SPICE_DISCONNECTED, qdict=0x5555567c73a0) at /home/elmarco/src/qemu/monitor.c:472 #9 0x000055555578838f in monitor_qapi_event_queue (event=QAPI_EVENT_SPICE_DISCONNECTED, qdict=0x5555567c73a0, errp=0x7fffffffca88) at /home/elmarco/src/qemu/monitor.c:497 #10 0x0000555555c15541 in qapi_event_send_spice_disconnected (server=0x5555571139d0, client=0x5555570d0db0, errp=0x5555566c0428 <error_abort>) at qapi-event.c:1038 #11 0x0000555555b11bc6 in channel_event (event=3, info=0x5555570d6c00) at ui/spice-core.c:248 #12 0x00007fffdcc9983a in adapter_channel_event (event=3, info=0x5555570d6c00) at reds.c:120 #13 0x00007fffdcc99a25 in reds_handle_channel_event (reds=0x5555567a9d60, event=3, info=0x5555570d6c00) at reds.c:324 #14 0x00007fffdcc7d4c4 in main_dispatcher_self_handle_channel_event (self=0x5555567b28b0, event=3, info=0x5555570d6c00) at main-dispatcher.c:175 #15 0x00007fffdcc7d5b1 in main_dispatcher_channel_event (self=0x5555567b28b0, event=3, info=0x5555570d6c00) at main-dispatcher.c:194 #16 0x00007fffdcca7674 in reds_stream_push_channel_event (s=0x5555570d9910, event=3) at reds-stream.c:354 #17 0x00007fffdcca749b in reds_stream_free (s=0x5555570d9910) at reds-stream.c:323 #18 0x00007fffdccb5dad in snd_disconnect_channel (channel=0x5555576a89a0) at sound.c:229 #19 0x00007fffdccb9e57 in snd_detach_common (worker=0x555557739720) at sound.c:1589 #20 0x00007fffdccb9f0e in snd_detach_playback (sin=0x5555569fe3f8) at sound.c:1602 #21 0x00007fffdcca3373 in spice_server_remove_interface (sin=0x5555569fe3f8) at reds.c:3387 #22 0x00005555558ff6e2 in line_out_fini (hw=0x5555569fe370) at audio/spiceaudio.c:152 #23 0x00005555558f909e in audio_atexit () at audio/audio.c:1754 #24 0x00007fffdb1941e8 in __run_exit_handlers (status=0, listp=0x7fffdb5175d8 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true) at exit.c:82 #25 0x00007fffdb194235 in __GI_exit (status=<optimized out>) at exit.c:104 #26 0x00007fffdb17b738 in __libc_start_main (main=0x5555558d7874 <main>, argc=67, argv=0x7fffffffcf48, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffcf38) at ../csu/libc-start.c:323 Add a monitor_cleanup() functions to remove all the monitors before cleaning up the chardev. Note that we are "losing" some events that used to be sent during atexit(). Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-Id: <20160801112343.29082-2-marcandre.lureau@redhat.com> Reviewed-by: Paolo Bonzini <pbonzini@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-08-01 14:23:42 +03:00
qemu_mutex_lock(&monitor_lock);
QTAILQ_FOREACH_SAFE(mon, &mon_list, entry, next) {
QTAILQ_REMOVE(&mon_list, mon, entry);
monitor_flush(mon);
monitor: fix crash when leaving qemu with spice audio Since aa5cb7f5e, the chardevs are being cleaned up when leaving qemu. However, the monitor has still references to them, which may lead to crashes when running atexit() and trying to send monitor events: #0 0x00007fffdb18f6f5 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54 #1 0x00007fffdb1912fa in __GI_abort () at abort.c:89 #2 0x0000555555c263e7 in error_exit (err=22, msg=0x555555d47980 <__func__.13537> "qemu_mutex_lock") at util/qemu-thread-posix.c:39 #3 0x0000555555c26488 in qemu_mutex_lock (mutex=0x5555567a2420) at util/qemu-thread-posix.c:66 #4 0x00005555558c52db in qemu_chr_fe_write (s=0x5555567a2420, buf=0x55555740dc40 "{\"timestamp\": {\"seconds\": 1470041716, \"microseconds\": 989699}, \"event\": \"SPICE_DISCONNECTED\", \"data\": {\"server\": {\"port\": \"5900\", \"family\": \"ipv4\", \"host\": \"127.0.0.1\"}, \"client\": {\"port\": \"40272\", \"f"..., len=240) at qemu-char.c:280 #5 0x0000555555787cad in monitor_flush_locked (mon=0x5555567bd9e0) at /home/elmarco/src/qemu/monitor.c:311 #6 0x0000555555787e46 in monitor_puts (mon=0x5555567bd9e0, str=0x5555567a44ef "") at /home/elmarco/src/qemu/monitor.c:353 #7 0x00005555557880fe in monitor_json_emitter (mon=0x5555567bd9e0, data=0x5555567c73a0) at /home/elmarco/src/qemu/monitor.c:401 #8 0x00005555557882d2 in monitor_qapi_event_emit (event=QAPI_EVENT_SPICE_DISCONNECTED, qdict=0x5555567c73a0) at /home/elmarco/src/qemu/monitor.c:472 #9 0x000055555578838f in monitor_qapi_event_queue (event=QAPI_EVENT_SPICE_DISCONNECTED, qdict=0x5555567c73a0, errp=0x7fffffffca88) at /home/elmarco/src/qemu/monitor.c:497 #10 0x0000555555c15541 in qapi_event_send_spice_disconnected (server=0x5555571139d0, client=0x5555570d0db0, errp=0x5555566c0428 <error_abort>) at qapi-event.c:1038 #11 0x0000555555b11bc6 in channel_event (event=3, info=0x5555570d6c00) at ui/spice-core.c:248 #12 0x00007fffdcc9983a in adapter_channel_event (event=3, info=0x5555570d6c00) at reds.c:120 #13 0x00007fffdcc99a25 in reds_handle_channel_event (reds=0x5555567a9d60, event=3, info=0x5555570d6c00) at reds.c:324 #14 0x00007fffdcc7d4c4 in main_dispatcher_self_handle_channel_event (self=0x5555567b28b0, event=3, info=0x5555570d6c00) at main-dispatcher.c:175 #15 0x00007fffdcc7d5b1 in main_dispatcher_channel_event (self=0x5555567b28b0, event=3, info=0x5555570d6c00) at main-dispatcher.c:194 #16 0x00007fffdcca7674 in reds_stream_push_channel_event (s=0x5555570d9910, event=3) at reds-stream.c:354 #17 0x00007fffdcca749b in reds_stream_free (s=0x5555570d9910) at reds-stream.c:323 #18 0x00007fffdccb5dad in snd_disconnect_channel (channel=0x5555576a89a0) at sound.c:229 #19 0x00007fffdccb9e57 in snd_detach_common (worker=0x555557739720) at sound.c:1589 #20 0x00007fffdccb9f0e in snd_detach_playback (sin=0x5555569fe3f8) at sound.c:1602 #21 0x00007fffdcca3373 in spice_server_remove_interface (sin=0x5555569fe3f8) at reds.c:3387 #22 0x00005555558ff6e2 in line_out_fini (hw=0x5555569fe370) at audio/spiceaudio.c:152 #23 0x00005555558f909e in audio_atexit () at audio/audio.c:1754 #24 0x00007fffdb1941e8 in __run_exit_handlers (status=0, listp=0x7fffdb5175d8 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true) at exit.c:82 #25 0x00007fffdb194235 in __GI_exit (status=<optimized out>) at exit.c:104 #26 0x00007fffdb17b738 in __libc_start_main (main=0x5555558d7874 <main>, argc=67, argv=0x7fffffffcf48, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffcf38) at ../csu/libc-start.c:323 Add a monitor_cleanup() functions to remove all the monitors before cleaning up the chardev. Note that we are "losing" some events that used to be sent during atexit(). Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-Id: <20160801112343.29082-2-marcandre.lureau@redhat.com> Reviewed-by: Paolo Bonzini <pbonzini@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-08-01 14:23:42 +03:00
monitor_data_destroy(mon);
g_free(mon);
}
qemu_mutex_unlock(&monitor_lock);
/* QEMUBHs needs to be deleted before destroying the IOThread. */
qemu_bh_delete(mon_global.qmp_dispatcher_bh);
mon_global.qmp_dispatcher_bh = NULL;
qemu_bh_delete(mon_global.qmp_respond_bh);
mon_global.qmp_respond_bh = NULL;
iothread_destroy(mon_global.mon_iothread);
mon_global.mon_iothread = NULL;
monitor: fix crash when leaving qemu with spice audio Since aa5cb7f5e, the chardevs are being cleaned up when leaving qemu. However, the monitor has still references to them, which may lead to crashes when running atexit() and trying to send monitor events: #0 0x00007fffdb18f6f5 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54 #1 0x00007fffdb1912fa in __GI_abort () at abort.c:89 #2 0x0000555555c263e7 in error_exit (err=22, msg=0x555555d47980 <__func__.13537> "qemu_mutex_lock") at util/qemu-thread-posix.c:39 #3 0x0000555555c26488 in qemu_mutex_lock (mutex=0x5555567a2420) at util/qemu-thread-posix.c:66 #4 0x00005555558c52db in qemu_chr_fe_write (s=0x5555567a2420, buf=0x55555740dc40 "{\"timestamp\": {\"seconds\": 1470041716, \"microseconds\": 989699}, \"event\": \"SPICE_DISCONNECTED\", \"data\": {\"server\": {\"port\": \"5900\", \"family\": \"ipv4\", \"host\": \"127.0.0.1\"}, \"client\": {\"port\": \"40272\", \"f"..., len=240) at qemu-char.c:280 #5 0x0000555555787cad in monitor_flush_locked (mon=0x5555567bd9e0) at /home/elmarco/src/qemu/monitor.c:311 #6 0x0000555555787e46 in monitor_puts (mon=0x5555567bd9e0, str=0x5555567a44ef "") at /home/elmarco/src/qemu/monitor.c:353 #7 0x00005555557880fe in monitor_json_emitter (mon=0x5555567bd9e0, data=0x5555567c73a0) at /home/elmarco/src/qemu/monitor.c:401 #8 0x00005555557882d2 in monitor_qapi_event_emit (event=QAPI_EVENT_SPICE_DISCONNECTED, qdict=0x5555567c73a0) at /home/elmarco/src/qemu/monitor.c:472 #9 0x000055555578838f in monitor_qapi_event_queue (event=QAPI_EVENT_SPICE_DISCONNECTED, qdict=0x5555567c73a0, errp=0x7fffffffca88) at /home/elmarco/src/qemu/monitor.c:497 #10 0x0000555555c15541 in qapi_event_send_spice_disconnected (server=0x5555571139d0, client=0x5555570d0db0, errp=0x5555566c0428 <error_abort>) at qapi-event.c:1038 #11 0x0000555555b11bc6 in channel_event (event=3, info=0x5555570d6c00) at ui/spice-core.c:248 #12 0x00007fffdcc9983a in adapter_channel_event (event=3, info=0x5555570d6c00) at reds.c:120 #13 0x00007fffdcc99a25 in reds_handle_channel_event (reds=0x5555567a9d60, event=3, info=0x5555570d6c00) at reds.c:324 #14 0x00007fffdcc7d4c4 in main_dispatcher_self_handle_channel_event (self=0x5555567b28b0, event=3, info=0x5555570d6c00) at main-dispatcher.c:175 #15 0x00007fffdcc7d5b1 in main_dispatcher_channel_event (self=0x5555567b28b0, event=3, info=0x5555570d6c00) at main-dispatcher.c:194 #16 0x00007fffdcca7674 in reds_stream_push_channel_event (s=0x5555570d9910, event=3) at reds-stream.c:354 #17 0x00007fffdcca749b in reds_stream_free (s=0x5555570d9910) at reds-stream.c:323 #18 0x00007fffdccb5dad in snd_disconnect_channel (channel=0x5555576a89a0) at sound.c:229 #19 0x00007fffdccb9e57 in snd_detach_common (worker=0x555557739720) at sound.c:1589 #20 0x00007fffdccb9f0e in snd_detach_playback (sin=0x5555569fe3f8) at sound.c:1602 #21 0x00007fffdcca3373 in spice_server_remove_interface (sin=0x5555569fe3f8) at reds.c:3387 #22 0x00005555558ff6e2 in line_out_fini (hw=0x5555569fe370) at audio/spiceaudio.c:152 #23 0x00005555558f909e in audio_atexit () at audio/audio.c:1754 #24 0x00007fffdb1941e8 in __run_exit_handlers (status=0, listp=0x7fffdb5175d8 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true) at exit.c:82 #25 0x00007fffdb194235 in __GI_exit (status=<optimized out>) at exit.c:104 #26 0x00007fffdb17b738 in __libc_start_main (main=0x5555558d7874 <main>, argc=67, argv=0x7fffffffcf48, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffcf38) at ../csu/libc-start.c:323 Add a monitor_cleanup() functions to remove all the monitors before cleaning up the chardev. Note that we are "losing" some events that used to be sent during atexit(). Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-Id: <20160801112343.29082-2-marcandre.lureau@redhat.com> Reviewed-by: Paolo Bonzini <pbonzini@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-08-01 14:23:42 +03:00
}
QemuOptsList qemu_mon_opts = {
.name = "mon",
.implied_opt_name = "chardev",
.head = QTAILQ_HEAD_INITIALIZER(qemu_mon_opts.head),
.desc = {
{
.name = "mode",
.type = QEMU_OPT_STRING,
},{
.name = "chardev",
.type = QEMU_OPT_STRING,
},{
.name = "pretty",
.type = QEMU_OPT_BOOL,
},{
.name = "x-oob",
.type = QEMU_OPT_BOOL,
},
{ /* end of list */ }
},
};
#ifndef TARGET_I386
void qmp_rtc_reset_reinjection(Error **errp)
{
error_setg(errp, QERR_FEATURE_DISABLED, "rtc-reset-reinjection");
}
SevInfo *qmp_query_sev(Error **errp)
{
error_setg(errp, QERR_FEATURE_DISABLED, "query-sev");
return NULL;
}
SevLaunchMeasureInfo *qmp_query_sev_launch_measure(Error **errp)
{
error_setg(errp, QERR_FEATURE_DISABLED, "query-sev-launch-measure");
return NULL;
}
SevCapability *qmp_query_sev_capabilities(Error **errp)
{
error_setg(errp, QERR_FEATURE_DISABLED, "query-sev-capabilities");
return NULL;
}
#endif
#ifndef TARGET_S390X
void qmp_dump_skeys(const char *filename, Error **errp)
{
error_setg(errp, QERR_FEATURE_DISABLED, "dump-skeys");
}
#endif
arm: qmp: add query-gic-capabilities interface This patch add "query-gic-capabilities" but does not implement it. The command is ARM-only. The command will return a list of GICCapability structs that describes all GIC versions that current QEMU and system support. Libvirt is possibly the first consumer of this new command. Before this patch, a libvirt user can successfully configure all kinds of GIC devices for ARM guests, no matter whether current QEMU/kernel supports them. If the specified GIC version/type is not supported, the user will get an ambiguous "QEMU boot failure" error when trying to start the VM. This is not user-friendly. With this patch, libvirt should be able to query which type (and which version) of GIC device is supported. Using this information, libvirt can warn the user during configuration of guests when specified GIC device type is not supported. Or better, we can just list those versions that we support, and filter out the unsupported ones. For example, if we got the query result: {"return": [{"emulated": false, "version": 3, "kernel": true}, {"emulated": true, "version": 2, "kernel": false}]} then it means that we support emulated GIC version 2 using: qemu-system-aarch64 -M virt,accel=tcg,gic-version=2 ... or KVM-accelerated GIC version 3 using: qemu-system-aarch64 -M virt,accel=kvm,gic-version=3 ... If we specify other explicit GIC versions rather than the above, QEMU will not be able to boot. The community is working on a more generic way to query these kinds of information about valid values of machine properties. However, due to the importance of supporting this specific use case, weecided to first implement this ad-hoc one; then when the generic method is ready, we can move on to that one smoothly. Signed-off-by: Peter Xu <peterx@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Message-id: 1458788142-17509-2-git-send-email-peterx@redhat.com [PMM: tweaked commit message a bit; monitor.o is CONFIG_SOFTMMU only] Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
2016-03-30 19:27:24 +03:00
#ifndef TARGET_ARM
GICCapabilityList *qmp_query_gic_capabilities(Error **errp)
{
error_setg(errp, QERR_FEATURE_DISABLED, "query-gic-capabilities");
return NULL;
}
#endif
HotpluggableCPUList *qmp_query_hotpluggable_cpus(Error **errp)
{
MachineState *ms = MACHINE(qdev_get_machine());
MachineClass *mc = MACHINE_GET_CLASS(ms);
if (!mc->has_hotpluggable_cpus) {
error_setg(errp, QERR_FEATURE_DISABLED, "query-hotpluggable-cpus");
return NULL;
}
return machine_query_hotpluggable_cpus(ms);
}