Merge remote-tracking branch 'mdroth/qga-pull-5-15-12' into staging
* mdroth/qga-pull-5-15-12: qemu-ga: align versioning with QEMU_VERSION qemu-ga: fix segv after failure to open log file qemu-ga: guest-shutdown: use only async-signal-safe functions qemu-ga: guest-shutdown: become synchronous qemu-ga: guest-suspend: make the API synchronous qemu-ga: become_daemon(): reopen standard fds to /dev/null qemu-ga: make reopen_fd_to_null() public qemu-ga: guest-suspend-hybrid: don't emit a success response qemu-ga: guest-suspend-ram: don't emit a success response qemu-ga: guest-suspend-disk: don't emit a success response qemu-ga: guest-shutdown: don't emit a success response qemu-ga: don't warn on no command return qapi: add support for command options
This commit is contained in:
commit
b4f1a7ca72
@ -126,16 +126,19 @@
|
||||
# @guest-shutdown:
|
||||
#
|
||||
# Initiate guest-activated shutdown. Note: this is an asynchronous
|
||||
# shutdown request, with no guaruntee of successful shutdown. Errors
|
||||
# will be logged to guest's syslog.
|
||||
# shutdown request, with no guarantee of successful shutdown.
|
||||
#
|
||||
# @mode: #optional "halt", "powerdown" (default), or "reboot"
|
||||
#
|
||||
# Returns: Nothing on success
|
||||
# This command does NOT return a response on success. Success condition
|
||||
# is indicated by the VM exiting with a zero exit status or, when
|
||||
# running with --no-shutdown, by issuing the query-status QMP command
|
||||
# to confirm the VM status is "shutdown".
|
||||
#
|
||||
# Since: 0.15.0
|
||||
##
|
||||
{ 'command': 'guest-shutdown', 'data': { '*mode': 'str' } }
|
||||
{ 'command': 'guest-shutdown', 'data': { '*mode': 'str' },
|
||||
'success-response': 'no' }
|
||||
|
||||
##
|
||||
# @guest-file-open:
|
||||
@ -359,17 +362,21 @@
|
||||
# For the best results it's strongly recommended to have the pm-utils
|
||||
# package installed in the guest.
|
||||
#
|
||||
# Returns: nothing on success
|
||||
# This command does NOT return a response on success. There is a high chance
|
||||
# the command succeeded if the VM exits with a zero exit status or, when
|
||||
# running with --no-shutdown, by issuing the query-status QMP command to
|
||||
# to confirm the VM status is "shutdown". However, the VM could also exit
|
||||
# (or set its status to "shutdown") due to other reasons.
|
||||
#
|
||||
# The following errors may be returned:
|
||||
# If suspend to disk is not supported, Unsupported
|
||||
#
|
||||
# Notes: o This is an asynchronous request. There's no guarantee a response
|
||||
# will be sent
|
||||
# o It's strongly recommended to issue the guest-sync command before
|
||||
# sending commands when the guest resumes
|
||||
# Notes: It's strongly recommended to issue the guest-sync command before
|
||||
# sending commands when the guest resumes
|
||||
#
|
||||
# Since: 1.1
|
||||
##
|
||||
{ 'command': 'guest-suspend-disk' }
|
||||
{ 'command': 'guest-suspend-disk', 'success-response': 'no' }
|
||||
|
||||
##
|
||||
# @guest-suspend-ram
|
||||
@ -387,17 +394,21 @@
|
||||
# command. Thus, it's *required* to query QEMU for the presence of the
|
||||
# 'system_wakeup' command before issuing guest-suspend-ram.
|
||||
#
|
||||
# Returns: nothing on success
|
||||
# This command does NOT return a response on success. There are two options
|
||||
# to check for success:
|
||||
# 1. Wait for the SUSPEND QMP event from QEMU
|
||||
# 2. Issue the query-status QMP command to confirm the VM status is
|
||||
# "suspended"
|
||||
#
|
||||
# The following errors may be returned:
|
||||
# If suspend to ram is not supported, Unsupported
|
||||
#
|
||||
# Notes: o This is an asynchronous request. There's no guarantee a response
|
||||
# will be sent
|
||||
# o It's strongly recommended to issue the guest-sync command before
|
||||
# sending commands when the guest resumes
|
||||
# Notes: It's strongly recommended to issue the guest-sync command before
|
||||
# sending commands when the guest resumes
|
||||
#
|
||||
# Since: 1.1
|
||||
##
|
||||
{ 'command': 'guest-suspend-ram' }
|
||||
{ 'command': 'guest-suspend-ram', 'success-response': 'no' }
|
||||
|
||||
##
|
||||
# @guest-suspend-hybrid
|
||||
@ -410,17 +421,21 @@
|
||||
# command. Thus, it's *required* to query QEMU for the presence of the
|
||||
# 'system_wakeup' command before issuing guest-suspend-hybrid.
|
||||
#
|
||||
# Returns: nothing on success
|
||||
# This command does NOT return a response on success. There are two options
|
||||
# to check for success:
|
||||
# 1. Wait for the SUSPEND QMP event from QEMU
|
||||
# 2. Issue the query-status QMP command to confirm the VM status is
|
||||
# "suspended"
|
||||
#
|
||||
# The following errors may be returned:
|
||||
# If hybrid suspend is not supported, Unsupported
|
||||
#
|
||||
# Notes: o This is an asynchronous request. There's no guarantee a response
|
||||
# will be sent
|
||||
# o It's strongly recommended to issue the guest-sync command before
|
||||
# sending commands when the guest resumes
|
||||
# Notes: It's strongly recommended to issue the guest-sync command before
|
||||
# sending commands when the guest resumes
|
||||
#
|
||||
# Since: 1.1
|
||||
##
|
||||
{ 'command': 'guest-suspend-hybrid' }
|
||||
{ 'command': 'guest-suspend-hybrid', 'success-response': 'no' }
|
||||
|
||||
##
|
||||
# @GuestIpAddressType:
|
||||
|
@ -25,16 +25,24 @@ typedef enum QmpCommandType
|
||||
QCT_NORMAL,
|
||||
} QmpCommandType;
|
||||
|
||||
typedef enum QmpCommandOptions
|
||||
{
|
||||
QCO_NO_OPTIONS = 0x0,
|
||||
QCO_NO_SUCCESS_RESP = 0x1,
|
||||
} QmpCommandOptions;
|
||||
|
||||
typedef struct QmpCommand
|
||||
{
|
||||
const char *name;
|
||||
QmpCommandType type;
|
||||
QmpCommandFunc *fn;
|
||||
QmpCommandOptions options;
|
||||
QTAILQ_ENTRY(QmpCommand) node;
|
||||
bool enabled;
|
||||
} QmpCommand;
|
||||
|
||||
void qmp_register_command(const char *name, QmpCommandFunc *fn);
|
||||
void qmp_register_command(const char *name, QmpCommandFunc *fn,
|
||||
QmpCommandOptions options);
|
||||
QmpCommand *qmp_find_command(const char *name);
|
||||
QObject *qmp_dispatch(QObject *request);
|
||||
void qmp_disable_command(const char *name);
|
||||
|
@ -94,8 +94,12 @@ static QObject *do_qmp_dispatch(QObject *request, Error **errp)
|
||||
switch (cmd->type) {
|
||||
case QCT_NORMAL:
|
||||
cmd->fn(args, &ret, errp);
|
||||
if (!error_is_set(errp) && ret == NULL) {
|
||||
ret = QOBJECT(qdict_new());
|
||||
if (!error_is_set(errp)) {
|
||||
if (cmd->options & QCO_NO_SUCCESS_RESP) {
|
||||
g_assert(!ret);
|
||||
} else if (!ret) {
|
||||
ret = QOBJECT(qdict_new());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -17,7 +17,8 @@
|
||||
static QTAILQ_HEAD(QmpCommandList, QmpCommand) qmp_commands =
|
||||
QTAILQ_HEAD_INITIALIZER(qmp_commands);
|
||||
|
||||
void qmp_register_command(const char *name, QmpCommandFunc *fn)
|
||||
void qmp_register_command(const char *name, QmpCommandFunc *fn,
|
||||
QmpCommandOptions options)
|
||||
{
|
||||
QmpCommand *cmd = g_malloc0(sizeof(*cmd));
|
||||
|
||||
@ -25,6 +26,7 @@ void qmp_register_command(const char *name, QmpCommandFunc *fn)
|
||||
cmd->type = QCT_NORMAL;
|
||||
cmd->fn = fn;
|
||||
cmd->enabled = true;
|
||||
cmd->options = options;
|
||||
QTAILQ_INSERT_TAIL(&qmp_commands, cmd, node);
|
||||
}
|
||||
|
||||
|
49
qemu-ga.c
49
qemu-ga.c
@ -104,16 +104,9 @@ static void quit_handler(int sig)
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
/* reap _all_ terminated children */
|
||||
static void child_handler(int sig)
|
||||
{
|
||||
int status;
|
||||
while (waitpid(-1, &status, WNOHANG) > 0) /* NOTHING */;
|
||||
}
|
||||
|
||||
static gboolean register_signal_handlers(void)
|
||||
{
|
||||
struct sigaction sigact, sigact_chld;
|
||||
struct sigaction sigact;
|
||||
int ret;
|
||||
|
||||
memset(&sigact, 0, sizeof(struct sigaction));
|
||||
@ -130,15 +123,24 @@ static gboolean register_signal_handlers(void)
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(&sigact_chld, 0, sizeof(struct sigaction));
|
||||
sigact_chld.sa_handler = child_handler;
|
||||
sigact_chld.sa_flags = SA_NOCLDSTOP;
|
||||
ret = sigaction(SIGCHLD, &sigact_chld, NULL);
|
||||
if (ret == -1) {
|
||||
g_error("error configuring signal handler: %s", strerror(errno));
|
||||
return true;
|
||||
}
|
||||
|
||||
/* TODO: use this in place of all post-fork() fclose(std*) callers */
|
||||
void reopen_fd_to_null(int fd)
|
||||
{
|
||||
int nullfd;
|
||||
|
||||
nullfd = open("/dev/null", O_RDWR);
|
||||
if (nullfd < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
return true;
|
||||
dup2(nullfd, fd);
|
||||
|
||||
if (nullfd != fd) {
|
||||
close(nullfd);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -167,7 +169,7 @@ static void usage(const char *cmd)
|
||||
" -h, --help display this help and exit\n"
|
||||
"\n"
|
||||
"Report bugs to <mdroth@linux.vnet.ibm.com>\n"
|
||||
, cmd, QGA_VERSION, QGA_VIRTIO_PATH_DEFAULT, QGA_PIDFILE_DEFAULT,
|
||||
, cmd, QEMU_VERSION, QGA_VIRTIO_PATH_DEFAULT, QGA_PIDFILE_DEFAULT,
|
||||
QGA_STATEDIR_DEFAULT);
|
||||
}
|
||||
|
||||
@ -428,9 +430,9 @@ static void become_daemon(const char *pidfile)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
close(STDIN_FILENO);
|
||||
close(STDOUT_FILENO);
|
||||
close(STDERR_FILENO);
|
||||
reopen_fd_to_null(STDIN_FILENO);
|
||||
reopen_fd_to_null(STDOUT_FILENO);
|
||||
reopen_fd_to_null(STDERR_FILENO);
|
||||
return;
|
||||
|
||||
fail:
|
||||
@ -488,8 +490,6 @@ static void process_command(GAState *s, QDict *req)
|
||||
g_warning("error sending response: %s", strerror(ret));
|
||||
}
|
||||
qobject_decref(rsp);
|
||||
} else {
|
||||
g_warning("error getting response");
|
||||
}
|
||||
}
|
||||
|
||||
@ -729,7 +729,7 @@ int main(int argc, char **argv)
|
||||
log_level = G_LOG_LEVEL_MASK;
|
||||
break;
|
||||
case 'V':
|
||||
printf("QEMU Guest Agent %s\n", QGA_VERSION);
|
||||
printf("QEMU Guest Agent %s\n", QEMU_VERSION);
|
||||
return 0;
|
||||
case 'd':
|
||||
daemonize = 1;
|
||||
@ -836,12 +836,13 @@ int main(int argc, char **argv)
|
||||
become_daemon(pid_filepath);
|
||||
}
|
||||
if (log_filepath) {
|
||||
s->log_file = fopen(log_filepath, "a");
|
||||
if (!s->log_file) {
|
||||
FILE *log_file = fopen(log_filepath, "a");
|
||||
if (!log_file) {
|
||||
g_critical("unable to open specified log file: %s",
|
||||
strerror(errno));
|
||||
goto out_bad;
|
||||
}
|
||||
s->log_file = log_file;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,29 +34,11 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__linux__)
|
||||
/* TODO: use this in place of all post-fork() fclose(std*) callers */
|
||||
static void reopen_fd_to_null(int fd)
|
||||
{
|
||||
int nullfd;
|
||||
|
||||
nullfd = open("/dev/null", O_RDWR);
|
||||
if (nullfd < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
dup2(nullfd, fd);
|
||||
|
||||
if (nullfd != fd) {
|
||||
close(nullfd);
|
||||
}
|
||||
}
|
||||
#endif /* defined(__linux__) */
|
||||
|
||||
void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err)
|
||||
{
|
||||
int ret;
|
||||
const char *shutdown_flag;
|
||||
pid_t rpid, pid;
|
||||
int status;
|
||||
|
||||
slog("guest-shutdown called, mode: %s", mode);
|
||||
if (!has_mode || strcmp(mode, "powerdown") == 0) {
|
||||
@ -71,23 +53,30 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err)
|
||||
return;
|
||||
}
|
||||
|
||||
ret = fork();
|
||||
if (ret == 0) {
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
/* child, start the shutdown */
|
||||
setsid();
|
||||
fclose(stdin);
|
||||
fclose(stdout);
|
||||
fclose(stderr);
|
||||
reopen_fd_to_null(0);
|
||||
reopen_fd_to_null(1);
|
||||
reopen_fd_to_null(2);
|
||||
|
||||
ret = execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
|
||||
"hypervisor initiated shutdown", (char*)NULL);
|
||||
if (ret) {
|
||||
slog("guest-shutdown failed: %s", strerror(errno));
|
||||
}
|
||||
exit(!!ret);
|
||||
} else if (ret < 0) {
|
||||
error_set(err, QERR_UNDEFINED_ERROR);
|
||||
execle("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
|
||||
"hypervisor initiated shutdown", (char*)NULL, environ);
|
||||
_exit(EXIT_FAILURE);
|
||||
} else if (pid < 0) {
|
||||
goto exit_err;
|
||||
}
|
||||
|
||||
do {
|
||||
rpid = waitpid(pid, &status, 0);
|
||||
} while (rpid == -1 && errno == EINTR);
|
||||
if (rpid == pid && WIFEXITED(status) && !WEXITSTATUS(status)) {
|
||||
return;
|
||||
}
|
||||
|
||||
exit_err:
|
||||
error_set(err, QERR_UNDEFINED_ERROR);
|
||||
}
|
||||
|
||||
typedef struct GuestFileHandle {
|
||||
@ -531,117 +520,88 @@ static void guest_fsfreeze_cleanup(void)
|
||||
#define SUSPEND_SUPPORTED 0
|
||||
#define SUSPEND_NOT_SUPPORTED 1
|
||||
|
||||
/**
|
||||
* This function forks twice and the information about the mode support
|
||||
* status is passed to the qemu-ga process via a pipe.
|
||||
*
|
||||
* This approach allows us to keep the way we reap terminated children
|
||||
* in qemu-ga quite simple.
|
||||
*/
|
||||
static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg,
|
||||
const char *sysfile_str, Error **err)
|
||||
{
|
||||
pid_t pid;
|
||||
ssize_t ret;
|
||||
char *pmutils_path;
|
||||
int status, pipefds[2];
|
||||
|
||||
if (pipe(pipefds) < 0) {
|
||||
error_set(err, QERR_UNDEFINED_ERROR);
|
||||
return;
|
||||
}
|
||||
pid_t pid, rpid;
|
||||
int status;
|
||||
|
||||
pmutils_path = g_find_program_in_path(pmutils_bin);
|
||||
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
struct sigaction act;
|
||||
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_handler = SIG_DFL;
|
||||
sigaction(SIGCHLD, &act, NULL);
|
||||
char buf[32]; /* hopefully big enough */
|
||||
ssize_t ret;
|
||||
int fd;
|
||||
|
||||
setsid();
|
||||
close(pipefds[0]);
|
||||
reopen_fd_to_null(0);
|
||||
reopen_fd_to_null(1);
|
||||
reopen_fd_to_null(2);
|
||||
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
int fd;
|
||||
char buf[32]; /* hopefully big enough */
|
||||
if (pmutils_path) {
|
||||
execle(pmutils_path, pmutils_bin, pmutils_arg, NULL, environ);
|
||||
}
|
||||
|
||||
if (pmutils_path) {
|
||||
execle(pmutils_path, pmutils_bin, pmutils_arg, NULL, environ);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we get here either pm-utils is not installed or execle() has
|
||||
* failed. Let's try the manual method if the caller wants it.
|
||||
*/
|
||||
|
||||
if (!sysfile_str) {
|
||||
_exit(SUSPEND_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
_exit(SUSPEND_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
ret = read(fd, buf, sizeof(buf)-1);
|
||||
if (ret <= 0) {
|
||||
_exit(SUSPEND_NOT_SUPPORTED);
|
||||
}
|
||||
buf[ret] = '\0';
|
||||
|
||||
if (strstr(buf, sysfile_str)) {
|
||||
_exit(SUSPEND_SUPPORTED);
|
||||
}
|
||||
/*
|
||||
* If we get here either pm-utils is not installed or execle() has
|
||||
* failed. Let's try the manual method if the caller wants it.
|
||||
*/
|
||||
|
||||
if (!sysfile_str) {
|
||||
_exit(SUSPEND_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
if (pid > 0) {
|
||||
wait(&status);
|
||||
} else {
|
||||
status = SUSPEND_NOT_SUPPORTED;
|
||||
fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
_exit(SUSPEND_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
ret = write(pipefds[1], &status, sizeof(status));
|
||||
if (ret != sizeof(status)) {
|
||||
_exit(EXIT_FAILURE);
|
||||
ret = read(fd, buf, sizeof(buf)-1);
|
||||
if (ret <= 0) {
|
||||
_exit(SUSPEND_NOT_SUPPORTED);
|
||||
}
|
||||
buf[ret] = '\0';
|
||||
|
||||
if (strstr(buf, sysfile_str)) {
|
||||
_exit(SUSPEND_SUPPORTED);
|
||||
}
|
||||
|
||||
_exit(EXIT_SUCCESS);
|
||||
_exit(SUSPEND_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
close(pipefds[1]);
|
||||
g_free(pmutils_path);
|
||||
|
||||
if (pid < 0) {
|
||||
error_set(err, QERR_UNDEFINED_ERROR);
|
||||
goto out;
|
||||
goto undef_err;
|
||||
}
|
||||
|
||||
ret = read(pipefds[0], &status, sizeof(status));
|
||||
if (ret == sizeof(status) && WIFEXITED(status) &&
|
||||
WEXITSTATUS(status) == SUSPEND_SUPPORTED) {
|
||||
goto out;
|
||||
do {
|
||||
rpid = waitpid(pid, &status, 0);
|
||||
} while (rpid == -1 && errno == EINTR);
|
||||
if (rpid == pid && WIFEXITED(status)) {
|
||||
switch (WEXITSTATUS(status)) {
|
||||
case SUSPEND_SUPPORTED:
|
||||
return;
|
||||
case SUSPEND_NOT_SUPPORTED:
|
||||
error_set(err, QERR_UNSUPPORTED);
|
||||
return;
|
||||
default:
|
||||
goto undef_err;
|
||||
}
|
||||
}
|
||||
|
||||
error_set(err, QERR_UNSUPPORTED);
|
||||
|
||||
out:
|
||||
close(pipefds[0]);
|
||||
undef_err:
|
||||
error_set(err, QERR_UNDEFINED_ERROR);
|
||||
}
|
||||
|
||||
static void guest_suspend(const char *pmutils_bin, const char *sysfile_str,
|
||||
Error **err)
|
||||
{
|
||||
pid_t pid;
|
||||
char *pmutils_path;
|
||||
pid_t rpid, pid;
|
||||
int status;
|
||||
|
||||
pmutils_path = g_find_program_in_path(pmutils_bin);
|
||||
|
||||
@ -683,9 +643,18 @@ static void guest_suspend(const char *pmutils_bin, const char *sysfile_str,
|
||||
g_free(pmutils_path);
|
||||
|
||||
if (pid < 0) {
|
||||
error_set(err, QERR_UNDEFINED_ERROR);
|
||||
goto exit_err;
|
||||
}
|
||||
|
||||
do {
|
||||
rpid = waitpid(pid, &status, 0);
|
||||
} while (rpid == -1 && errno == EINTR);
|
||||
if (rpid == pid && WIFEXITED(status) && !WEXITSTATUS(status)) {
|
||||
return;
|
||||
}
|
||||
|
||||
exit_err:
|
||||
error_set(err, QERR_UNDEFINED_ERROR);
|
||||
}
|
||||
|
||||
void qmp_guest_suspend_disk(Error **err)
|
||||
|
@ -52,7 +52,7 @@ struct GuestAgentInfo *qmp_guest_info(Error **err)
|
||||
GuestAgentCommandInfoList *cmd_info_list;
|
||||
char **cmd_list_head, **cmd_list;
|
||||
|
||||
info->version = g_strdup(QGA_VERSION);
|
||||
info->version = g_strdup(QEMU_VERSION);
|
||||
|
||||
cmd_list_head = cmd_list = qmp_get_command_list();
|
||||
if (*cmd_list_head == NULL) {
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include "qapi/qmp-core.h"
|
||||
#include "qemu-common.h"
|
||||
|
||||
#define QGA_VERSION "1.0"
|
||||
#define QGA_READ_COUNT_DEFAULT 4096
|
||||
|
||||
typedef struct GAState GAState;
|
||||
@ -35,3 +34,7 @@ void ga_set_response_delimited(GAState *s);
|
||||
bool ga_is_frozen(GAState *s);
|
||||
void ga_set_frozen(GAState *s);
|
||||
void ga_unset_frozen(GAState *s);
|
||||
|
||||
#ifndef _WIN32
|
||||
void reopen_fd_to_null(int fd);
|
||||
#endif
|
||||
|
@ -291,14 +291,24 @@ out:
|
||||
|
||||
return ret
|
||||
|
||||
def option_value_matches(opt, val, cmd):
|
||||
if opt in cmd and cmd[opt] == val:
|
||||
return True
|
||||
return False
|
||||
|
||||
def gen_registry(commands):
|
||||
registry=""
|
||||
push_indent()
|
||||
for cmd in commands:
|
||||
options = 'QCO_NO_OPTIONS'
|
||||
if option_value_matches('success-response', 'no', cmd):
|
||||
options = 'QCO_NO_SUCCESS_RESP'
|
||||
|
||||
registry += mcgen('''
|
||||
qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s);
|
||||
qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s, %(opts)s);
|
||||
''',
|
||||
name=cmd['command'], c_name=c_fun(cmd['command']))
|
||||
name=cmd['command'], c_name=c_fun(cmd['command']),
|
||||
opts=options)
|
||||
pop_indent()
|
||||
ret = mcgen('''
|
||||
static void qmp_init_marshal(void)
|
||||
|
Loading…
Reference in New Issue
Block a user