mirror of https://github.com/xemu-project/xemu.git
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:
|
# @guest-shutdown:
|
||||||
#
|
#
|
||||||
# Initiate guest-activated shutdown. Note: this is an asynchronous
|
# Initiate guest-activated shutdown. Note: this is an asynchronous
|
||||||
# shutdown request, with no guaruntee of successful shutdown. Errors
|
# shutdown request, with no guarantee of successful shutdown.
|
||||||
# will be logged to guest's syslog.
|
|
||||||
#
|
#
|
||||||
# @mode: #optional "halt", "powerdown" (default), or "reboot"
|
# @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
|
# Since: 0.15.0
|
||||||
##
|
##
|
||||||
{ 'command': 'guest-shutdown', 'data': { '*mode': 'str' } }
|
{ 'command': 'guest-shutdown', 'data': { '*mode': 'str' },
|
||||||
|
'success-response': 'no' }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @guest-file-open:
|
# @guest-file-open:
|
||||||
|
@ -359,17 +362,21 @@
|
||||||
# For the best results it's strongly recommended to have the pm-utils
|
# For the best results it's strongly recommended to have the pm-utils
|
||||||
# package installed in the guest.
|
# 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
|
# If suspend to disk is not supported, Unsupported
|
||||||
#
|
#
|
||||||
# Notes: o This is an asynchronous request. There's no guarantee a response
|
# Notes: It's strongly recommended to issue the guest-sync command before
|
||||||
# will be sent
|
# sending commands when the guest resumes
|
||||||
# o It's strongly recommended to issue the guest-sync command before
|
|
||||||
# sending commands when the guest resumes
|
|
||||||
#
|
#
|
||||||
# Since: 1.1
|
# Since: 1.1
|
||||||
##
|
##
|
||||||
{ 'command': 'guest-suspend-disk' }
|
{ 'command': 'guest-suspend-disk', 'success-response': 'no' }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @guest-suspend-ram
|
# @guest-suspend-ram
|
||||||
|
@ -387,17 +394,21 @@
|
||||||
# command. Thus, it's *required* to query QEMU for the presence of the
|
# command. Thus, it's *required* to query QEMU for the presence of the
|
||||||
# 'system_wakeup' command before issuing guest-suspend-ram.
|
# '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
|
# If suspend to ram is not supported, Unsupported
|
||||||
#
|
#
|
||||||
# Notes: o This is an asynchronous request. There's no guarantee a response
|
# Notes: It's strongly recommended to issue the guest-sync command before
|
||||||
# will be sent
|
# sending commands when the guest resumes
|
||||||
# o It's strongly recommended to issue the guest-sync command before
|
|
||||||
# sending commands when the guest resumes
|
|
||||||
#
|
#
|
||||||
# Since: 1.1
|
# Since: 1.1
|
||||||
##
|
##
|
||||||
{ 'command': 'guest-suspend-ram' }
|
{ 'command': 'guest-suspend-ram', 'success-response': 'no' }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @guest-suspend-hybrid
|
# @guest-suspend-hybrid
|
||||||
|
@ -410,17 +421,21 @@
|
||||||
# command. Thus, it's *required* to query QEMU for the presence of the
|
# command. Thus, it's *required* to query QEMU for the presence of the
|
||||||
# 'system_wakeup' command before issuing guest-suspend-hybrid.
|
# '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
|
# If hybrid suspend is not supported, Unsupported
|
||||||
#
|
#
|
||||||
# Notes: o This is an asynchronous request. There's no guarantee a response
|
# Notes: It's strongly recommended to issue the guest-sync command before
|
||||||
# will be sent
|
# sending commands when the guest resumes
|
||||||
# o It's strongly recommended to issue the guest-sync command before
|
|
||||||
# sending commands when the guest resumes
|
|
||||||
#
|
#
|
||||||
# Since: 1.1
|
# Since: 1.1
|
||||||
##
|
##
|
||||||
{ 'command': 'guest-suspend-hybrid' }
|
{ 'command': 'guest-suspend-hybrid', 'success-response': 'no' }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @GuestIpAddressType:
|
# @GuestIpAddressType:
|
||||||
|
|
|
@ -25,16 +25,24 @@ typedef enum QmpCommandType
|
||||||
QCT_NORMAL,
|
QCT_NORMAL,
|
||||||
} QmpCommandType;
|
} QmpCommandType;
|
||||||
|
|
||||||
|
typedef enum QmpCommandOptions
|
||||||
|
{
|
||||||
|
QCO_NO_OPTIONS = 0x0,
|
||||||
|
QCO_NO_SUCCESS_RESP = 0x1,
|
||||||
|
} QmpCommandOptions;
|
||||||
|
|
||||||
typedef struct QmpCommand
|
typedef struct QmpCommand
|
||||||
{
|
{
|
||||||
const char *name;
|
const char *name;
|
||||||
QmpCommandType type;
|
QmpCommandType type;
|
||||||
QmpCommandFunc *fn;
|
QmpCommandFunc *fn;
|
||||||
|
QmpCommandOptions options;
|
||||||
QTAILQ_ENTRY(QmpCommand) node;
|
QTAILQ_ENTRY(QmpCommand) node;
|
||||||
bool enabled;
|
bool enabled;
|
||||||
} QmpCommand;
|
} 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);
|
QmpCommand *qmp_find_command(const char *name);
|
||||||
QObject *qmp_dispatch(QObject *request);
|
QObject *qmp_dispatch(QObject *request);
|
||||||
void qmp_disable_command(const char *name);
|
void qmp_disable_command(const char *name);
|
||||||
|
|
|
@ -94,8 +94,12 @@ static QObject *do_qmp_dispatch(QObject *request, Error **errp)
|
||||||
switch (cmd->type) {
|
switch (cmd->type) {
|
||||||
case QCT_NORMAL:
|
case QCT_NORMAL:
|
||||||
cmd->fn(args, &ret, errp);
|
cmd->fn(args, &ret, errp);
|
||||||
if (!error_is_set(errp) && ret == NULL) {
|
if (!error_is_set(errp)) {
|
||||||
ret = QOBJECT(qdict_new());
|
if (cmd->options & QCO_NO_SUCCESS_RESP) {
|
||||||
|
g_assert(!ret);
|
||||||
|
} else if (!ret) {
|
||||||
|
ret = QOBJECT(qdict_new());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,8 @@
|
||||||
static QTAILQ_HEAD(QmpCommandList, QmpCommand) qmp_commands =
|
static QTAILQ_HEAD(QmpCommandList, QmpCommand) qmp_commands =
|
||||||
QTAILQ_HEAD_INITIALIZER(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));
|
QmpCommand *cmd = g_malloc0(sizeof(*cmd));
|
||||||
|
|
||||||
|
@ -25,6 +26,7 @@ void qmp_register_command(const char *name, QmpCommandFunc *fn)
|
||||||
cmd->type = QCT_NORMAL;
|
cmd->type = QCT_NORMAL;
|
||||||
cmd->fn = fn;
|
cmd->fn = fn;
|
||||||
cmd->enabled = true;
|
cmd->enabled = true;
|
||||||
|
cmd->options = options;
|
||||||
QTAILQ_INSERT_TAIL(&qmp_commands, cmd, node);
|
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
|
#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)
|
static gboolean register_signal_handlers(void)
|
||||||
{
|
{
|
||||||
struct sigaction sigact, sigact_chld;
|
struct sigaction sigact;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
memset(&sigact, 0, sizeof(struct sigaction));
|
memset(&sigact, 0, sizeof(struct sigaction));
|
||||||
|
@ -130,15 +123,24 @@ static gboolean register_signal_handlers(void)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&sigact_chld, 0, sizeof(struct sigaction));
|
return true;
|
||||||
sigact_chld.sa_handler = child_handler;
|
}
|
||||||
sigact_chld.sa_flags = SA_NOCLDSTOP;
|
|
||||||
ret = sigaction(SIGCHLD, &sigact_chld, NULL);
|
/* TODO: use this in place of all post-fork() fclose(std*) callers */
|
||||||
if (ret == -1) {
|
void reopen_fd_to_null(int fd)
|
||||||
g_error("error configuring signal handler: %s", strerror(errno));
|
{
|
||||||
|
int nullfd;
|
||||||
|
|
||||||
|
nullfd = open("/dev/null", O_RDWR);
|
||||||
|
if (nullfd < 0) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
dup2(nullfd, fd);
|
||||||
|
|
||||||
|
if (nullfd != fd) {
|
||||||
|
close(nullfd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -167,7 +169,7 @@ static void usage(const char *cmd)
|
||||||
" -h, --help display this help and exit\n"
|
" -h, --help display this help and exit\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Report bugs to <mdroth@linux.vnet.ibm.com>\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);
|
QGA_STATEDIR_DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -428,9 +430,9 @@ static void become_daemon(const char *pidfile)
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
close(STDIN_FILENO);
|
reopen_fd_to_null(STDIN_FILENO);
|
||||||
close(STDOUT_FILENO);
|
reopen_fd_to_null(STDOUT_FILENO);
|
||||||
close(STDERR_FILENO);
|
reopen_fd_to_null(STDERR_FILENO);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
@ -488,8 +490,6 @@ static void process_command(GAState *s, QDict *req)
|
||||||
g_warning("error sending response: %s", strerror(ret));
|
g_warning("error sending response: %s", strerror(ret));
|
||||||
}
|
}
|
||||||
qobject_decref(rsp);
|
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;
|
log_level = G_LOG_LEVEL_MASK;
|
||||||
break;
|
break;
|
||||||
case 'V':
|
case 'V':
|
||||||
printf("QEMU Guest Agent %s\n", QGA_VERSION);
|
printf("QEMU Guest Agent %s\n", QEMU_VERSION);
|
||||||
return 0;
|
return 0;
|
||||||
case 'd':
|
case 'd':
|
||||||
daemonize = 1;
|
daemonize = 1;
|
||||||
|
@ -836,12 +836,13 @@ int main(int argc, char **argv)
|
||||||
become_daemon(pid_filepath);
|
become_daemon(pid_filepath);
|
||||||
}
|
}
|
||||||
if (log_filepath) {
|
if (log_filepath) {
|
||||||
s->log_file = fopen(log_filepath, "a");
|
FILE *log_file = fopen(log_filepath, "a");
|
||||||
if (!s->log_file) {
|
if (!log_file) {
|
||||||
g_critical("unable to open specified log file: %s",
|
g_critical("unable to open specified log file: %s",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
goto out_bad;
|
goto out_bad;
|
||||||
}
|
}
|
||||||
|
s->log_file = log_file;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,29 +34,11 @@
|
||||||
#endif
|
#endif
|
||||||
#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)
|
void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
const char *shutdown_flag;
|
const char *shutdown_flag;
|
||||||
|
pid_t rpid, pid;
|
||||||
|
int status;
|
||||||
|
|
||||||
slog("guest-shutdown called, mode: %s", mode);
|
slog("guest-shutdown called, mode: %s", mode);
|
||||||
if (!has_mode || strcmp(mode, "powerdown") == 0) {
|
if (!has_mode || strcmp(mode, "powerdown") == 0) {
|
||||||
|
@ -71,23 +53,30 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = fork();
|
pid = fork();
|
||||||
if (ret == 0) {
|
if (pid == 0) {
|
||||||
/* child, start the shutdown */
|
/* child, start the shutdown */
|
||||||
setsid();
|
setsid();
|
||||||
fclose(stdin);
|
reopen_fd_to_null(0);
|
||||||
fclose(stdout);
|
reopen_fd_to_null(1);
|
||||||
fclose(stderr);
|
reopen_fd_to_null(2);
|
||||||
|
|
||||||
ret = execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
|
execle("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
|
||||||
"hypervisor initiated shutdown", (char*)NULL);
|
"hypervisor initiated shutdown", (char*)NULL, environ);
|
||||||
if (ret) {
|
_exit(EXIT_FAILURE);
|
||||||
slog("guest-shutdown failed: %s", strerror(errno));
|
} else if (pid < 0) {
|
||||||
}
|
goto exit_err;
|
||||||
exit(!!ret);
|
|
||||||
} else if (ret < 0) {
|
|
||||||
error_set(err, QERR_UNDEFINED_ERROR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
typedef struct GuestFileHandle {
|
||||||
|
@ -531,117 +520,88 @@ static void guest_fsfreeze_cleanup(void)
|
||||||
#define SUSPEND_SUPPORTED 0
|
#define SUSPEND_SUPPORTED 0
|
||||||
#define SUSPEND_NOT_SUPPORTED 1
|
#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,
|
static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg,
|
||||||
const char *sysfile_str, Error **err)
|
const char *sysfile_str, Error **err)
|
||||||
{
|
{
|
||||||
pid_t pid;
|
|
||||||
ssize_t ret;
|
|
||||||
char *pmutils_path;
|
char *pmutils_path;
|
||||||
int status, pipefds[2];
|
pid_t pid, rpid;
|
||||||
|
int status;
|
||||||
if (pipe(pipefds) < 0) {
|
|
||||||
error_set(err, QERR_UNDEFINED_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pmutils_path = g_find_program_in_path(pmutils_bin);
|
pmutils_path = g_find_program_in_path(pmutils_bin);
|
||||||
|
|
||||||
pid = fork();
|
pid = fork();
|
||||||
if (!pid) {
|
if (!pid) {
|
||||||
struct sigaction act;
|
char buf[32]; /* hopefully big enough */
|
||||||
|
ssize_t ret;
|
||||||
memset(&act, 0, sizeof(act));
|
int fd;
|
||||||
act.sa_handler = SIG_DFL;
|
|
||||||
sigaction(SIGCHLD, &act, NULL);
|
|
||||||
|
|
||||||
setsid();
|
setsid();
|
||||||
close(pipefds[0]);
|
|
||||||
reopen_fd_to_null(0);
|
reopen_fd_to_null(0);
|
||||||
reopen_fd_to_null(1);
|
reopen_fd_to_null(1);
|
||||||
reopen_fd_to_null(2);
|
reopen_fd_to_null(2);
|
||||||
|
|
||||||
pid = fork();
|
if (pmutils_path) {
|
||||||
if (!pid) {
|
execle(pmutils_path, pmutils_bin, pmutils_arg, NULL, environ);
|
||||||
int fd;
|
}
|
||||||
char buf[32]; /* hopefully big enough */
|
|
||||||
|
|
||||||
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 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 (!sysfile_str) {
|
||||||
_exit(SUSPEND_NOT_SUPPORTED);
|
_exit(SUSPEND_NOT_SUPPORTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pid > 0) {
|
fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
|
||||||
wait(&status);
|
if (fd < 0) {
|
||||||
} else {
|
_exit(SUSPEND_NOT_SUPPORTED);
|
||||||
status = SUSPEND_NOT_SUPPORTED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = write(pipefds[1], &status, sizeof(status));
|
ret = read(fd, buf, sizeof(buf)-1);
|
||||||
if (ret != sizeof(status)) {
|
if (ret <= 0) {
|
||||||
_exit(EXIT_FAILURE);
|
_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);
|
g_free(pmutils_path);
|
||||||
|
|
||||||
if (pid < 0) {
|
if (pid < 0) {
|
||||||
error_set(err, QERR_UNDEFINED_ERROR);
|
goto undef_err;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = read(pipefds[0], &status, sizeof(status));
|
do {
|
||||||
if (ret == sizeof(status) && WIFEXITED(status) &&
|
rpid = waitpid(pid, &status, 0);
|
||||||
WEXITSTATUS(status) == SUSPEND_SUPPORTED) {
|
} while (rpid == -1 && errno == EINTR);
|
||||||
goto out;
|
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);
|
undef_err:
|
||||||
|
error_set(err, QERR_UNDEFINED_ERROR);
|
||||||
out:
|
|
||||||
close(pipefds[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void guest_suspend(const char *pmutils_bin, const char *sysfile_str,
|
static void guest_suspend(const char *pmutils_bin, const char *sysfile_str,
|
||||||
Error **err)
|
Error **err)
|
||||||
{
|
{
|
||||||
pid_t pid;
|
|
||||||
char *pmutils_path;
|
char *pmutils_path;
|
||||||
|
pid_t rpid, pid;
|
||||||
|
int status;
|
||||||
|
|
||||||
pmutils_path = g_find_program_in_path(pmutils_bin);
|
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);
|
g_free(pmutils_path);
|
||||||
|
|
||||||
if (pid < 0) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exit_err:
|
||||||
|
error_set(err, QERR_UNDEFINED_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qmp_guest_suspend_disk(Error **err)
|
void qmp_guest_suspend_disk(Error **err)
|
||||||
|
|
|
@ -52,7 +52,7 @@ struct GuestAgentInfo *qmp_guest_info(Error **err)
|
||||||
GuestAgentCommandInfoList *cmd_info_list;
|
GuestAgentCommandInfoList *cmd_info_list;
|
||||||
char **cmd_list_head, **cmd_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();
|
cmd_list_head = cmd_list = qmp_get_command_list();
|
||||||
if (*cmd_list_head == NULL) {
|
if (*cmd_list_head == NULL) {
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
#include "qapi/qmp-core.h"
|
#include "qapi/qmp-core.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
|
|
||||||
#define QGA_VERSION "1.0"
|
|
||||||
#define QGA_READ_COUNT_DEFAULT 4096
|
#define QGA_READ_COUNT_DEFAULT 4096
|
||||||
|
|
||||||
typedef struct GAState GAState;
|
typedef struct GAState GAState;
|
||||||
|
@ -35,3 +34,7 @@ void ga_set_response_delimited(GAState *s);
|
||||||
bool ga_is_frozen(GAState *s);
|
bool ga_is_frozen(GAState *s);
|
||||||
void ga_set_frozen(GAState *s);
|
void ga_set_frozen(GAState *s);
|
||||||
void ga_unset_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
|
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):
|
def gen_registry(commands):
|
||||||
registry=""
|
registry=""
|
||||||
push_indent()
|
push_indent()
|
||||||
for cmd in commands:
|
for cmd in commands:
|
||||||
|
options = 'QCO_NO_OPTIONS'
|
||||||
|
if option_value_matches('success-response', 'no', cmd):
|
||||||
|
options = 'QCO_NO_SUCCESS_RESP'
|
||||||
|
|
||||||
registry += mcgen('''
|
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()
|
pop_indent()
|
||||||
ret = mcgen('''
|
ret = mcgen('''
|
||||||
static void qmp_init_marshal(void)
|
static void qmp_init_marshal(void)
|
||||||
|
|
Loading…
Reference in New Issue