Monitor patches for 2019-06-17

-----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEENUvIs9frKmtoZ05fOHC0AOuRhlMFAl0IgqkSHGFybWJydUBy
 ZWRoYXQuY29tAAoJEDhwtADrkYZTz2QP/jqMYcXuCZ3ApgOrZEEZh6r5RN0pbssL
 t8+QLeRYBSn53QErteoYYbNgzQLUPCmBLviX9wGr3aKzJ4UWOBfya+9ktgcqntYE
 3hcjHFpFtI4kccc2Gtsutb7lRuS4yLOteTryFK79/QxnlZYyfrwMOGxSJvE8nr9r
 VgeY7YSgBp2YjFOp451AjAjCqp+nZymZFW2yvCi81L8Gqn5H6Tc4SQZG+WcrYYUX
 zcixKgtHfUn9nBCpoRdSZoOF7WMBBK1Ctrca/NBpGiJjt84KrnmzVTPtgsjRZM52
 B8ZxArFjpL0NQ1AXNonM1H/pRK/fgbKvOfDOKrsMjg3nUt0t4QPjAONph8umif5s
 EFQQQBql/frNmZMGPFPKubSYLxzr/kNjjOeOgR6eEKQTwy9OC0nZF6IAFhr67GgN
 lpoRYXbbyRnuyD5DgSuUVft5D7Tw2PZQG8waN1MCexjmETticomAEh9pxQ6oYG5k
 +k7V0+8L85D7kNddEkIH5iv1PbERHeXiSGEv0GStSm6WwFQun9YpRAyDHgoCnWty
 sVPXNBHUy+3BMOum/z+mXP+SbkrGBksUfViBsWTCc2TNvMoOfXkKYuRqZKB71kpt
 EH5WoqFlNYOmPJnOsxNGfi3WejX8vILMn/VYNvxHnItzmyr8dV6MEkfom8bFEP31
 Vuh57gwcoji4
 =BeCF
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/armbru/tags/pull-monitor-2019-06-17-v2' into staging

Monitor patches for 2019-06-17

# gpg: Signature made Tue 18 Jun 2019 07:20:25 BST
# gpg:                using RSA key 354BC8B3D7EB2A6B68674E5F3870B400EB918653
# gpg:                issuer "armbru@redhat.com"
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" [full]
# gpg:                 aka "Markus Armbruster <armbru@pond.sub.org>" [full]
# Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867  4E5F 3870 B400 EB91 8653

* remotes/armbru/tags/pull-monitor-2019-06-17-v2:
  vl: Deprecate -mon pretty=... for HMP monitors
  monitor: Replace monitor_init() with monitor_init_{hmp, qmp}()
  monitor: Split Monitor.flags into separate bools
  monitor: Split out monitor/monitor.c
  monitor: Split out monitor/hmp.c
  monitor: Split out monitor/qmp.c
  monitor: Create monitor-internal.h with common definitions
  monitor: Move {hmp, qmp}.c to monitor/{hmp, qmp}-cmds.c
  Move monitor.c to monitor/misc.c
  monitor: Rename HMP command type and tables
  monitor: Remove Monitor.cmd_table indirection
  monitor: Create MonitorHMP with readline state
  monitor: Make MonitorQMP a child class of Monitor
  monitor: Split monitor_init in HMP and QMP function
  monitor: Remove unused password prompting fields
  monitor: Fix return type of monitor_fdset_dup_fd_find

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2019-06-18 10:47:00 +01:00
commit cdfaa2720f
24 changed files with 5073 additions and 4777 deletions

View File

@ -1918,8 +1918,11 @@ F: qapi/run-state.json
Human Monitor (HMP)
M: Dr. David Alan Gilbert <dgilbert@redhat.com>
S: Maintained
F: monitor.c
F: hmp.[ch]
F: monitor/monitor-internal.h
F: monitor/misc.c
F: monitor/monitor.c
F: monitor/hmp*
F: hmp.h
F: hmp-commands*.hx
F: include/monitor/hmp-target.h
F: tests/test-hmp.c
@ -2039,8 +2042,10 @@ F: tests/check-qom-proplist.c
QMP
M: Markus Armbruster <armbru@redhat.com>
S: Supported
F: qmp.c
F: monitor.c
F: monitor/monitor-internal.h
F: monitor/qmp*
F: monitor/misc.c
F: monitor/monitor.c
F: docs/devel/*qmp-*
F: docs/interop/*qmp-*
F: scripts/qmp/

View File

@ -46,6 +46,7 @@ ifeq ($(CONFIG_SOFTMMU),y)
common-obj-y = blockdev.o blockdev-nbd.o block/
common-obj-y += bootdevice.o iothread.o
common-obj-y += job-qmp.o
common-obj-y += monitor/
common-obj-y += net/
common-obj-y += qdev-monitor.o device-hotplug.o
common-obj-$(CONFIG_WIN32) += os-win32.o
@ -83,8 +84,8 @@ common-obj-$(CONFIG_FDT) += device_tree.o
######################################################################
# qapi
common-obj-y += qmp.o hmp.o
common-obj-y += qapi/
common-obj-y += monitor/
endif
#######################################################################
@ -130,6 +131,7 @@ trace-events-subdirs =
trace-events-subdirs += accel/kvm
trace-events-subdirs += accel/tcg
trace-events-subdirs += crypto
trace-events-subdirs += monitor
ifeq ($(CONFIG_USER_ONLY),y)
trace-events-subdirs += linux-user
endif

View File

@ -148,9 +148,10 @@ endif #CONFIG_BSD_USER
#########################################################
# System emulator target
ifdef CONFIG_SOFTMMU
obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o
obj-y += arch_init.o cpus.o gdbstub.o balloon.o ioport.o numa.o
obj-y += qtest.o
obj-y += hw/
obj-y += monitor/
obj-y += qapi/
obj-y += memory.o
obj-y += memory_mapping.o

View File

@ -731,7 +731,7 @@ Chardev *qemu_chr_new_noreplay(const char *label, const char *filename,
if (qemu_opt_get_bool(opts, "mux", 0)) {
assert(permit_mux_mon);
monitor_init(chr, MONITOR_USE_READLINE);
monitor_init_hmp(chr, true);
}
out:

View File

@ -20,7 +20,7 @@ new QMP command.
2. Write the QMP command itself, which is a regular C function. Preferably,
the command should be exported by some QEMU subsystem. But it can also be
added to the qmp.c file
added to the monitor/qmp-cmds.c file
3. At this point the command can be tested under the QMP protocol
@ -101,7 +101,8 @@ protocol data.
The next step is to write the "hello-world" implementation. As explained
earlier, it's preferable for commands to live in QEMU subsystems. But
"hello-world" doesn't pertain to any, so we put its implementation in qmp.c:
"hello-world" doesn't pertain to any, so we put its implementation in
monitor/qmp-cmds.c:
void qmp_hello_world(Error **errp)
{
@ -146,7 +147,7 @@ for mandatory arguments). Finally, 'str' is the argument's type, which
stands for "string". The QAPI also supports integers, booleans, enumerations
and user defined types.
Now, let's update our C implementation in qmp.c:
Now, let's update our C implementation in monitor/qmp-cmds.c:
void qmp_hello_world(bool has_message, const char *message, Error **errp)
{
@ -267,7 +268,7 @@ monitor (HMP).
With the introduction of the QAPI, HMP commands make QMP calls. Most of the
time HMP commands are simple wrappers. All HMP commands implementation exist in
the hmp.c file.
the monitor/hmp-cmds.c file.
Here's the implementation of the "hello-world" HMP command:
@ -470,7 +471,7 @@ it's good practice to always check for errors.
Another important detail is that HMP's "info" commands don't go into the
hmp-commands.hx. Instead, they go into the info_cmds[] table, which is defined
in the monitor.c file. The entry for the "info alarmclock" follows:
in the monitor/misc.c file. The entry for the "info alarmclock" follows:
{
.name = "alarmclock",

View File

@ -3344,7 +3344,7 @@ int gdbserver_start(const char *device)
/* Initialize a monitor terminal for gdb */
mon_chr = qemu_chardev_new(NULL, TYPE_CHARDEV_GDB,
NULL, NULL, &error_abort);
monitor_init(mon_chr, 0);
monitor_init_hmp(mon_chr, false);
} else {
qemu_chr_fe_deinit(&s->chr, true);
mon_chr = s->mon_chr;

View File

@ -1934,7 +1934,7 @@ ETEXI
.params = "[subcommand]",
.help = "show various information about the system state",
.cmd = hmp_info_help,
.sub_table = info_cmds,
.sub_table = hmp_info_cmds,
.flags = "p",
},

View File

@ -6,19 +6,16 @@
#include "qemu/readline.h"
extern __thread Monitor *cur_mon;
/* flags for monitor_init */
/* 0x01 unused */
#define MONITOR_USE_READLINE 0x02
#define MONITOR_USE_CONTROL 0x04
#define MONITOR_USE_PRETTY 0x08
typedef struct MonitorHMP MonitorHMP;
#define QMP_REQ_QUEUE_LEN_MAX 8
bool monitor_cur_is_qmp(void);
void monitor_init_globals(void);
void monitor_init(Chardev *chr, int flags);
void monitor_init_globals_core(void);
void monitor_init_qmp(Chardev *chr, bool pretty);
void monitor_init_hmp(Chardev *chr, bool use_readline);
void monitor_cleanup(void);
int monitor_suspend(Monitor *mon);
@ -34,8 +31,8 @@ void monitor_flush(Monitor *mon);
int monitor_set_cpu(int cpu_index);
int monitor_get_cpu_index(void);
void monitor_read_command(Monitor *mon, int show_prompt);
int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func,
void monitor_read_command(MonitorHMP *mon, int show_prompt);
int monitor_read_password(MonitorHMP *mon, ReadLineFunc *readline_func,
void *opaque);
AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id,
@ -44,6 +41,6 @@ AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id,
int monitor_fdset_get_fd(int64_t fdset_id, int flags);
int monitor_fdset_dup_fd_add(int64_t fdset_id, int dup_fd);
void monitor_fdset_dup_fd_remove(int dup_fd);
int monitor_fdset_dup_fd_find(int dup_fd);
int64_t monitor_fdset_dup_fd_find(int dup_fd);
#endif /* MONITOR_H */

4729
monitor.c

File diff suppressed because it is too large Load Diff

3
monitor/Makefile.objs Normal file
View File

@ -0,0 +1,3 @@
obj-y += misc.o
common-obj-y += monitor.o qmp.o hmp.o
common-obj-y += qmp-cmds.o hmp-cmds.o

View File

@ -1,5 +1,5 @@
/*
* Human Monitor Interface
* Human Monitor Interface commands
*
* Copyright IBM, Corp. 2011
*
@ -24,7 +24,7 @@
#include "qemu/option.h"
#include "qemu/timer.h"
#include "qemu/sockets.h"
#include "monitor/monitor.h"
#include "monitor/monitor-internal.h"
#include "monitor/qdev.h"
#include "qapi/error.h"
#include "qapi/opts-visitor.h"
@ -1943,6 +1943,7 @@ static void hmp_change_read_arg(void *opaque, const char *password,
void hmp_change(Monitor *mon, const QDict *qdict)
{
MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
const char *device = qdict_get_str(qdict, "device");
const char *target = qdict_get_str(qdict, "target");
const char *arg = qdict_get_try_str(qdict, "arg");
@ -1960,7 +1961,7 @@ void hmp_change(Monitor *mon, const QDict *qdict)
if (strcmp(target, "passwd") == 0 ||
strcmp(target, "password") == 0) {
if (!arg) {
monitor_read_password(mon, hmp_change_read_arg, NULL);
monitor_read_password(hmp_mon, hmp_change_read_arg, NULL);
return;
}
}

1417
monitor/hmp.c Normal file

File diff suppressed because it is too large Load Diff

2353
monitor/misc.c Normal file

File diff suppressed because it is too large Load Diff

183
monitor/monitor-internal.h Normal file
View File

@ -0,0 +1,183 @@
/*
* 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.
*/
#ifndef MONITOR_INTERNAL_H
#define MONITOR_INTERNAL_H
#include "chardev/char-fe.h"
#include "monitor/monitor.h"
#include "qapi/qapi-types-misc.h"
#include "qapi/qmp/dispatch.h"
#include "qapi/qmp/json-parser.h"
#include "qemu/readline.h"
#include "sysemu/iothread.h"
/*
* 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 HMPCommand {
const char *name;
const char *args_type;
const char *params;
const char *help;
const char *flags; /* p=preconfig */
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 HMPCommand *sub_table;
void (*command_completion)(ReadLineState *rs, int nb_args, const char *str);
} HMPCommand;
struct Monitor {
CharBackend chr;
int reset_seen;
int suspend_cnt; /* Needs to be accessed atomically */
bool is_qmp;
bool skip_flush;
bool use_io_thread;
gchar *mon_cpu_path;
QTAILQ_ENTRY(Monitor) entry;
/*
* The per-monitor lock. We can't access guest memory when holding
* the lock.
*/
QemuMutex mon_lock;
/*
* Members that are protected by the per-monitor lock
*/
QLIST_HEAD(, mon_fd_t) fds;
QString *outbuf;
guint out_watch;
/* Read under either BQL or mon_lock, written with BQL+mon_lock. */
int mux_out;
};
struct MonitorHMP {
Monitor common;
bool use_readline;
/*
* State used only in the thread "owning" the monitor.
* If @use_io_thread, this is @mon_iothread. (This does not actually happen
* in the current state of the code.)
* Else, it's the main thread.
* These members can be safely accessed without locks.
*/
ReadLineState *rs;
};
typedef struct {
Monitor common;
JSONMessageParser parser;
bool pretty;
/*
* When a client connects, we're in capabilities negotiation mode.
* @commands is &qmp_cap_negotiation_commands then. When command
* qmp_capabilities succeeds, we go into command mode, and
* @command becomes &qmp_commands.
*/
QmpCommandList *commands;
bool capab_offered[QMP_CAPABILITY__MAX]; /* capabilities offered */
bool capab[QMP_CAPABILITY__MAX]; /* offered and accepted */
/*
* Protects qmp request/response queue.
* Take monitor_lock first when you need both.
*/
QemuMutex qmp_queue_lock;
/* Input queue that holds all the parsed QMP requests */
GQueue *qmp_requests;
} MonitorQMP;
/**
* Is @mon a QMP monitor?
*/
static inline bool monitor_is_qmp(const Monitor *mon)
{
return mon->is_qmp;
}
typedef QTAILQ_HEAD(MonitorList, Monitor) MonitorList;
extern IOThread *mon_iothread;
extern QEMUBH *qmp_dispatcher_bh;
extern QmpCommandList qmp_commands, qmp_cap_negotiation_commands;
extern QemuMutex monitor_lock;
extern MonitorList mon_list;
extern int mon_refcount;
extern HMPCommand hmp_cmds[];
int monitor_puts(Monitor *mon, const char *str);
void monitor_data_init(Monitor *mon, bool is_qmp, bool skip_flush,
bool use_io_thread);
void monitor_data_destroy(Monitor *mon);
int monitor_can_read(void *opaque);
void monitor_list_append(Monitor *mon);
void monitor_fdsets_cleanup(void);
void qmp_send_response(MonitorQMP *mon, const QDict *rsp);
void monitor_data_destroy_qmp(MonitorQMP *mon);
void monitor_qmp_bh_dispatcher(void *data);
int get_monitor_def(int64_t *pval, const char *name);
void help_cmd(Monitor *mon, const char *name);
void handle_hmp_command(MonitorHMP *mon, const char *cmdline);
int hmp_compare_cmd(const char *name, const char *list);
#endif

628
monitor/monitor.c Normal file
View File

@ -0,0 +1,628 @@
/*
* 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 "monitor-internal.h"
#include "qapi/error.h"
#include "qapi/qapi-emit-events.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "qemu/error-report.h"
#include "qemu/option.h"
#include "sysemu/qtest.h"
#include "trace.h"
/*
* 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;
/* Shared monitor I/O thread */
IOThread *mon_iothread;
/* Bottom half to dispatch the requests received from I/O thread */
QEMUBH *qmp_dispatcher_bh;
/* Protects mon_list, monitor_qapi_event_state, monitor_destroyed. */
QemuMutex monitor_lock;
static GHashTable *monitor_qapi_event_state;
MonitorList mon_list;
int mon_refcount;
static bool monitor_destroyed;
__thread Monitor *cur_mon;
/**
* Is the current monitor, if any, a QMP monitor?
*/
bool monitor_cur_is_qmp(void)
{
return cur_mon && monitor_is_qmp(cur_mon);
}
/**
* Is @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 MonitorHMP *mon)
{
return mon->use_readline;
}
static inline bool monitor_is_hmp_non_interactive(const Monitor *mon)
{
if (monitor_is_qmp(mon)) {
return false;
}
return !monitor_uses_readline(container_of(mon, MonitorHMP, common));
}
static void monitor_flush_locked(Monitor *mon);
static gboolean monitor_unblocked(GIOChannel *chan, GIOCondition cond,
void *opaque)
{
Monitor *mon = opaque;
qemu_mutex_lock(&mon->mon_lock);
mon->out_watch = 0;
monitor_flush_locked(mon);
qemu_mutex_unlock(&mon->mon_lock);
return FALSE;
}
/* Caller must hold mon->mon_lock */
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 */
qobject_unref(mon->outbuf);
mon->outbuf = qstring_new();
return;
}
if (rc > 0) {
/* partial write */
QString *tmp = qstring_from_str(buf + rc);
qobject_unref(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->mon_lock);
monitor_flush_locked(mon);
qemu_mutex_unlock(&mon->mon_lock);
}
/* flush at every end of line */
int monitor_puts(Monitor *mon, const char *str)
{
int i;
char c;
qemu_mutex_lock(&mon->mon_lock);
for (i = 0; str[i]; i++) {
c = str[i];
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->mon_lock);
return i;
}
int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
{
char *buf;
int n;
if (!mon) {
return -1;
}
if (monitor_is_qmp(mon)) {
return -1;
}
buf = g_strdup_vprintf(fmt, ap);
n = monitor_puts(mon, buf);
g_free(buf);
return n;
}
int monitor_printf(Monitor *mon, const char *fmt, ...)
{
int ret;
va_list ap;
va_start(ap, fmt);
ret = monitor_vprintf(mon, fmt, ap);
va_end(ap);
return ret;
}
/*
* Print to current monitor if we have one, else to stderr.
*/
int error_vprintf(const char *fmt, va_list ap)
{
if (cur_mon && !monitor_cur_is_qmp()) {
return monitor_vprintf(cur_mon, fmt, ap);
}
return vfprintf(stderr, fmt, ap);
}
int error_vprintf_unless_qmp(const char *fmt, va_list ap)
{
if (!cur_mon) {
return vfprintf(stderr, fmt, ap);
}
if (!monitor_cur_is_qmp()) {
return monitor_vprintf(cur_mon, fmt, ap);
}
return -1;
}
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 },
};
/*
* Return the clock to use for recording an event's time.
* It's QEMU_CLOCK_REALTIME, except for qtests it's
* QEMU_CLOCK_VIRTUAL, to support testing rate limits.
* Beware: result is invalid before configure_accelerator().
*/
static inline QEMUClockType monitor_get_event_clock(void)
{
return qtest_enabled() ? QEMU_CLOCK_VIRTUAL : QEMU_CLOCK_REALTIME;
}
/*
* Broadcast an event to all monitors.
* @qdict is the event object. Its member "event" must match @event.
* Caller must hold monitor_lock.
*/
static void monitor_qapi_event_emit(QAPIEvent event, QDict *qdict)
{
Monitor *mon;
MonitorQMP *qmp_mon;
trace_monitor_protocol_event_emit(event, qdict);
QTAILQ_FOREACH(mon, &mon_list, entry) {
if (!monitor_is_qmp(mon)) {
continue;
}
qmp_mon = container_of(mon, MonitorQMP, common);
if (qmp_mon->commands != &qmp_cap_negotiation_commands) {
qmp_send_response(qmp_mon, 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_no_reenter(QAPIEvent event, QDict *qdict)
{
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.
*/
qobject_unref(evstate->qdict);
evstate->qdict = qobject_ref(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(monitor_get_event_clock());
monitor_qapi_event_emit(event, qdict);
evstate = g_new(MonitorQAPIEventState, 1);
evstate->event = event;
evstate->data = qobject_ref(data);
evstate->qdict = NULL;
evstate->timer = timer_new_ns(monitor_get_event_clock(),
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);
}
void qapi_event_emit(QAPIEvent event, QDict *qdict)
{
/*
* monitor_qapi_event_queue_no_reenter() is not reentrant: it
* would deadlock on monitor_lock. Work around by queueing
* events in thread-local storage.
* TODO: remove this, make it re-enter safe.
*/
typedef struct MonitorQapiEvent {
QAPIEvent event;
QDict *qdict;
QSIMPLEQ_ENTRY(MonitorQapiEvent) entry;
} MonitorQapiEvent;
static __thread QSIMPLEQ_HEAD(, MonitorQapiEvent) event_queue;
static __thread bool reentered;
MonitorQapiEvent *ev;
if (!reentered) {
QSIMPLEQ_INIT(&event_queue);
}
ev = g_new(MonitorQapiEvent, 1);
ev->qdict = qobject_ref(qdict);
ev->event = event;
QSIMPLEQ_INSERT_TAIL(&event_queue, ev, entry);
if (reentered) {
return;
}
reentered = true;
while ((ev = QSIMPLEQ_FIRST(&event_queue)) != NULL) {
QSIMPLEQ_REMOVE_HEAD(&event_queue, entry);
monitor_qapi_event_queue_no_reenter(ev->event, ev->qdict);
qobject_unref(ev->qdict);
g_free(ev);
}
reentered = false;
}
/*
* 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(monitor_get_event_clock());
monitor_qapi_event_emit(evstate->event, evstate->qdict);
qobject_unref(evstate->qdict);
evstate->qdict = NULL;
timer_mod_ns(evstate->timer, now + evconf->rate);
} else {
g_hash_table_remove(monitor_qapi_event_state, evstate);
qobject_unref(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;
}
int monitor_suspend(Monitor *mon)
{
if (monitor_is_hmp_non_interactive(mon)) {
return -ENOTTY;
}
atomic_inc(&mon->suspend_cnt);
if (mon->use_io_thread) {
/*
* Kick I/O thread to make sure this takes effect. It'll be
* evaluated again in prepare() of the watch object.
*/
aio_notify(iothread_get_aio_context(mon_iothread));
}
trace_monitor_suspend(mon, 1);
return 0;
}
static void monitor_accept_input(void *opaque)
{
Monitor *mon = opaque;
qemu_chr_fe_accept_input(&mon->chr);
}
void monitor_resume(Monitor *mon)
{
if (monitor_is_hmp_non_interactive(mon)) {
return;
}
if (atomic_dec_fetch(&mon->suspend_cnt) == 0) {
AioContext *ctx;
if (mon->use_io_thread) {
ctx = iothread_get_aio_context(mon_iothread);
} else {
ctx = qemu_get_aio_context();
}
if (!monitor_is_qmp(mon)) {
MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
assert(hmp_mon->rs);
readline_show_prompt(hmp_mon->rs);
}
aio_bh_schedule_oneshot(ctx, monitor_accept_input, mon);
}
trace_monitor_suspend(mon, -1);
}
int monitor_can_read(void *opaque)
{
Monitor *mon = opaque;
return !atomic_mb_read(&mon->suspend_cnt);
}
void monitor_list_append(Monitor *mon)
{
qemu_mutex_lock(&monitor_lock);
/*
* This prevents inserting new monitors during monitor_cleanup().
* A cleaner solution would involve the main thread telling other
* threads to terminate, waiting for their termination.
*/
if (!monitor_destroyed) {
QTAILQ_INSERT_HEAD(&mon_list, mon, entry);
mon = NULL;
}
qemu_mutex_unlock(&monitor_lock);
if (mon) {
monitor_data_destroy(mon);
g_free(mon);
}
}
static void monitor_iothread_init(void)
{
mon_iothread = iothread_create("mon_iothread", &error_abort);
}
void monitor_data_init(Monitor *mon, bool is_qmp, bool skip_flush,
bool use_io_thread)
{
if (use_io_thread && !mon_iothread) {
monitor_iothread_init();
}
qemu_mutex_init(&mon->mon_lock);
mon->is_qmp = is_qmp;
mon->outbuf = qstring_new();
mon->skip_flush = skip_flush;
mon->use_io_thread = use_io_thread;
}
void monitor_data_destroy(Monitor *mon)
{
g_free(mon->mon_cpu_path);
qemu_chr_fe_deinit(&mon->chr, false);
if (monitor_is_qmp(mon)) {
monitor_data_destroy_qmp(container_of(mon, MonitorQMP, common));
} else {
readline_free(container_of(mon, MonitorHMP, common)->rs);
}
qobject_unref(mon->outbuf);
qemu_mutex_destroy(&mon->mon_lock);
}
void monitor_cleanup(void)
{
/*
* We need to explicitly stop the I/O thread (but not destroy it),
* clean up the monitor resources, then destroy the I/O thread since
* we need to unregister from chardev below in
* monitor_data_destroy(), and chardev is not thread-safe yet
*/
if (mon_iothread) {
iothread_stop(mon_iothread);
}
/* Flush output buffers and destroy monitors */
qemu_mutex_lock(&monitor_lock);
monitor_destroyed = true;
while (!QTAILQ_EMPTY(&mon_list)) {
Monitor *mon = QTAILQ_FIRST(&mon_list);
QTAILQ_REMOVE(&mon_list, mon, entry);
/* Permit QAPI event emission from character frontend release */
qemu_mutex_unlock(&monitor_lock);
monitor_flush(mon);
monitor_data_destroy(mon);
qemu_mutex_lock(&monitor_lock);
g_free(mon);
}
qemu_mutex_unlock(&monitor_lock);
/* QEMUBHs needs to be deleted before destroying the I/O thread */
qemu_bh_delete(qmp_dispatcher_bh);
qmp_dispatcher_bh = NULL;
if (mon_iothread) {
iothread_destroy(mon_iothread);
mon_iothread = NULL;
}
}
static void monitor_qapi_event_init(void)
{
monitor_qapi_event_state = g_hash_table_new(qapi_event_throttle_hash,
qapi_event_throttle_equal);
}
void monitor_init_globals_core(void)
{
monitor_qapi_event_init();
qemu_mutex_init(&monitor_lock);
/*
* The dispatcher BH must run in the main loop thread, since we
* have commands assuming that context. It would be nice to get
* rid of those assumptions.
*/
qmp_dispatcher_bh = aio_bh_new(iohandler_get_aio_context(),
monitor_qmp_bh_dispatcher,
NULL);
}
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,
},
{ /* end of list */ }
},
};

View File

@ -1,5 +1,5 @@
/*
* QEMU Management Protocol
* QEMU Management Protocol commands
*
* Copyright IBM, Corp. 2011
*

404
monitor/qmp.c Normal file
View File

@ -0,0 +1,404 @@
/*
* 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 "chardev/char-io.h"
#include "monitor-internal.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-misc.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qjson.h"
#include "qapi/qmp/qlist.h"
#include "qapi/qmp/qstring.h"
#include "trace.h"
struct QMPRequest {
/* Owner of the request */
MonitorQMP *mon;
/*
* Request object to be handled or Error to be reported
* (exactly one of them is non-null)
*/
QObject *req;
Error *err;
};
typedef struct QMPRequest QMPRequest;
QmpCommandList qmp_commands, qmp_cap_negotiation_commands;
static bool qmp_oob_enabled(MonitorQMP *mon)
{
return mon->capab[QMP_CAPABILITY_OOB];
}
static void monitor_qmp_caps_reset(MonitorQMP *mon)
{
memset(mon->capab_offered, 0, sizeof(mon->capab_offered));
memset(mon->capab, 0, sizeof(mon->capab));
mon->capab_offered[QMP_CAPABILITY_OOB] = mon->common.use_io_thread;
}
static void qmp_request_free(QMPRequest *req)
{
qobject_unref(req->req);
error_free(req->err);
g_free(req);
}
/* Caller must hold mon->qmp.qmp_queue_lock */
static void monitor_qmp_cleanup_req_queue_locked(MonitorQMP *mon)
{
while (!g_queue_is_empty(mon->qmp_requests)) {
qmp_request_free(g_queue_pop_head(mon->qmp_requests));
}
}
static void monitor_qmp_cleanup_queues(MonitorQMP *mon)
{
qemu_mutex_lock(&mon->qmp_queue_lock);
monitor_qmp_cleanup_req_queue_locked(mon);
qemu_mutex_unlock(&mon->qmp_queue_lock);
}
void qmp_send_response(MonitorQMP *mon, const QDict *rsp)
{
const QObject *data = QOBJECT(rsp);
QString *json;
json = mon->pretty ? qobject_to_json_pretty(data) : qobject_to_json(data);
assert(json != NULL);
qstring_append_chr(json, '\n');
monitor_puts(&mon->common, qstring_get_str(json));
qobject_unref(json);
}
/*
* Emit QMP response @rsp with ID @id to @mon.
* Null @rsp can only happen for commands with QCO_NO_SUCCESS_RESP.
* Nothing is emitted then.
*/
static void monitor_qmp_respond(MonitorQMP *mon, QDict *rsp)
{
if (rsp) {
qmp_send_response(mon, rsp);
}
}
static void monitor_qmp_dispatch(MonitorQMP *mon, QObject *req)
{
Monitor *old_mon;
QDict *rsp;
QDict *error;
old_mon = cur_mon;
cur_mon = &mon->common;
rsp = qmp_dispatch(mon->commands, req, qmp_oob_enabled(mon));
cur_mon = old_mon;
if (mon->commands == &qmp_cap_negotiation_commands) {
error = qdict_get_qdict(rsp, "error");
if (error
&& !g_strcmp0(qdict_get_try_str(error, "class"),
QapiErrorClass_str(ERROR_CLASS_COMMAND_NOT_FOUND))) {
/* Provide a more useful error message */
qdict_del(error, "desc");
qdict_put_str(error, "desc", "Expecting capabilities negotiation"
" with 'qmp_capabilities'");
}
}
monitor_qmp_respond(mon, rsp);
qobject_unref(rsp);
}
/*
* Pop a QMP request from a monitor request queue.
* Return the request, or NULL all request queues are empty.
* 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.
*
* Note: if the function returned with non-NULL, then the caller will
* be with qmp_mon->qmp_queue_lock held, and the caller is responsible
* to release it.
*/
static QMPRequest *monitor_qmp_requests_pop_any_with_lock(void)
{
QMPRequest *req_obj = NULL;
Monitor *mon;
MonitorQMP *qmp_mon;
qemu_mutex_lock(&monitor_lock);
QTAILQ_FOREACH(mon, &mon_list, entry) {
if (!monitor_is_qmp(mon)) {
continue;
}
qmp_mon = container_of(mon, MonitorQMP, common);
qemu_mutex_lock(&qmp_mon->qmp_queue_lock);
req_obj = g_queue_pop_head(qmp_mon->qmp_requests);
if (req_obj) {
/* With the lock of corresponding queue held */
break;
}
qemu_mutex_unlock(&qmp_mon->qmp_queue_lock);
}
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;
}
void monitor_qmp_bh_dispatcher(void *data)
{
QMPRequest *req_obj = monitor_qmp_requests_pop_any_with_lock();
QDict *rsp;
bool need_resume;
MonitorQMP *mon;
if (!req_obj) {
return;
}
mon = req_obj->mon;
/* qmp_oob_enabled() might change after "qmp_capabilities" */
need_resume = !qmp_oob_enabled(mon) ||
mon->qmp_requests->length == QMP_REQ_QUEUE_LEN_MAX - 1;
qemu_mutex_unlock(&mon->qmp_queue_lock);
if (req_obj->req) {
QDict *qdict = qobject_to(QDict, req_obj->req);
QObject *id = qdict ? qdict_get(qdict, "id") : NULL;
trace_monitor_qmp_cmd_in_band(qobject_get_try_str(id) ?: "");
monitor_qmp_dispatch(mon, req_obj->req);
} else {
assert(req_obj->err);
rsp = qmp_error_response(req_obj->err);
req_obj->err = NULL;
monitor_qmp_respond(mon, rsp);
qobject_unref(rsp);
}
if (need_resume) {
/* Pairs with the monitor_suspend() in handle_qmp_command() */
monitor_resume(&mon->common);
}
qmp_request_free(req_obj);
/* Reschedule instead of looping so the main loop stays responsive */
qemu_bh_schedule(qmp_dispatcher_bh);
}
static void handle_qmp_command(void *opaque, QObject *req, Error *err)
{
MonitorQMP *mon = opaque;
QObject *id = NULL;
QDict *qdict;
QMPRequest *req_obj;
assert(!req != !err);
qdict = qobject_to(QDict, req);
if (qdict) {
id = qdict_get(qdict, "id");
} /* else will fail qmp_dispatch() */
if (req && 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));
qobject_unref(req_json);
}
if (qdict && qmp_is_oob(qdict)) {
/* OOB commands are executed immediately */
trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(id) ?: "");
monitor_qmp_dispatch(mon, req);
qobject_unref(req);
return;
}
req_obj = g_new0(QMPRequest, 1);
req_obj->mon = mon;
req_obj->req = req;
req_obj->err = err;
/* Protect qmp_requests and fetching its length. */
qemu_mutex_lock(&mon->qmp_queue_lock);
/*
* Suspend the monitor when we can't queue more requests after
* this one. Dequeuing in monitor_qmp_bh_dispatcher() will resume
* it. Note that when OOB is disabled, we queue at most one
* command, for backward compatibility.
*/
if (!qmp_oob_enabled(mon) ||
mon->qmp_requests->length == QMP_REQ_QUEUE_LEN_MAX - 1) {
monitor_suspend(&mon->common);
}
/*
* Put the request to the end of queue so that requests will be
* handled in time order. Ownership for req_obj, req,
* etc. will be delivered to the handler side.
*/
assert(mon->qmp_requests->length < QMP_REQ_QUEUE_LEN_MAX);
g_queue_push_tail(mon->qmp_requests, req_obj);
qemu_mutex_unlock(&mon->qmp_queue_lock);
/* Kick the dispatcher routine */
qemu_bh_schedule(qmp_dispatcher_bh);
}
static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size)
{
MonitorQMP *mon = opaque;
json_message_parser_feed(&mon->parser, (const char *) buf, size);
}
static QDict *qmp_greeting(MonitorQMP *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->capab_offered[cap]) {
qlist_append_str(cap_list, QMPCapability_str(cap));
}
}
return qdict_from_jsonf_nofail(
"{'QMP': {'version': %p, 'capabilities': %p}}",
ver, cap_list);
}
static void monitor_qmp_event(void *opaque, int event)
{
QDict *data;
MonitorQMP *mon = opaque;
switch (event) {
case CHR_EVENT_OPENED:
mon->commands = &qmp_cap_negotiation_commands;
monitor_qmp_caps_reset(mon);
data = qmp_greeting(mon);
qmp_send_response(mon, data);
qobject_unref(data);
mon_refcount++;
break;
case CHR_EVENT_CLOSED:
/*
* Note: this is only useful when the output of the chardev
* backend is still open. For example, when the backend is
* stdio, it's possible that stdout is still open when stdin
* is closed.
*/
monitor_qmp_cleanup_queues(mon);
json_message_parser_destroy(&mon->parser);
json_message_parser_init(&mon->parser, handle_qmp_command,
mon, NULL);
mon_refcount--;
monitor_fdsets_cleanup();
break;
}
}
void monitor_data_destroy_qmp(MonitorQMP *mon)
{
json_message_parser_destroy(&mon->parser);
qemu_mutex_destroy(&mon->qmp_queue_lock);
monitor_qmp_cleanup_req_queue_locked(mon);
g_queue_free(mon->qmp_requests);
}
static void monitor_qmp_setup_handlers_bh(void *opaque)
{
MonitorQMP *mon = opaque;
GMainContext *context;
assert(mon->common.use_io_thread);
context = iothread_get_g_main_context(mon_iothread);
assert(context);
qemu_chr_fe_set_handlers(&mon->common.chr, monitor_can_read,
monitor_qmp_read, monitor_qmp_event,
NULL, &mon->common, context, true);
monitor_list_append(&mon->common);
}
void monitor_init_qmp(Chardev *chr, bool pretty)
{
MonitorQMP *mon = g_new0(MonitorQMP, 1);
/* Note: we run QMP monitor in I/O thread when @chr supports that */
monitor_data_init(&mon->common, true, false,
qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_GCONTEXT));
mon->pretty = pretty;
qemu_mutex_init(&mon->qmp_queue_lock);
mon->qmp_requests = g_queue_new();
qemu_chr_fe_init(&mon->common.chr, chr, &error_abort);
qemu_chr_fe_set_echo(&mon->common.chr, true);
json_message_parser_init(&mon->parser, handle_qmp_command, mon, NULL);
if (mon->common.use_io_thread) {
/*
* 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 chardev might be running in the monitor I/O
* thread. Schedule a bottom half.
*/
aio_bh_schedule_oneshot(iothread_get_aio_context(mon_iothread),
monitor_qmp_setup_handlers_bh, mon);
/* The bottom half will add @mon to @mon_list */
} else {
qemu_chr_fe_set_handlers(&mon->common.chr, monitor_can_read,
monitor_qmp_read, monitor_qmp_event,
NULL, &mon->common, NULL, true);
monitor_list_append(&mon->common);
}
}

15
monitor/trace-events Normal file
View File

@ -0,0 +1,15 @@
# See docs/devel/tracing.txt for syntax documentation.
# hmp.c
handle_hmp_command(void *mon, const char *cmdline) "mon %p cmdline: %s"
# monitor.c
monitor_protocol_event_handler(uint32_t event, void *qdict) "event=%d data=%p"
monitor_protocol_event_emit(uint32_t event, void *data) "event=%d data=%p"
monitor_protocol_event_queue(uint32_t event, void *qdict, uint64_t rate) "event=%d data=%p rate=%" PRId64
monitor_suspend(void *ptr, int cnt) "mon %p: %d"
# qmp.c
monitor_qmp_cmd_in_band(const char *id) "%s"
monitor_qmp_cmd_out_of_band(const char *id) "%s"
handle_qmp_command(void *mon, const char *req) "mon %p req: %s"

View File

@ -72,6 +72,12 @@ backend settings instead of environment variables. To ease migration to
the new format, the ``-audiodev-help'' option can be used to convert
the current values of the environment variables to ``-audiodev'' options.
@subsection -mon ...,control=readline,pretty=on|off (since 4.1)
The @code{pretty=on|off} switch has no effect for HMP monitors, but is
silently ignored. Using the switch with HMP monitors will become an
error in the future.
@subsection -realtime (since 4.1)
The @code{-realtime mlock=on|off} argument has been replaced by the

View File

@ -6,7 +6,7 @@ int monitor_fdset_dup_fd_add(int64_t fdset_id, int dup_fd)
return -1;
}
int monitor_fdset_dup_fd_find(int dup_fd)
int64_t monitor_fdset_dup_fd_find(int dup_fd)
{
return -1;
}

View File

@ -16,7 +16,11 @@ int monitor_get_fd(Monitor *mon, const char *name, Error **errp)
return -1;
}
void monitor_init(Chardev *chr, int flags)
void monitor_init_qmp(Chardev *chr, bool pretty)
{
}
void monitor_init_hmp(Chardev *chr, bool use_readline)
{
}

View File

@ -71,7 +71,8 @@ int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp)
*/
__thread Monitor *cur_mon;
int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) { abort(); }
void monitor_init(Chardev *chr, int flags) {}
void monitor_init_qmp(Chardev *chr, bool pretty) {}
void monitor_init_hmp(Chardev *chr, bool use_readline) {}
static void test_socket_fd_pass_name_good(void)

View File

@ -41,16 +41,6 @@ system_wakeup_request(int reason) "reason=%d"
qemu_system_shutdown_request(int reason) "reason=%d"
qemu_system_powerdown_request(void) ""
# monitor.c
monitor_protocol_event_handler(uint32_t event, void *qdict) "event=%d data=%p"
monitor_protocol_event_emit(uint32_t event, void *data) "event=%d data=%p"
monitor_protocol_event_queue(uint32_t event, void *qdict, uint64_t rate) "event=%d data=%p rate=%" PRId64
handle_hmp_command(void *mon, const char *cmdline) "mon %p cmdline: %s"
handle_qmp_command(void *mon, const char *req) "mon %p req: %s"
monitor_suspend(void *ptr, int cnt) "mon %p: %d"
monitor_qmp_cmd_in_band(const char *id) "%s"
monitor_qmp_cmd_out_of_band(const char *id) "%s"
# dma-helpers.c
dma_blk_io(void *dbs, void *bs, int64_t offset, bool to_dev) "dbs=%p bs=%p offset=%" PRId64 " to_dev=%d"
dma_aio_cancel(void *dbs) "dbs=%p"

28
vl.c
View File

@ -2299,25 +2299,31 @@ static int fsdev_init_func(void *opaque, QemuOpts *opts, Error **errp)
static int mon_init_func(void *opaque, QemuOpts *opts, Error **errp)
{
Chardev *chr;
bool qmp;
bool pretty = false;
const char *chardev;
const char *mode;
int flags;
mode = qemu_opt_get(opts, "mode");
if (mode == NULL) {
mode = "readline";
}
if (strcmp(mode, "readline") == 0) {
flags = MONITOR_USE_READLINE;
qmp = false;
} else if (strcmp(mode, "control") == 0) {
flags = MONITOR_USE_CONTROL;
qmp = true;
} else {
error_setg(errp, "unknown monitor mode \"%s\"", mode);
return -1;
}
if (qemu_opt_get_bool(opts, "pretty", 0))
flags |= MONITOR_USE_PRETTY;
if (!qmp && qemu_opt_get(opts, "pretty")) {
warn_report("'pretty' is deprecated for HMP monitors, it has no effect "
"and will be removed in future versions");
}
if (qemu_opt_get_bool(opts, "pretty", 0)) {
pretty = true;
}
chardev = qemu_opt_get(opts, "chardev");
if (!chardev) {
@ -2330,7 +2336,11 @@ static int mon_init_func(void *opaque, QemuOpts *opts, Error **errp)
return -1;
}
monitor_init(chr, flags);
if (qmp) {
monitor_init_qmp(chr, pretty);
} else {
monitor_init_hmp(chr, true);
}
return 0;
}
@ -2356,7 +2366,11 @@ static void monitor_parse(const char *optarg, const char *mode, bool pretty)
opts = qemu_opts_create(qemu_find_opts("mon"), label, 1, &error_fatal);
qemu_opt_set(opts, "mode", mode, &error_abort);
qemu_opt_set(opts, "chardev", label, &error_abort);
qemu_opt_set_bool(opts, "pretty", pretty, &error_abort);
if (!strcmp(mode, "control")) {
qemu_opt_set_bool(opts, "pretty", pretty, &error_abort);
} else {
assert(pretty == false);
}
monitor_device_index++;
}