From 869781558499b75390fe2d81bf8d064569d66441 Mon Sep 17 00:00:00 2001 Message-Id: <869781558499b75390fe2d81bf8d064569d66441.1342518105.git.minovotn@redhat.com> In-Reply-To: <27a73856ecc481c66c7afac8171f753887f32e31.1342518105.git.minovotn@redhat.com> References: <27a73856ecc481c66c7afac8171f753887f32e31.1342518105.git.minovotn@redhat.com> From: Luiz Capitulino Date: Tue, 5 Jun 2012 14:58:19 +0200 Subject: [PATCH 10/41] qemu-ga: add win32 guest-suspend-disk command. RH-Author: Luiz Capitulino Message-id: <1338908331-15633-5-git-send-email-lcapitulino@redhat.com> Patchwork-id: 39901 O-Subject: [PATCH RHEL6.4 qemu-kvm 04/36] qemu-ga: add win32 guest-suspend-disk command. Bugzilla: 827612 RH-Acked-by: Paolo Bonzini RH-Acked-by: Jeffrey Cody RH-Acked-by: Juan Quintela From: Gal Hammer Implement guest-suspend-disk RPC for Windows. Functionally this should be equivalent to the posix implementation. Signed-off-by: Gal Hammer (cherry picked from commit aa59637ea1c6a4c83430933f9c44c43e6c3f1b69) Conflicts: configure Signed-off-by: Luiz Capitulino --- configure | 2 +- qga/commands-win32.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 121 insertions(+), 13 deletions(-) Signed-off-by: Michal Novotny --- configure | 2 +- qga/commands-win32.c | 132 +++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 121 insertions(+), 13 deletions(-) diff --git a/configure b/configure index 9dc2426..b62c0eb 100755 --- a/configure +++ b/configure @@ -441,7 +441,7 @@ if test "$mingw32" = "yes" ; then EXESUF=".exe" QEMU_CFLAGS="-DWIN32_LEAN_AND_MEAN -DWINVER=0x501 $QEMU_CFLAGS" LIBS="-lwinmm -lws2_32 -liphlpapi $LIBS" - libs_qga="-lws2_32 -lwinmm $lib_qga" + libs_qga="-lws2_32 -lwinmm -lpowrprof $lib_qga" fi # find source path diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 7ef185f..062e519 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -5,12 +5,15 @@ * * Authors: * Michael Roth + * Gal Hammer * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. */ #include +#include +#include #include "qga/guest-agent-core.h" #include "qga-qmp-commands.h" #include "qerror.h" @@ -19,10 +22,63 @@ #define SHTDN_REASON_FLAG_PLANNED 0x80000000 #endif -void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err) +static void acquire_privilege(const char *name, Error **err) { HANDLE token; TOKEN_PRIVILEGES priv; + Error *local_err = NULL; + + if (error_is_set(err)) { + return; + } + + if (OpenProcessToken(GetCurrentProcess(), + TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &token)) + { + if (!LookupPrivilegeValue(NULL, name, &priv.Privileges[0].Luid)) { + error_set(&local_err, QERR_QGA_COMMAND_FAILED, + "no luid for requested privilege"); + goto out; + } + + priv.PrivilegeCount = 1; + priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + if (!AdjustTokenPrivileges(token, FALSE, &priv, 0, NULL, 0)) { + error_set(&local_err, QERR_QGA_COMMAND_FAILED, + "unable to acquire requested privilege"); + goto out; + } + + CloseHandle(token); + } else { + error_set(&local_err, QERR_QGA_COMMAND_FAILED, + "failed to open privilege token"); + } + +out: + if (local_err) { + error_propagate(err, local_err); + } +} + +static void execute_async(DWORD WINAPI (*func)(LPVOID), LPVOID opaque, Error **err) +{ + Error *local_err = NULL; + + if (error_is_set(err)) { + return; + } + HANDLE thread = CreateThread(NULL, 0, func, opaque, 0, NULL); + if (!thread) { + error_set(&local_err, QERR_QGA_COMMAND_FAILED, + "failed to dispatch asynchronous command"); + error_propagate(err, local_err); + } +} + +void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err) +{ UINT shutdown_flag = EWX_FORCE; slog("guest-shutdown called, mode: %s", mode); @@ -41,16 +97,9 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err) /* Request a shutdown privilege, but try to shut down the system anyway. */ - if (OpenProcessToken(GetCurrentProcess(), - TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &token)) - { - LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, - &priv.Privileges[0].Luid); - - priv.PrivilegeCount = 1; - priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - - AdjustTokenPrivileges(token, FALSE, &priv, 0, NULL, 0); + acquire_privilege(SE_SHUTDOWN_NAME, err); + if (error_is_set(err)) { + return; } if (!ExitWindowsEx(shutdown_flag, SHTDN_REASON_FLAG_PLANNED)) { @@ -124,9 +173,68 @@ int64_t qmp_guest_fsfreeze_thaw(Error **err) return 0; } +typedef enum { + GUEST_SUSPEND_MODE_DISK +} GuestSuspendMode; + +static void check_suspend_mode(GuestSuspendMode mode, Error **err) +{ + SYSTEM_POWER_CAPABILITIES sys_pwr_caps; + Error *local_err = NULL; + + if (error_is_set(err)) { + return; + } + ZeroMemory(&sys_pwr_caps, sizeof(sys_pwr_caps)); + if (!GetPwrCapabilities(&sys_pwr_caps)) { + error_set(&local_err, QERR_QGA_COMMAND_FAILED, + "failed to determine guest suspend capabilities"); + goto out; + } + + if (mode == GUEST_SUSPEND_MODE_DISK) { + if (sys_pwr_caps.SystemS4) { + return; + } + } else { + error_set(&local_err, QERR_INVALID_PARAMETER_VALUE, "mode", + "GuestSuspendMode"); + goto out; + } + + error_set(&local_err, QERR_QGA_COMMAND_FAILED, + "suspend mode not supported by OS"); +out: + if (local_err) { + error_propagate(err, local_err); + } +} + +static DWORD WINAPI do_suspend(LPVOID opaque) +{ + GuestSuspendMode *mode = opaque; + DWORD ret = 0; + + if (!SetSuspendState(*mode == GUEST_SUSPEND_MODE_DISK, TRUE, TRUE)) { + slog("failed to suspend guest, %s", GetLastError()); + ret = -1; + } + g_free(mode); + return ret; +} + void qmp_guest_suspend_disk(Error **err) { - error_set(err, QERR_UNSUPPORTED); + GuestSuspendMode *mode = g_malloc(sizeof(GuestSuspendMode)); + + *mode = GUEST_SUSPEND_MODE_DISK; + check_suspend_mode(*mode, err); + acquire_privilege(SE_SHUTDOWN_NAME, err); + execute_async(do_suspend, mode, err); + + if (error_is_set(err)) { + g_free(mode); + } } void qmp_guest_suspend_ram(Error **err) -- 1.7.10.4