qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PULL] qemu-ga patch queue
@ 2012-03-12 20:16 Michael Roth
  2012-03-12 20:16 ` [Qemu-devel] [PATCH 1/7] qemu-ga: add guest-suspend-disk Michael Roth
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: Michael Roth @ 2012-03-12 20:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: ghammer, mprivozn, aliguori, lcapitulino

The following changes since commit a348f108842fb928563865c9918642900cd0d477:

  Add missing const attributes for MemoryRegionOps (2012-03-11 11:40:15 +0000)

are available in the git repository at:
  git://github.com/mdroth/qemu.git qga-pull-3-12-2012

Gal Hammer (1):
      qemu-ga: add win32 guest-suspend-disk command.

Luiz Capitulino (3):
      qemu-ga: add guest-suspend-disk
      qemu-ga: add guest-suspend-ram
      qemu-ga: add guest-suspend-hybrid

Michael Roth (2):
      qemu-ga: add win32 guest-suspend-ram command
      qemu-ga: add guest-sync-delimited

Michal Privoznik (1):
      qemu-ga: add guest-network-get-interfaces command

 configure              |    2 +-
 qapi-schema-guest.json |  182 ++++++++++++++++++++++-
 qemu-ga.c              |   46 +++++-
 qga/commands-posix.c   |  390 +++++++++++++++++++++++++++++++++++++++++++++++-
 qga/commands-win32.c   |  167 +++++++++++++++++++--
 qga/commands.c         |    6 +
 qga/guest-agent-core.h |    2 +
 7 files changed, 774 insertions(+), 21 deletions(-)

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Qemu-devel] [PATCH 1/7] qemu-ga: add guest-suspend-disk
  2012-03-12 20:16 [Qemu-devel] [PULL] qemu-ga patch queue Michael Roth
@ 2012-03-12 20:16 ` Michael Roth
  2012-03-12 20:16 ` [Qemu-devel] [PATCH 2/7] qemu-ga: add guest-suspend-ram Michael Roth
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Michael Roth @ 2012-03-12 20:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: ghammer, mprivozn, aliguori, lcapitulino

From: Luiz Capitulino <lcapitulino@redhat.com>

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 <lcapitulino@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 qapi-schema-guest.json |   24 ++++++
 qemu-ga.c              |   19 +++++-
 qga/commands-posix.c   |  188 ++++++++++++++++++++++++++++++++++++++++++++++++
 qga/commands-win32.c   |    5 ++
 4 files changed, 235 insertions(+), 1 deletions(-)

diff --git a/qapi-schema-guest.json b/qapi-schema-guest.json
index 706925d..f4e0e1d 100644
--- a/qapi-schema-guest.json
+++ b/qapi-schema-guest.json
@@ -295,3 +295,27 @@
 ##
 { 'command': 'guest-fsfreeze-thaw',
   'returns': 'int' }
+
+##
+# @guest-suspend-disk
+#
+# Suspend guest to disk.
+#
+# This command tries to execute the scripts provided by the pm-utils package.
+# If it's not available, the suspend operation will be performed 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.
+#
+# Returns: nothing on success
+#          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
+#          sending commands when the guest resumes
+#
+# Since: 1.1
+##
+{ 'command': 'guest-suspend-disk' }
diff --git a/qemu-ga.c b/qemu-ga.c
index ba355d8..1c90e6e 100644
--- a/qemu-ga.c
+++ b/qemu-ga.c
@@ -17,6 +17,7 @@
 #include <getopt.h>
 #ifndef _WIN32
 #include <syslog.h>
+#include <sys/wait.h>
 #endif
 #include "json-streamer.h"
 #include "json-parser.h"
@@ -73,9 +74,16 @@ 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;
+    struct sigaction sigact, sigact_chld;
     int ret;
 
     memset(&sigact, 0, sizeof(struct sigaction));
@@ -91,6 +99,15 @@ static gboolean register_signal_handlers(void)
         g_error("error configuring signal handler: %s", strerror(errno));
         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;
 }
 #endif
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 126127a..af785f5 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -23,6 +23,7 @@
 
 #include <sys/types.h>
 #include <sys/ioctl.h>
+#include <sys/wait.h>
 #include "qga/guest-agent-core.h"
 #include "qga-qmp-commands.h"
 #include "qerror.h"
@@ -30,6 +31,22 @@
 
 static GAState *ga_state;
 
+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);
+    }
+}
+
 void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err)
 {
     int ret;
@@ -517,6 +534,177 @@ int64_t qmp_guest_fsfreeze_thaw(Error **err)
 }
 #endif
 
+#define LINUX_SYS_STATE_FILE "/sys/power/state"
+#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;
+    }
+
+    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);
+
+        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);
+            }
+
+            /*
+             * 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(SUSPEND_NOT_SUPPORTED);
+            }
+
+            fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
+            if (fd < 0) {
+                _exit(SUSPEND_NOT_SUPPORTED);
+            }
+
+            ret = read(fd, buf, sizeof(buf)-1);
+            if (ret <= 0) {
+                _exit(SUSPEND_NOT_SUPPORTED);
+            }
+            buf[ret] = '\0';
+
+            if (strstr(buf, sysfile_str)) {
+                _exit(SUSPEND_SUPPORTED);
+            }
+
+            _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;
+    }
+
+    error_set(err, QERR_UNSUPPORTED);
+
+out:
+    close(pipefds[0]);
+}
+
+static void guest_suspend(const char *pmutils_bin, const char *sysfile_str,
+                          Error **err)
+{
+    pid_t pid;
+    char *pmutils_path;
+
+    pmutils_path = g_find_program_in_path(pmutils_bin);
+
+    pid = fork();
+    if (pid == 0) {
+        /* child */
+        int fd;
+
+        setsid();
+        reopen_fd_to_null(0);
+        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);
+        }
+
+        if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) {
+            _exit(EXIT_FAILURE);
+        }
+
+        _exit(EXIT_SUCCESS);
+    }
+
+    g_free(pmutils_path);
+
+    if (pid < 0) {
+        error_set(err, QERR_UNDEFINED_ERROR);
+        return;
+    }
+}
+
+void qmp_guest_suspend_disk(Error **err)
+{
+    bios_supports_mode("pm-is-supported", "--hibernate", "disk", err);
+    if (error_is_set(err)) {
+        return;
+    }
+
+    guest_suspend("pm-hibernate", "disk", err);
+}
+
 /* register init/cleanup routines for stateful command groups */
 void ga_command_state_init(GAState *s, GACommandState *cs)
 {
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 4aa0f0d..c688476 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)
 {
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [Qemu-devel] [PATCH 2/7] qemu-ga: add guest-suspend-ram
  2012-03-12 20:16 [Qemu-devel] [PULL] qemu-ga patch queue Michael Roth
  2012-03-12 20:16 ` [Qemu-devel] [PATCH 1/7] qemu-ga: add guest-suspend-disk Michael Roth
@ 2012-03-12 20:16 ` Michael Roth
  2012-03-12 20:16 ` [Qemu-devel] [PATCH 3/7] qemu-ga: add guest-suspend-hybrid Michael Roth
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Michael Roth @ 2012-03-12 20:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: ghammer, mprivozn, aliguori, lcapitulino

From: Luiz Capitulino <lcapitulino@redhat.com>

Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 qapi-schema-guest.json |   28 ++++++++++++++++++++++++++++
 qga/commands-posix.c   |   10 ++++++++++
 qga/commands-win32.c   |    5 +++++
 3 files changed, 43 insertions(+), 0 deletions(-)

diff --git a/qapi-schema-guest.json b/qapi-schema-guest.json
index f4e0e1d..b102311 100644
--- a/qapi-schema-guest.json
+++ b/qapi-schema-guest.json
@@ -319,3 +319,31 @@
 # Since: 1.1
 ##
 { 'command': 'guest-suspend-disk' }
+
+##
+# @guest-suspend-ram
+#
+# Suspend guest to ram.
+#
+# This command tries to execute the scripts provided by the pm-utils package.
+# If it's not available, the suspend operation will be performed 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.
+#
+# IMPORTANT: guest-suspend-ram requires QEMU to support the 'system_wakeup'
+# 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
+#          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
+#          sending commands when the guest resumes
+#
+# Since: 1.1
+##
+{ 'command': 'guest-suspend-ram' }
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index af785f5..134c130 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -705,6 +705,16 @@ void qmp_guest_suspend_disk(Error **err)
     guest_suspend("pm-hibernate", "disk", err);
 }
 
+void qmp_guest_suspend_ram(Error **err)
+{
+    bios_supports_mode("pm-is-supported", "--suspend", "mem", err);
+    if (error_is_set(err)) {
+        return;
+    }
+
+    guest_suspend("pm-suspend", "mem", err);
+}
+
 /* register init/cleanup routines for stateful command groups */
 void ga_command_state_init(GAState *s, GACommandState *cs)
 {
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index c688476..b19a63c 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)
 {
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [Qemu-devel] [PATCH 3/7] qemu-ga: add guest-suspend-hybrid
  2012-03-12 20:16 [Qemu-devel] [PULL] qemu-ga patch queue Michael Roth
  2012-03-12 20:16 ` [Qemu-devel] [PATCH 1/7] qemu-ga: add guest-suspend-disk Michael Roth
  2012-03-12 20:16 ` [Qemu-devel] [PATCH 2/7] qemu-ga: add guest-suspend-ram Michael Roth
@ 2012-03-12 20:16 ` Michael Roth
  2012-03-12 20:16 ` [Qemu-devel] [PATCH 4/7] qemu-ga: add win32 guest-suspend-disk command Michael Roth
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Michael Roth @ 2012-03-12 20:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: ghammer, mprivozn, aliguori, lcapitulino

From: Luiz Capitulino <lcapitulino@redhat.com>

Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 qapi-schema-guest.json |   23 +++++++++++++++++++++++
 qga/commands-posix.c   |   10 ++++++++++
 qga/commands-win32.c   |    5 +++++
 3 files changed, 38 insertions(+), 0 deletions(-)

diff --git a/qapi-schema-guest.json b/qapi-schema-guest.json
index b102311..6a75552 100644
--- a/qapi-schema-guest.json
+++ b/qapi-schema-guest.json
@@ -347,3 +347,26 @@
 # Since: 1.1
 ##
 { 'command': 'guest-suspend-ram' }
+
+##
+# @guest-suspend-hybrid
+#
+# Save guest state to disk and suspend to ram.
+#
+# This command requires the pm-utils package to be installed in the guest.
+#
+# IMPORTANT: guest-suspend-hybrid requires QEMU to support the 'system_wakeup'
+# 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
+#          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
+#          sending commands when the guest resumes
+#
+# Since: 1.1
+##
+{ 'command': 'guest-suspend-hybrid' }
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 134c130..79571bf 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -715,6 +715,16 @@ void qmp_guest_suspend_ram(Error **err)
     guest_suspend("pm-suspend", "mem", err);
 }
 
+void qmp_guest_suspend_hybrid(Error **err)
+{
+    bios_supports_mode("pm-is-supported", "--suspend-hybrid", NULL, err);
+    if (error_is_set(err)) {
+        return;
+    }
+
+    guest_suspend("pm-suspend-hybrid", NULL, err);
+}
+
 /* register init/cleanup routines for stateful command groups */
 void ga_command_state_init(GAState *s, GACommandState *cs)
 {
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index b19a63c..7ef185f 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)
 {
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [Qemu-devel] [PATCH 4/7] qemu-ga: add win32 guest-suspend-disk command.
  2012-03-12 20:16 [Qemu-devel] [PULL] qemu-ga patch queue Michael Roth
                   ` (2 preceding siblings ...)
  2012-03-12 20:16 ` [Qemu-devel] [PATCH 3/7] qemu-ga: add guest-suspend-hybrid Michael Roth
@ 2012-03-12 20:16 ` Michael Roth
  2012-03-12 20:16 ` [Qemu-devel] [PATCH 5/7] qemu-ga: add win32 guest-suspend-ram command Michael Roth
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Michael Roth @ 2012-03-12 20:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: ghammer, mprivozn, aliguori, lcapitulino

From: Gal Hammer <ghammer@redhat.com>

Implement guest-suspend-disk RPC for Windows. Functionally this should be
equivalent to the posix implementation.

Signed-off-by: Gal Hammer <ghammer@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 configure            |    2 +-
 qga/commands-win32.c |  132 +++++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 121 insertions(+), 13 deletions(-)

diff --git a/configure b/configure
index 39d2b54..709e4b9 100755
--- a/configure
+++ b/configure
@@ -525,7 +525,7 @@ EOF
   bindir="\${prefix}"
   sysconfdir="\${prefix}"
   confsuffix=""
-  libs_qga="-lws2_32 -lwinmm $lib_qga"
+  libs_qga="-lws2_32 -lwinmm -lpowrprof $lib_qga"
 fi
 
 werror=""
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      <mdroth@linux.vnet.ibm.com>
+ *  Gal Hammer        <ghammer@redhat.com>
  *
  * 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 <glib.h>
+#include <wtypes.h>
+#include <powrprof.h>
 #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.4.1

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [Qemu-devel] [PATCH 5/7] qemu-ga: add win32 guest-suspend-ram command
  2012-03-12 20:16 [Qemu-devel] [PULL] qemu-ga patch queue Michael Roth
                   ` (3 preceding siblings ...)
  2012-03-12 20:16 ` [Qemu-devel] [PATCH 4/7] qemu-ga: add win32 guest-suspend-disk command Michael Roth
@ 2012-03-12 20:16 ` Michael Roth
  2012-03-12 20:16 ` [Qemu-devel] [PATCH 6/7] qemu-ga: add guest-network-get-interfaces command Michael Roth
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Michael Roth @ 2012-03-12 20:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: ghammer, mprivozn, aliguori, lcapitulino

S3 sleep implementation for windows.

Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 qga/commands-win32.c |   34 +++++++++++++++++++++++++---------
 1 files changed, 25 insertions(+), 9 deletions(-)

diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 062e519..b7600ed 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)
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [Qemu-devel] [PATCH 6/7] qemu-ga: add guest-network-get-interfaces command
  2012-03-12 20:16 [Qemu-devel] [PULL] qemu-ga patch queue Michael Roth
                   ` (4 preceding siblings ...)
  2012-03-12 20:16 ` [Qemu-devel] [PATCH 5/7] qemu-ga: add win32 guest-suspend-ram command Michael Roth
@ 2012-03-12 20:16 ` Michael Roth
  2012-03-12 20:16 ` [Qemu-devel] [PATCH 7/7] qemu-ga: add guest-sync-delimited Michael Roth
  2012-03-13  2:22 ` [Qemu-devel] [PULL] qemu-ga patch queue Anthony Liguori
  7 siblings, 0 replies; 9+ messages in thread
From: Michael Roth @ 2012-03-12 20:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: ghammer, mprivozn, aliguori, lcapitulino

From: Michal Privoznik <mprivozn@redhat.com>

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 <mprivozn@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 qapi-schema-guest.json |   59 ++++++++++++++++
 qga/commands-posix.c   |  181 ++++++++++++++++++++++++++++++++++++++++++++++++
 qga/commands-win32.c   |    6 ++
 3 files changed, 246 insertions(+), 0 deletions(-)

diff --git a/qapi-schema-guest.json b/qapi-schema-guest.json
index 6a75552..12b5d4f 100644
--- a/qapi-schema-guest.json
+++ b/qapi-schema-guest.json
@@ -370,3 +370,62 @@
 # Since: 1.1
 ##
 { 'command': 'guest-suspend-hybrid' }
+
+##
+# @GuestIpAddressType:
+#
+# An enumeration of supported IP address types
+#
+# @ipv4: IP version 4
+#
+# @ipv6: IP version 6
+#
+# Since: 1.1
+##
+{ 'enum': 'GuestIpAddressType',
+  'data': [ 'ipv4', 'ipv6' ] }
+
+##
+# @GuestIpAddress:
+#
+# @ip-address: IP address
+#
+# @ip-address-type: Type of @ip-address (e.g. ipv4, ipv6)
+#
+# @prefix: Network prefix length of @ip-address
+#
+# Since: 1.1
+##
+{ 'type': 'GuestIpAddress',
+  'data': {'ip-address': 'str',
+           'ip-address-type': 'GuestIpAddressType',
+           'prefix': 'int'} }
+
+##
+# @GuestNetworkInterface:
+#
+# @name: The name of interface for which info are being delivered
+#
+# @hardware-address: Hardware address of @name
+#
+# @ip-addresses: List of addresses assigned to @name
+#
+# Since: 1.1
+##
+{ 'type': 'GuestNetworkInterface',
+  'data': {'name': 'str',
+           '*hardware-address': 'str',
+           '*ip-addresses': ['GuestIpAddress'] } }
+
+##
+# @guest-network-get-interfaces:
+#
+# Get list of guest IP addresses, MAC addresses
+# and netmasks.
+#
+# Returns: List of GuestNetworkInfo on success.
+#
+# Since: 1.1
+##
+{ 'command': 'guest-network-get-interfaces',
+  'returns': ['GuestNetworkInterface'] }
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 79571bf..5b77fa9 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -5,6 +5,7 @@
  *
  * Authors:
  *  Michael Roth      <mdroth@linux.vnet.ibm.com>
+ *  Michal Privoznik  <mprivozn@redhat.com>
  *
  * This work is licensed under the terms of the GNU GPL, version 2 or later.
  * See the COPYING file in the top-level directory.
@@ -23,11 +24,16 @@
 
 #include <sys/types.h>
 #include <sys/ioctl.h>
+#include <ifaddrs.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <net/if.h>
 #include <sys/wait.h>
 #include "qga/guest-agent-core.h"
 #include "qga-qmp-commands.h"
 #include "qerror.h"
 #include "qemu-queue.h"
+#include "host-utils.h"
 
 static GAState *ga_state;
 
@@ -725,6 +731,181 @@ void qmp_guest_suspend_hybrid(Error **err)
     guest_suspend("pm-suspend-hybrid", NULL, err);
 }
 
+static GuestNetworkInterfaceList *
+guest_find_interface(GuestNetworkInterfaceList *head,
+                     const char *name)
+{
+    for (; head; head = head->next) {
+        if (strcmp(head->value->name, name) == 0) {
+            break;
+        }
+    }
+
+    return head;
+}
+
+/*
+ * Build information about guest interfaces
+ */
+GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
+{
+    GuestNetworkInterfaceList *head = NULL, *cur_item = NULL;
+    struct ifaddrs *ifap, *ifa;
+    char err_msg[512];
+
+    if (getifaddrs(&ifap) < 0) {
+        snprintf(err_msg, sizeof(err_msg),
+                 "getifaddrs failed: %s", strerror(errno));
+        error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
+        goto error;
+    }
+
+    for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+        GuestNetworkInterfaceList *info;
+        GuestIpAddressList **address_list = NULL, *address_item = NULL;
+        char addr4[INET_ADDRSTRLEN];
+        char addr6[INET6_ADDRSTRLEN];
+        int sock;
+        struct ifreq ifr;
+        unsigned char *mac_addr;
+        void *p;
+
+        g_debug("Processing %s interface", ifa->ifa_name);
+
+        info = guest_find_interface(head, ifa->ifa_name);
+
+        if (!info) {
+            info = g_malloc0(sizeof(*info));
+            info->value = g_malloc0(sizeof(*info->value));
+            info->value->name = g_strdup(ifa->ifa_name);
+
+            if (!cur_item) {
+                head = cur_item = info;
+            } else {
+                cur_item->next = info;
+                cur_item = info;
+            }
+        }
+
+        if (!info->value->has_hardware_address &&
+            ifa->ifa_flags & SIOCGIFHWADDR) {
+            /* we haven't obtained HW address yet */
+            sock = socket(PF_INET, SOCK_STREAM, 0);
+            if (sock == -1) {
+                snprintf(err_msg, sizeof(err_msg),
+                         "failed to create socket: %s", strerror(errno));
+                error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
+                goto error;
+            }
+
+            memset(&ifr, 0, sizeof(ifr));
+            strncpy(ifr.ifr_name,  info->value->name, IF_NAMESIZE);
+            if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) {
+                snprintf(err_msg, sizeof(err_msg),
+                         "failed to get MAC addres of %s: %s",
+                         ifa->ifa_name,
+                         strerror(errno));
+                error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
+                goto error;
+            }
+
+            mac_addr = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
+
+            if (asprintf(&info->value->hardware_address,
+                         "%02x:%02x:%02x:%02x:%02x:%02x",
+                         (int) mac_addr[0], (int) mac_addr[1],
+                         (int) mac_addr[2], (int) mac_addr[3],
+                         (int) mac_addr[4], (int) mac_addr[5]) == -1) {
+                snprintf(err_msg, sizeof(err_msg),
+                         "failed to format MAC: %s", strerror(errno));
+                error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
+                goto error;
+            }
+
+            info->value->has_hardware_address = true;
+            close(sock);
+        }
+
+        if (ifa->ifa_addr &&
+            ifa->ifa_addr->sa_family == AF_INET) {
+            /* interface with IPv4 address */
+            address_item = g_malloc0(sizeof(*address_item));
+            address_item->value = g_malloc0(sizeof(*address_item->value));
+            p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
+            if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) {
+                snprintf(err_msg, sizeof(err_msg),
+                         "inet_ntop failed : %s", strerror(errno));
+                error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
+                goto error;
+            }
+
+            address_item->value->ip_address = g_strdup(addr4);
+            address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4;
+
+            if (ifa->ifa_netmask) {
+                /* Count the number of set bits in netmask.
+                 * This is safe as '1' and '0' cannot be shuffled in netmask. */
+                p = &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr;
+                address_item->value->prefix = ctpop32(((uint32_t *) p)[0]);
+            }
+        } else if (ifa->ifa_addr &&
+                   ifa->ifa_addr->sa_family == AF_INET6) {
+            /* interface with IPv6 address */
+            address_item = g_malloc0(sizeof(*address_item));
+            address_item->value = g_malloc0(sizeof(*address_item->value));
+            p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
+            if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) {
+                snprintf(err_msg, sizeof(err_msg),
+                         "inet_ntop failed : %s", strerror(errno));
+                error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
+                goto error;
+            }
+
+            address_item->value->ip_address = g_strdup(addr6);
+            address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6;
+
+            if (ifa->ifa_netmask) {
+                /* Count the number of set bits in netmask.
+                 * This is safe as '1' and '0' cannot be shuffled in netmask. */
+                p = &((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
+                address_item->value->prefix =
+                    ctpop32(((uint32_t *) p)[0]) +
+                    ctpop32(((uint32_t *) p)[1]) +
+                    ctpop32(((uint32_t *) p)[2]) +
+                    ctpop32(((uint32_t *) p)[3]);
+            }
+        }
+
+        if (!address_item) {
+            continue;
+        }
+
+        address_list = &info->value->ip_addresses;
+
+        while (*address_list && (*address_list)->next) {
+            address_list = &(*address_list)->next;
+        }
+
+        if (!*address_list) {
+            *address_list = address_item;
+        } else {
+            (*address_list)->next = address_item;
+        }
+
+        info->value->has_ip_addresses = true;
+
+
+    }
+
+    freeifaddrs(ifap);
+    return head;
+
+error:
+    freeifaddrs(ifap);
+    qapi_free_GuestNetworkInterfaceList(head);
+    return NULL;
+}
+
 /* register init/cleanup routines for stateful command groups */
 void ga_command_state_init(GAState *s, GACommandState *cs)
 {
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index b7600ed..eb8d140 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)
 {
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [Qemu-devel] [PATCH 7/7] qemu-ga: add guest-sync-delimited
  2012-03-12 20:16 [Qemu-devel] [PULL] qemu-ga patch queue Michael Roth
                   ` (5 preceding siblings ...)
  2012-03-12 20:16 ` [Qemu-devel] [PATCH 6/7] qemu-ga: add guest-network-get-interfaces command Michael Roth
@ 2012-03-12 20:16 ` Michael Roth
  2012-03-13  2:22 ` [Qemu-devel] [PULL] qemu-ga patch queue Anthony Liguori
  7 siblings, 0 replies; 9+ messages in thread
From: Michael Roth @ 2012-03-12 20:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: ghammer, mprivozn, aliguori, lcapitulino

guest-sync leaves it as an exercise to the user as to how to reliably
obtain the response to guest-sync if the client had previously read in a
partial response (due qemu-ga previously being restarted mid-"sentence"
due to reboot, forced restart, etc).

qemu-ga handles this situation on its end by having a client precede
their guest-sync request with a 0xFF byte (invalid UTF-8), which
qemu-ga/QEMU JSON parsers will treat as a flush event. Thus we can
reliably flush the qemu-ga parser state in preparation for receiving
the guest-sync request.

guest-sync-delimited provides the same functionality for a client: when
a guest-sync-delimited is issued, qemu-ga will precede it's response
with a 0xFF byte that the client can use as an indicator to flush its
buffer/parser state in preparation for reliably receiving the
guest-sync-delimited response.

It is also useful as an optimization for clients, since, after issuing a
guest-sync-delimited, clients can safely discard all stale data read
from the channel until the 0xFF is found.

More information available on the wiki:

http://wiki.qemu.org/Features/QAPI/GuestAgent#QEMU_Guest_Agent_Protocol

Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 qapi-schema-guest.json |   48 +++++++++++++++++++++++++++++++++++++++++++++++-
 qemu-ga.c              |   27 ++++++++++++++++++++++-----
 qga/commands-posix.c   |    3 ---
 qga/commands.c         |    6 ++++++
 qga/guest-agent-core.h |    2 ++
 5 files changed, 77 insertions(+), 9 deletions(-)

diff --git a/qapi-schema-guest.json b/qapi-schema-guest.json
index 12b5d4f..cf18876 100644
--- a/qapi-schema-guest.json
+++ b/qapi-schema-guest.json
@@ -1,6 +1,41 @@
 # *-*- Mode: Python -*-*
 
 ##
+#
+# Echo back a unique integer value, and prepend to response a
+# leading sentinel byte (0xFF) the client can check scan for.
+#
+# This is used by clients talking to the guest agent over the
+# wire to ensure the stream is in sync and doesn't contain stale
+# data from previous client. It must be issued upon initial
+# connection, and after any client-side timeouts (including
+# timeouts on receiving a response to this command).
+#
+# After issuing this request, all guest agent responses should be
+# ignored until the response containing the unique integer value
+# the client passed in is returned. Receival of the 0xFF sentinel
+# byte must be handled as an indication that the client's
+# lexer/tokenizer/parser state should be flushed/reset in
+# preparation for reliably receiving the subsequent response. As
+# an optimization, clients may opt to ignore all data until a
+# sentinel value is receiving to avoid unecessary processing of
+# stale data.
+#
+# Similarly, clients should also precede this *request*
+# with a 0xFF byte to make sure the guest agent flushes any
+# partially read JSON data from a previous client connection.
+#
+# @id: randomly generated 64-bit integer
+#
+# Returns: The unique integer id passed in by the client
+#
+# Since: 1.1
+# ##
+{ 'command': 'guest-sync-delimited'
+  'data':    { 'id': 'int' },
+  'returns': 'int' }
+
+##
 # @guest-sync:
 #
 # Echo back a unique integer value
@@ -13,8 +48,19 @@
 # partially-delivered JSON text in such a way that this response
 # can be obtained.
 #
+# In cases where a partial stale response was previously
+# received by the client, this cannot always be done reliably.
+# One particular scenario being if qemu-ga responses are fed
+# character-by-character into a JSON parser. In these situations,
+# using guest-sync-delimited may be optimal.
+#
+# For clients that fetch responses line by line and convert them
+# to JSON objects, guest-sync should be sufficient, but note that
+# in cases where the channel is dirty some attempts at parsing the
+# response may result in a parser error.
+#
 # Such clients should also precede this command
-# with a 0xFF byte to make such the guest agent flushes any
+# with a 0xFF byte to make sure the guest agent flushes any
 # partially read JSON data from a previous session.
 #
 # @id: randomly generated 64-bit integer
diff --git a/qemu-ga.c b/qemu-ga.c
index 1c90e6e..d6f786e 100644
--- a/qemu-ga.c
+++ b/qemu-ga.c
@@ -41,6 +41,7 @@
 #define QGA_VIRTIO_PATH_DEFAULT "\\\\.\\Global\\org.qemu.guest_agent.0"
 #endif
 #define QGA_PIDFILE_DEFAULT "/var/run/qemu-ga.pid"
+#define QGA_SENTINEL_BYTE 0xFF
 
 struct GAState {
     JSONMessageParser parser;
@@ -54,9 +55,10 @@ struct GAState {
 #ifdef _WIN32
     GAService service;
 #endif
+    bool delimit_response;
 };
 
-static struct GAState *ga_state;
+struct GAState *ga_state;
 
 #ifdef _WIN32
 DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data,
@@ -198,6 +200,11 @@ static void ga_log(const gchar *domain, GLogLevelFlags level,
     }
 }
 
+void ga_set_response_delimited(GAState *s)
+{
+    s->delimit_response = true;
+}
+
 #ifndef _WIN32
 static void become_daemon(const char *pidfile)
 {
@@ -254,7 +261,7 @@ fail:
 static int send_response(GAState *s, QObject *payload)
 {
     const char *buf;
-    QString *payload_qstr;
+    QString *payload_qstr, *response_qstr;
     GIOStatus status;
 
     g_assert(payload && s->channel);
@@ -264,10 +271,20 @@ static int send_response(GAState *s, QObject *payload)
         return -EINVAL;
     }
 
-    qstring_append_chr(payload_qstr, '\n');
-    buf = qstring_get_str(payload_qstr);
+    if (s->delimit_response) {
+        s->delimit_response = false;
+        response_qstr = qstring_new();
+        qstring_append_chr(response_qstr, QGA_SENTINEL_BYTE);
+        qstring_append(response_qstr, qstring_get_str(payload_qstr));
+        QDECREF(payload_qstr);
+    } else {
+        response_qstr = payload_qstr;
+    }
+
+    qstring_append_chr(response_qstr, '\n');
+    buf = qstring_get_str(response_qstr);
     status = ga_channel_write_all(s->channel, buf, strlen(buf));
-    QDECREF(payload_qstr);
+    QDECREF(response_qstr);
     if (status != G_IO_STATUS_NORMAL) {
         return -EIO;
     }
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 5b77fa9..7b2be2f 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -35,8 +35,6 @@
 #include "qemu-queue.h"
 #include "host-utils.h"
 
-static GAState *ga_state;
-
 static void reopen_fd_to_null(int fd)
 {
     int nullfd;
@@ -909,7 +907,6 @@ error:
 /* register init/cleanup routines for stateful command groups */
 void ga_command_state_init(GAState *s, GACommandState *cs)
 {
-    ga_state = s;
 #if defined(CONFIG_FSFREEZE)
     ga_command_state_add(cs, guest_fsfreeze_init, guest_fsfreeze_cleanup);
 #endif
diff --git a/qga/commands.c b/qga/commands.c
index b27407d..5bcceaa 100644
--- a/qga/commands.c
+++ b/qga/commands.c
@@ -29,6 +29,12 @@ void slog(const gchar *fmt, ...)
     va_end(ap);
 }
 
+int64_t qmp_guest_sync_delimited(int64_t id, Error **errp)
+{
+    ga_set_response_delimited(ga_state);
+    return id;
+}
+
 int64_t qmp_guest_sync(int64_t id, Error **errp)
 {
     return id;
diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
index b5dfa5b..304525d 100644
--- a/qga/guest-agent-core.h
+++ b/qga/guest-agent-core.h
@@ -18,6 +18,7 @@
 
 typedef struct GAState GAState;
 typedef struct GACommandState GACommandState;
+extern GAState *ga_state;
 
 void ga_command_state_init(GAState *s, GACommandState *cs);
 void ga_command_state_add(GACommandState *cs,
@@ -30,3 +31,4 @@ bool ga_logging_enabled(GAState *s);
 void ga_disable_logging(GAState *s);
 void ga_enable_logging(GAState *s);
 void slog(const gchar *fmt, ...);
+void ga_set_response_delimited(GAState *s);
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [Qemu-devel] [PULL] qemu-ga patch queue
  2012-03-12 20:16 [Qemu-devel] [PULL] qemu-ga patch queue Michael Roth
                   ` (6 preceding siblings ...)
  2012-03-12 20:16 ` [Qemu-devel] [PATCH 7/7] qemu-ga: add guest-sync-delimited Michael Roth
@ 2012-03-13  2:22 ` Anthony Liguori
  7 siblings, 0 replies; 9+ messages in thread
From: Anthony Liguori @ 2012-03-13  2:22 UTC (permalink / raw)
  To: Michael Roth; +Cc: ghammer, mprivozn, qemu-devel, lcapitulino

On 03/12/2012 03:16 PM, Michael Roth wrote:
> The following changes since commit a348f108842fb928563865c9918642900cd0d477:
>
>    Add missing const attributes for MemoryRegionOps (2012-03-11 11:40:15 +0000)
>
> are available in the git repository at:
>    git://github.com/mdroth/qemu.git qga-pull-3-12-2012

Pulled.  Thanks.

Regards,

Anthony Liguori

>
> Gal Hammer (1):
>        qemu-ga: add win32 guest-suspend-disk command.
>
> Luiz Capitulino (3):
>        qemu-ga: add guest-suspend-disk
>        qemu-ga: add guest-suspend-ram
>        qemu-ga: add guest-suspend-hybrid
>
> Michael Roth (2):
>        qemu-ga: add win32 guest-suspend-ram command
>        qemu-ga: add guest-sync-delimited
>
> Michal Privoznik (1):
>        qemu-ga: add guest-network-get-interfaces command
>
>   configure              |    2 +-
>   qapi-schema-guest.json |  182 ++++++++++++++++++++++-
>   qemu-ga.c              |   46 +++++-
>   qga/commands-posix.c   |  390 +++++++++++++++++++++++++++++++++++++++++++++++-
>   qga/commands-win32.c   |  167 +++++++++++++++++++--
>   qga/commands.c         |    6 +
>   qga/guest-agent-core.h |    2 +
>   7 files changed, 774 insertions(+), 21 deletions(-)
>
>
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2012-03-13  2:22 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-03-12 20:16 [Qemu-devel] [PULL] qemu-ga patch queue Michael Roth
2012-03-12 20:16 ` [Qemu-devel] [PATCH 1/7] qemu-ga: add guest-suspend-disk Michael Roth
2012-03-12 20:16 ` [Qemu-devel] [PATCH 2/7] qemu-ga: add guest-suspend-ram Michael Roth
2012-03-12 20:16 ` [Qemu-devel] [PATCH 3/7] qemu-ga: add guest-suspend-hybrid Michael Roth
2012-03-12 20:16 ` [Qemu-devel] [PATCH 4/7] qemu-ga: add win32 guest-suspend-disk command Michael Roth
2012-03-12 20:16 ` [Qemu-devel] [PATCH 5/7] qemu-ga: add win32 guest-suspend-ram command Michael Roth
2012-03-12 20:16 ` [Qemu-devel] [PATCH 6/7] qemu-ga: add guest-network-get-interfaces command Michael Roth
2012-03-12 20:16 ` [Qemu-devel] [PATCH 7/7] qemu-ga: add guest-sync-delimited Michael Roth
2012-03-13  2:22 ` [Qemu-devel] [PULL] qemu-ga patch queue Anthony Liguori

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).