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
 | 
			
		||||
# 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
 | 
			
		||||
# 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
 | 
			
		||||
# 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,9 +94,13 @@ 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) {
 | 
			
		||||
        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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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,24 +53,31 @@ 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));
 | 
			
		||||
        execle("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
 | 
			
		||||
               "hypervisor initiated shutdown", (char*)NULL, environ);
 | 
			
		||||
        _exit(EXIT_FAILURE);
 | 
			
		||||
    } else if (pid < 0) {
 | 
			
		||||
        goto exit_err;
 | 
			
		||||
    }
 | 
			
		||||
        exit(!!ret);
 | 
			
		||||
    } else if (ret < 0) {
 | 
			
		||||
 | 
			
		||||
    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 {
 | 
			
		||||
    uint64_t id;
 | 
			
		||||
| 
						 | 
				
			
			@ -531,47 +520,26 @@ 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);
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -603,45 +571,37 @@ static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg,
 | 
			
		|||
        _exit(SUSPEND_NOT_SUPPORTED);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        if (pid > 0) {
 | 
			
		||||
            wait(&status);
 | 
			
		||||
        } else {
 | 
			
		||||
            status = SUSPEND_NOT_SUPPORTED;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = write(pipefds[1], &status, sizeof(status));
 | 
			
		||||
        if (ret != sizeof(status)) {
 | 
			
		||||
            _exit(EXIT_FAILURE);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        _exit(EXIT_SUCCESS);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    close(pipefds[1]);
 | 
			
		||||
    g_free(pmutils_path);
 | 
			
		||||
 | 
			
		||||
    if (pid < 0) {
 | 
			
		||||
        error_set(err, QERR_UNDEFINED_ERROR);
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = read(pipefds[0], &status, sizeof(status));
 | 
			
		||||
    if (ret == sizeof(status) && WIFEXITED(status) &&
 | 
			
		||||
        WEXITSTATUS(status) == SUSPEND_SUPPORTED) {
 | 
			
		||||
            goto out;
 | 
			
		||||
        goto undef_err;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
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