From 11d0f1255bd5651f628280dc96c4ce9d63ae9236 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Tue, 28 Feb 2012 11:03:03 -0300 Subject: qemu-ga: add guest-suspend-disk As the command name implies, this command suspends the guest to disk. The suspend operation is implemented by two functions: bios_supports_mode() and guest_suspend(). Both functions are generic enough to be used by other suspend modes (introduced by next commits). Both functions will try to use the scripts provided by the pm-utils package if it's available. If it's not available, a manual method, which consists of directly writing to '/sys/power/state', will be used. To reap terminated children, a new signal handler is installed in the parent to catch SIGCHLD signals and a non-blocking call to waitpid() is done to collect their exit statuses. The statuses, however, are discarded. The approach used to query the guest for suspend support deserves some explanation. It's implemented by bios_supports_mode() and shown below: qemu-ga | create pipe | fork() ----------------- | | | | | fork() | -------------------------- | | | | | | | | exec('pm-is-supported') | | | wait() | write exit status to pipe | exit | read pipe This might look complex, but the resulting code is quite simple. The purpose of that approach is to allow qemu-ga to reap its children (semi-)automatically from its SIGCHLD handler. Implementing this the obvious way, that's, doing the exec() call from the first child process, would force us to introduce a more complex way to reap qemu-ga's children. Like registering PIDs to be reaped and having a way to wait for them when returning their exit status to qemu-ga is necessary. The approach explained above avoids that complexity. Signed-off-by: Luiz Capitulino --- qga/commands-win32.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'qga/commands-win32.c') diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 4aa0f0d1e4..c688476c89 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -124,6 +124,11 @@ int64_t qmp_guest_fsfreeze_thaw(Error **err) return 0; } +void qmp_guest_suspend_disk(Error **err) +{ + error_set(err, QERR_UNSUPPORTED); +} + /* register init/cleanup routines for stateful command groups */ void ga_command_state_init(GAState *s, GACommandState *cs) { -- cgit v1.2.3 From fbf42210c19ec4315e409b7f9f0bfa46c7dfb921 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Tue, 28 Feb 2012 11:03:04 -0300 Subject: qemu-ga: add guest-suspend-ram Signed-off-by: Luiz Capitulino --- qga/commands-win32.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'qga/commands-win32.c') diff --git a/qga/commands-win32.c b/qga/commands-win32.c index c688476c89..b19a63ccf6 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -129,6 +129,11 @@ void qmp_guest_suspend_disk(Error **err) error_set(err, QERR_UNSUPPORTED); } +void qmp_guest_suspend_ram(Error **err) +{ + error_set(err, QERR_UNSUPPORTED); +} + /* register init/cleanup routines for stateful command groups */ void ga_command_state_init(GAState *s, GACommandState *cs) { -- cgit v1.2.3 From 95f4f404e108f8c6b937ddf2ba973f3799b51fb5 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Tue, 28 Feb 2012 11:03:05 -0300 Subject: qemu-ga: add guest-suspend-hybrid Signed-off-by: Luiz Capitulino --- qga/commands-win32.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'qga/commands-win32.c') diff --git a/qga/commands-win32.c b/qga/commands-win32.c index b19a63ccf6..7ef185f587 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -134,6 +134,11 @@ void qmp_guest_suspend_ram(Error **err) error_set(err, QERR_UNSUPPORTED); } +void qmp_guest_suspend_hybrid(Error **err) +{ + error_set(err, QERR_UNSUPPORTED); +} + /* register init/cleanup routines for stateful command groups */ void ga_command_state_init(GAState *s, GACommandState *cs) { -- cgit v1.2.3 From aa59637ea1c6a4c83430933f9c44c43e6c3f1b69 Mon Sep 17 00:00:00 2001 From: Gal Hammer Date: Sun, 29 Jan 2012 11:53:31 +0200 Subject: qemu-ga: add win32 guest-suspend-disk command. Implement guest-suspend-disk RPC for Windows. Functionally this should be equivalent to the posix implementation. Signed-off-by: Gal Hammer --- qga/commands-win32.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 120 insertions(+), 12 deletions(-) (limited to 'qga/commands-win32.c') diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 7ef185f587..062e519054 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) -- cgit v1.2.3 From f54603b6aa765514b2519e74114a2f417759d727 Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Mon, 12 Mar 2012 12:50:02 -0500 Subject: qemu-ga: add win32 guest-suspend-ram command S3 sleep implementation for windows. --- qga/commands-win32.c | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) (limited to 'qga/commands-win32.c') diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 062e519054..b7600ed89a 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -174,7 +174,8 @@ int64_t qmp_guest_fsfreeze_thaw(Error **err) } typedef enum { - GUEST_SUSPEND_MODE_DISK + GUEST_SUSPEND_MODE_DISK, + GUEST_SUSPEND_MODE_RAM } GuestSuspendMode; static void check_suspend_mode(GuestSuspendMode mode, Error **err) @@ -192,18 +193,24 @@ static void check_suspend_mode(GuestSuspendMode mode, Error **err) goto out; } - if (mode == GUEST_SUSPEND_MODE_DISK) { - if (sys_pwr_caps.SystemS4) { - return; + switch (mode) { + case GUEST_SUSPEND_MODE_DISK: + if (!sys_pwr_caps.SystemS4) { + error_set(&local_err, QERR_QGA_COMMAND_FAILED, + "suspend-to-disk not supported by OS"); } - } else { + break; + case GUEST_SUSPEND_MODE_RAM: + if (!sys_pwr_caps.SystemS3) { + error_set(&local_err, QERR_QGA_COMMAND_FAILED, + "suspend-to-ram not supported by OS"); + } + break; + default: 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); @@ -239,7 +246,16 @@ void qmp_guest_suspend_disk(Error **err) void qmp_guest_suspend_ram(Error **err) { - error_set(err, QERR_UNSUPPORTED); + GuestSuspendMode *mode = g_malloc(sizeof(GuestSuspendMode)); + + *mode = GUEST_SUSPEND_MODE_RAM; + 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_hybrid(Error **err) -- cgit v1.2.3 From 3424fc9f16a1e7d1c48eb6d605eb0ca63e199ec2 Mon Sep 17 00:00:00 2001 From: Michal Privoznik Date: Wed, 29 Feb 2012 17:02:23 +0100 Subject: qemu-ga: add guest-network-get-interfaces command This command returns an array of: [ifname, hwaddr, [ipaddr, ipaddr_family, prefix] ] for each interface in the system. Currently, only IPv4 and IPv6 are supported. Signed-off-by: Michal Privoznik --- qga/commands-win32.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'qga/commands-win32.c') diff --git a/qga/commands-win32.c b/qga/commands-win32.c index b7600ed89a..eb8d1405d3 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -263,6 +263,12 @@ void qmp_guest_suspend_hybrid(Error **err) error_set(err, QERR_UNSUPPORTED); } +GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **err) +{ + error_set(err, QERR_UNSUPPORTED); + return NULL; +} + /* register init/cleanup routines for stateful command groups */ void ga_command_state_init(GAState *s, GACommandState *cs) { -- cgit v1.2.3