From patchwork Thu Dec 15 15:09:12 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luiz Capitulino X-Patchwork-Id: 131662 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 5A20F1007E2 for ; Fri, 16 Dec 2011 02:10:17 +1100 (EST) Received: from localhost ([::1]:53162 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RbCwm-0004cq-Hs for incoming@patchwork.ozlabs.org; Thu, 15 Dec 2011 10:10:12 -0500 Received: from eggs.gnu.org ([140.186.70.92]:35649) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RbCwa-0004XA-GS for qemu-devel@nongnu.org; Thu, 15 Dec 2011 10:10:07 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1RbCwU-0001p4-26 for qemu-devel@nongnu.org; Thu, 15 Dec 2011 10:10:00 -0500 Received: from mx1.redhat.com ([209.132.183.28]:5362) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RbCwT-0001oy-Lj for qemu-devel@nongnu.org; Thu, 15 Dec 2011 10:09:53 -0500 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id pBFF9FUY019052 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Thu, 15 Dec 2011 10:09:15 -0500 Received: from doriath (ovpn-113-72.phx2.redhat.com [10.3.113.72]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id pBFF9De4024628; Thu, 15 Dec 2011 10:09:13 -0500 Date: Thu, 15 Dec 2011 13:09:12 -0200 From: Luiz Capitulino To: qemu-devel Message-ID: <20111215130912.63622f29@doriath> Organization: Red Hat Mime-Version: 1.0 X-Scanned-By: MIMEDefang 2.68 on 10.5.11.23 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 3) X-Received-From: 209.132.183.28 Cc: Amit Shah , aliguori@us.ibm.com, mdroth@linux.vnet.ibm.com Subject: [Qemu-devel] [PATCH v3] qemu-ga: Add the guest-suspend command X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org It supports three modes: "hibernate" (suspend to disk), "sleep" (suspend to ram) and "hybrid" (save RAM contents to disk, but suspend instead of powering off). The command will try to execute the scripts provided by the pm-utils package. If that fails, it will try to suspend manually by writing to the "/sys/power/state" file. To reap terminated children, a new signal handler is installed to catch SIGCHLD signals and a non-blocking call to waitpid() is done to collect their exit statuses. Signed-off-by: Luiz Capitulino --- v3 o Add 'hybrid' mode o Use execlp() instead of execl() o Don't ignore SIGCHLD, catch it instead and call waitpid() from the signal handler to avoid zombies o Improve the schema doc qapi-schema-guest.json | 26 ++++++++++++++++++ qemu-ga.c | 17 +++++++++++- qga/guest-agent-commands.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 1 deletions(-) diff --git a/qapi-schema-guest.json b/qapi-schema-guest.json index 5f8a18d..6b9657a 100644 --- a/qapi-schema-guest.json +++ b/qapi-schema-guest.json @@ -219,3 +219,29 @@ ## { 'command': 'guest-fsfreeze-thaw', 'returns': 'int' } + +## +# @guest-suspend +# +# Suspend guest execution by entering ACPI power state S3 or S4. +# +# This command tries to execute the scripts provided by the pm-utils +# package. If they are not available, it will perform the suspend +# operation by manually writing to a sysfs file. +# +# For the best results it's strongly recommended to have the pm-utils +# package installed in the guest. +# +# @mode: 'hibernate' RAM content is saved to the disk and the guest is +# powered off (this corresponds to ACPI S4) +# 'sleep' execution is suspended but the RAM retains its contents +# (this corresponds to ACPI S3) +# 'hybrid' RAM content is saved to the disk but the guest is +# suspended instead of powering off +# +# Notes: This is an asynchronous request. There's no guarantee a response +# will be sent. Errors will be logged to guest's syslog. +# +# Since: 1.1 +## +{ 'command': 'guest-suspend', 'data': { 'mode': 'str' } } diff --git a/qemu-ga.c b/qemu-ga.c index 60d4972..a7c5973 100644 --- a/qemu-ga.c +++ b/qemu-ga.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "qemu_socket.h" #include "json-streamer.h" #include "json-parser.h" @@ -59,9 +60,15 @@ static void quit_handler(int sig) } } +static void child_handler(int sig) +{ + int status; + waitpid(-1, &status, WNOHANG); +} + static void register_signal_handlers(void) { - struct sigaction sigact; + struct sigaction sigact, sigact_chld; int ret; memset(&sigact, 0, sizeof(struct sigaction)); @@ -76,6 +83,14 @@ static void register_signal_handlers(void) if (ret == -1) { g_error("error configuring signal handler: %s", strerror(errno)); } + + 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)); + } } static void usage(const char *cmd) diff --git a/qga/guest-agent-commands.c b/qga/guest-agent-commands.c index a09c8ca..cc466ba 100644 --- a/qga/guest-agent-commands.c +++ b/qga/guest-agent-commands.c @@ -574,6 +574,70 @@ int64_t qmp_guest_fsfreeze_thaw(Error **err) } #endif +#define LINUX_SYS_STATE_FILE "/sys/power/state" + +void qmp_guest_suspend(const char *mode, Error **err) +{ + pid_t pid; + const char *pmutils_bin; + + if (strcmp(mode, "hibernate") == 0) { + pmutils_bin = "pm-hibernate"; + } else if (strcmp(mode, "sleep") == 0) { + pmutils_bin = "pm-suspend"; + } else if (strcmp(mode, "hybrid") == 0) { + pmutils_bin = "pm-suspend-hybrid"; + } else { + error_set(err, QERR_INVALID_PARAMETER, "mode"); + return; + } + + pid = fork(); + if (pid == 0) { + /* child */ + int fd; + const char *cmd; + + setsid(); + fclose(stdin); + fclose(stdout); + fclose(stderr); + + execlp(pmutils_bin, pmutils_bin, NULL); + + /* + * The exec call should not return, if it does something went wrong. + * In this case we try to suspend manually if 'mode' is 'hibernate' + * or 'sleep' + */ + slog("could not execute %s: %s\n", pmutils_bin, strerror(errno)); + if (strcmp(mode, "hybrid") == 0) { + exit(1); + } + + slog("trying to suspend using the manual method...\n"); + + fd = open(LINUX_SYS_STATE_FILE, O_WRONLY); + if (fd < 0) { + slog("can't open file %s: %s\n", LINUX_SYS_STATE_FILE, + strerror(errno)); + exit(1); + } + + cmd = strcmp(mode, "sleep") == 0 ? "mem" : "disk"; + if (write(fd, cmd, strlen(cmd)) < 0) { + slog("can't write to %s: %s\n", LINUX_SYS_STATE_FILE, + strerror(errno)); + exit(1); + } + + exit(0); + } else if (pid < 0) { + error_set(err, QERR_UNDEFINED_ERROR); + return; + } +} + /* register init/cleanup routines for stateful command groups */ void ga_command_state_init(GAState *s, GACommandState *cs) {