diff options
Diffstat (limited to 'qga/commands-posix.c')
-rw-r--r-- | qga/commands-posix.c | 170 |
1 files changed, 108 insertions, 62 deletions
diff --git a/qga/commands-posix.c b/qga/commands-posix.c index a8299ecc77..9ebd77b894 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1545,6 +1545,65 @@ out: return ret; } +static void pmutils_suspend(int suspend_mode, Error **errp) +{ + Error *local_err = NULL; + const char *pmutils_bin; + char *pmutils_path; + pid_t pid; + int status; + + switch (suspend_mode) { + + case SUSPEND_MODE_DISK: + pmutils_bin = "pm-hibernate"; + break; + case SUSPEND_MODE_RAM: + pmutils_bin = "pm-suspend"; + break; + case SUSPEND_MODE_HYBRID: + pmutils_bin = "pm-suspend-hybrid"; + break; + default: + error_setg(errp, "unknown guest suspend mode"); + return; + } + + pmutils_path = g_find_program_in_path(pmutils_bin); + if (!pmutils_path) { + error_setg(errp, "the helper program '%s' was not found", pmutils_bin); + return; + } + + pid = fork(); + if (!pid) { + setsid(); + execle(pmutils_path, pmutils_bin, NULL, environ); + /* + * If we get here execle() has failed. + */ + _exit(EXIT_FAILURE); + } else if (pid < 0) { + error_setg_errno(errp, errno, "failed to create child process"); + goto out; + } + + ga_wait_child(pid, &status, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto out; + } + + if (WEXITSTATUS(status)) { + error_setg(errp, + "the helper program '%s' returned an unexpected exit status" + " code (%d)", pmutils_path, WEXITSTATUS(status)); + } + +out: + g_free(pmutils_path); +} + static bool linux_sys_state_supports_mode(int suspend_mode, Error **errp) { const char *sysfile_str; @@ -1581,64 +1640,28 @@ static bool linux_sys_state_supports_mode(int suspend_mode, Error **errp) return false; } -static void bios_supports_mode(int suspend_mode, Error **errp) -{ - Error *local_err = NULL; - bool ret; - - ret = pmutils_supports_mode(suspend_mode, &local_err); - if (ret) { - return; - } - if (local_err) { - error_propagate(errp, local_err); - return; - } - ret = linux_sys_state_supports_mode(suspend_mode, errp); - if (!ret) { - error_setg(errp, - "the requested suspend mode is not supported by the guest"); - return; - } -} - -static void guest_suspend(int suspend_mode, Error **errp) +static void linux_sys_state_suspend(int suspend_mode, Error **errp) { Error *local_err = NULL; - const char *pmutils_bin, *sysfile_str; - char *pmutils_path; + const char *sysfile_str; pid_t pid; int status; - bios_supports_mode(suspend_mode, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - switch (suspend_mode) { case SUSPEND_MODE_DISK: - pmutils_bin = "pm-hibernate"; sysfile_str = "disk"; break; case SUSPEND_MODE_RAM: - pmutils_bin = "pm-suspend"; sysfile_str = "mem"; break; - case SUSPEND_MODE_HYBRID: - pmutils_bin = "pm-suspend-hybrid"; - sysfile_str = NULL; - break; default: error_setg(errp, "unknown guest suspend mode"); return; } - pmutils_path = g_find_program_in_path(pmutils_bin); - pid = fork(); - if (pid == 0) { + if (!pid) { /* child */ int fd; @@ -1647,19 +1670,6 @@ static void guest_suspend(int suspend_mode, Error **errp) reopen_fd_to_null(1); reopen_fd_to_null(2); - if (pmutils_path) { - execle(pmutils_path, pmutils_bin, 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(EXIT_FAILURE); - } - fd = open(LINUX_SYS_STATE_FILE, O_WRONLY); if (fd < 0) { _exit(EXIT_FAILURE); @@ -1672,27 +1682,63 @@ static void guest_suspend(int suspend_mode, Error **errp) _exit(EXIT_SUCCESS); } else if (pid < 0) { error_setg_errno(errp, errno, "failed to create child process"); - goto out; + return; } ga_wait_child(pid, &status, &local_err); if (local_err) { error_propagate(errp, local_err); - goto out; - } - - if (!WIFEXITED(status)) { - error_setg(errp, "child process has terminated abnormally"); - goto out; + return; } if (WEXITSTATUS(status)) { error_setg(errp, "child process has failed to suspend"); - goto out; } -out: - g_free(pmutils_path); +} + +static void bios_supports_mode(int suspend_mode, Error **errp) +{ + Error *local_err = NULL; + bool ret; + + ret = pmutils_supports_mode(suspend_mode, &local_err); + if (ret) { + return; + } + if (local_err) { + error_propagate(errp, local_err); + return; + } + ret = linux_sys_state_supports_mode(suspend_mode, errp); + if (!ret) { + error_setg(errp, + "the requested suspend mode is not supported by the guest"); + return; + } +} + +static void guest_suspend(int suspend_mode, Error **errp) +{ + Error *local_err = NULL; + + bios_supports_mode(suspend_mode, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + pmutils_suspend(suspend_mode, &local_err); + if (!local_err) { + return; + } + + error_free(local_err); + + linux_sys_state_suspend(suspend_mode, &local_err); + if (local_err) { + error_propagate(errp, local_err); + } } void qmp_guest_suspend_disk(Error **errp) |